From b7deeb4f3963c4f20c31653c6dd687681f888b03 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 | 45 ++++++++++++++++++- pkg/eosclient/eosgrpc/eoshttp.go | 14 +++++- 3 files changed, 62 insertions(+), 3 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..4a417dc79fa 100644 --- a/pkg/eosclient/eosgrpc/eosgrpc.go +++ b/pkg/eosclient/eosgrpc/eosgrpc.go @@ -1603,7 +1603,50 @@ 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 + // TODO(jgeens): right now hardcoded to 3600, but in binary client this was set to c.opt.TokenExpiry + msg.Token.Token.Expires = uint64(time.Now().Add(time.Duration(3600) * 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..76ad2dafc99 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) {