diff --git a/internal/grpc/interceptors/auth/auth.go b/internal/grpc/interceptors/auth/auth.go index a611023c56d..f7cc18e0e88 100644 --- a/internal/grpc/interceptors/auth/auth.go +++ b/internal/grpc/interceptors/auth/auth.go @@ -106,6 +106,8 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI return handler(ctx, req) } + log.Info().Msgf("GRPC unary interceptor %s, %+v", info.FullMethod, req) + span.AddAttributes(trace.BoolAttribute("auth_enabled", true)) tkn, ok := token.ContextGetToken(ctx) diff --git a/pkg/auth/manager/demo/demo.go b/pkg/auth/manager/demo/demo.go index 1ae50dbdc03..1e855f66c10 100644 --- a/pkg/auth/manager/demo/demo.go +++ b/pkg/auth/manager/demo/demo.go @@ -51,7 +51,7 @@ func New(m map[string]interface{}) (auth.Manager, error) { } func (m *manager) Authenticate(ctx context.Context, clientID, clientSecret string) (*user.User, map[string]*authpb.Scope, error) { - scope, err := scope.GetOwnerScope() + scope, err := scope.AddOwnerScope(nil) if err != nil { return nil, nil, err } diff --git a/pkg/auth/manager/impersonator/impersonator.go b/pkg/auth/manager/impersonator/impersonator.go index 26e73009305..0f9fd612baa 100644 --- a/pkg/auth/manager/impersonator/impersonator.go +++ b/pkg/auth/manager/impersonator/impersonator.go @@ -51,7 +51,7 @@ func (m *mgr) Authenticate(ctx context.Context, clientID, clientSecret string) ( uid.Idp = clientID[at+1:] } - scope, err := scope.GetOwnerScope() + scope, err := scope.AddOwnerScope(nil) if err != nil { return nil, nil, err } diff --git a/pkg/auth/manager/json/json.go b/pkg/auth/manager/json/json.go index 6d8607ca98a..a8ce9ae7498 100644 --- a/pkg/auth/manager/json/json.go +++ b/pkg/auth/manager/json/json.go @@ -104,7 +104,7 @@ func New(m map[string]interface{}) (auth.Manager, error) { } func (m *manager) Authenticate(ctx context.Context, username string, secret string) (*user.User, map[string]*authpb.Scope, error) { - scope, err := scope.GetOwnerScope() + scope, err := scope.AddOwnerScope(nil) if err != nil { return nil, nil, err } diff --git a/pkg/auth/manager/ldap/ldap.go b/pkg/auth/manager/ldap/ldap.go index 234d47e279f..942affe728b 100644 --- a/pkg/auth/manager/ldap/ldap.go +++ b/pkg/auth/manager/ldap/ldap.go @@ -208,7 +208,7 @@ func (am *mgr) Authenticate(ctx context.Context, clientID, clientSecret string) }, } - scope, err := scope.GetOwnerScope() + scope, err := scope.AddOwnerScope(nil) if err != nil { return nil, nil, err } diff --git a/pkg/auth/manager/oidc/oidc.go b/pkg/auth/manager/oidc/oidc.go index a2a545a5940..e38e2e06d47 100644 --- a/pkg/auth/manager/oidc/oidc.go +++ b/pkg/auth/manager/oidc/oidc.go @@ -186,7 +186,7 @@ func (am *mgr) Authenticate(ctx context.Context, clientID, clientSecret string) Opaque: opaqueObj, } - scope, err := scope.GetOwnerScope() + scope, err := scope.AddOwnerScope(nil) if err != nil { return nil, nil, err } diff --git a/pkg/auth/manager/publicshares/publicshares.go b/pkg/auth/manager/publicshares/publicshares.go index 0b2df90ee75..ceed1421116 100644 --- a/pkg/auth/manager/publicshares/publicshares.go +++ b/pkg/auth/manager/publicshares/publicshares.go @@ -132,7 +132,7 @@ func (m *manager) Authenticate(ctx context.Context, token, secret string) (*user if share.Permissions.Permissions.InitiateFileUpload { role = authpb.Role_ROLE_EDITOR } - scope, err := scope.GetPublicShareScope(share, role) + scope, err := scope.AddPublicShareScope(share, role, nil) if err != nil { return nil, nil, err } diff --git a/pkg/auth/scope/lightweight.go b/pkg/auth/scope/lightweight.go new file mode 100644 index 00000000000..aaf2942f3fd --- /dev/null +++ b/pkg/auth/scope/lightweight.go @@ -0,0 +1,117 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package scope + +// import ( +// "fmt" +// "strings" +// +// authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" +// provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" +// registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1" +// types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" +// "github.com/cs3org/reva/pkg/errtypes" +// "github.com/cs3org/reva/pkg/utils" +// ) +// +// func lightweightAccountScope(scope *authpb.Scope, resource interface{}) (bool, error) { +// var r provider.ResourceInfo +// err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &r) +// if err != nil { +// return false, err +// } +// +// switch v := resource.(type) { +// // Viewer role +// case *registry.GetStorageProvidersRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// case *provider.StatRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// case *provider.ListContainerRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// case *provider.InitiateFileDownloadRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// +// // Editor role +// // TODO(ishank011): Add role checks, +// // need to return appropriate status codes in the ocs/ocdav layers. +// case *provider.CreateContainerRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// case *provider.DeleteRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// case *provider.MoveRequest: +// return checkResourceInfo(&r, v.GetSource()) && checkResourceInfo(&r, v.GetDestination()), nil +// case *provider.InitiateFileUploadRequest: +// return checkResourceInfo(&r, v.GetRef()), nil +// +// case string: +// return checkPath(v), nil +// } +// +// return false, errtypes.InternalError(fmt.Sprintf("resource type assertion failed: %+v", resource)) +// } +// +// func checkResourceInfo(inf *provider.ResourceInfo, ref *provider.Reference) bool { +// // ref: > +// if ref.GetId() != nil { +// return inf.Id.StorageId == ref.GetId().StorageId && inf.Id.OpaqueId == ref.GetId().OpaqueId +// } +// // ref: +// if strings.HasPrefix(ref.GetPath(), inf.Path) { +// return true +// } +// return false +// } +// +// func checkPath(path string) bool { +// paths := []string{ +// "/dataprovider", +// "/data", +// } +// for _, p := range paths { +// if strings.HasPrefix(path, p) { +// return true +// } +// } +// return false +// } +// +// // AddLightweightAccountScope adds the scope to allow access to lightweight user. +// func AddLightweightAccountScope(scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) { +// ref := &provider.Reference{ +// Spec: &provider.Reference_Path{ +// Path: "/", +// }, +// } +// val, err := utils.MarshalProtoV1ToJSON(ref) +// if err != nil { +// return nil, err +// } +// if scopes == nil { +// scopes = make(map[string]*authpb.Scope) +// } +// scopes["resourceinfo:"+r.Id.String()] = &authpb.Scope{ +// Resource: &types.OpaqueEntry{ +// Decoder: "json", +// Value: val, +// }, +// Role: role, +// } +// return scopes, nil +// } diff --git a/pkg/auth/scope/publicshare.go b/pkg/auth/scope/publicshare.go index 1b7b686631e..4f9cff2b163 100644 --- a/pkg/auth/scope/publicshare.go +++ b/pkg/auth/scope/publicshare.go @@ -64,7 +64,7 @@ func publicshareScope(scope *authpb.Scope, resource interface{}) (bool, error) { case *link.GetPublicShareRequest: return checkPublicShareRef(&share, v.GetRef()), nil case string: - return checkPath(v), nil + return checkResourcePath(v), nil } return false, errtypes.InternalError(fmt.Sprintf("resource type assertion failed: %+v", resource)) @@ -87,20 +87,22 @@ func checkPublicShareRef(s *link.PublicShare, ref *link.PublicShareReference) bo return ref.GetToken() == s.Token } -// GetPublicShareScope returns the scope to allow access to a public share and +// AddPublicShareScope adds the scope to allow access to a public share and // the shared resource. -func GetPublicShareScope(share *link.PublicShare, role authpb.Role) (map[string]*authpb.Scope, error) { +func AddPublicShareScope(share *link.PublicShare, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) { val, err := utils.MarshalProtoV1ToJSON(share) if err != nil { return nil, err } - return map[string]*authpb.Scope{ - "publicshare:" + share.Id.OpaqueId: &authpb.Scope{ - Resource: &types.OpaqueEntry{ - Decoder: "json", - Value: val, - }, - Role: role, + if scopes == nil { + scopes = make(map[string]*authpb.Scope) + } + scopes["publicshare:"+share.Id.OpaqueId] = &authpb.Scope{ + Resource: &types.OpaqueEntry{ + Decoder: "json", + Value: val, }, - }, nil + Role: role, + } + return scopes, nil } diff --git a/pkg/auth/scope/resourceinfo.go b/pkg/auth/scope/resourceinfo.go index 3d75938bce4..42e05e4c31c 100644 --- a/pkg/auth/scope/resourceinfo.go +++ b/pkg/auth/scope/resourceinfo.go @@ -61,7 +61,7 @@ func resourceinfoScope(scope *authpb.Scope, resource interface{}) (bool, error) return checkResourceInfo(&r, v.GetRef()), nil case string: - return checkPath(v), nil + return checkResourcePath(v), nil } return false, errtypes.InternalError(fmt.Sprintf("resource type assertion failed: %+v", resource)) @@ -79,7 +79,7 @@ func checkResourceInfo(inf *provider.ResourceInfo, ref *provider.Reference) bool return false } -func checkPath(path string) bool { +func checkResourcePath(path string) bool { paths := []string{ "/dataprovider", "/data", @@ -92,19 +92,21 @@ func checkPath(path string) bool { return false } -// GetResourceInfoScope returns the scope to allow access to a resource info object. -func GetResourceInfoScope(r *provider.ResourceInfo, role authpb.Role) (map[string]*authpb.Scope, error) { +// AddResourceInfoScope adds the scope to allow access to a resource info object. +func AddResourceInfoScope(r *provider.ResourceInfo, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) { val, err := utils.MarshalProtoV1ToJSON(r) if err != nil { return nil, err } - return map[string]*authpb.Scope{ - "resourceinfo:" + r.Id.String(): &authpb.Scope{ - Resource: &types.OpaqueEntry{ - Decoder: "json", - Value: val, - }, - Role: role, + if scopes == nil { + scopes = make(map[string]*authpb.Scope) + } + scopes["resourceinfo:"+r.Id.String()] = &authpb.Scope{ + Resource: &types.OpaqueEntry{ + Decoder: "json", + Value: val, }, - }, nil + Role: role, + } + return scopes, nil } diff --git a/pkg/auth/scope/scope.go b/pkg/auth/scope/scope.go index 35bdb29cfc5..2f4df8dba5b 100644 --- a/pkg/auth/scope/scope.go +++ b/pkg/auth/scope/scope.go @@ -31,6 +31,7 @@ var supportedScopes = map[string]Verifier{ "user": userScope, "publicshare": publicshareScope, "resourceinfo": resourceinfoScope, + "share": shareScope, } // VerifyScope is the function to be called when dismantling tokens to check if @@ -39,11 +40,7 @@ func VerifyScope(scopeMap map[string]*authpb.Scope, resource interface{}) (bool, for k, scope := range scopeMap { for s, f := range supportedScopes { if strings.HasPrefix(k, s) { - valid, err := f(scope, resource) - if err != nil { - continue - } - if valid { + if valid, err := f(scope, resource); err == nil && valid { return true, nil } } diff --git a/pkg/auth/scope/share.go b/pkg/auth/scope/share.go new file mode 100644 index 00000000000..f6204730ca3 --- /dev/null +++ b/pkg/auth/scope/share.go @@ -0,0 +1,125 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package scope + +import ( + "fmt" + "strings" + + authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1" + types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/utils" +) + +func shareScope(scope *authpb.Scope, resource interface{}) (bool, error) { + var share collaboration.Share + err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &share) + if err != nil { + return false, err + } + + switch v := resource.(type) { + // Viewer role + case *registry.GetStorageProvidersRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + case *provider.StatRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + case *provider.ListContainerRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + case *provider.InitiateFileDownloadRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + + // Editor role + // TODO(ishank011): Add role checks, + // need to return appropriate status codes in the ocs/ocdav layers. + case *provider.CreateContainerRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + case *provider.DeleteRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + case *provider.MoveRequest: + return checkShareStorageRef(&share, v.GetSource()) && checkShareStorageRef(&share, v.GetDestination()), nil + case *provider.InitiateFileUploadRequest: + return checkShareStorageRef(&share, v.GetRef()), nil + + case *collaboration.ListReceivedSharesRequest: + return true, nil + case *collaboration.GetReceivedShareRequest: + return checkShareRef(&share, v.GetRef()), nil + case string: + return checkSharePath(v) || checkResourcePath(v), nil + } + + return false, errtypes.InternalError(fmt.Sprintf("resource type assertion failed: %+v", resource)) +} + +func checkShareStorageRef(s *collaboration.Share, r *provider.Reference) bool { + // ref: > + if r.GetId() != nil { + return s.ResourceId.StorageId == r.GetId().StorageId && s.ResourceId.OpaqueId == r.GetId().OpaqueId + } + return false +} + +func checkShareRef(s *collaboration.Share, ref *collaboration.ShareReference) bool { + if ref.GetId() != nil { + return ref.GetId().OpaqueId == s.Id.OpaqueId + } + if key := ref.GetKey(); key != nil { + return (utils.UserEqual(key.Owner, s.Owner) || utils.UserEqual(key.Owner, s.Creator)) && + utils.ResourceEqual(key.ResourceId, s.ResourceId) && utils.GranteeEqual(key.Grantee, s.Grantee) + } + return false +} + +func checkSharePath(path string) bool { + paths := []string{ + "/ocs/v2.php/apps/files_sharing/api/v1/shares", + "/ocs/v1.php/apps/files_sharing/api/v1/shares", + } + for _, p := range paths { + if strings.HasPrefix(path, p) { + return true + } + } + return false +} + +// AddPShareScope adds the scope to allow access to a user/group share and +// the shared resource. +func AddShareScope(share *collaboration.Share, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) { + val, err := utils.MarshalProtoV1ToJSON(share) + if err != nil { + return nil, err + } + if scopes == nil { + scopes = make(map[string]*authpb.Scope) + } + scopes["share:"+share.Id.OpaqueId] = &authpb.Scope{ + Resource: &types.OpaqueEntry{ + Decoder: "json", + Value: val, + }, + Role: role, + } + return scopes, nil +} diff --git a/pkg/auth/scope/user.go b/pkg/auth/scope/user.go index 42ebf47728c..8b39ed6cb17 100644 --- a/pkg/auth/scope/user.go +++ b/pkg/auth/scope/user.go @@ -31,8 +31,8 @@ func userScope(scope *authpb.Scope, resource interface{}) (bool, error) { return true, nil } -// GetOwnerScope returns the default owner scope with access to all resources. -func GetOwnerScope() (map[string]*authpb.Scope, error) { +// AddOwnerScope adds the default owner scope with access to all resources. +func AddOwnerScope(scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) { ref := &provider.Reference{ Spec: &provider.Reference_Path{ Path: "/", @@ -42,13 +42,15 @@ func GetOwnerScope() (map[string]*authpb.Scope, error) { if err != nil { return nil, err } - return map[string]*authpb.Scope{ - "user": &authpb.Scope{ - Resource: &types.OpaqueEntry{ - Decoder: "json", - Value: val, - }, - Role: authpb.Role_ROLE_OWNER, + if scopes == nil { + scopes = make(map[string]*authpb.Scope) + } + scopes["user"] = &authpb.Scope{ + Resource: &types.OpaqueEntry{ + Decoder: "json", + Value: val, }, - }, nil + Role: authpb.Role_ROLE_OWNER, + } + return scopes, nil }