github.com/cs3org/reva/v2@v2.27.7/pkg/share/manager/cs3/cs3.go (about) 1 // Copyright 2018-2022 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 cs3 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "net/url" 26 "path" 27 "strings" 28 "sync" 29 30 gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 31 groupv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" 32 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 33 rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 34 collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 35 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 36 "github.com/cs3org/reva/v2/pkg/appctx" 37 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 38 "github.com/cs3org/reva/v2/pkg/errtypes" 39 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 40 "github.com/cs3org/reva/v2/pkg/share" 41 "github.com/cs3org/reva/v2/pkg/share/manager/registry" 42 "github.com/cs3org/reva/v2/pkg/storage/utils/indexer" 43 indexerErrors "github.com/cs3org/reva/v2/pkg/storage/utils/indexer/errors" 44 "github.com/cs3org/reva/v2/pkg/storage/utils/indexer/option" 45 "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" 46 "github.com/cs3org/reva/v2/pkg/storagespace" 47 "github.com/cs3org/reva/v2/pkg/utils" 48 "github.com/google/uuid" 49 "github.com/mitchellh/mapstructure" 50 "github.com/pkg/errors" 51 "google.golang.org/genproto/protobuf/field_mask" 52 ) 53 54 // Manager implements a share manager using a cs3 storage backend 55 type Manager struct { 56 gatewayClient gatewayv1beta1.GatewayAPIClient 57 58 sync.RWMutex 59 storage metadata.Storage 60 indexer indexer.Indexer 61 62 initialized bool 63 } 64 65 // ReceivedShareMetadata hold the state information or a received share 66 type ReceivedShareMetadata struct { 67 State collaboration.ShareState `json:"state"` 68 MountPoint *provider.Reference `json:"mountpoint"` 69 } 70 71 func init() { 72 registry.Register("cs3", NewDefault) 73 } 74 75 type config struct { 76 GatewayAddr string `mapstructure:"gateway_addr"` 77 ProviderAddr string `mapstructure:"provider_addr"` 78 ServiceUserID string `mapstructure:"service_user_id"` 79 ServiceUserIdp string `mapstructure:"service_user_idp"` 80 MachineAuthAPIKey string `mapstructure:"machine_auth_apikey"` 81 } 82 83 // NewDefault returns a new manager instance with default dependencies 84 func NewDefault(m map[string]interface{}) (share.Manager, error) { 85 c := &config{} 86 if err := mapstructure.Decode(m, c); err != nil { 87 err = errors.Wrap(err, "error creating a new manager") 88 return nil, err 89 } 90 91 s, err := metadata.NewCS3Storage(c.GatewayAddr, c.ProviderAddr, c.ServiceUserID, c.ServiceUserIdp, c.MachineAuthAPIKey) 92 if err != nil { 93 return nil, err 94 } 95 indexer := indexer.CreateIndexer(s) 96 97 client, err := pool.GetGatewayServiceClient(c.GatewayAddr) 98 if err != nil { 99 return nil, err 100 } 101 102 return New(client, s, indexer) 103 } 104 105 // New returns a new manager instance 106 func New(gatewayClient gatewayv1beta1.GatewayAPIClient, s metadata.Storage, indexer indexer.Indexer) (*Manager, error) { 107 return &Manager{ 108 gatewayClient: gatewayClient, 109 storage: s, 110 indexer: indexer, 111 initialized: false, 112 }, nil 113 } 114 115 func (m *Manager) initialize() error { 116 if m.initialized { 117 return nil 118 } 119 120 m.Lock() 121 defer m.Unlock() 122 123 if m.initialized { // check if initialization happened while grabbing the lock 124 return nil 125 } 126 127 err := m.storage.Init(context.Background(), "cs3-share-manager-metadata") 128 if err != nil { 129 return err 130 } 131 if err := m.storage.MakeDirIfNotExist(context.Background(), "shares"); err != nil { 132 return err 133 } 134 if err := m.storage.MakeDirIfNotExist(context.Background(), "metadata"); err != nil { 135 return err 136 } 137 err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{ 138 Name: "OwnerId", 139 Func: indexOwnerFunc, 140 }, "Id.OpaqueId", "shares", "non_unique", nil, true) 141 if err != nil { 142 return err 143 } 144 err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{ 145 Name: "CreatorId", 146 Func: indexCreatorFunc, 147 }, "Id.OpaqueId", "shares", "non_unique", nil, true) 148 if err != nil { 149 return err 150 } 151 err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{ 152 Name: "GranteeId", 153 Func: indexGranteeFunc, 154 }, "Id.OpaqueId", "shares", "non_unique", nil, true) 155 if err != nil { 156 return err 157 } 158 err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{ 159 Name: "ResourceId", 160 Func: indexResourceIDFunc, 161 }, "Id.OpaqueId", "shares", "non_unique", nil, true) 162 if err != nil { 163 return err 164 } 165 m.initialized = true 166 return nil 167 } 168 169 // Load imports shares and received shares from channels (e.g. during migration) 170 func (m *Manager) Load(ctx context.Context, shareChan <-chan *collaboration.Share, receivedShareChan <-chan share.ReceivedShareWithUser) error { 171 log := appctx.GetLogger(ctx) 172 if err := m.initialize(); err != nil { 173 return err 174 } 175 176 var mu sync.Mutex 177 var wg sync.WaitGroup 178 wg.Add(2) 179 go func() { 180 for s := range shareChan { 181 if s == nil { 182 continue 183 } 184 mu.Lock() 185 if err := m.persistShare(context.Background(), s); err != nil { 186 log.Error().Err(err).Interface("share", s).Msg("error persisting share") 187 } 188 mu.Unlock() 189 } 190 wg.Done() 191 }() 192 go func() { 193 for s := range receivedShareChan { 194 if s.ReceivedShare != nil && s.UserID != nil { 195 mu.Lock() 196 if err := m.persistReceivedShare(context.Background(), s.UserID, s.ReceivedShare); err != nil { 197 log.Error().Err(err).Interface("received share", s).Msg("error persisting received share") 198 } 199 mu.Unlock() 200 } 201 } 202 wg.Done() 203 }() 204 wg.Wait() 205 206 return nil 207 } 208 209 func (m *Manager) getMetadata(ctx context.Context, shareid, grantee string) ReceivedShareMetadata { 210 // use default values if the grantee didn't configure anything yet 211 metadata := ReceivedShareMetadata{ 212 State: collaboration.ShareState_SHARE_STATE_PENDING, 213 } 214 data, err := m.storage.SimpleDownload(ctx, path.Join("metadata", shareid, grantee)) 215 if err != nil { 216 return metadata 217 } 218 err = json.Unmarshal(data, &metadata) 219 if err != nil { 220 appctx.GetLogger(ctx).Error().Err(err).Str("shareid", shareid).Msg("error fetching share") 221 } 222 return metadata 223 } 224 225 // Dump exports shares and received shares to channels (e.g. during migration) 226 func (m *Manager) Dump(ctx context.Context, shareChan chan<- *collaboration.Share, receivedShareChan chan<- share.ReceivedShareWithUser) error { 227 log := appctx.GetLogger(ctx) 228 if err := m.initialize(); err != nil { 229 return err 230 } 231 232 shareids, err := m.storage.ReadDir(ctx, "shares") 233 if err != nil { 234 return err 235 } 236 for _, shareid := range shareids { 237 var s *collaboration.Share 238 if s, err = m.getShareByID(ctx, shareid); err != nil { 239 log.Error().Err(err).Str("shareid", shareid).Msg("error fetching share") 240 continue 241 } 242 // dump share data 243 shareChan <- s 244 // dump grantee metadata that includes share state and mount path 245 grantees, err := m.storage.ReadDir(ctx, path.Join("metadata", s.Id.OpaqueId)) 246 if err != nil { 247 continue 248 } 249 for _, grantee := range grantees { 250 metadata := m.getMetadata(ctx, s.GetId().GetOpaqueId(), grantee) 251 g, err := indexToGrantee(grantee) 252 if err != nil || g.Type != provider.GranteeType_GRANTEE_TYPE_USER { 253 // ignore group grants, as every user has his own received state 254 continue 255 } 256 receivedShareChan <- share.ReceivedShareWithUser{ 257 UserID: g.GetUserId(), 258 ReceivedShare: &collaboration.ReceivedShare{ 259 Share: s, 260 State: metadata.State, 261 MountPoint: metadata.MountPoint, 262 }, 263 } 264 } 265 } 266 return nil 267 } 268 269 // Share creates a new share 270 func (m *Manager) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { 271 if err := m.initialize(); err != nil { 272 return nil, err 273 } 274 user := ctxpkg.ContextMustGetUser(ctx) 275 // do not allow share to myself or the owner if share is for a user 276 if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && 277 (utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) { 278 return nil, errtypes.BadRequest("cs3: owner/creator and grantee are the same") 279 } 280 ts := utils.TSNow() 281 282 share := &collaboration.Share{ 283 Id: &collaboration.ShareId{ 284 OpaqueId: uuid.NewString(), 285 }, 286 ResourceId: md.Id, 287 Permissions: g.Permissions, 288 Grantee: g.Grantee, 289 Owner: md.Owner, 290 Creator: user.Id, 291 Ctime: ts, 292 Mtime: ts, 293 } 294 295 err := m.persistShare(ctx, share) 296 return share, err 297 } 298 299 func (m *Manager) persistShare(ctx context.Context, share *collaboration.Share) error { 300 data, err := json.Marshal(share) 301 if err != nil { 302 return err 303 } 304 305 err = m.storage.SimpleUpload(ctx, shareFilename(share.Id.OpaqueId), data) 306 if err != nil { 307 return err 308 } 309 310 metadataPath := path.Join("metadata", share.Id.OpaqueId) 311 err = m.storage.MakeDirIfNotExist(ctx, metadataPath) 312 if err != nil { 313 return err 314 } 315 316 _, err = m.indexer.Add(share) 317 if _, ok := err.(*indexerErrors.AlreadyExistsErr); ok { 318 return nil 319 } 320 return err 321 } 322 323 // GetShare gets the information for a share by the given ref. 324 func (m *Manager) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { 325 err := m.initialize() 326 if err != nil { 327 return nil, err 328 } 329 330 var s *collaboration.Share 331 switch { 332 case ref.GetId() != nil: 333 s, err = m.getShareByID(ctx, ref.GetId().OpaqueId) 334 case ref.GetKey() != nil: 335 s, err = m.getShareByKey(ctx, ref.GetKey()) 336 default: 337 return nil, errtypes.BadRequest("neither share id nor key was given") 338 } 339 340 if err != nil { 341 return nil, err 342 } 343 344 // check if we are the owner or the grantee 345 user := ctxpkg.ContextMustGetUser(ctx) 346 if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE || share.IsCreatedByUser(s, user) || share.IsGrantedToUser(s, user) { 347 return s, nil 348 } 349 350 return nil, errtypes.NotFound("not found") 351 } 352 353 // Unshare deletes the share pointed by ref. 354 func (m *Manager) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { 355 if err := m.initialize(); err != nil { 356 return err 357 } 358 share, err := m.GetShare(ctx, ref) 359 if err != nil { 360 return err 361 } 362 363 err = m.storage.Delete(ctx, shareFilename(ref.GetId().OpaqueId)) 364 if err != nil { 365 if _, ok := err.(errtypes.NotFound); !ok { 366 return err 367 } 368 } 369 370 return m.indexer.Delete(share) 371 } 372 373 // ListShares returns the shares created by the user 374 func (m *Manager) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { 375 if err := m.initialize(); err != nil { 376 return nil, err 377 } 378 379 user, ok := ctxpkg.ContextGetUser(ctx) 380 if !ok { 381 return nil, errtypes.UserRequired("error getting user from context") 382 } 383 var rIDs []*provider.ResourceId 384 if len(filters) != 0 { 385 grouped := share.GroupFiltersByType(filters) 386 for _, g := range grouped { 387 for _, f := range g { 388 if f.GetResourceId() != nil { 389 rIDs = append(rIDs, f.GetResourceId()) 390 } 391 } 392 } 393 } 394 var ( 395 createdShareIds []string 396 err error 397 ) 398 // in spaces, always use the resourceId 399 // We could have more than one resourceID 400 // which would form a logical OR 401 if len(rIDs) != 0 { 402 for _, rID := range rIDs { 403 shareIDs, err := m.indexer.FindBy(&collaboration.Share{}, 404 indexer.NewField("ResourceId", resourceIDToIndex(rID)), 405 ) 406 if err != nil { 407 return nil, err 408 } 409 createdShareIds = append(createdShareIds, shareIDs...) 410 } 411 } else { 412 createdShareIds, err = m.indexer.FindBy(&collaboration.Share{}, 413 indexer.NewField("OwnerId", userIDToIndex(user.Id)), 414 indexer.NewField("CreatorId", userIDToIndex(user.Id)), 415 ) 416 if err != nil { 417 return nil, err 418 } 419 } 420 421 // We use shareMem as a temporary lookup store to check which shares were 422 // already added. This is to prevent duplicates. 423 shareMem := make(map[string]struct{}) 424 result := []*collaboration.Share{} 425 for _, id := range createdShareIds { 426 s, err := m.getShareByID(ctx, id) 427 if err != nil { 428 return nil, err 429 } 430 if share.MatchesFilters(s, filters) { 431 result = append(result, s) 432 shareMem[s.Id.OpaqueId] = struct{}{} 433 } 434 } 435 436 // If a user requests to list shares which have not been created by them 437 // we have to explicitly fetch these shares and check if the user is 438 // allowed to list the shares. 439 // Only then can we add these shares to the result. 440 grouped := share.GroupFiltersByType(filters) 441 idFilter, ok := grouped[collaboration.Filter_TYPE_RESOURCE_ID] 442 if !ok { 443 return result, nil 444 } 445 446 shareIDsByResourceID := make(map[string]*provider.ResourceId) 447 for _, filter := range idFilter { 448 resourceID := filter.GetResourceId() 449 shareIDs, err := m.indexer.FindBy(&collaboration.Share{}, 450 indexer.NewField("ResourceId", resourceIDToIndex(resourceID)), 451 ) 452 if err != nil { 453 continue 454 } 455 for _, shareID := range shareIDs { 456 shareIDsByResourceID[shareID] = resourceID 457 } 458 } 459 460 // statMem is used as a local cache to prevent statting resources which 461 // already have been checked. 462 statMem := make(map[string]struct{}) 463 for shareID, resourceID := range shareIDsByResourceID { 464 if _, handled := shareMem[shareID]; handled { 465 // We don't want to add a share multiple times when we added it 466 // already. 467 continue 468 } 469 470 if _, checked := statMem[resourceIDToIndex(resourceID)]; !checked { 471 sReq := &provider.StatRequest{ 472 Ref: &provider.Reference{ResourceId: resourceID}, 473 } 474 sRes, err := m.gatewayClient.Stat(ctx, sReq) 475 if err != nil { 476 continue 477 } 478 if sRes.Status.Code != rpcv1beta1.Code_CODE_OK { 479 continue 480 } 481 if !sRes.Info.PermissionSet.ListGrants { 482 continue 483 } 484 statMem[resourceIDToIndex(resourceID)] = struct{}{} 485 } 486 487 s, err := m.getShareByID(ctx, shareID) 488 if err != nil { 489 return nil, err 490 } 491 if share.MatchesFilters(s, filters) { 492 result = append(result, s) 493 shareMem[s.Id.OpaqueId] = struct{}{} 494 } 495 } 496 497 return result, nil 498 } 499 500 // UpdateShare updates the mode of the given share. 501 func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) { 502 if err := m.initialize(); err != nil { 503 return nil, err 504 } 505 share, err := m.GetShare(ctx, ref) 506 if err != nil { 507 return nil, err 508 } 509 share.Permissions = p 510 511 data, err := json.Marshal(share) 512 if err != nil { 513 return nil, err 514 } 515 516 err = m.storage.SimpleUpload(ctx, shareFilename(share.Id.OpaqueId), data) 517 518 return share, err 519 } 520 521 // ListReceivedShares returns the list of shares the user has access to. 522 func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userpb.UserId) ([]*collaboration.ReceivedShare, error) { 523 if err := m.initialize(); err != nil { 524 return nil, err 525 } 526 527 user, ok := ctxpkg.ContextGetUser(ctx) 528 if !ok { 529 return nil, errtypes.UserRequired("error getting user from context") 530 } 531 532 uid, groups := user.GetId(), user.GetGroups() 533 if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { 534 u, err := utils.GetUser(forUser, m.gatewayClient) 535 if err != nil { 536 return nil, errtypes.BadRequest("user not found") 537 } 538 uid = forUser 539 groups = u.GetGroups() 540 } 541 result := []*collaboration.ReceivedShare{} 542 543 ids, err := granteeToIndex(&provider.Grantee{ 544 Type: provider.GranteeType_GRANTEE_TYPE_USER, 545 Id: &provider.Grantee_UserId{UserId: uid}, 546 }) 547 if err != nil { 548 return nil, err 549 } 550 receivedIds, err := m.indexer.FindBy(&collaboration.Share{}, 551 indexer.NewField("GranteeId", ids), 552 ) 553 if err != nil { 554 return nil, err 555 } 556 for _, group := range groups { 557 index, err := granteeToIndex(&provider.Grantee{ 558 Type: provider.GranteeType_GRANTEE_TYPE_GROUP, 559 Id: &provider.Grantee_GroupId{GroupId: &groupv1beta1.GroupId{OpaqueId: group}}, 560 }) 561 if err != nil { 562 return nil, err 563 } 564 groupIds, err := m.indexer.FindBy(&collaboration.Share{}, 565 indexer.NewField("GranteeId", index), 566 ) 567 if err != nil { 568 return nil, err 569 } 570 receivedIds = append(receivedIds, groupIds...) 571 } 572 573 for _, id := range receivedIds { 574 s, err := m.getShareByID(ctx, id) 575 if err != nil { 576 return nil, err 577 } 578 if !share.MatchesFilters(s, filters) { 579 continue 580 } 581 metadata, err := m.downloadMetadata(ctx, s) 582 if err != nil { 583 if _, ok := err.(errtypes.NotFound); !ok { 584 return nil, err 585 } 586 // use default values if the grantee didn't configure anything yet 587 metadata = ReceivedShareMetadata{ 588 State: collaboration.ShareState_SHARE_STATE_PENDING, 589 } 590 } 591 result = append(result, &collaboration.ReceivedShare{ 592 Share: s, 593 State: metadata.State, 594 MountPoint: metadata.MountPoint, 595 }) 596 } 597 return result, nil 598 } 599 600 // GetReceivedShare returns the information for a received share. 601 func (m *Manager) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { 602 if err := m.initialize(); err != nil { 603 return nil, err 604 } 605 606 share, err := m.GetShare(ctx, ref) 607 if err != nil { 608 return nil, err 609 } 610 611 metadata, err := m.downloadMetadata(ctx, share) 612 if err != nil { 613 if _, ok := err.(errtypes.NotFound); !ok { 614 return nil, err 615 } 616 // use default values if the grantee didn't configure anything yet 617 metadata = ReceivedShareMetadata{ 618 State: collaboration.ShareState_SHARE_STATE_PENDING, 619 } 620 } 621 return &collaboration.ReceivedShare{ 622 Share: share, 623 State: metadata.State, 624 MountPoint: metadata.MountPoint, 625 }, nil 626 } 627 628 // UpdateReceivedShare updates the received share with share state. 629 func (m *Manager) UpdateReceivedShare(ctx context.Context, rshare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userpb.UserId) (*collaboration.ReceivedShare, error) { 630 if err := m.initialize(); err != nil { 631 return nil, err 632 } 633 634 user, ok := ctxpkg.ContextGetUser(ctx) 635 if !ok { 636 return nil, errtypes.UserRequired("error getting user from context") 637 } 638 639 rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: rshare.Share.Id}}) 640 if err != nil { 641 return nil, err 642 } 643 644 for i := range fieldMask.Paths { 645 switch fieldMask.Paths[i] { 646 case "state": 647 rs.State = rshare.State 648 case "mount_point": 649 rs.MountPoint = rshare.MountPoint 650 case "hidden": 651 continue 652 default: 653 return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported") 654 } 655 } 656 657 uid := user.GetId() 658 if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { 659 uid = forUser 660 } 661 662 err = m.persistReceivedShare(ctx, uid, rs) 663 if err != nil { 664 return nil, err 665 } 666 return rs, nil 667 } 668 669 func (m *Manager) persistReceivedShare(ctx context.Context, userID *userpb.UserId, rs *collaboration.ReceivedShare) error { 670 err := m.persistShare(ctx, rs.Share) 671 if err != nil { 672 return err 673 } 674 675 meta := ReceivedShareMetadata{ 676 State: rs.State, 677 MountPoint: rs.MountPoint, 678 } 679 data, err := json.Marshal(meta) 680 if err != nil { 681 return err 682 } 683 684 fn, err := metadataFilename(rs.Share, userID) 685 if err != nil { 686 return err 687 } 688 return m.storage.SimpleUpload(ctx, fn, data) 689 } 690 691 func (m *Manager) downloadMetadata(ctx context.Context, share *collaboration.Share) (ReceivedShareMetadata, error) { 692 user, ok := ctxpkg.ContextGetUser(ctx) 693 if !ok { 694 return ReceivedShareMetadata{}, errtypes.UserRequired("error getting user from context") 695 } 696 697 metadataFn, err := metadataFilename(share, user.Id) 698 if err != nil { 699 return ReceivedShareMetadata{}, err 700 } 701 data, err := m.storage.SimpleDownload(ctx, metadataFn) 702 if err != nil { 703 return ReceivedShareMetadata{}, err 704 } 705 metadata := ReceivedShareMetadata{} 706 err = json.Unmarshal(data, &metadata) 707 return metadata, err 708 } 709 710 func (m *Manager) getShareByID(ctx context.Context, id string) (*collaboration.Share, error) { 711 data, err := m.storage.SimpleDownload(ctx, shareFilename(id)) 712 if err != nil { 713 return nil, err 714 } 715 716 userShare := &collaboration.Share{ 717 Grantee: &provider.Grantee{Id: &provider.Grantee_UserId{}}, 718 } 719 err = json.Unmarshal(data, userShare) 720 if err == nil && userShare.Grantee.GetUserId() != nil { 721 userShare.ResourceId = storagespace.UpdateLegacyResourceID(userShare.GetResourceId()) 722 return userShare, nil 723 } 724 725 groupShare := &collaboration.Share{ 726 Grantee: &provider.Grantee{Id: &provider.Grantee_GroupId{}}, 727 } 728 err = json.Unmarshal(data, groupShare) // try to unmarshal to a group share if the user share unmarshalling failed 729 if err == nil && groupShare.Grantee.GetGroupId() != nil { 730 groupShare.ResourceId = storagespace.UpdateLegacyResourceID(groupShare.GetResourceId()) 731 return groupShare, nil 732 } 733 734 return nil, errtypes.InternalError("failed to unmarshal share data") 735 } 736 737 func (m *Manager) getShareByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) { 738 ownerIds, err := m.indexer.FindBy(&collaboration.Share{}, 739 indexer.NewField("OwnerId", userIDToIndex(key.Owner)), 740 ) 741 if err != nil { 742 return nil, err 743 } 744 granteeIndex, err := granteeToIndex(key.Grantee) 745 if err != nil { 746 return nil, err 747 } 748 granteeIds, err := m.indexer.FindBy(&collaboration.Share{}, 749 indexer.NewField("GranteeId", granteeIndex), 750 ) 751 if err != nil { 752 return nil, err 753 } 754 755 ids := intersectSlices(ownerIds, granteeIds) 756 for _, id := range ids { 757 share, err := m.getShareByID(ctx, id) 758 if err != nil { 759 return nil, err 760 } 761 if utils.ResourceIDEqual(share.ResourceId, key.ResourceId) { 762 return share, nil 763 } 764 } 765 return nil, errtypes.NotFound("share not found") 766 } 767 768 func shareFilename(id string) string { 769 return path.Join("shares", id) 770 } 771 772 func metadataFilename(s *collaboration.Share, g interface{}) (string, error) { 773 var granteePart string 774 switch v := g.(type) { 775 case *userpb.UserId: 776 granteePart = url.QueryEscape("user:" + v.Idp + ":" + v.OpaqueId) 777 case *provider.Grantee: 778 var err error 779 granteePart, err = granteeToIndex(v) 780 if err != nil { 781 return "", err 782 } 783 } 784 return path.Join("metadata", s.Id.OpaqueId, granteePart), nil 785 } 786 787 func indexOwnerFunc(v interface{}) (string, error) { 788 share, ok := v.(*collaboration.Share) 789 if !ok { 790 return "", fmt.Errorf("given entity is not a share") 791 } 792 return userIDToIndex(share.Owner), nil 793 } 794 795 func indexCreatorFunc(v interface{}) (string, error) { 796 share, ok := v.(*collaboration.Share) 797 if !ok { 798 return "", fmt.Errorf("given entity is not a share") 799 } 800 return userIDToIndex(share.Creator), nil 801 } 802 803 func userIDToIndex(id *userpb.UserId) string { 804 return url.QueryEscape(id.Idp + ":" + id.OpaqueId) 805 } 806 807 func indexGranteeFunc(v interface{}) (string, error) { 808 share, ok := v.(*collaboration.Share) 809 if !ok { 810 return "", fmt.Errorf("given entity is not a share") 811 } 812 return granteeToIndex(share.Grantee) 813 } 814 815 func indexResourceIDFunc(v interface{}) (string, error) { 816 share, ok := v.(*collaboration.Share) 817 if !ok { 818 return "", fmt.Errorf("given entity is not a share") 819 } 820 return resourceIDToIndex(share.ResourceId), nil 821 } 822 823 func resourceIDToIndex(id *provider.ResourceId) string { 824 return strings.Join([]string{id.SpaceId, id.OpaqueId}, "!") 825 } 826 827 func granteeToIndex(grantee *provider.Grantee) (string, error) { 828 switch { 829 case grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER: 830 return url.QueryEscape("user:" + grantee.GetUserId().Idp + ":" + grantee.GetUserId().OpaqueId), nil 831 case grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP: 832 return url.QueryEscape("group:" + grantee.GetGroupId().OpaqueId), nil 833 default: 834 return "", fmt.Errorf("unknown grantee type") 835 } 836 } 837 838 // indexToGrantee tries to unparse a grantee in a metadata dir 839 // unfortunately, it is just concatenated by :, causing nasty corner cases 840 func indexToGrantee(name string) (*provider.Grantee, error) { 841 unescaped, err := url.QueryUnescape(name) 842 if err != nil { 843 return nil, err 844 } 845 parts := strings.SplitN(unescaped, ":", 2) 846 if len(parts) != 2 { 847 return nil, fmt.Errorf("invalid grantee %s", unescaped) 848 } 849 switch parts[0] { 850 case "user": 851 lastInd := strings.LastIndex(parts[1], ":") 852 return &provider.Grantee{ 853 Type: provider.GranteeType_GRANTEE_TYPE_USER, 854 Id: &provider.Grantee_UserId{ 855 UserId: &userpb.UserId{ 856 Idp: parts[1][:lastInd], 857 OpaqueId: parts[1][lastInd+1:], 858 }, 859 }, 860 }, nil 861 case "group": 862 return &provider.Grantee{ 863 Type: provider.GranteeType_GRANTEE_TYPE_GROUP, 864 Id: &provider.Grantee_GroupId{ 865 GroupId: &groupv1beta1.GroupId{ 866 OpaqueId: parts[1], 867 }, 868 }, 869 }, nil 870 default: 871 return nil, fmt.Errorf("invalid grantee %s", unescaped) 872 } 873 } 874 875 func intersectSlices(a, b []string) []string { 876 aMap := map[string]bool{} 877 for _, s := range a { 878 aMap[s] = true 879 } 880 result := []string{} 881 for _, s := range b { 882 if _, ok := aMap[s]; ok { 883 result = append(result, s) 884 } 885 } 886 return result 887 }