github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/grants.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 decomposedfs
    20  
    21  import (
    22  	"context"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    27  	"github.com/cs3org/reva/v2/internal/grpc/services/storageprovider"
    28  	"github.com/cs3org/reva/v2/pkg/appctx"
    29  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    30  	"github.com/cs3org/reva/v2/pkg/errtypes"
    31  	"github.com/cs3org/reva/v2/pkg/storage/utils/ace"
    32  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata"
    33  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes"
    34  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    35  	"github.com/cs3org/reva/v2/pkg/storagespace"
    36  	"github.com/cs3org/reva/v2/pkg/utils"
    37  )
    38  
    39  // DenyGrant denies access to a resource.
    40  func (fs *Decomposedfs) DenyGrant(ctx context.Context, ref *provider.Reference, grantee *provider.Grantee) error {
    41  	_, span := tracer.Start(ctx, "DenyGrant")
    42  	defer span.End()
    43  	log := appctx.GetLogger(ctx)
    44  
    45  	log.Debug().Interface("ref", ref).Interface("grantee", grantee).Msg("DenyGrant()")
    46  
    47  	grantNode, err := fs.lu.NodeFromResource(ctx, ref)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	if !grantNode.Exists {
    52  		return errtypes.NotFound(filepath.Join(grantNode.ParentID, grantNode.Name))
    53  	}
    54  
    55  	// set all permissions to false
    56  	grant := &provider.Grant{
    57  		Grantee:     grantee,
    58  		Permissions: &provider.ResourcePermissions{},
    59  	}
    60  
    61  	// add acting user
    62  	u := ctxpkg.ContextMustGetUser(ctx)
    63  	grant.Creator = u.GetId()
    64  
    65  	rp, err := fs.p.AssemblePermissions(ctx, grantNode)
    66  
    67  	switch {
    68  	case err != nil:
    69  		return err
    70  	case !rp.DenyGrant:
    71  		return errtypes.PermissionDenied(filepath.Join(grantNode.ParentID, grantNode.Name))
    72  	}
    73  
    74  	return fs.storeGrant(ctx, grantNode, grant)
    75  }
    76  
    77  // AddGrant adds a grant to a resource
    78  func (fs *Decomposedfs) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) {
    79  	_, span := tracer.Start(ctx, "AddGrant")
    80  	defer span.End()
    81  	log := appctx.GetLogger(ctx)
    82  	log.Debug().Interface("ref", ref).Interface("grant", g).Msg("AddGrant()")
    83  	grantNode, unlockFunc, grant, err := fs.loadGrant(ctx, ref, g)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	defer func() {
    88  		_ = unlockFunc()
    89  	}()
    90  
    91  	if grant != nil {
    92  		return errtypes.AlreadyExists(filepath.Join(grantNode.ParentID, grantNode.Name))
    93  	}
    94  
    95  	owner := grantNode.Owner()
    96  	grants, err := grantNode.ListGrants(ctx)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	// If the owner is empty and there are no grantees then we are dealing with a just created project space.
   102  	// In this case we don't need to check for permissions and just add the grant since this will be the project
   103  	// manager.
   104  	// When the owner is empty but grants are set then we do want to check the grants.
   105  	// However, if we are trying to edit an existing grant we do not have to check for permission if the user owns the grant
   106  	// TODO: find a better to check this
   107  	if !(len(grants) == 0 && (owner == nil || owner.OpaqueId == "" || (owner.OpaqueId == grantNode.SpaceID && owner.Type == 8))) {
   108  		rp, err := fs.p.AssemblePermissions(ctx, grantNode)
   109  		switch {
   110  		case err != nil:
   111  			return err
   112  		case !rp.AddGrant:
   113  			f, _ := storagespace.FormatReference(ref)
   114  			if rp.Stat {
   115  				return errtypes.PermissionDenied(f)
   116  			}
   117  			return errtypes.NotFound(f)
   118  		}
   119  	}
   120  
   121  	return fs.storeGrant(ctx, grantNode, g)
   122  }
   123  
   124  // ListGrants lists the grants on the specified resource
   125  func (fs *Decomposedfs) ListGrants(ctx context.Context, ref *provider.Reference) (grants []*provider.Grant, err error) {
   126  	_, span := tracer.Start(ctx, "ListGrants")
   127  	defer span.End()
   128  	var grantNode *node.Node
   129  	if grantNode, err = fs.lu.NodeFromResource(ctx, ref); err != nil {
   130  		return
   131  	}
   132  	if !grantNode.Exists {
   133  		err = errtypes.NotFound(filepath.Join(grantNode.ParentID, grantNode.Name))
   134  		return
   135  	}
   136  	rp, err := fs.p.AssemblePermissions(ctx, grantNode)
   137  	switch {
   138  	case err != nil:
   139  		return nil, err
   140  	case !rp.ListGrants && !rp.Stat:
   141  		f, _ := storagespace.FormatReference(ref)
   142  		return nil, errtypes.NotFound(f)
   143  	}
   144  	log := appctx.GetLogger(ctx)
   145  	var attrs node.Attributes
   146  	if attrs, err = grantNode.Xattrs(ctx); err != nil {
   147  		log.Error().Err(err).Msg("error listing attributes")
   148  		return nil, err
   149  	}
   150  
   151  	aces := []*ace.ACE{}
   152  	for k, v := range attrs {
   153  		if strings.HasPrefix(k, prefixes.GrantPrefix) {
   154  			var err error
   155  			var e *ace.ACE
   156  			principal := k[len(prefixes.GrantPrefix):]
   157  			if e, err = ace.Unmarshal(principal, v); err != nil {
   158  				log.Error().Err(err).Str("principal", principal).Str("attr", k).Msg("could not unmarshal ace")
   159  				continue
   160  			}
   161  			aces = append(aces, e)
   162  		}
   163  	}
   164  
   165  	uid := ctxpkg.ContextMustGetUser(ctx).GetId()
   166  	grants = make([]*provider.Grant, 0, len(aces))
   167  	for i := range aces {
   168  		g := aces[i].Grant()
   169  
   170  		// you may list your own grants even without listgrants permission
   171  		if !rp.ListGrants && !utils.UserIDEqual(g.Creator, uid) && !utils.UserIDEqual(g.Grantee.GetUserId(), uid) {
   172  			continue
   173  		}
   174  
   175  		grants = append(grants, g)
   176  	}
   177  
   178  	return grants, nil
   179  }
   180  
   181  // RemoveGrant removes a grant from resource
   182  func (fs *Decomposedfs) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) {
   183  	_, span := tracer.Start(ctx, "RemoveGrant")
   184  	defer span.End()
   185  	grantNode, unlockFunc, grant, err := fs.loadGrant(ctx, ref, g)
   186  	if err != nil {
   187  		return err
   188  	}
   189  	defer func() {
   190  		_ = unlockFunc()
   191  	}()
   192  
   193  	if grant == nil {
   194  		return errtypes.NotFound("grant not found")
   195  	}
   196  
   197  	// you are allowed to remove grants if you created them yourself or have the proper permission
   198  	if !utils.UserIDEqual(grant.Creator, ctxpkg.ContextMustGetUser(ctx).GetId()) {
   199  		rp, err := fs.p.AssemblePermissions(ctx, grantNode)
   200  		switch {
   201  		case err != nil:
   202  			return err
   203  		case !rp.RemoveGrant:
   204  			f, _ := storagespace.FormatReference(ref)
   205  			if rp.Stat {
   206  				return errtypes.PermissionDenied(f)
   207  			}
   208  			return errtypes.NotFound(f)
   209  		}
   210  	}
   211  
   212  	if err := grantNode.DeleteGrant(ctx, g, false); err != nil {
   213  		return err
   214  	}
   215  
   216  	if isShareGrant(ctx) {
   217  		// do not invalidate by user or group indexes
   218  		// FIXME we should invalidate the by-type index, but that requires reference counting
   219  	} else {
   220  		// invalidate space grant
   221  		switch {
   222  		case g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER:
   223  			// remove from user index
   224  			if err := fs.userSpaceIndex.Remove(g.Grantee.GetUserId().GetOpaqueId(), grantNode.SpaceID); err != nil {
   225  				return err
   226  			}
   227  		case g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP:
   228  			// remove from group index
   229  			if err := fs.groupSpaceIndex.Remove(g.Grantee.GetGroupId().GetOpaqueId(), grantNode.SpaceID); err != nil {
   230  				return err
   231  			}
   232  		}
   233  	}
   234  
   235  	return fs.tp.Propagate(ctx, grantNode, 0)
   236  }
   237  
   238  func isShareGrant(ctx context.Context) bool {
   239  	_, ok := storageprovider.SpaceTypeFromContext(ctx)
   240  	return !ok
   241  }
   242  
   243  // UpdateGrant updates a grant on a resource
   244  // TODO remove AddGrant or UpdateGrant grant from CS3 api, redundant? tracked in https://github.com/cs3org/cs3apis/issues/92
   245  func (fs *Decomposedfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error {
   246  	_, span := tracer.Start(ctx, "UpdateGrant")
   247  	defer span.End()
   248  	log := appctx.GetLogger(ctx)
   249  	log.Debug().Interface("ref", ref).Interface("grant", g).Msg("UpdateGrant()")
   250  
   251  	grantNode, unlockFunc, grant, err := fs.loadGrant(ctx, ref, g)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	defer func() {
   256  		_ = unlockFunc()
   257  	}()
   258  
   259  	if grant == nil {
   260  		// grant not found
   261  		// TODO: fallback to AddGrant?
   262  		return errtypes.NotFound(g.Grantee.GetUserId().GetOpaqueId())
   263  	}
   264  
   265  	// You may update a grant when you have the UpdateGrant permission or created the grant (regardless what your permissions are now)
   266  	if !utils.UserIDEqual(grant.Creator, ctxpkg.ContextMustGetUser(ctx).GetId()) {
   267  		rp, err := fs.p.AssemblePermissions(ctx, grantNode)
   268  		switch {
   269  		case err != nil:
   270  			return err
   271  		case !rp.UpdateGrant:
   272  			f, _ := storagespace.FormatReference(ref)
   273  			if rp.Stat {
   274  				return errtypes.PermissionDenied(f)
   275  			}
   276  			return errtypes.NotFound(f)
   277  		}
   278  	}
   279  
   280  	return fs.storeGrant(ctx, grantNode, g)
   281  }
   282  
   283  // checks if the given grant exists and returns it. Nil grant means it doesn't exist
   284  func (fs *Decomposedfs) loadGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (*node.Node, metadata.UnlockFunc, *provider.Grant, error) {
   285  	_, span := tracer.Start(ctx, "loadGrant")
   286  	defer span.End()
   287  	n, err := fs.lu.NodeFromResource(ctx, ref)
   288  	if err != nil {
   289  		return nil, nil, nil, err
   290  	}
   291  	if !n.Exists {
   292  		return nil, nil, nil, errtypes.NotFound(filepath.Join(n.ParentID, n.Name))
   293  	}
   294  
   295  	// lock the metadata file
   296  	unlockFunc, err := fs.lu.MetadataBackend().Lock(n.InternalPath())
   297  	if err != nil {
   298  		return nil, nil, nil, err
   299  	}
   300  
   301  	grants, err := n.ListGrants(ctx)
   302  	if err != nil {
   303  		return nil, nil, nil, err
   304  	}
   305  
   306  	for _, grant := range grants {
   307  		switch grant.Grantee.GetType() {
   308  		case provider.GranteeType_GRANTEE_TYPE_USER:
   309  			if g.Grantee.GetUserId().GetOpaqueId() == grant.Grantee.GetUserId().GetOpaqueId() {
   310  				return n, unlockFunc, grant, nil
   311  			}
   312  		case provider.GranteeType_GRANTEE_TYPE_GROUP:
   313  			if g.Grantee.GetGroupId().GetOpaqueId() == grant.Grantee.GetGroupId().GetOpaqueId() {
   314  				return n, unlockFunc, grant, nil
   315  			}
   316  		}
   317  	}
   318  
   319  	return n, unlockFunc, nil, nil
   320  }
   321  
   322  func (fs *Decomposedfs) storeGrant(ctx context.Context, n *node.Node, g *provider.Grant) error {
   323  	_, span := tracer.Start(ctx, "storeGrant")
   324  	defer span.End()
   325  	// if is a grant to a space root, the receiver needs the space type to update the indexes
   326  	spaceType, ok := storageprovider.SpaceTypeFromContext(ctx)
   327  	if !ok {
   328  		// this is not a grant on a space root we are just adding a share
   329  		spaceType = spaceTypeShare
   330  	}
   331  
   332  	// set the grant
   333  	e := ace.FromGrant(g)
   334  	principal, value := e.Marshal()
   335  	attribs := node.Attributes{
   336  		prefixes.GrantPrefix + principal: value,
   337  	}
   338  	if err := n.SetXattrsWithContext(ctx, attribs, false); err != nil {
   339  		appctx.GetLogger(ctx).Error().Err(err).
   340  			Str("principal", principal).Msg("Could not set grant for principal")
   341  		return err
   342  	}
   343  
   344  	// update the indexes only after successfully setting the grant
   345  	err := fs.updateIndexes(ctx, g.GetGrantee(), spaceType, n.SpaceID, n.ID)
   346  	if err != nil {
   347  		return err
   348  	}
   349  
   350  	return fs.tp.Propagate(ctx, n, 0)
   351  }