github.com/cs3org/reva/v2@v2.27.7/pkg/auth/scope/resourceinfo.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package scope
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  
    26  	appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
    27  	authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
    28  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    29  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    30  	registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
    31  	"github.com/rs/zerolog"
    32  
    33  	types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    34  	"github.com/cs3org/reva/v2/pkg/errtypes"
    35  	"github.com/cs3org/reva/v2/pkg/utils"
    36  )
    37  
    38  func resourceinfoScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (bool, error) {
    39  	var r provider.ResourceInfo
    40  	err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &r)
    41  	if err != nil {
    42  		return false, err
    43  	}
    44  
    45  	switch v := resource.(type) {
    46  	// Viewer role
    47  	case *registry.GetStorageProvidersRequest:
    48  		return checkResourceInfo(&r, v.GetRef()), nil
    49  	case *registry.ListStorageProvidersRequest:
    50  		// the call will only return spaces the current user has access to
    51  		ref := &provider.Reference{}
    52  		if v.Opaque != nil && v.Opaque.Map != nil {
    53  			if e, ok := v.Opaque.Map["storage_id"]; ok {
    54  				ref.ResourceId = &provider.ResourceId{
    55  					StorageId: string(e.Value),
    56  				}
    57  			}
    58  			if e, ok := v.Opaque.Map["opaque_id"]; ok {
    59  				if ref.ResourceId == nil {
    60  					ref.ResourceId = &provider.ResourceId{}
    61  				}
    62  				ref.ResourceId.OpaqueId = string(e.Value)
    63  			}
    64  			if e, ok := v.Opaque.Map["path"]; ok {
    65  				ref.Path = string(e.Value)
    66  			}
    67  		}
    68  		return checkResourceInfo(&r, ref), nil
    69  	case *provider.ListStorageSpacesRequest:
    70  		// the call will only return spaces the current user has access to
    71  		return true, nil
    72  	case *provider.StatRequest:
    73  		return checkResourceInfo(&r, v.GetRef()), nil
    74  	case *provider.ListContainerRequest:
    75  		return checkResourceInfo(&r, v.GetRef()), nil
    76  	case *provider.InitiateFileDownloadRequest:
    77  		return checkResourceInfo(&r, v.GetRef()), nil
    78  	case *appprovider.OpenInAppRequest:
    79  		return checkResourceInfo(&r, &provider.Reference{ResourceId: v.ResourceInfo.Id}), nil
    80  	case *gateway.OpenInAppRequest:
    81  		return checkResourceInfo(&r, v.GetRef()), nil
    82  
    83  	// Editor role
    84  	// need to return appropriate status codes in the ocs/ocdav layers.
    85  	case *provider.CreateContainerRequest:
    86  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
    87  	case *provider.TouchFileRequest:
    88  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
    89  	case *provider.DeleteRequest:
    90  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
    91  	case *provider.MoveRequest:
    92  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetSource()) && checkResourceInfo(&r, v.GetDestination()), nil
    93  	case *provider.InitiateFileUploadRequest:
    94  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
    95  	case *provider.SetArbitraryMetadataRequest:
    96  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
    97  	case *provider.UnsetArbitraryMetadataRequest:
    98  		return hasRoleEditor(scope) && checkResourceInfo(&r, v.GetRef()), nil
    99  
   100  	case string:
   101  		return checkResourcePath(v), nil
   102  	}
   103  
   104  	msg := fmt.Sprintf("resource type assertion failed: %+v", resource)
   105  	logger.Debug().Str("scope", "resourceinfoScope").Msg(msg)
   106  	return false, errtypes.InternalError(msg)
   107  }
   108  
   109  func checkResourceInfo(inf *provider.ResourceInfo, ref *provider.Reference) bool {
   110  	// ref: <resource_id:<storage_id:$storageID opaque_id:$opaqueID path:$path> >
   111  	if ref.ResourceId != nil { // path can be empty or a relative path
   112  		if inf.Id.SpaceId == ref.ResourceId.SpaceId && inf.Id.OpaqueId == ref.ResourceId.OpaqueId {
   113  			if ref.Path == "" {
   114  				// id only reference
   115  				return true
   116  			}
   117  			// check path has same prefix below
   118  		} else {
   119  			return false
   120  		}
   121  	}
   122  	// ref: <path:$path >
   123  	if strings.HasPrefix(ref.GetPath(), inf.Path) {
   124  		return true
   125  	}
   126  	return false
   127  }
   128  
   129  func checkResourcePath(path string) bool {
   130  	paths := []string{
   131  		"/dataprovider",
   132  		"/data",
   133  		"/app/open",
   134  		"/app/new",
   135  		"/archiver",
   136  		"/ocs/v2.php/cloud/capabilities",
   137  		"/ocs/v1.php/cloud/capabilities",
   138  	}
   139  	for _, p := range paths {
   140  		if strings.HasPrefix(path, p) {
   141  			return true
   142  		}
   143  	}
   144  	return false
   145  }
   146  
   147  // AddResourceInfoScope adds the scope to allow access to a resource info object.
   148  func AddResourceInfoScope(r *provider.ResourceInfo, role authpb.Role, scopes map[string]*authpb.Scope) (map[string]*authpb.Scope, error) {
   149  	// Create a new "scope info" to only expose the required fields `Id` and `Path` to the scope.
   150  	scopeInfo := &provider.ResourceInfo{Id: r.Id, Path: r.Path}
   151  	val, err := utils.MarshalProtoV1ToJSON(scopeInfo)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	if scopes == nil {
   156  		scopes = make(map[string]*authpb.Scope)
   157  	}
   158  	scopes["resourceinfo:"+r.Id.String()] = &authpb.Scope{
   159  		Resource: &types.OpaqueEntry{
   160  			Decoder: "json",
   161  			Value:   val,
   162  		},
   163  		Role: role,
   164  	}
   165  	return scopes, nil
   166  }