github.com/cs3org/reva/v2@v2.27.7/pkg/share/manager/memory/memory.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 memory 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "sync" 26 "sync/atomic" 27 "time" 28 29 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 30 "github.com/cs3org/reva/v2/pkg/share" 31 "google.golang.org/genproto/protobuf/field_mask" 32 33 userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 34 collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 35 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 36 typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 37 "github.com/cs3org/reva/v2/pkg/errtypes" 38 "github.com/cs3org/reva/v2/pkg/share/manager/registry" 39 "github.com/cs3org/reva/v2/pkg/utils" 40 ) 41 42 var counter uint64 43 44 func init() { 45 registry.Register("memory", New) 46 } 47 48 // New returns a new manager. 49 func New(c map[string]interface{}) (share.Manager, error) { 50 state := map[string]map[*collaboration.ShareId]collaboration.ShareState{} 51 mp := map[string]map[*collaboration.ShareId]*provider.Reference{} 52 return &manager{ 53 shareState: state, 54 shareMountPoint: mp, 55 lock: &sync.Mutex{}, 56 }, nil 57 } 58 59 type manager struct { 60 lock *sync.Mutex 61 shares []*collaboration.Share 62 // shareState contains the share state for a user. 63 // map["alice"]["share-id"]state. 64 shareState map[string]map[*collaboration.ShareId]collaboration.ShareState 65 // shareMountPoint contains the mountpoint of a share for a user. 66 // map["alice"]["share-id"]reference. 67 shareMountPoint map[string]map[*collaboration.ShareId]*provider.Reference 68 } 69 70 func (m *manager) add(ctx context.Context, s *collaboration.Share) { 71 m.lock.Lock() 72 defer m.lock.Unlock() 73 m.shares = append(m.shares, s) 74 } 75 76 func (m *manager) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { 77 id := atomic.AddUint64(&counter, 1) 78 user := ctxpkg.ContextMustGetUser(ctx) 79 now := time.Now().UnixNano() 80 ts := &typespb.Timestamp{ 81 Seconds: uint64(now / 1000000000), 82 Nanos: uint32(now % 1000000000), 83 } 84 85 if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && 86 (utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) { 87 return nil, errtypes.BadRequest("memory: owner/creator and grantee are the same") 88 } 89 90 // check if share already exists. 91 key := &collaboration.ShareKey{ 92 Owner: md.Owner, 93 ResourceId: md.Id, 94 Grantee: g.Grantee, 95 } 96 _, err := m.getByKey(ctx, key) 97 // share already exists 98 if err == nil { 99 return nil, errtypes.AlreadyExists(key.String()) 100 } 101 102 s := &collaboration.Share{ 103 Id: &collaboration.ShareId{ 104 OpaqueId: fmt.Sprintf("%d", id), 105 }, 106 ResourceId: md.Id, 107 Permissions: g.Permissions, 108 Grantee: g.Grantee, 109 Owner: md.Owner, 110 Creator: user.Id, 111 Ctime: ts, 112 Mtime: ts, 113 } 114 115 m.add(ctx, s) 116 return s, nil 117 } 118 119 func (m *manager) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) { 120 m.lock.Lock() 121 defer m.lock.Unlock() 122 for _, s := range m.shares { 123 if s.GetId().OpaqueId == id.OpaqueId { 124 return s, nil 125 } 126 } 127 return nil, errtypes.NotFound(id.String()) 128 } 129 130 func (m *manager) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) { 131 m.lock.Lock() 132 defer m.lock.Unlock() 133 for _, s := range m.shares { 134 if (utils.UserEqual(key.Owner, s.Owner) || utils.UserEqual(key.Owner, s.Creator)) && 135 utils.ResourceIDEqual(key.ResourceId, s.ResourceId) && utils.GranteeEqual(key.Grantee, s.Grantee) { 136 return s, nil 137 } 138 } 139 return nil, errtypes.NotFound(key.String()) 140 } 141 142 func (m *manager) get(ctx context.Context, ref *collaboration.ShareReference) (s *collaboration.Share, err error) { 143 switch { 144 case ref.GetId() != nil: 145 s, err = m.getByID(ctx, ref.GetId()) 146 case ref.GetKey() != nil: 147 s, err = m.getByKey(ctx, ref.GetKey()) 148 default: 149 err = errtypes.NotFound(ref.String()) 150 } 151 152 if err != nil { 153 return nil, err 154 } 155 156 // check if we are the owner 157 user := ctxpkg.ContextMustGetUser(ctx) 158 if share.IsCreatedByUser(s, user) { 159 return s, nil 160 } 161 162 // or the grantee 163 if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, s.Grantee.GetUserId()) { 164 return s, nil 165 } else if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP { 166 // check if all user groups match this share; TODO(labkode): filter shares created by us. 167 for _, g := range user.Groups { 168 if g == s.Grantee.GetGroupId().OpaqueId { 169 return s, nil 170 } 171 } 172 } 173 174 // we return not found to not disclose information 175 return nil, errtypes.NotFound(ref.String()) 176 } 177 178 func (m *manager) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { 179 share, err := m.get(ctx, ref) 180 if err != nil { 181 return nil, err 182 } 183 184 return share, nil 185 } 186 187 func (m *manager) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { 188 m.lock.Lock() 189 defer m.lock.Unlock() 190 user := ctxpkg.ContextMustGetUser(ctx) 191 for i, s := range m.shares { 192 if sharesEqual(ref, s) { 193 if share.IsCreatedByUser(s, user) { 194 m.shares[len(m.shares)-1], m.shares[i] = m.shares[i], m.shares[len(m.shares)-1] 195 m.shares = m.shares[:len(m.shares)-1] 196 return nil 197 } 198 } 199 } 200 return errtypes.NotFound(ref.String()) 201 } 202 203 func sharesEqual(ref *collaboration.ShareReference, s *collaboration.Share) bool { 204 if ref.GetId() != nil && s.Id != nil { 205 if ref.GetId().OpaqueId == s.Id.OpaqueId { 206 return true 207 } 208 } else if ref.GetKey() != nil { 209 if (utils.UserEqual(ref.GetKey().Owner, s.Owner) || utils.UserEqual(ref.GetKey().Owner, s.Creator)) && 210 utils.ResourceIDEqual(ref.GetKey().ResourceId, s.ResourceId) && utils.GranteeEqual(ref.GetKey().Grantee, s.Grantee) { 211 return true 212 } 213 } 214 return false 215 } 216 217 func (m *manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) { 218 m.lock.Lock() 219 defer m.lock.Unlock() 220 user := ctxpkg.ContextMustGetUser(ctx) 221 var shareRef *collaboration.ShareReference 222 if ref != nil { 223 shareRef = ref 224 } else if updated != nil { 225 shareRef = &collaboration.ShareReference{ 226 Spec: &collaboration.ShareReference_Id{ 227 Id: updated.Id, 228 }, 229 } 230 } 231 232 for i, s := range m.shares { 233 if sharesEqual(shareRef, s) { 234 if share.IsCreatedByUser(s, user) { 235 now := time.Now().UnixNano() 236 if p != nil { 237 m.shares[i].Permissions = p 238 } 239 if fieldMask != nil { 240 for _, path := range fieldMask.Paths { 241 switch path { 242 case "permissions": 243 m.shares[i].Permissions = updated.Permissions 244 case "expiration": 245 m.shares[i].Expiration = updated.Expiration 246 default: 247 return nil, errtypes.NotSupported("updating " + path + " is not supported") 248 } 249 } 250 } 251 m.shares[i].Mtime = &typespb.Timestamp{ 252 Seconds: uint64(now / 1000000000), 253 Nanos: uint32(now % 1000000000), 254 } 255 return m.shares[i], nil 256 } 257 } 258 } 259 return nil, errtypes.NotFound(ref.String()) 260 } 261 262 func (m *manager) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { 263 var ss []*collaboration.Share 264 m.lock.Lock() 265 defer m.lock.Unlock() 266 user := ctxpkg.ContextMustGetUser(ctx) 267 for _, s := range m.shares { 268 if share.IsCreatedByUser(s, user) { 269 // no filter we return earlier 270 if len(filters) == 0 { 271 ss = append(ss, s) 272 continue 273 } 274 // check filters 275 if share.MatchesFilters(s, filters) { 276 ss = append(ss, s) 277 } 278 } 279 } 280 return ss, nil 281 } 282 283 // we list the shares that are targeted to the user in context or to the user groups. 284 func (m *manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error) { 285 var rss []*collaboration.ReceivedShare 286 m.lock.Lock() 287 defer m.lock.Unlock() 288 user := ctxpkg.ContextMustGetUser(ctx) 289 if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { 290 // TODO: gateway missing! 291 return nil, errors.New("can't use inmem share manager and service accounts") 292 } 293 for _, s := range m.shares { 294 if share.IsCreatedByUser(s, user) || !share.IsGrantedToUser(s, user) { 295 // omit shares created by the user or shares the user can't access 296 continue 297 } 298 299 if len(filters) == 0 { 300 rs := m.convert(ctx, s) 301 rss = append(rss, rs) 302 continue 303 } 304 305 if share.MatchesFilters(s, filters) { 306 rs := m.convert(ctx, s) 307 rss = append(rss, rs) 308 } 309 } 310 return rss, nil 311 } 312 313 // convert must be called in a lock-controlled block. 314 func (m *manager) convert(ctx context.Context, s *collaboration.Share) *collaboration.ReceivedShare { 315 rs := &collaboration.ReceivedShare{ 316 Share: s, 317 State: collaboration.ShareState_SHARE_STATE_PENDING, 318 } 319 user := ctxpkg.ContextMustGetUser(ctx) 320 if v, ok := m.shareState[user.Id.String()]; ok { 321 if state, ok := v[s.Id]; ok { 322 rs.State = state 323 } 324 } 325 if v, ok := m.shareMountPoint[user.Id.String()]; ok { 326 if mp, ok := v[s.Id]; ok { 327 rs.MountPoint = mp 328 } 329 } 330 return rs 331 } 332 333 func (m *manager) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { 334 return m.getReceived(ctx, ref) 335 } 336 337 func (m *manager) getReceived(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { 338 m.lock.Lock() 339 defer m.lock.Unlock() 340 user := ctxpkg.ContextMustGetUser(ctx) 341 for _, s := range m.shares { 342 if sharesEqual(ref, s) { 343 if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE || share.IsGrantedToUser(s, user) { 344 rs := m.convert(ctx, s) 345 return rs, nil 346 } 347 } 348 } 349 return nil, errtypes.NotFound(ref.String()) 350 } 351 352 func (m *manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) { 353 rs, err := m.getReceived(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}}) 354 if err != nil { 355 return nil, err 356 } 357 358 m.lock.Lock() 359 defer m.lock.Unlock() 360 361 for i := range fieldMask.Paths { 362 switch fieldMask.Paths[i] { 363 case "state": 364 rs.State = receivedShare.State 365 case "mount_point": 366 rs.MountPoint = receivedShare.MountPoint 367 case "hidden": 368 continue 369 default: 370 return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported") 371 } 372 } 373 374 u := ctxpkg.ContextMustGetUser(ctx) 375 uid := u.GetId().String() 376 if u.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { 377 uid = forUser.String() 378 } 379 380 // Persist state 381 if v, ok := m.shareState[uid]; ok { 382 v[rs.Share.Id] = rs.State 383 m.shareState[uid] = v 384 } else { 385 a := map[*collaboration.ShareId]collaboration.ShareState{ 386 rs.Share.Id: rs.State, 387 } 388 m.shareState[uid] = a 389 } 390 // Persist mount point 391 if v, ok := m.shareMountPoint[uid]; ok { 392 v[rs.Share.Id] = rs.MountPoint 393 m.shareMountPoint[uid] = v 394 } else { 395 a := map[*collaboration.ShareId]*provider.Reference{ 396 rs.Share.Id: rs.MountPoint, 397 } 398 m.shareMountPoint[uid] = a 399 } 400 401 return rs, nil 402 }