From bf72c710661f97d53da9a8b454ab3a25fa772504 Mon Sep 17 00:00:00 2001 From: Jesse Geens Date: Wed, 13 Nov 2024 16:03:59 +0100 Subject: [PATCH] Fixed access to files via token, i.e. for guest accounts --- changelog/unreleased/eos-grpc-token-access.md | 6 +++ pkg/eosclient/eosgrpc/eosgrpc.go | 48 ++++++++++++++++++- pkg/eosclient/eosgrpc/eoshttp.go | 21 ++++++-- pkg/storage/utils/eosfs/eosfs.go | 1 + 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 changelog/unreleased/eos-grpc-token-access.md diff --git a/changelog/unreleased/eos-grpc-token-access.md b/changelog/unreleased/eos-grpc-token-access.md new file mode 100644 index 00000000000..7842c4b83cc --- /dev/null +++ b/changelog/unreleased/eos-grpc-token-access.md @@ -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 \ No newline at end of file diff --git a/pkg/eosclient/eosgrpc/eosgrpc.go b/pkg/eosclient/eosgrpc/eosgrpc.go index 22f46e590cc..70c5f9fd80f 100644 --- a/pkg/eosclient/eosgrpc/eosgrpc.go +++ b/pkg/eosclient/eosgrpc/eosgrpc.go @@ -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) { @@ -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 + 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) { diff --git a/pkg/eosclient/eosgrpc/eoshttp.go b/pkg/eosclient/eosgrpc/eoshttp.go index e447afea135..b7ac954bda4 100644 --- a/pkg/eosclient/eosgrpc/eoshttp.go +++ b/pkg/eosclient/eosgrpc/eoshttp.go @@ -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) } @@ -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) { @@ -390,7 +400,12 @@ 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) + 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 retry at the same FST if resp != nil && resp.StatusCode == 307 { diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index b4860c5927d..33ff26a4b0d 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -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,