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 }