github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/node/permissions.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 node
    20  
    21  import (
    22  	"context"
    23  	"strings"
    24  
    25  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    26  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    27  	"github.com/cs3org/reva/v2/pkg/appctx"
    28  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    29  	"github.com/cs3org/reva/v2/pkg/errtypes"
    30  	"github.com/cs3org/reva/v2/pkg/utils"
    31  	"github.com/pkg/errors"
    32  )
    33  
    34  // PermissionFunc should return true when the user has permission to access the node
    35  type PermissionFunc func(*Node) bool
    36  
    37  var (
    38  	// NoCheck doesn't check permissions, returns true always
    39  	NoCheck PermissionFunc = func(_ *Node) bool {
    40  		return true
    41  	}
    42  )
    43  
    44  // NoPermissions represents an empty set of permissions
    45  func NoPermissions() *provider.ResourcePermissions {
    46  	return &provider.ResourcePermissions{}
    47  }
    48  
    49  // ShareFolderPermissions defines permissions for the shared jail
    50  func ShareFolderPermissions() *provider.ResourcePermissions {
    51  	return &provider.ResourcePermissions{
    52  		// read permissions
    53  		ListContainer:        true,
    54  		Stat:                 true,
    55  		InitiateFileDownload: true,
    56  		GetPath:              true,
    57  		GetQuota:             true,
    58  		ListFileVersions:     true,
    59  	}
    60  }
    61  
    62  // OwnerPermissions defines permissions for nodes owned by the user
    63  func OwnerPermissions() *provider.ResourcePermissions {
    64  	return &provider.ResourcePermissions{
    65  		// all permissions
    66  		AddGrant:             true,
    67  		CreateContainer:      true,
    68  		Delete:               true,
    69  		GetPath:              true,
    70  		GetQuota:             true,
    71  		InitiateFileDownload: true,
    72  		InitiateFileUpload:   true,
    73  		ListContainer:        true,
    74  		ListFileVersions:     true,
    75  		ListGrants:           true,
    76  		ListRecycle:          true,
    77  		Move:                 true,
    78  		PurgeRecycle:         true,
    79  		RemoveGrant:          true,
    80  		RestoreFileVersion:   true,
    81  		RestoreRecycleItem:   true,
    82  		Stat:                 true,
    83  		UpdateGrant:          true,
    84  		DenyGrant:            true,
    85  	}
    86  }
    87  
    88  // ServiceAccountPermissions defines the permissions for nodes when requested by a service account
    89  func ServiceAccountPermissions() *provider.ResourcePermissions {
    90  	// TODO: Different permissions for different service accounts
    91  	return &provider.ResourcePermissions{
    92  		Stat:                 true,
    93  		ListContainer:        true,
    94  		GetPath:              true, // for search index
    95  		InitiateFileUpload:   true, // for personal data export
    96  		InitiateFileDownload: true, // for full-text-search
    97  		RemoveGrant:          true, // for share expiry
    98  		ListRecycle:          true, // for purge-trash-bin command
    99  		PurgeRecycle:         true, // for purge-trash-bin command
   100  		RestoreRecycleItem:   true, // for cli restore command
   101  		Delete:               true, // for cli restore command with replace option
   102  		CreateContainer:      true, // for space provisioning
   103  		AddGrant:             true, // for initial project space member assignment
   104  	}
   105  }
   106  
   107  // Permissions implements permission checks
   108  type Permissions struct {
   109  	lu PathLookup
   110  }
   111  
   112  // NewPermissions returns a new Permissions instance
   113  func NewPermissions(lu PathLookup) *Permissions {
   114  	return &Permissions{
   115  		lu: lu,
   116  	}
   117  }
   118  
   119  // AssemblePermissions will assemble the permissions for the current user on the given node, taking into account all parent nodes
   120  func (p *Permissions) AssemblePermissions(ctx context.Context, n *Node) (ap *provider.ResourcePermissions, err error) {
   121  	return p.assemblePermissions(ctx, n, true)
   122  }
   123  
   124  // AssembleTrashPermissions will assemble the permissions for the current user on the given node, taking into account all parent nodes
   125  func (p *Permissions) AssembleTrashPermissions(ctx context.Context, n *Node) (ap *provider.ResourcePermissions, err error) {
   126  	return p.assemblePermissions(ctx, n, false)
   127  }
   128  
   129  // assemblePermissions will assemble the permissions for the current user on the given node, taking into account all parent nodes
   130  func (p *Permissions) assemblePermissions(ctx context.Context, n *Node, failOnTrashedSubtree bool) (ap *provider.ResourcePermissions, err error) {
   131  	u, ok := ctxpkg.ContextGetUser(ctx)
   132  	if !ok {
   133  		return NoPermissions(), nil
   134  	}
   135  
   136  	if u.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
   137  		return ServiceAccountPermissions(), nil
   138  	}
   139  
   140  	// are we reading a revision?
   141  	if strings.Contains(n.ID, RevisionIDDelimiter) {
   142  		// verify revision key format
   143  		kp := strings.SplitN(n.ID, RevisionIDDelimiter, 2)
   144  		if len(kp) != 2 {
   145  			return NoPermissions(), errtypes.NotFound(n.ID)
   146  		}
   147  		// use the actual node for the permission assembly
   148  		n.ID = kp[0]
   149  	}
   150  
   151  	// determine root
   152  	rn := n.SpaceRoot
   153  	cn := n
   154  	ap = &provider.ResourcePermissions{}
   155  
   156  	// for an efficient group lookup convert the list of groups to a map
   157  	// groups are just strings ... groupnames ... or group ids ??? AAARGH !!!
   158  	groupsMap := make(map[string]bool, len(u.Groups))
   159  	for i := range u.Groups {
   160  		groupsMap[u.Groups[i]] = true
   161  	}
   162  
   163  	// for all segments, starting at the leaf
   164  	for cn.ID != rn.ID {
   165  		if np, accessDenied, err := cn.ReadUserPermissions(ctx, u); err == nil {
   166  			// check if we have a denial on this node
   167  			if accessDenied {
   168  				return np, nil
   169  			}
   170  			AddPermissions(ap, np)
   171  		} else {
   172  			appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", cn.SpaceID).Str("nodeid", cn.ID).Msg("error reading permissions")
   173  			// continue with next segment
   174  		}
   175  
   176  		if cn, err = cn.Parent(ctx); err != nil {
   177  			// We get an error but get a parent, but can not read it from disk (eg. it has been deleted already)
   178  			if cn != nil {
   179  				return ap, errors.Wrap(err, "Decomposedfs: error getting parent for node "+cn.ID)
   180  			}
   181  			// We do not have a parent, so we assume the next valid parent is the spaceRoot (which must always exist)
   182  			cn = n.SpaceRoot
   183  		}
   184  		if failOnTrashedSubtree && !cn.Exists {
   185  			return NoPermissions(), errtypes.NotFound(n.ID)
   186  		}
   187  
   188  	}
   189  
   190  	// for the root node
   191  	if np, accessDenied, err := cn.ReadUserPermissions(ctx, u); err == nil {
   192  		// check if we have a denial on this node
   193  		if accessDenied {
   194  			return np, nil
   195  		}
   196  		AddPermissions(ap, np)
   197  	} else {
   198  		appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", cn.SpaceID).Str("nodeid", cn.ID).Msg("error reading root node permissions")
   199  	}
   200  
   201  	// check if the current user is the owner
   202  	if utils.UserIDEqual(u.Id, n.Owner()) {
   203  		return OwnerPermissions(), nil
   204  	}
   205  
   206  	appctx.GetLogger(ctx).Debug().Interface("permissions", ap).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Interface("user", u).Msg("returning agregated permissions")
   207  	return ap, nil
   208  }
   209  
   210  // AddPermissions merges a set of permissions into another
   211  // TODO we should use a bitfield for this ...
   212  func AddPermissions(l *provider.ResourcePermissions, r *provider.ResourcePermissions) {
   213  	l.AddGrant = l.AddGrant || r.AddGrant
   214  	l.CreateContainer = l.CreateContainer || r.CreateContainer
   215  	l.Delete = l.Delete || r.Delete
   216  	l.GetPath = l.GetPath || r.GetPath
   217  	l.GetQuota = l.GetQuota || r.GetQuota
   218  	l.InitiateFileDownload = l.InitiateFileDownload || r.InitiateFileDownload
   219  	l.InitiateFileUpload = l.InitiateFileUpload || r.InitiateFileUpload
   220  	l.ListContainer = l.ListContainer || r.ListContainer
   221  	l.ListFileVersions = l.ListFileVersions || r.ListFileVersions
   222  	l.ListGrants = l.ListGrants || r.ListGrants
   223  	l.ListRecycle = l.ListRecycle || r.ListRecycle
   224  	l.Move = l.Move || r.Move
   225  	l.PurgeRecycle = l.PurgeRecycle || r.PurgeRecycle
   226  	l.RemoveGrant = l.RemoveGrant || r.RemoveGrant
   227  	l.RestoreFileVersion = l.RestoreFileVersion || r.RestoreFileVersion
   228  	l.RestoreRecycleItem = l.RestoreRecycleItem || r.RestoreRecycleItem
   229  	l.Stat = l.Stat || r.Stat
   230  	l.UpdateGrant = l.UpdateGrant || r.UpdateGrant
   231  	l.DenyGrant = l.DenyGrant || r.DenyGrant
   232  }