Use the opencloud reva from now on

This commit is contained in:
André Duffeck
2025-01-21 11:16:38 +01:00
parent 2c1afafb35
commit e8d35e1280
1007 changed files with 2988 additions and 27822 deletions
+91
View File
@@ -0,0 +1,91 @@
// 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 (
"context"
"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"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/rs/zerolog"
)
func lightweightAccountScope(_ context.Context, scope *authpb.Scope, resource interface{}, _ *zerolog.Logger) (bool, error) {
// Lightweight accounts have access to resources shared with them.
// These cannot be resolved from here, but need to be added to the scope from
// where the call to mint tokens is made.
// From here, we only allow ListReceivedShares calls
switch v := resource.(type) {
case *collaboration.ListReceivedSharesRequest:
return true, nil
case string:
return checkLightweightPath(v), nil
}
return false, nil
}
func checkLightweightPath(path string) bool {
paths := []string{
"/ocs/v2.php/apps/files_sharing/api/v1/shares",
"/ocs/v1.php/apps/files_sharing/api/v1/shares",
"/ocs/v2.php/apps/files_sharing//api/v1/shares",
"/ocs/v1.php/apps/files_sharing//api/v1/shares",
"/ocs/v2.php/cloud/capabilities",
"/ocs/v1.php/cloud/capabilities",
"/ocs/v2.php/cloud/user",
"/ocs/v1.php/cloud/user",
"/remote.php/webdav",
"/remote.php/dav/files",
"/app/open",
"/app/new",
"/archiver",
"/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(role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) {
ref := &provider.Reference{Path: "/"}
val, err := utils.MarshalProtoV1ToJSON(ref)
if err != nil {
return nil, err
}
if scopes == nil {
scopes = make(map[string]*authpb.Scope)
}
scopes["lightweight"] = &authpb.Scope{
Resource: &types.OpaqueEntry{
Decoder: "json",
Value: val,
},
Role: role,
}
return scopes, nil
}
+201
View File
@@ -0,0 +1,201 @@
// Copyright 2018-2023 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 (
"context"
"path/filepath"
"strings"
appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
appregistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
ocmv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/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/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/rs/zerolog"
)
// FIXME: the namespace here is hardcoded
// find a way to pass it from the config.
const ocmNamespace = "/ocm"
func ocmShareScope(_ context.Context, scope *authpb.Scope, resource interface{}, _ *zerolog.Logger) (bool, error) {
var share ocmv1beta1.Share
if err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &share); err != nil {
return false, err
}
switch v := resource.(type) {
// viewer role
case *registry.ListStorageProvidersRequest:
ref := &provider.Reference{}
if v.Opaque != nil && v.Opaque.Map != nil {
if e, ok := v.Opaque.Map["storage_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.StorageId = string(e.Value)
}
if e, ok := v.Opaque.Map["space_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.SpaceId = string(e.Value)
}
if e, ok := v.Opaque.Map["opaque_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.OpaqueId = string(e.Value)
}
if e, ok := v.Opaque.Map["path"]; ok {
ref.Path = string(e.Value)
}
}
return checkStorageRefForOCMShare(&share, ref, ocmNamespace), nil
case *registry.GetStorageProvidersRequest:
return checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.StatRequest:
return checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.ListContainerRequest:
return checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.InitiateFileDownloadRequest:
return checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *appprovider.OpenInAppRequest:
return checkStorageRefForOCMShare(&share, &provider.Reference{ResourceId: v.ResourceInfo.Id}, ocmNamespace), nil
case *gateway.OpenInAppRequest:
return checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.GetLockRequest:
return checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
// editor role
case *provider.CreateContainerRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.TouchFileRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.DeleteRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.MoveRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetSource(), ocmNamespace) && checkStorageRefForOCMShare(&share, v.GetDestination(), ocmNamespace), nil
case *provider.InitiateFileUploadRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.SetArbitraryMetadataRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.UnsetArbitraryMetadataRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.SetLockRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.RefreshLockRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
case *provider.UnlockRequest:
return hasRoleEditor(scope) && checkStorageRefForOCMShare(&share, v.GetRef(), ocmNamespace), nil
// App provider requests
case *appregistry.GetDefaultAppProviderForMimeTypeRequest:
return true, nil
case *appregistry.GetAppProvidersRequest:
return true, nil
case *userv1beta1.GetUserByClaimRequest:
return true, nil
case *userv1beta1.GetUserRequest:
return true, nil
case *provider.ListStorageSpacesRequest:
return true, nil
// FIXME why do we need to add them? I think the whole listing of received OCM shares change might be unnecessary ... need to reevaluate that after switching the dav namespace for from /ocm back to /public
case *ocmv1beta1.ListReceivedOCMSharesRequest:
return true, nil
case *ocmv1beta1.ListOCMSharesRequest:
return true, nil
case *collaboration.ListReceivedSharesRequest:
return true, nil
case *ocmv1beta1.GetOCMShareRequest:
return checkOCMShareRef(&share, v.GetRef()), nil
case *ocmv1beta1.GetOCMShareByTokenRequest:
return share.Token == v.GetToken(), nil
case string:
return checkResourcePath(v), nil
}
return false, nil
}
func checkStorageRefForOCMShare(s *ocmv1beta1.Share, r *provider.Reference, ns string) bool {
if r.ResourceId != nil {
return utils.ResourceIDEqual(s.ResourceId, r.GetResourceId()) || strings.HasPrefix(r.ResourceId.OpaqueId, s.GetId().GetOpaqueId())
}
// FIXME: the paths here are hardcoded
if strings.HasPrefix(r.GetPath(), "/public/"+s.GetId().GetOpaqueId()) {
return true
}
return strings.HasPrefix(r.GetPath(), filepath.Join(ns, s.GetId().GetOpaqueId()))
}
func checkOCMShareRef(s *ocmv1beta1.Share, ref *ocmv1beta1.ShareReference) bool {
return ref.GetId().GetOpaqueId() == s.GetId().GetOpaqueId()
}
// AddOCMShareScope adds the scope to allow access to an OCM share and the share resource.
func AddOCMShareScope(share *ocmv1beta1.Share, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) {
// Create a new "scope share" to only expose the required fields `ResourceId`, `Id` and `Token` to the scope.
scopeShare := ocmv1beta1.Share{ResourceId: share.ResourceId, Id: share.GetId(), Token: share.Token}
val, err := utils.MarshalProtoV1ToJSON(&scopeShare)
if err != nil {
return nil, err
}
if scopes == nil {
scopes = make(map[string]*authpb.Scope)
}
scopes["ocmshare:"+share.Id.OpaqueId] = &authpb.Scope{
Resource: &types.OpaqueEntry{
Decoder: "json",
Value: val,
},
Role: role,
}
return scopes, nil
}
// GetOCMSharesFromScopes returns all OCM shares in the given scope.
func GetOCMSharesFromScopes(scopes map[string]*authpb.Scope) ([]*ocmv1beta1.Share, error) {
var shares []*ocmv1beta1.Share
for k, s := range scopes {
if strings.HasPrefix(k, "ocmshare:") {
res := s.Resource
if res.Decoder != "json" {
return nil, errtypes.InternalError("resource should be json encoded")
}
var share ocmv1beta1.Share
err := utils.UnmarshalJSONToProtoV1(res.Value, &share)
if err != nil {
return nil, err
}
shares = append(shares, &share)
}
}
return shares, nil
}
+204
View File
@@ -0,0 +1,204 @@
// 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 (
"context"
"strings"
appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
appregistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
permissionsv1beta1 "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/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/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/rs/zerolog"
)
// PublicStorageProviderID is the space id used for the public links storage space
const PublicStorageProviderID = "7993447f-687f-490d-875c-ac95e89a62a4"
func publicshareScope(ctx context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (bool, error) {
var share link.PublicShare
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &share)
if err != nil {
return false, err
}
switch v := resource.(type) {
// Viewer role
case *registry.GetStorageProvidersRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *registry.ListStorageProvidersRequest:
ref := &provider.Reference{}
if v.Opaque != nil && v.Opaque.Map != nil {
if e, ok := v.Opaque.Map["storage_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.StorageId = string(e.Value)
}
if e, ok := v.Opaque.Map["space_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.SpaceId = string(e.Value)
}
if e, ok := v.Opaque.Map["opaque_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.OpaqueId = string(e.Value)
}
if e, ok := v.Opaque.Map["path"]; ok {
ref.Path = string(e.Value)
}
}
return checkStorageRef(ctx, &share, ref), nil
case *provider.CreateHomeRequest:
return false, nil
case *provider.GetPathRequest:
return checkStorageRef(ctx, &share, &provider.Reference{ResourceId: v.GetResourceId()}), nil
case *provider.StatRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.GetLockRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.UnlockRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.RefreshLockRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.SetLockRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.ListContainerRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.InitiateFileDownloadRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *appprovider.OpenInAppRequest:
return checkStorageRef(ctx, &share, &provider.Reference{ResourceId: v.ResourceInfo.Id}), nil
case *gateway.OpenInAppRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *permissionsv1beta1.CheckPermissionRequest:
return true, nil
// Editor role
// need to return appropriate status codes in the ocs/ocdav layers.
case *provider.CreateContainerRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.TouchFileRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.DeleteRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.MoveRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetSource()) && checkStorageRef(ctx, &share, v.GetDestination()), nil
case *provider.InitiateFileUploadRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.SetArbitraryMetadataRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.UnsetArbitraryMetadataRequest:
return hasRoleEditor(scope) && checkStorageRef(ctx, &share, v.GetRef()), nil
// App provider requests
case *appregistry.GetDefaultAppProviderForMimeTypeRequest:
return true, nil
case *appregistry.GetAppProvidersRequest:
return true, nil
case *userv1beta1.GetUserByClaimRequest:
return true, nil
case *userv1beta1.GetUserRequest:
return true, nil
case *provider.ListStorageSpacesRequest:
return true, nil
case *link.GetPublicShareRequest:
return checkPublicShareRef(&share, v.GetRef()), nil
case *link.ListPublicSharesRequest:
// public links must not leak info about other links
return false, nil
case *collaboration.ListReceivedSharesRequest:
// public links must not leak info about collaborative shares
return false, nil
case string:
return checkResourcePath(v), nil
}
msg := "public resource type assertion failed"
logger.Debug().Str("scope", "publicshareScope").Interface("resource", resource).Msg(msg)
return false, errtypes.InternalError(msg)
}
func checkStorageRef(ctx context.Context, s *link.PublicShare, r *provider.Reference) bool {
// r: <resource_id:<storage_id:$storageID space_id:$spaceID opaque_id:$opaqueID> path:$path > >
if utils.ResourceIDEqual(s.ResourceId, r.GetResourceId()) {
return true
}
// r: <path:"/public/$token" >
if strings.HasPrefix(r.GetPath(), "/public/"+s.Token) || strings.HasPrefix(r.GetPath(), "./"+s.Token) {
return true
}
// r: <resource_id:<storage_id: space_id: opaque_id:$token> path:$path>
if id := r.GetResourceId(); id.GetStorageId() == PublicStorageProviderID {
// access to /public
if id.GetOpaqueId() == PublicStorageProviderID {
return true
}
// access relative to /public/$token
if id.GetOpaqueId() == s.Token {
return true
}
}
return false
}
func checkPublicShareRef(s *link.PublicShare, ref *link.PublicShareReference) bool {
// ref: <token:$token >
return ref.GetToken() == s.Token
}
// AddPublicShareScope adds the scope to allow access to a public share and
// the shared resource.
func AddPublicShareScope(share *link.PublicShare, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) {
// Create a new "scope share" to only expose the required fields `ResourceId` and `Token` to the scope.
scopeShare := &link.PublicShare{ResourceId: share.ResourceId, Token: share.Token}
val, err := utils.MarshalProtoV1ToJSON(scopeShare)
if err != nil {
return nil, err
}
if scopes == nil {
scopes = make(map[string]*authpb.Scope)
}
scopes["publicshare:"+share.Id.OpaqueId] = &authpb.Scope{
Resource: &types.OpaqueEntry{
Decoder: "json",
Value: val,
},
Role: role,
}
return scopes, nil
}
+75
View File
@@ -0,0 +1,75 @@
// 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 (
"context"
"fmt"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/rs/zerolog"
)
func receivedShareScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (bool, error) {
var share collaboration.ReceivedShare
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &share)
if err != nil {
return false, err
}
switch v := resource.(type) {
case *collaboration.GetReceivedShareRequest:
return checkShareRef(share.Share, v.GetRef()), nil
case *collaboration.UpdateReceivedShareRequest:
return checkShare(share.Share, v.GetShare().GetShare()), nil
case string:
return checkSharePath(v) || checkResourcePath(v), nil
}
msg := fmt.Sprintf("resource type assertion failed: %+v", resource)
logger.Debug().Str("scope", "receivedShareScope").Msg(msg)
return false, errtypes.InternalError(msg)
}
// AddReceivedShareScope adds the scope to allow access to a received user/group share and
// the shared resource.
func AddReceivedShareScope(share *collaboration.ReceivedShare, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) {
// Create a new "scope share" to only expose the required fields to the scope.
scopeShare := &collaboration.Share{Id: share.Share.Id, Owner: share.Share.Owner, Creator: share.Share.Creator, ResourceId: share.Share.ResourceId}
val, err := utils.MarshalProtoV1ToJSON(&collaboration.ReceivedShare{Share: scopeShare})
if err != nil {
return nil, err
}
if scopes == nil {
scopes = make(map[string]*authpb.Scope)
}
scopes["receivedshare:"+share.Share.Id.OpaqueId] = &authpb.Scope{
Resource: &types.OpaqueEntry{
Decoder: "json",
Value: val,
},
Role: role,
}
return scopes, nil
}
+166
View File
@@ -0,0 +1,166 @@
// 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 (
"context"
"fmt"
"strings"
appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
"github.com/rs/zerolog"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/utils"
)
func resourceinfoScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (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 *registry.ListStorageProvidersRequest:
// the call will only return spaces the current user has access to
ref := &provider.Reference{}
if v.Opaque != nil && v.Opaque.Map != nil {
if e, ok := v.Opaque.Map["storage_id"]; ok {
ref.ResourceId = &provider.ResourceId{
StorageId: string(e.Value),
}
}
if e, ok := v.Opaque.Map["opaque_id"]; ok {
if ref.ResourceId == nil {
ref.ResourceId = &provider.ResourceId{}
}
ref.ResourceId.OpaqueId = string(e.Value)
}
if e, ok := v.Opaque.Map["path"]; ok {
ref.Path = string(e.Value)
}
}
return checkResourceInfo(&r, ref), nil
case *provider.ListStorageSpacesRequest:
// the call will only return spaces the current user has access to
return true, 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
case *appprovider.OpenInAppRequest:
return checkResourceInfo(&r, &provider.Reference{ResourceId: v.ResourceInfo.Id}), nil
case *gateway.OpenInAppRequest:
return checkResourceInfo(&r, v.GetRef()), nil
// Editor role
// need to return appropriate status codes in the ocs/ocdav layers.
case *provider.CreateContainerRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
case *provider.TouchFileRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
case *provider.DeleteRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
case *provider.MoveRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetSource()) && checkResourceInfo(&r, v.GetDestination()), nil
case *provider.InitiateFileUploadRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
case *provider.SetArbitraryMetadataRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
case *provider.UnsetArbitraryMetadataRequest:
return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
case string:
return checkResourcePath(v), nil
}
msg := fmt.Sprintf("resource type assertion failed: %+v", resource)
logger.Debug().Str("scope", "resourceinfoScope").Msg(msg)
return false, errtypes.InternalError(msg)
}
func checkResourceInfo(inf *provider.ResourceInfo, ref *provider.Reference) bool {
// ref: <resource_id:<storage_id:$storageID opaque_id:$opaqueID path:$path> >
if ref.ResourceId != nil { // path can be empty or a relative path
if inf.Id.SpaceId == ref.ResourceId.SpaceId && inf.Id.OpaqueId == ref.ResourceId.OpaqueId {
if ref.Path == "" {
// id only reference
return true
}
// check path has same prefix below
} else {
return false
}
}
// ref: <path:$path >
if strings.HasPrefix(ref.GetPath(), inf.Path) {
return true
}
return false
}
func checkResourcePath(path string) bool {
paths := []string{
"/dataprovider",
"/data",
"/app/open",
"/app/new",
"/archiver",
"/ocs/v2.php/cloud/capabilities",
"/ocs/v1.php/cloud/capabilities",
}
for _, p := range paths {
if strings.HasPrefix(path, p) {
return true
}
}
return false
}
// 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) {
// Create a new "scope info" to only expose the required fields `Id` and `Path` to the scope.
scopeInfo := &provider.ResourceInfo{Id: r.Id, Path: r.Path}
val, err := utils.MarshalProtoV1ToJSON(scopeInfo)
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
}
+61
View File
@@ -0,0 +1,61 @@
// 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 (
"context"
"strings"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/rs/zerolog"
)
// Verifier is the function signature which every scope verifier should implement.
type Verifier func(context.Context, *authpb.Scope, interface{}, *zerolog.Logger) (bool, error)
var supportedScopes = map[string]Verifier{
"user": userScope,
"publicshare": publicshareScope,
"resourceinfo": resourceinfoScope,
"share": shareScope,
"receivedshare": receivedShareScope,
"lightweight": lightweightAccountScope,
"ocmshare": ocmShareScope,
}
// VerifyScope is the function to be called when dismantling tokens to check if
// the token has access to a particular resource.
func VerifyScope(ctx context.Context, scopeMap map[string]*authpb.Scope, resource interface{}) (bool, error) {
logger := appctx.GetLogger(ctx)
for k, scope := range scopeMap {
for s, f := range supportedScopes {
if strings.HasPrefix(k, s) {
if valid, err := f(ctx, scope, resource, logger); err == nil && valid {
return true, nil
}
}
}
}
return false, nil
}
func hasRoleEditor(scope *authpb.Scope) bool {
return scope.Role == authpb.Role_ROLE_OWNER || scope.Role == authpb.Role_ROLE_EDITOR || scope.Role == authpb.Role_ROLE_UPLOADER
}
+142
View File
@@ -0,0 +1,142 @@
// 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 (
"context"
"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/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/rs/zerolog"
)
func shareScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (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.TouchFileRequest:
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
}
msg := fmt.Sprintf("resource type assertion failed: %+v", resource)
logger.Debug().Str("scope", "shareScope").Msg(msg)
return false, errtypes.InternalError(msg)
}
func checkShareStorageRef(s *collaboration.Share, r *provider.Reference) bool {
// ref: <id:<storage_id:$storageID opaque_id:$opaqueID > >
if r.GetResourceId() != nil && r.Path == "" { // path must be empty
return utils.ResourceIDEqual(s.ResourceId, r.GetResourceId())
}
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.ResourceIDEqual(key.ResourceId, s.ResourceId) && utils.GranteeEqual(key.Grantee, s.Grantee)
}
return false
}
func checkShare(s1 *collaboration.Share, s2 *collaboration.Share) bool {
if s2.GetId() != nil {
return s2.GetId().OpaqueId == s1.Id.OpaqueId
}
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",
"/remote.php/webdav",
"/remote.php/dav/files",
}
for _, p := range paths {
if strings.HasPrefix(path, p) {
return true
}
}
return false
}
// AddShareScope 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) {
// Create a new "scope share" to only expose the required fields to the scope.
scopeShare := &collaboration.Share{Id: share.Id, Owner: share.Owner, Creator: share.Creator, ResourceId: share.ResourceId}
val, err := utils.MarshalProtoV1ToJSON(scopeShare)
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
}
+55
View File
@@ -0,0 +1,55 @@
// 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 (
"context"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/rs/zerolog"
)
func userScope(_ context.Context, scope *authpb.Scope, resource interface{}, _ *zerolog.Logger) (bool, error) {
// Always return true. Registered users can access all paths.
// TODO(ishank011): Add checks for read/write permissions.
return true, nil
}
// 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{Path: "/"}
val, err := utils.MarshalProtoV1ToJSON(ref)
if err != nil {
return nil, err
}
if scopes == nil {
scopes = make(map[string]*authpb.Scope)
}
scopes["user"] = &authpb.Scope{
Resource: &types.OpaqueEntry{
Decoder: "json",
Value: val,
},
Role: authpb.Role_ROLE_OWNER,
}
return scopes, nil
}
+62
View File
@@ -0,0 +1,62 @@
// 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"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/utils"
)
// FormatScope create a pretty print of the scope
func FormatScope(scopeType string, scope *authpb.Scope) (string, error) {
// TODO(gmgigi96): check decoder type
switch {
case strings.HasPrefix(scopeType, "user"):
// user scope
var ref provider.Reference
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &ref)
if err != nil {
return "", err
}
return fmt.Sprintf("%s %s", ref.String(), scope.Role.String()), nil
case strings.HasPrefix(scopeType, "publicshare"):
// public share
var pShare link.PublicShare
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &pShare)
if err != nil {
return "", err
}
return fmt.Sprintf("share:\"%s\" %s", pShare.Id.OpaqueId, scope.Role.String()), nil
case strings.HasPrefix(scopeType, "resourceinfo"):
var resInfo provider.ResourceInfo
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &resInfo)
if err != nil {
return "", err
}
return fmt.Sprintf("path:\"%s\" %s", resInfo.Path, scope.Role.String()), nil
default:
return "", errtypes.NotSupported("scope not yet supported")
}
}