github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/gateway/usershareprovider.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 gateway 20 21 import ( 22 "context" 23 "slices" 24 25 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 26 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 27 collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 28 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 29 typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 30 "github.com/cs3org/reva/v2/pkg/appctx" 31 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 32 "github.com/cs3org/reva/v2/pkg/errtypes" 33 "github.com/cs3org/reva/v2/pkg/rgrpc/status" 34 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 35 "github.com/cs3org/reva/v2/pkg/storage/utils/grants" 36 "github.com/cs3org/reva/v2/pkg/utils" 37 "github.com/pkg/errors" 38 ) 39 40 // TODO(labkode): add multi-phase commit logic when commit share or commit ref is enabled. 41 func (s *svc) CreateShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) { 42 // Don't use the share manager when sharing a space root 43 if !s.c.UseCommonSpaceRootShareLogic && refIsSpaceRoot(req.ResourceInfo.Id) { 44 return s.addSpaceShare(ctx, req) 45 } 46 return s.addShare(ctx, req) 47 } 48 49 func (s *svc) RemoveShare(ctx context.Context, req *collaboration.RemoveShareRequest) (*collaboration.RemoveShareResponse, error) { 50 key := req.GetRef().GetKey() 51 if !s.c.UseCommonSpaceRootShareLogic && shareIsSpaceRoot(key) { 52 return s.removeSpaceShare(ctx, key.GetResourceId(), key.GetGrantee()) 53 } 54 return s.removeShare(ctx, req) 55 } 56 57 func (s *svc) UpdateShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) { 58 if !s.c.UseCommonSpaceRootShareLogic && refIsSpaceRoot(req.GetShare().GetResourceId()) { 59 return s.updateSpaceShare(ctx, req) 60 } 61 return s.updateShare(ctx, req) 62 } 63 64 // TODO(labkode): we need to validate share state vs storage grant and storage ref 65 // If there are any inconsistencies, the share needs to be flag as invalid and a background process 66 // or active fix needs to be performed. 67 func (s *svc) GetShare(ctx context.Context, req *collaboration.GetShareRequest) (*collaboration.GetShareResponse, error) { 68 return s.getShare(ctx, req) 69 } 70 71 func (s *svc) getShare(ctx context.Context, req *collaboration.GetShareRequest) (*collaboration.GetShareResponse, error) { 72 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 73 if err != nil { 74 appctx.GetLogger(ctx). 75 Err(err). 76 Msg("getShare: failed to get user share provider") 77 return &collaboration.GetShareResponse{ 78 Status: status.NewInternal(ctx, "error getting user share provider client"), 79 }, nil 80 } 81 82 res, err := c.GetShare(ctx, req) 83 if err != nil { 84 return nil, errors.Wrap(err, "gateway: error calling GetShare") 85 } 86 87 return res, nil 88 } 89 90 // TODO(labkode): read GetShare comment. 91 func (s *svc) ListShares(ctx context.Context, req *collaboration.ListSharesRequest) (*collaboration.ListSharesResponse, error) { 92 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 93 if err != nil { 94 appctx.GetLogger(ctx). 95 Err(err). 96 Msg("ListShares: failed to get user share provider") 97 return &collaboration.ListSharesResponse{ 98 Status: status.NewInternal(ctx, "error getting user share provider client"), 99 }, nil 100 } 101 102 res, err := c.ListShares(ctx, req) 103 if err != nil { 104 return nil, errors.Wrap(err, "gateway: error calling ListShares") 105 } 106 107 return res, nil 108 } 109 110 func (s *svc) ListExistingShares(_ context.Context, _ *collaboration.ListSharesRequest) (*gateway.ListExistingSharesResponse, error) { 111 return nil, errtypes.NotSupported("method ListExistingShares not implemented") 112 } 113 114 func (s *svc) updateShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) { 115 // TODO: update wopi server 116 // FIXME This is a workaround that should prevent removing or changing the share permissions when the file is locked. 117 // https://github.com/owncloud/ocis/issues/8474 118 if status, err := s.checkLock(ctx, req.GetShare().GetId()); err != nil { 119 return &collaboration.UpdateShareResponse{ 120 Status: status, 121 }, nil 122 } 123 124 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 125 if err != nil { 126 appctx.GetLogger(ctx). 127 Err(err). 128 Msg("UpdateShare: failed to get user share provider") 129 return &collaboration.UpdateShareResponse{ 130 Status: status.NewInternal(ctx, "error getting share provider client"), 131 }, nil 132 } 133 res, err := c.UpdateShare(ctx, req) 134 if err != nil { 135 return nil, errors.Wrap(err, "gateway: error calling UpdateShare") 136 } 137 if res.GetStatus().GetCode() != rpc.Code_CODE_OK { 138 return res, nil 139 } 140 141 if s.c.CommitShareToStorageGrant { 142 creator, ok := ctxpkg.ContextGetUser(ctx) 143 if !ok { 144 return nil, errors.New("user not found in context") 145 } 146 147 grant := &provider.Grant{ 148 Grantee: res.GetShare().GetGrantee(), 149 Permissions: res.GetShare().GetPermissions().GetPermissions(), 150 Expiration: res.GetShare().GetExpiration(), 151 Creator: creator.GetId(), 152 } 153 updateGrantStatus, err := s.updateGrant(ctx, res.GetShare().GetResourceId(), grant, nil) 154 155 if err != nil { 156 return nil, errors.Wrap(err, "gateway: error calling updateGrant") 157 } 158 159 if updateGrantStatus.Code != rpc.Code_CODE_OK { 160 return &collaboration.UpdateShareResponse{ 161 Status: updateGrantStatus, 162 Share: res.GetShare(), 163 }, nil 164 } 165 } 166 167 return res, nil 168 } 169 170 func (s *svc) updateSpaceShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) { 171 if req.GetShare().GetGrantee() == nil { 172 return &collaboration.UpdateShareResponse{Status: status.NewInvalid(ctx, "updating requires a received grantee object")}, nil 173 } 174 // If the share is a denial we call denyGrant instead. 175 var st *rpc.Status 176 var err error 177 // TODO: change CS3 APIs 178 opaque := &typesv1beta1.Opaque{ 179 Map: map[string]*typesv1beta1.OpaqueEntry{ 180 "spacegrant": {}, 181 }, 182 } 183 utils.AppendPlainToOpaque(opaque, "spacetype", utils.ReadPlainFromOpaque(req.Opaque, "spacetype")) 184 185 if grants.PermissionsEqual(req.Share.GetPermissions().GetPermissions(), &provider.ResourcePermissions{}) { 186 st, err = s.denyGrant(ctx, req.GetShare().GetResourceId(), req.GetShare().GetGrantee(), opaque) 187 if err != nil { 188 return nil, errors.Wrap(err, "gateway: error denying grant in storage") 189 } 190 } else { 191 listGrantRes, err := s.listGrants(ctx, req.GetShare().GetResourceId()) 192 if err != nil { 193 return nil, errors.Wrap(err, "gateway: error getting grant to remove from storage") 194 } 195 existsGrant := s.getGranteeGrant(listGrantRes.GetGrants(), req.GetShare().GetGrantee()) 196 197 if !slices.Contains(req.GetUpdateMask().GetPaths(), "permissions") { 198 req.Share.Permissions = &collaboration.SharePermissions{Permissions: existsGrant.GetPermissions()} 199 } 200 201 if !slices.Contains(req.GetUpdateMask().GetPaths(), "expiration") { 202 req.Share.Expiration = existsGrant.GetExpiration() 203 } 204 205 u, ok := ctxpkg.ContextGetUser(ctx) 206 if !ok { 207 return nil, errors.New("user not found in context") 208 } 209 210 grant := &provider.Grant{ 211 Grantee: req.GetShare().GetGrantee(), 212 Permissions: req.GetShare().GetPermissions().GetPermissions(), 213 Expiration: req.GetShare().GetExpiration(), 214 Creator: u.GetId(), 215 } 216 217 if grant.GetPermissions() == nil { 218 return &collaboration.UpdateShareResponse{Status: status.NewInvalid(ctx, "updating requires a received permission object")}, nil 219 } 220 221 if !grant.GetPermissions().GetRemoveGrant() { 222 // this request might remove Manager Permissions so we need to 223 // check if there is at least one manager remaining of the 224 // resource. 225 if !isSpaceManagerRemaining(listGrantRes.GetGrants(), grant.GetGrantee()) { 226 return &collaboration.UpdateShareResponse{ 227 Status: status.NewPermissionDenied(ctx, errtypes.PermissionDenied(""), "can't remove the last manager"), 228 }, nil 229 } 230 231 } 232 st, err = s.updateGrant(ctx, req.GetShare().GetResourceId(), grant, opaque) 233 if err != nil { 234 return nil, errors.Wrap(err, "gateway: error adding grant to storage") 235 } 236 } 237 238 res := &collaboration.UpdateShareResponse{ 239 Status: st, 240 Share: req.Share, 241 } 242 243 if st.Code != rpc.Code_CODE_OK { 244 return res, nil 245 } 246 247 s.providerCache.RemoveListStorageProviders(req.GetShare().GetResourceId()) 248 return res, nil 249 } 250 251 // TODO(labkode): listing received shares just goes to the user share manager and gets the list of 252 // received shares. The display name of the shares should be the a friendly name, like the basename 253 // of the original file. 254 func (s *svc) ListReceivedShares(ctx context.Context, req *collaboration.ListReceivedSharesRequest) (*collaboration.ListReceivedSharesResponse, error) { 255 logger := appctx.GetLogger(ctx) 256 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 257 if err != nil { 258 logger.Error(). 259 Err(err). 260 Msg("ListReceivedShares: failed to get user share provider") 261 return &collaboration.ListReceivedSharesResponse{ 262 Status: status.NewInternal(ctx, "error getting share provider client"), 263 }, nil 264 } 265 266 res, err := c.ListReceivedShares(ctx, req) 267 if err != nil { 268 return nil, errors.Wrap(err, "gateway: error calling ListReceivedShares") 269 } 270 return res, nil 271 } 272 273 func (s *svc) GetReceivedShare(ctx context.Context, req *collaboration.GetReceivedShareRequest) (*collaboration.GetReceivedShareResponse, error) { 274 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 275 if err != nil { 276 appctx.GetLogger(ctx). 277 Err(err). 278 Msg("GetReceivedShare: failed to get user share provider") 279 return &collaboration.GetReceivedShareResponse{ 280 Status: status.NewInternal(ctx, "error getting received share"), 281 }, nil 282 } 283 284 res, err := c.GetReceivedShare(ctx, req) 285 if err != nil { 286 return nil, errors.Wrap(err, "gateway: error calling GetReceivedShare") 287 } 288 289 return res, nil 290 } 291 292 // When updating a received share: 293 // if the update contains update for displayName: 294 // 1. if received share is mounted: we also do a rename in the storage 295 // 2. if received share is not mounted: we only rename in user share provider. 296 func (s *svc) UpdateReceivedShare(ctx context.Context, req *collaboration.UpdateReceivedShareRequest) (*collaboration.UpdateReceivedShareResponse, error) { 297 ctx, span := appctx.GetTracerProvider(ctx).Tracer("gateway").Start(ctx, "Gateway.UpdateReceivedShare") 298 defer span.End() 299 300 // sanity checks 301 switch { 302 case req.GetShare() == nil: 303 return &collaboration.UpdateReceivedShareResponse{ 304 Status: status.NewInvalid(ctx, "updating requires a received share object"), 305 }, nil 306 case req.GetShare().GetShare() == nil: 307 return &collaboration.UpdateReceivedShareResponse{ 308 Status: status.NewInvalid(ctx, "share missing"), 309 }, nil 310 case req.GetShare().GetShare().GetId() == nil: 311 return &collaboration.UpdateReceivedShareResponse{ 312 Status: status.NewInvalid(ctx, "share id missing"), 313 }, nil 314 case req.GetShare().GetShare().GetId().GetOpaqueId() == "": 315 return &collaboration.UpdateReceivedShareResponse{ 316 Status: status.NewInvalid(ctx, "share id empty"), 317 }, nil 318 } 319 320 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 321 if err != nil { 322 appctx.GetLogger(ctx). 323 Err(err). 324 Msg("UpdateReceivedShare: failed to get user share provider") 325 return &collaboration.UpdateReceivedShareResponse{ 326 Status: status.NewInternal(ctx, "error getting share provider client"), 327 }, nil 328 } 329 330 return c.UpdateReceivedShare(ctx, req) 331 /* 332 TODO: Leftover from master merge. Do we need this? 333 if err != nil { 334 appctx.GetLogger(ctx). 335 Err(err). 336 Msg("UpdateReceivedShare: failed to get user share provider") 337 return &collaboration.UpdateReceivedShareResponse{ 338 Status: status.NewInternal(ctx, "error getting share provider client"), 339 }, nil 340 } 341 // check if we have a resource id in the update response that we can use to update references 342 if res.GetShare().GetShare().GetResourceId() == nil { 343 log.Err(err).Msg("gateway: UpdateReceivedShare must return a ResourceId") 344 return &collaboration.UpdateReceivedShareResponse{ 345 Status: &rpc.Status{ 346 Code: rpc.Code_CODE_INTERNAL, 347 }, 348 }, nil 349 } 350 351 // properties are updated in the order they appear in the field mask 352 // when an error occurs the request ends and no further fields are updated 353 for i := range req.UpdateMask.Paths { 354 switch req.UpdateMask.Paths[i] { 355 case "state": 356 switch req.GetShare().GetState() { 357 case collaboration.ShareState_SHARE_STATE_ACCEPTED: 358 rpcStatus := s.createReference(ctx, res.GetShare().GetShare().GetResourceId()) 359 if rpcStatus.Code != rpc.Code_CODE_OK { 360 return &collaboration.UpdateReceivedShareResponse{Status: rpcStatus}, nil 361 } 362 case collaboration.ShareState_SHARE_STATE_REJECTED: 363 rpcStatus := s.removeReference(ctx, res.GetShare().GetShare().ResourceId) 364 if rpcStatus.Code != rpc.Code_CODE_OK && rpcStatus.Code != rpc.Code_CODE_NOT_FOUND { 365 return &collaboration.UpdateReceivedShareResponse{Status: rpcStatus}, nil 366 } 367 } 368 case "mount_point": 369 // TODO(labkode): implementing updating mount point 370 err = errtypes.NotSupported("gateway: update of mount point is not yet implemented") 371 return &collaboration.UpdateReceivedShareResponse{ 372 Status: status.NewUnimplemented(ctx, err, "error updating received share"), 373 }, nil 374 default: 375 return nil, errtypes.NotSupported("updating " + req.UpdateMask.Paths[i] + " is not supported") 376 } 377 } 378 return res, nil 379 */ 380 } 381 382 func (s *svc) ListExistingReceivedShares(_ context.Context, _ *collaboration.ListReceivedSharesRequest) (*gateway.ListExistingReceivedSharesResponse, error) { 383 return nil, errtypes.NotSupported("Unimplemented") 384 } 385 386 func (s *svc) denyGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, opaque *typesv1beta1.Opaque) (*rpc.Status, error) { 387 ref := &provider.Reference{ 388 ResourceId: id, 389 } 390 391 grantReq := &provider.DenyGrantRequest{ 392 Ref: ref, 393 Grantee: g, 394 Opaque: opaque, 395 // TODO add creator 396 } 397 398 c, _, err := s.find(ctx, ref) 399 if err != nil { 400 appctx.GetLogger(ctx). 401 Err(err). 402 Interface("reference", ref). 403 Msg("denyGrant: failed to get storage provider") 404 if _, ok := err.(errtypes.IsNotFound); ok { 405 return status.NewNotFound(ctx, "storage provider not found"), nil 406 } 407 return status.NewInternal(ctx, "error finding storage provider"), nil 408 } 409 410 grantRes, err := c.DenyGrant(ctx, grantReq) 411 if err != nil { 412 return nil, errors.Wrap(err, "gateway: error calling DenyGrant") 413 } 414 return grantRes.Status, nil 415 } 416 417 func (s *svc) addGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, p *provider.ResourcePermissions, expiration *typesv1beta1.Timestamp, opaque *typesv1beta1.Opaque) (*rpc.Status, error) { 418 ref := &provider.Reference{ 419 ResourceId: id, 420 } 421 422 creator, ok := ctxpkg.ContextGetUser(ctx) 423 if !ok { 424 return nil, errors.New("user not found in context") 425 } 426 427 grantReq := &provider.AddGrantRequest{ 428 Ref: ref, 429 Grant: &provider.Grant{ 430 Grantee: g, 431 Permissions: p, 432 Creator: creator.GetId(), 433 Expiration: expiration, 434 }, 435 Opaque: opaque, 436 } 437 438 c, _, err := s.find(ctx, ref) 439 if err != nil { 440 appctx.GetLogger(ctx). 441 Err(err). 442 Interface("reference", ref). 443 Msg("addGrant: failed to get storage provider") 444 if _, ok := err.(errtypes.IsNotFound); ok { 445 return status.NewNotFound(ctx, "storage provider not found"), nil 446 } 447 return status.NewInternal(ctx, "error finding storage provider"), nil 448 } 449 450 grantRes, err := c.AddGrant(ctx, grantReq) 451 if err != nil { 452 return nil, errors.Wrap(err, "gateway: error calling AddGrant") 453 } 454 return grantRes.Status, nil 455 } 456 457 func (s *svc) updateGrant(ctx context.Context, id *provider.ResourceId, grant *provider.Grant, opaque *typesv1beta1.Opaque) (*rpc.Status, error) { 458 ref := &provider.Reference{ 459 ResourceId: id, 460 } 461 462 grantReq := &provider.UpdateGrantRequest{ 463 Opaque: opaque, 464 Ref: ref, 465 Grant: grant, 466 } 467 468 c, _, err := s.find(ctx, ref) 469 if err != nil { 470 appctx.GetLogger(ctx). 471 Err(err). 472 Interface("reference", ref). 473 Msg("updateGrant: failed to get storage provider") 474 if _, ok := err.(errtypes.IsNotFound); ok { 475 return status.NewNotFound(ctx, "storage provider not found"), nil 476 } 477 return status.NewInternal(ctx, "error finding storage provider"), nil 478 } 479 480 grantRes, err := c.UpdateGrant(ctx, grantReq) 481 if err != nil { 482 return nil, errors.Wrap(err, "gateway: error calling UpdateGrant") 483 } 484 if grantRes.Status.Code != rpc.Code_CODE_OK { 485 return status.NewInternal(ctx, 486 "error committing share to storage grant"), nil 487 } 488 489 return status.NewOK(ctx), nil 490 } 491 492 func (s *svc) removeGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, p *provider.ResourcePermissions, opaque *typesv1beta1.Opaque) (*rpc.Status, error) { 493 ref := &provider.Reference{ 494 ResourceId: id, 495 } 496 497 grantReq := &provider.RemoveGrantRequest{ 498 Ref: ref, 499 Grant: &provider.Grant{ 500 Grantee: g, 501 Permissions: p, 502 }, 503 Opaque: opaque, 504 } 505 506 c, _, err := s.find(ctx, ref) 507 if err != nil { 508 appctx.GetLogger(ctx). 509 Err(err). 510 Interface("reference", ref). 511 Msg("removeGrant: failed to get storage provider") 512 if _, ok := err.(errtypes.IsNotFound); ok { 513 return status.NewNotFound(ctx, "storage provider not found"), nil 514 } 515 return status.NewInternal(ctx, "error finding storage provider"), nil 516 } 517 518 grantRes, err := c.RemoveGrant(ctx, grantReq) 519 if err != nil { 520 return nil, errors.Wrap(err, "gateway: error calling RemoveGrant") 521 } 522 if grantRes.Status.Code != rpc.Code_CODE_OK { 523 return grantRes.GetStatus(), nil 524 } 525 526 return status.NewOK(ctx), nil 527 } 528 529 func (s *svc) listGrants(ctx context.Context, id *provider.ResourceId) (*provider.ListGrantsResponse, error) { 530 ref := &provider.Reference{ 531 ResourceId: id, 532 } 533 534 grantReq := &provider.ListGrantsRequest{ 535 Ref: ref, 536 } 537 538 c, _, err := s.find(ctx, ref) 539 if err != nil { 540 appctx.GetLogger(ctx). 541 Err(err). 542 Interface("reference", ref). 543 Msg("listGrants: failed to get storage provider") 544 if _, ok := err.(errtypes.IsNotFound); ok { 545 return &provider.ListGrantsResponse{ 546 Status: status.NewNotFound(ctx, "storage provider not found"), 547 }, nil 548 } 549 return &provider.ListGrantsResponse{ 550 Status: status.NewInternal(ctx, "error finding storage provider"), 551 }, nil 552 } 553 554 grantRes, err := c.ListGrants(ctx, grantReq) 555 if err != nil { 556 return nil, errors.Wrap(err, "gateway: error calling ListGrants") 557 } 558 if grantRes.Status.Code != rpc.Code_CODE_OK { 559 return &provider.ListGrantsResponse{Status: status.NewInternal(ctx, 560 "error listing storage grants"), 561 }, 562 nil 563 } 564 return grantRes, nil 565 } 566 567 func (s *svc) getGranteeGrant(grants []*provider.Grant, grantee *provider.Grantee) *provider.Grant { 568 for _, g := range grants { 569 if isEqualGrantee(g.Grantee, grantee) { 570 return g 571 } 572 } 573 return nil 574 } 575 576 func (s *svc) addShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) { 577 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 578 if err != nil { 579 appctx.GetLogger(ctx). 580 Err(err). 581 Msg("CreateShare: failed to get user share provider") 582 return &collaboration.CreateShareResponse{ 583 Status: status.NewInternal(ctx, "error getting user share provider client"), 584 }, nil 585 } 586 // TODO the user share manager needs to be able to decide if the current user is allowed to create that share (and not eg. incerase permissions) 587 // jfd: AFAICT this can only be determined by a storage driver - either the storage provider is queried first or the share manager needs to access the storage using a storage driver 588 res, err := c.CreateShare(ctx, req) 589 if err != nil { 590 return nil, errors.Wrap(err, "gateway: error calling CreateShare") 591 } 592 if res.Status.Code != rpc.Code_CODE_OK { 593 return res, nil 594 } 595 596 rollBackFn := func(status *rpc.Status) { 597 rmvReq := &collaboration.RemoveShareRequest{ 598 Ref: &collaboration.ShareReference{ 599 Spec: &collaboration.ShareReference_Key{ 600 Key: &collaboration.ShareKey{ 601 ResourceId: req.ResourceInfo.Id, 602 Grantee: req.Grant.Grantee, 603 }, 604 }, 605 }, 606 } 607 appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg("rollback the CreateShare attempt") 608 if resp, err := s.removeShare(ctx, rmvReq); err != nil { 609 appctx.GetLogger(ctx).Debug().Interface("status", resp.GetStatus()).Interface("req", req).Msg(err.Error()) 610 } 611 } 612 613 if s.c.CommitShareToStorageGrant { 614 // If the share is a denial we call denyGrant instead. 615 var status *rpc.Status 616 if grants.PermissionsEqual(req.Grant.Permissions.Permissions, &provider.ResourcePermissions{}) { 617 status, err = s.denyGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, nil) 618 if err != nil { 619 return nil, errors.Wrap(err, "gateway: error denying grant in storage") 620 } 621 } else { 622 status, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, req.Grant.Expiration, nil) 623 if err != nil { 624 appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg(err.Error()) 625 rollBackFn(status) 626 return nil, errors.Wrap(err, "gateway: error adding grant to storage") 627 } 628 } 629 630 switch status.Code { 631 case rpc.Code_CODE_OK: 632 // ok 633 case rpc.Code_CODE_UNIMPLEMENTED: 634 appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg("storing grants not supported, ignoring") 635 rollBackFn(status) 636 default: 637 appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg("storing grants is not successful") 638 rollBackFn(status) 639 return &collaboration.CreateShareResponse{ 640 Status: status, 641 }, err 642 } 643 } 644 return res, nil 645 } 646 647 func (s *svc) addSpaceShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) { 648 if refIsSpaceRoot(req.GetResourceInfo().GetId()) && 649 (req.GetResourceInfo().GetSpace().GetSpaceType() == _spaceTypePersonal || req.GetResourceInfo().GetSpace().GetSpaceType() == _spaceTypeVirtual) { 650 return &collaboration.CreateShareResponse{Status: status.NewInvalid(ctx, "space type is not eligible for sharing")}, nil 651 } 652 // If the share is a denial we call denyGrant instead. 653 var st *rpc.Status 654 var err error 655 // TODO: change CS3 APIs 656 opaque := &typesv1beta1.Opaque{ 657 Map: map[string]*typesv1beta1.OpaqueEntry{ 658 "spacegrant": {}, 659 }, 660 } 661 utils.AppendPlainToOpaque( 662 opaque, 663 "spacetype", 664 req.ResourceInfo.GetSpace().GetSpaceType(), 665 ) 666 if grants.PermissionsEqual(req.Grant.Permissions.Permissions, &provider.ResourcePermissions{}) { 667 st, err = s.denyGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, opaque) 668 if err != nil { 669 return nil, errors.Wrap(err, "gateway: error denying grant in storage") 670 } 671 } else { 672 st, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, req.Grant.Expiration, opaque) 673 if err != nil { 674 return nil, errors.Wrap(err, "gateway: error adding grant to storage") 675 } 676 } 677 678 switch st.Code { 679 case rpc.Code_CODE_OK: 680 s.providerCache.RemoveListStorageProviders(req.ResourceInfo.Id) 681 case rpc.Code_CODE_UNIMPLEMENTED: 682 appctx.GetLogger(ctx).Debug().Interface("status", st).Interface("req", req).Msg("storing grants not supported, ignoring") 683 default: 684 return &collaboration.CreateShareResponse{ 685 Status: st, 686 }, err 687 } 688 689 return &collaboration.CreateShareResponse{ 690 Status: status.NewOK(ctx), 691 Share: &collaboration.Share{ 692 ResourceId: req.ResourceInfo.Id, 693 Permissions: &collaboration.SharePermissions{Permissions: req.Grant.Permissions.GetPermissions()}, 694 Grantee: req.Grant.Grantee, 695 }, 696 }, nil 697 } 698 699 func (s *svc) removeShare(ctx context.Context, req *collaboration.RemoveShareRequest) (*collaboration.RemoveShareResponse, error) { 700 c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint) 701 if err != nil { 702 appctx.GetLogger(ctx). 703 Err(err). 704 Msg("RemoveShare: failed to get user share provider") 705 return &collaboration.RemoveShareResponse{ 706 Status: status.NewInternal(ctx, "error getting user share provider client"), 707 }, nil 708 } 709 710 // if we need to commit the share, we need the resource it points to. 711 var share *collaboration.Share 712 // FIXME: I will cause a panic as share will be nil when I'm false 713 if s.c.CommitShareToStorageGrant { 714 getShareReq := &collaboration.GetShareRequest{ 715 Ref: req.Ref, 716 } 717 getShareRes, err := c.GetShare(ctx, getShareReq) 718 if err != nil { 719 return nil, errors.Wrap(err, "gateway: error calling GetShare") 720 } 721 722 if getShareRes.Status.Code != rpc.Code_CODE_OK { 723 res := &collaboration.RemoveShareResponse{ 724 Status: status.NewInternal(ctx, 725 "error getting share when committing to the storage"), 726 } 727 return res, nil 728 } 729 share = getShareRes.Share 730 } 731 732 // TODO: update wopi server 733 // FIXME This is a workaround that should prevent removing or changing the share permissions when the file is locked. 734 // https://github.com/owncloud/ocis/issues/8474 735 if status, err := s.checkShareLock(ctx, share); err != nil { 736 return &collaboration.RemoveShareResponse{ 737 Status: status, 738 }, nil 739 } 740 741 res, err := c.RemoveShare(ctx, req) 742 if err != nil { 743 return nil, errors.Wrap(err, "gateway: error calling RemoveShare") 744 } 745 746 if s.c.CommitShareToStorageGrant { 747 removeGrantStatus, err := s.removeGrant(ctx, share.ResourceId, share.Grantee, share.Permissions.Permissions, nil) 748 if err != nil { 749 return nil, errors.Wrap(err, "gateway: error removing grant from storage") 750 } 751 if removeGrantStatus.Code != rpc.Code_CODE_OK { 752 return &collaboration.RemoveShareResponse{ 753 Status: removeGrantStatus, 754 }, err 755 } 756 } 757 758 return res, nil 759 } 760 761 func (s *svc) removeSpaceShare(ctx context.Context, ref *provider.ResourceId, grantee *provider.Grantee) (*collaboration.RemoveShareResponse, error) { 762 listGrantRes, err := s.listGrants(ctx, ref) 763 if err != nil { 764 return nil, errors.Wrap(err, "gateway: error getting grant to remove from storage") 765 } 766 var permissions *provider.ResourcePermissions 767 for _, g := range listGrantRes.Grants { 768 if isEqualGrantee(g.Grantee, grantee) { 769 permissions = g.Permissions 770 } 771 } 772 if permissions == nil { 773 return nil, errors.New("gateway: error getting grant to remove from storage") 774 } 775 776 if len(listGrantRes.Grants) == 1 || !isSpaceManagerRemaining(listGrantRes.Grants, grantee) { 777 return &collaboration.RemoveShareResponse{ 778 Status: status.NewPermissionDenied(ctx, errtypes.PermissionDenied(""), "can't remove the last manager"), 779 }, nil 780 } 781 782 // TODO: change CS3 APIs 783 opaque := &typesv1beta1.Opaque{ 784 Map: map[string]*typesv1beta1.OpaqueEntry{ 785 "spacegrant": {}, 786 }, 787 } 788 removeGrantStatus, err := s.removeGrant(ctx, ref, grantee, permissions, opaque) 789 if err != nil { 790 return nil, errors.Wrap(err, "gateway: error removing grant from storage") 791 } 792 if removeGrantStatus.Code != rpc.Code_CODE_OK { 793 return &collaboration.RemoveShareResponse{ 794 Status: removeGrantStatus, 795 }, err 796 } 797 s.providerCache.RemoveListStorageProviders(ref) 798 return &collaboration.RemoveShareResponse{Status: status.NewOK(ctx)}, nil 799 } 800 801 func isSpaceManagerRemaining(grants []*provider.Grant, grantee *provider.Grantee) bool { 802 for _, g := range grants { 803 // RemoveGrant is currently the way to check for the manager role 804 // If it is not set than the current grant is not for a manager and 805 // we can just continue with the next one. 806 if g.Permissions.RemoveGrant && !isEqualGrantee(g.Grantee, grantee) { 807 return true 808 } 809 } 810 return false 811 } 812 813 func (s *svc) checkLock(ctx context.Context, shareId *collaboration.ShareId) (*rpc.Status, error) { 814 logger := appctx.GetLogger(ctx) 815 getShareRes, err := s.GetShare(ctx, &collaboration.GetShareRequest{ 816 Ref: &collaboration.ShareReference{ 817 Spec: &collaboration.ShareReference_Id{Id: shareId}, 818 }, 819 }) 820 if err != nil { 821 msg := "gateway: error calling GetShare" 822 logger.Err(err).Interface("share_id", shareId).Msg(msg) 823 return status.NewInternal(ctx, msg), errors.Wrap(err, msg) 824 } 825 if getShareRes.GetStatus().GetCode() != rpc.Code_CODE_OK { 826 msg := "can not get share stat " + getShareRes.GetStatus().GetMessage() 827 logger.Debug().Interface("share", shareId).Msg(msg) 828 if getShareRes.GetStatus().GetCode() != rpc.Code_CODE_NOT_FOUND { 829 return status.NewNotFound(ctx, msg), errors.New(msg) 830 } 831 return status.NewInternal(ctx, msg), errors.New(msg) 832 } 833 return s.checkShareLock(ctx, getShareRes.Share) 834 } 835 836 func (s *svc) checkShareLock(ctx context.Context, share *collaboration.Share) (*rpc.Status, error) { 837 logger := appctx.GetLogger(ctx) 838 sRes, err := s.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: share.GetResourceId()}, 839 ArbitraryMetadataKeys: []string{"lockdiscovery"}}) 840 if err != nil { 841 msg := "failed to stat shared resource" 842 logger.Err(err).Interface("resource_id", share.GetResourceId()).Msg(msg) 843 return status.NewInternal(ctx, msg), errors.Wrap(err, msg) 844 } 845 if sRes.GetStatus().GetCode() != rpc.Code_CODE_OK { 846 msg := "can not get share stat " + sRes.GetStatus().GetMessage() 847 logger.Debug().Interface("lock", sRes.GetInfo().GetLock()).Msg(msg) 848 if sRes.GetStatus().GetCode() != rpc.Code_CODE_NOT_FOUND { 849 return status.NewNotFound(ctx, msg), errors.New(msg) 850 } 851 return status.NewInternal(ctx, msg), errors.New(msg) 852 } 853 854 if sRes.GetInfo().GetLock() != nil { 855 msg := "can not change grants, the shared resource is locked" 856 logger.Debug().Interface("lock", sRes.GetInfo().GetLock()).Msg(msg) 857 return status.NewLocked(ctx, msg), errors.New(msg) 858 } 859 return nil, nil 860 } 861 862 func refIsSpaceRoot(ref *provider.ResourceId) bool { 863 if ref == nil { 864 return false 865 } 866 if ref.SpaceId == "" || ref.OpaqueId == "" { 867 return false 868 } 869 870 return ref.SpaceId == ref.OpaqueId 871 } 872 873 func shareIsSpaceRoot(key *collaboration.ShareKey) bool { 874 if key == nil { 875 return false 876 } 877 return refIsSpaceRoot(key.ResourceId) 878 } 879 880 func isEqualGrantee(a, b *provider.Grantee) bool { 881 // Ideally we would want to use utils.GranteeEqual() 882 // but the grants stored in the decomposedfs aren't complete (missing usertype and idp) 883 // because of that the check would fail so we can only check the ... for now. 884 if a.Type != b.Type { 885 return false 886 } 887 888 var aID, bID string 889 switch a.Type { 890 case provider.GranteeType_GRANTEE_TYPE_GROUP: 891 aID = a.GetGroupId().GetOpaqueId() 892 bID = b.GetGroupId().GetOpaqueId() 893 case provider.GranteeType_GRANTEE_TYPE_USER: 894 aID = a.GetUserId().GetOpaqueId() 895 bID = b.GetUserId().GetOpaqueId() 896 } 897 return aID == bID 898 }