Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable access to EOS via tokens over gRPC (e.g. for guest accounts) #4934

Merged
merged 1 commit into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/unreleased/eos-grpc-token-access.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: access to EOS via tokens over gRPC

As a guest account, accessing a file shared with you relies on a token that is generated on behalf of the resource owner. This method, GenerateToken, has now been implemented in the EOS gRPC client. Additionally, the HTTP client now takes tokens into account.


https://github.com/cs3org/reva/pull/4934
48 changes: 47 additions & 1 deletion pkg/eosclient/eosgrpc/eosgrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ type Options struct {
// SecProtocol is the comma separated list of security protocols used by xrootd.
// For example: "sss, unix"
SecProtocol string

// TokenExpiry stores in seconds the time after which generated tokens will expire
// Default is 3600
TokenExpiry int
}

func getUser(ctx context.Context) (*userpb.User, error) {
Expand Down Expand Up @@ -1603,7 +1607,49 @@ func (c *Client) ReadVersion(ctx context.Context, auth eosclient.Authorization,

// GenerateToken returns a token on behalf of the resource owner to be used by lightweight accounts.
func (c *Client) GenerateToken(ctx context.Context, auth eosclient.Authorization, path string, a *acl.Entry) (string, error) {
return "", errtypes.NotSupported("TODO")
log := appctx.GetLogger(ctx)
log.Info().Str("func", "GenerateToken").Str("uid,gid", auth.Role.UID+","+auth.Role.GID).Str("path", path).Msg("")

// Initialize the common fields of the NSReq
rq, err := c.initNSRequest(ctx, auth, "")
if err != nil {
log.Error().Str("func", "GenerateToken").Str("err", err.Error()).Msg("Error on initNSRequest")
return "", err
}

msg := new(erpc.NSRequest_TokenRequest)
msg.Token = &erpc.ShareToken{}
msg.Token.Token = &erpc.ShareProto{}
msg.Token.Token.Permission = a.Permissions
msg.Token.Token.Expires = uint64(time.Now().Add(time.Duration(c.opt.TokenExpiry) * time.Second).Unix())
msg.Token.Token.Allowtree = true
msg.Token.Token.Path = path

rq.Command = &erpc.NSRequest_Token{
Token: msg,
}

// Now send the req and see what happens
resp, err := c.cl.Exec(appctx.ContextGetClean(ctx), rq)
e := c.getRespError(resp, err)
if e != nil {
log.Error().Str("func", "GenerateToken").Str("err", e.Error()).Msg("")
return "", e
}

if resp == nil {
log.Error().Str("func", "GenerateToken").Msg("nil grpc response")
return "", errtypes.InternalError(fmt.Sprintf("nil response for uid: '%s' ", auth.Role.UID))
}

// For some reason, the token is embedded in the error, with error code 0
jessegeens marked this conversation as resolved.
Show resolved Hide resolved
if resp.GetError() != nil {
if resp.GetError().Code == 0 {
return resp.GetError().Msg, nil
}
}
log.Error().Str("func", "GenerateToken").Msg("GenerateToken over gRPC expected an error but did not receive one")
return "", err
}

func (c *Client) getVersionFolderInode(ctx context.Context, auth eosclient.Authorization, p string) (uint64, error) {
Expand Down
24 changes: 21 additions & 3 deletions pkg/eosclient/eosgrpc/eoshttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ func (c *EOSHTTPClient) buildFullURL(urlpath string, auth eosclient.Authorizatio
fullurl += "?"
}

if auth.Role.UID != "" {
if auth.Token != "" {
fullurl += "authz=" + auth.Token
} else if auth.Role.UID != "" {
fullurl += fmt.Sprintf("eos.ruid=%s&eos.rgid=%s", auth.Role.UID, auth.Role.GID)
}

Expand Down Expand Up @@ -291,7 +293,15 @@ func (c *EOSHTTPClient) GETFile(ctx context.Context, remoteuser string, auth eos
// Execute the request. I don't like that there is no explicit timeout or buffer control on the input stream
log.Debug().Str("func", "GETFile").Str("finalurl", finalurl).Msg("sending req")

resp, err := c.doReq(req, remoteuser)
// c.doReq sets headers such as remoteuser and x-gateway-authorization
// we don't want those when using a token (i.e. ?authz=), so in this case
// we skip this and call the HTTP client directly
var resp *http.Response
if auth.Token != "" {
resp, err = c.cl.Do(req)
} else {
resp, err = c.doReq(req, remoteuser)
}

// Let's support redirections... and if we retry we have to retry at the same FST, avoid going back to the MGM
if resp != nil && (resp.StatusCode == http.StatusFound || resp.StatusCode == http.StatusTemporaryRedirect) {
Expand Down Expand Up @@ -390,7 +400,15 @@ func (c *EOSHTTPClient) PUTFile(ctx context.Context, remoteuser string, auth eos
// Execute the request. I don't like that there is no explicit timeout or buffer control on the input stream
log.Debug().Str("func", "PUTFile").Msg("sending req")

resp, err := c.doReq(req, remoteuser)
// c.doReq sets headers such as remoteuser and x-gateway-authorization
// we don't want those when using a token (i.e. ?authz=), so in this case
// we skip this and call the HTTP client directly
var resp *http.Response
jessegeens marked this conversation as resolved.
Show resolved Hide resolved
if auth.Token != "" {
resp, err = c.cl.Do(req)
} else {
resp, err = c.doReq(req, remoteuser)
}

// Let's support redirections... and if we retry we retry at the same FST
if resp != nil && resp.StatusCode == 307 {
Expand Down
1 change: 1 addition & 0 deletions pkg/storage/utils/eosfs/eosfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ func NewEOSFS(ctx context.Context, c *Config) (storage.FS, error) {
VersionInvariant: c.VersionInvariant,
ReadUsesLocalTemp: c.ReadUsesLocalTemp,
WriteUsesLocalTemp: c.WriteUsesLocalTemp,
TokenExpiry: c.TokenExpiry,
}
eosHTTPOpts := &eosgrpc.HTTPOptions{
BaseURL: c.MasterURL,
Expand Down
Loading