From ecef02d000153084fcc5c97f50361ceadc58358d Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Fri, 18 Sep 2020 10:28:23 +0200 Subject: [PATCH] Functionality to map home directory to different storage providers (#1142) * Functionality to map home directory to different storage providers * Default to /home rule if no match is found * Replace layouts only for home paths * Don't log client secrets --- changelog/unreleased/home-sp-mapping.md | 7 ++++ internal/grpc/interceptors/auth/auth.go | 25 +++++++++-- .../grpc/services/gateway/authprovider.go | 5 ++- internal/grpc/services/gateway/gateway.go | 1 + .../grpc/services/gateway/storageprovider.go | 41 +++++++++++++++---- .../services/gateway/usershareprovider.go | 7 +--- internal/http/interceptors/auth/auth.go | 5 +-- 7 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 changelog/unreleased/home-sp-mapping.md diff --git a/changelog/unreleased/home-sp-mapping.md b/changelog/unreleased/home-sp-mapping.md new file mode 100644 index 0000000000..3f3120889b --- /dev/null +++ b/changelog/unreleased/home-sp-mapping.md @@ -0,0 +1,7 @@ +Enhancement: Functionality to map home directory to different storage providers + +We hardcode the home path for all users to /home. This forbids redirecting +requests for different users to multiple storage providers. This PR provides the +option to map the home directories of different users using user attributes. + +https://github.com/cs3org/reva/pull/1142 diff --git a/internal/grpc/interceptors/auth/auth.go b/internal/grpc/interceptors/auth/auth.go index 79cefebc6d..8a8814f1a5 100644 --- a/internal/grpc/interceptors/auth/auth.go +++ b/internal/grpc/interceptors/auth/auth.go @@ -83,6 +83,16 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI if utils.Skip(info.FullMethod, unprotected) { span.AddAttributes(trace.BoolAttribute("auth_enabled", false)) log.Debug().Str("method", info.FullMethod).Msg("skipping auth") + + // If a token is present, set it anyway, as we might need the user info + // to decide the storage provider. + tkn, ok := token.ContextGetToken(ctx) + if ok { + u, err := tokenManager.DismantleToken(ctx, tkn) + if err == nil { + ctx = user.ContextSetUser(ctx, u) + } + } return handler(ctx, req) } @@ -111,7 +121,6 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI span.AddAttributes(trace.StringAttribute("user", u.String()), trace.StringAttribute("token", tkn)) ctx = user.ContextSetUser(ctx, u) - ctx = token.ContextSetToken(ctx, tkn) return handler(ctx, req) } return interceptor, nil @@ -145,6 +154,18 @@ func NewStream(m map[string]interface{}, unprotected []string) (grpc.StreamServe if utils.Skip(info.FullMethod, unprotected) { log.Debug().Str("method", info.FullMethod).Msg("skipping auth") + + // If a token is present, set it anyway, as we might need the user info + // to decide the storage provider. + tkn, ok := token.ContextGetToken(ctx) + if ok { + u, err := tokenManager.DismantleToken(ctx, tkn) + if err == nil { + ctx = user.ContextSetUser(ctx, u) + ss = newWrappedServerStream(ctx, ss) + } + } + return handler(srv, ss) } @@ -170,8 +191,6 @@ func NewStream(m map[string]interface{}, unprotected []string) (grpc.StreamServe // store user and core access token in context. ctx = user.ContextSetUser(ctx, u) - ctx = token.ContextSetToken(ctx, tkn) - wrapped := newWrappedServerStream(ctx, ss) return handler(srv, wrapped) } diff --git a/internal/grpc/services/gateway/authprovider.go b/internal/grpc/services/gateway/authprovider.go index 5396658ce2..15a3113239 100644 --- a/internal/grpc/services/gateway/authprovider.go +++ b/internal/grpc/services/gateway/authprovider.go @@ -31,6 +31,7 @@ import ( "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" tokenpkg "github.com/cs3org/reva/pkg/token" + userpkg "github.com/cs3org/reva/pkg/user" "github.com/pkg/errors" "google.golang.org/grpc/metadata" ) @@ -108,11 +109,11 @@ func (s *svc) Authenticate(ctx context.Context, req *gateway.AuthenticateRequest // we need to pass the token to authenticate the CreateHome request. // TODO(labkode): appending to existing context will not pass the token. ctx = tokenpkg.ContextSetToken(ctx, token) + ctx = userpkg.ContextSetUser(ctx, user) ctx = metadata.AppendToOutgoingContext(ctx, tokenpkg.TokenHeader, token) // TODO(jfd): hardcoded metadata key. use PerRPCCredentials? // create home directory - createHomeReq := &storageprovider.CreateHomeRequest{} - createHomeRes, err := s.CreateHome(ctx, createHomeReq) + createHomeRes, err := s.CreateHome(ctx, &storageprovider.CreateHomeRequest{}) if err != nil { log.Err(err).Msg("error calling CreateHome") return &gateway.AuthenticateResponse{ diff --git a/internal/grpc/services/gateway/gateway.go b/internal/grpc/services/gateway/gateway.go index 2a4384bbce..6d36159c27 100644 --- a/internal/grpc/services/gateway/gateway.go +++ b/internal/grpc/services/gateway/gateway.go @@ -59,6 +59,7 @@ type config struct { TokenManager string `mapstructure:"token_manager"` // ShareFolder is the location where to create shares in the recipient's storage provider. ShareFolder string `mapstructure:"share_folder"` + HomeMapping string `mapstructure:"home_mapping"` TokenManagers map[string]map[string]interface{} `mapstructure:"token_managers"` } diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index 7a15d989dd..f044579058 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -34,6 +34,8 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/storage/utils/templates" + "github.com/cs3org/reva/pkg/user" "github.com/dgrijalva/jwt-go" "github.com/pkg/errors" ) @@ -1795,32 +1797,55 @@ func (s *svc) getStorageProviderClient(_ context.Context, p *registry.ProviderIn } func (s *svc) findProvider(ctx context.Context, ref *provider.Reference) (*registry.ProviderInfo, error) { - c, err := pool.GetStorageRegistryClient(s.c.StorageRegistryEndpoint) + home, err := s.GetHome(ctx, &provider.GetHomeRequest{}) if err != nil { - err = errors.Wrap(err, "gateway: error getting storage registry client") return nil, err } + if strings.HasPrefix(ref.GetPath(), home.Path) && s.c.HomeMapping != "" { + if u, ok := user.ContextGetUser(ctx); ok { + layout := templates.WithUser(u, s.c.HomeMapping) + newRef := &provider.Reference{ + Spec: &provider.Reference_Path{ + Path: path.Join(layout, strings.TrimPrefix(ref.GetPath(), home.Path)), + }, + } + res, err := s.getStorageProvider(ctx, newRef) + if err != nil { + // if we get a NotFound error, default to the original reference + if _, ok := err.(errtypes.IsNotFound); !ok { + return nil, err + } + } else { + return res, nil + } + } + } + return s.getStorageProvider(ctx, ref) +} + +func (s *svc) getStorageProvider(ctx context.Context, ref *provider.Reference) (*registry.ProviderInfo, error) { + c, err := pool.GetStorageRegistryClient(s.c.StorageRegistryEndpoint) + if err != nil { + return nil, errors.Wrap(err, "gateway: error getting storage registry client") + } res, err := c.GetStorageProvider(ctx, ®istry.GetStorageProviderRequest{ Ref: ref, }) if err != nil { - err = errors.Wrap(err, "gateway: error calling GetStorageProvider") - return nil, err + return nil, errors.Wrap(err, "gateway: error calling GetStorageProvider") } if res.Status.Code != rpc.Code_CODE_OK { if res.Status.Code == rpc.Code_CODE_NOT_FOUND { return nil, errtypes.NotFound("gateway: storage provider not found for reference:" + ref.String()) } - err := status.NewErrorFromCode(res.Status.Code, "gateway") - return nil, err + return nil, status.NewErrorFromCode(res.Status.Code, "gateway") } if res.Provider == nil { - err := errors.New("gateway: provider is nil") - return nil, err + return nil, errors.New("gateway: provider is nil") } return res.Provider, nil diff --git a/internal/grpc/services/gateway/usershareprovider.go b/internal/grpc/services/gateway/usershareprovider.go index 30086aedd2..29ca23f1c0 100644 --- a/internal/grpc/services/gateway/usershareprovider.go +++ b/internal/grpc/services/gateway/usershareprovider.go @@ -381,10 +381,7 @@ func (s *svc) createReference(ctx context.Context, resourceID *provider.Resource return status.NewInternal(ctx, err, "error updating received share"), nil } - fileInfo := statRes.Info - - homeReq := &provider.GetHomeRequest{} - homeRes, err := s.GetHome(ctx, homeReq) + homeRes, err := s.GetHome(ctx, &provider.GetHomeRequest{}) if err != nil { err := errors.Wrap(err, "gateway: error calling GetHome") return status.NewInternal(ctx, err, "error updating received share"), nil @@ -400,7 +397,7 @@ func (s *svc) createReference(ctx context.Context, resourceID *provider.Resource // It is the responsibility of the gateway to resolve these references and merge the response back // from the main request. // TODO(labkode): the name of the share should be the filename it points to by default. - refPath := path.Join(homeRes.Path, s.c.ShareFolder, path.Base(fileInfo.Path)) + refPath := path.Join(homeRes.Path, s.c.ShareFolder, path.Base(statRes.Info.Path)) log.Info().Msg("mount path will be:" + refPath) createRefReq := &provider.CreateReferenceRequest{ diff --git a/internal/http/interceptors/auth/auth.go b/internal/http/interceptors/auth/auth.go index 7b6fbd822a..8aa21d405b 100644 --- a/internal/http/interceptors/auth/auth.go +++ b/internal/http/interceptors/auth/auth.go @@ -169,8 +169,7 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err log.Debug().Err(err).Msg("error retrieving credentials") } if creds != nil { - log.Debug().Msgf("credentials obtained from credential strategy: %+v", creds) - + log.Debug().Msgf("credentials obtained from credential strategy: type: %s, client_id: %s", creds.Type, creds.ClientID) break } } @@ -191,7 +190,7 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err ClientSecret: creds.ClientSecret, } - log.Debug().Msgf("AuthenticateRequest: %+v against %s", req, conf.GatewaySvc) + log.Debug().Msgf("AuthenticateRequest: type: %s, client_id: %s against %s", req.Type, req.ClientId, conf.GatewaySvc) client, err := pool.GetGatewayServiceClient(conf.GatewaySvc) if err != nil {