github.com/cs3org/reva/v2@v2.27.7/pkg/share/share.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 share
    20  
    21  import (
    22  	"context"
    23  	"time"
    24  
    25  	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    26  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    27  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    28  	types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    29  	"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
    30  	"github.com/cs3org/reva/v2/pkg/utils"
    31  	"google.golang.org/genproto/protobuf/field_mask"
    32  )
    33  
    34  const (
    35  	// NoState can be used to signal the filter matching functions to ignore the share state.
    36  	NoState collaboration.ShareState = -1
    37  )
    38  
    39  // Metadata contains Metadata for a share
    40  type Metadata struct {
    41  	ETag  string
    42  	Mtime *types.Timestamp
    43  }
    44  
    45  // Manager is the interface that manipulates shares.
    46  type Manager interface {
    47  	// Create a new share in fn with the given acl.
    48  	Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error)
    49  
    50  	// GetShare gets the information for a share by the given ref.
    51  	GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error)
    52  
    53  	// Unshare deletes the share pointed by ref.
    54  	Unshare(ctx context.Context, ref *collaboration.ShareReference) error
    55  
    56  	// UpdateShare updates the mode of the given share.
    57  	UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error)
    58  
    59  	// ListShares returns the shares created by the user. If md is provided is not nil,
    60  	// it returns only shares attached to the given resource.
    61  	ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error)
    62  
    63  	// ListReceivedShares returns the list of shares the user has access to. `forUser` parameter for service accounts only
    64  	ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error)
    65  
    66  	// GetReceivedShare returns the information for a received share.
    67  	GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error)
    68  
    69  	// UpdateReceivedShare updates the received share with share state.`forUser` parameter for service accounts only
    70  	UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error)
    71  }
    72  
    73  // ReceivedShareWithUser holds the relevant information for representing a received share of a user
    74  type ReceivedShareWithUser struct {
    75  	UserID        *userv1beta1.UserId
    76  	ReceivedShare *collaboration.ReceivedShare
    77  }
    78  
    79  // DumpableManager defines a share manager which supports dumping its contents
    80  type DumpableManager interface {
    81  	Dump(ctx context.Context, shareChan chan<- *collaboration.Share, receivedShareChan chan<- ReceivedShareWithUser) error
    82  }
    83  
    84  // LoadableManager defines a share manager which supports loading contents from a dump
    85  type LoadableManager interface {
    86  	Load(ctx context.Context, shareChan <-chan *collaboration.Share, receivedShareChan <-chan ReceivedShareWithUser) error
    87  }
    88  
    89  // GroupGranteeFilter is an abstraction for creating filter by grantee type group.
    90  func GroupGranteeFilter() *collaboration.Filter {
    91  	return &collaboration.Filter{
    92  		Type: collaboration.Filter_TYPE_GRANTEE_TYPE,
    93  		Term: &collaboration.Filter_GranteeType{
    94  			GranteeType: provider.GranteeType_GRANTEE_TYPE_GROUP,
    95  		},
    96  	}
    97  }
    98  
    99  // UserGranteeFilter is an abstraction for creating filter by grantee type user.
   100  func UserGranteeFilter() *collaboration.Filter {
   101  	return &collaboration.Filter{
   102  		Type: collaboration.Filter_TYPE_GRANTEE_TYPE,
   103  		Term: &collaboration.Filter_GranteeType{
   104  			GranteeType: provider.GranteeType_GRANTEE_TYPE_USER,
   105  		},
   106  	}
   107  }
   108  
   109  // ResourceIDFilter is an abstraction for creating filter by resource id.
   110  func ResourceIDFilter(id *provider.ResourceId) *collaboration.Filter {
   111  	return &collaboration.Filter{
   112  		Type: collaboration.Filter_TYPE_RESOURCE_ID,
   113  		Term: &collaboration.Filter_ResourceId{
   114  			ResourceId: id,
   115  		},
   116  	}
   117  }
   118  
   119  // SpaceIDFilter is an abstraction for creating filter by space id.
   120  func SpaceIDFilter(id string) *collaboration.Filter {
   121  	return &collaboration.Filter{
   122  		Type: collaboration.Filter_TYPE_SPACE_ID,
   123  		Term: &collaboration.Filter_SpaceId{
   124  			SpaceId: id,
   125  		},
   126  	}
   127  }
   128  
   129  // StateFilter is an abstraction for creating filter by share state.
   130  func StateFilter(state collaboration.ShareState) *collaboration.Filter {
   131  	return &collaboration.Filter{
   132  		Type: collaboration.Filter_TYPE_STATE,
   133  		Term: &collaboration.Filter_State{
   134  			State: state,
   135  		},
   136  	}
   137  }
   138  
   139  // IsCreatedByUser checks if the user is the owner or creator of the share.
   140  func IsCreatedByUser(share *collaboration.Share, user *userv1beta1.User) bool {
   141  	return utils.UserEqual(user.Id, share.Owner) || utils.UserEqual(user.Id, share.Creator)
   142  }
   143  
   144  // IsGrantedToUser checks if the user is a grantee of the share. Either by a user grant or by a group grant.
   145  func IsGrantedToUser(share *collaboration.Share, user *userv1beta1.User) bool {
   146  	if share.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, share.Grantee.GetUserId()) {
   147  		return true
   148  	}
   149  	if share.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
   150  		// check if any of the user's group is the grantee of the share
   151  		for _, g := range user.Groups {
   152  			if g == share.Grantee.GetGroupId().OpaqueId {
   153  				return true
   154  			}
   155  		}
   156  	}
   157  	return false
   158  }
   159  
   160  // MatchesFilter tests if the share passes the filter.
   161  func MatchesFilter(share *collaboration.Share, state collaboration.ShareState, filter *collaboration.Filter) bool {
   162  	switch filter.Type {
   163  	case collaboration.Filter_TYPE_RESOURCE_ID:
   164  		return utils.ResourceIDEqual(share.ResourceId, filter.GetResourceId())
   165  	case collaboration.Filter_TYPE_GRANTEE_TYPE:
   166  		return share.Grantee.Type == filter.GetGranteeType()
   167  	case collaboration.Filter_TYPE_EXCLUDE_DENIALS:
   168  		// This filter type is used to filter out "denial shares". These are currently implemented by having the permission "0".
   169  		// I.e. if the permission is 0 we don't want to show it.
   170  		return !grants.PermissionsEqual(share.Permissions.Permissions, &provider.ResourcePermissions{})
   171  	case collaboration.Filter_TYPE_SPACE_ID:
   172  		return share.ResourceId.SpaceId == filter.GetSpaceId()
   173  	case collaboration.Filter_TYPE_STATE:
   174  		return state == filter.GetState()
   175  	default:
   176  		return false
   177  	}
   178  }
   179  
   180  // MatchesAnyFilter checks if the share passes at least one of the given filters.
   181  func MatchesAnyFilter(share *collaboration.Share, state collaboration.ShareState, filters []*collaboration.Filter) bool {
   182  	for _, f := range filters {
   183  		if MatchesFilter(share, state, f) {
   184  			return true
   185  		}
   186  	}
   187  	return false
   188  }
   189  
   190  // MatchesFilters checks if the share passes the given filters.
   191  // Filters of the same type form a disjuntion, a logical OR. Filters of separate type form a conjunction, a logical AND.
   192  // Here is an example:
   193  // (resource_id=1 OR resource_id=2) AND (grantee_type=USER OR grantee_type=GROUP)
   194  func MatchesFilters(share *collaboration.Share, filters []*collaboration.Filter) bool {
   195  	if len(filters) == 0 {
   196  		return true
   197  	}
   198  	grouped := GroupFiltersByType(filters)
   199  	for _, f := range grouped {
   200  		if !MatchesAnyFilter(share, NoState, f) {
   201  			return false
   202  		}
   203  	}
   204  	return true
   205  }
   206  
   207  // MatchesFiltersWithState checks if the share passes the given filters.
   208  // This can check filter by share state
   209  // Filters of the same type form a disjuntion, a logical OR. Filters of separate type form a conjunction, a logical AND.
   210  // Here is an example:
   211  // (resource_id=1 OR resource_id=2) AND (grantee_type=USER OR grantee_type=GROUP)
   212  func MatchesFiltersWithState(share *collaboration.Share, state collaboration.ShareState, filters []*collaboration.Filter) bool {
   213  	if len(filters) == 0 {
   214  		return true
   215  	}
   216  	grouped := GroupFiltersByType(filters)
   217  	for _, f := range grouped {
   218  		if !MatchesAnyFilter(share, state, f) {
   219  			return false
   220  		}
   221  	}
   222  	return true
   223  }
   224  
   225  // GroupFiltersByType groups the given filters and returns a map using the filter type as the key.
   226  func GroupFiltersByType(filters []*collaboration.Filter) map[collaboration.Filter_Type][]*collaboration.Filter {
   227  	grouped := make(map[collaboration.Filter_Type][]*collaboration.Filter)
   228  	for _, f := range filters {
   229  		grouped[f.Type] = append(grouped[f.Type], f)
   230  	}
   231  	return grouped
   232  }
   233  
   234  // FilterFiltersByType returns a slice of filters by a given type.
   235  // If no filter with the given type exists within the filters, then an
   236  // empty slice is returned.
   237  func FilterFiltersByType(f []*collaboration.Filter, t collaboration.Filter_Type) []*collaboration.Filter {
   238  	return GroupFiltersByType(f)[t]
   239  }
   240  
   241  // IsExpired tests whether a share is expired
   242  func IsExpired(s *collaboration.Share) bool {
   243  	if e := s.GetExpiration(); e != nil {
   244  		expiration := time.Unix(int64(e.Seconds), int64(e.Nanos))
   245  		return expiration.Before(time.Now())
   246  	}
   247  	return false
   248  }