github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/publicstorageprovider/publicstorageprovider.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 publicstorageprovider provides a CS3 storageprovider implementation for public links. 20 // It will list spaces with type `grant` and `mountpoint` when a public scope is present. 21 package publicstorageprovider 22 23 import ( 24 "context" 25 "encoding/json" 26 "path" 27 "strings" 28 29 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 30 userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 31 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 32 link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" 33 ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" 34 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 35 typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/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" 40 "github.com/cs3org/reva/v2/pkg/rgrpc/status" 41 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 42 "github.com/cs3org/reva/v2/pkg/storagespace" 43 "github.com/cs3org/reva/v2/pkg/utils" 44 "github.com/mitchellh/mapstructure" 45 "github.com/pkg/errors" 46 "github.com/rs/zerolog" 47 "go.opentelemetry.io/otel/attribute" 48 "google.golang.org/grpc" 49 "google.golang.org/grpc/codes" 50 gstatus "google.golang.org/grpc/status" 51 ) 52 53 // name is the Tracer name used to identify this instrumentation library. 54 const tracerName = "publicstorageprovider" 55 56 func init() { 57 rgrpc.Register("publicstorageprovider", New) 58 } 59 60 type config struct { 61 GatewayAddr string `mapstructure:"gateway_addr"` 62 } 63 64 type service struct { 65 conf *config 66 gatewaySelector pool.Selectable[gateway.GatewayAPIClient] 67 } 68 69 func (s *service) Close() error { 70 return nil 71 } 72 73 func (s *service) UnprotectedEndpoints() []string { 74 return []string{} 75 } 76 77 func (s *service) Register(ss *grpc.Server) { 78 provider.RegisterProviderAPIServer(ss, s) 79 provider.RegisterSpacesAPIServer(ss, s) 80 } 81 82 func parseConfig(m map[string]interface{}) (*config, error) { 83 c := &config{} 84 if err := mapstructure.Decode(m, c); err != nil { 85 err = errors.Wrap(err, "error decoding conf") 86 return nil, err 87 } 88 return c, nil 89 } 90 91 // New creates a new publicstorageprovider service. 92 func New(m map[string]interface{}, ss *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) { 93 c, err := parseConfig(m) 94 if err != nil { 95 return nil, err 96 } 97 98 gatewaySelector, err := pool.GatewaySelector(c.GatewayAddr) 99 if err != nil { 100 return nil, err 101 } 102 103 service := &service{ 104 conf: c, 105 gatewaySelector: gatewaySelector, 106 } 107 108 return service, nil 109 } 110 111 func (s *service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArbitraryMetadataRequest) (*provider.SetArbitraryMetadataResponse, error) { 112 ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 113 if err != nil { 114 return &provider.SetArbitraryMetadataResponse{ 115 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 116 }, nil 117 } 118 gatewayClient, err := s.gatewaySelector.Next() 119 if err != nil { 120 return nil, err 121 } 122 return gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{Opaque: req.Opaque, Ref: ref, ArbitraryMetadata: req.ArbitraryMetadata}) 123 } 124 125 func (s *service) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) { 126 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 127 } 128 129 // SetLock puts a lock on the given reference 130 func (s *service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*provider.SetLockResponse, error) { 131 ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 132 if err != nil { 133 return &provider.SetLockResponse{ 134 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 135 }, nil 136 } 137 gatewayClient, err := s.gatewaySelector.Next() 138 if err != nil { 139 return nil, err 140 } 141 return gatewayClient.SetLock(ctx, &provider.SetLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) 142 } 143 144 // GetLock returns an existing lock on the given reference 145 func (s *service) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provider.GetLockResponse, error) { 146 ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 147 if err != nil { 148 return &provider.GetLockResponse{ 149 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 150 }, nil 151 } 152 gatewayClient, err := s.gatewaySelector.Next() 153 if err != nil { 154 return nil, err 155 } 156 return gatewayClient.GetLock(ctx, &provider.GetLockRequest{Opaque: req.Opaque, Ref: ref}) 157 } 158 159 // RefreshLock refreshes an existing lock on the given reference 160 func (s *service) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) { 161 ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 162 if err != nil { 163 return &provider.RefreshLockResponse{ 164 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 165 }, nil 166 } 167 gatewayClient, err := s.gatewaySelector.Next() 168 if err != nil { 169 return nil, err 170 } 171 return gatewayClient.RefreshLock(ctx, &provider.RefreshLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) 172 } 173 174 // Unlock removes an existing lock from the given reference 175 func (s *service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) { 176 ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 177 if err != nil { 178 return &provider.UnlockResponse{ 179 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 180 }, nil 181 } 182 gatewayClient, err := s.gatewaySelector.Next() 183 if err != nil { 184 return nil, err 185 } 186 return gatewayClient.Unlock(ctx, &provider.UnlockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) 187 } 188 189 func (s *service) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) { 190 statReq := &provider.StatRequest{Ref: req.Ref} 191 statRes, err := s.Stat(ctx, statReq) 192 if err != nil { 193 return &provider.InitiateFileDownloadResponse{ 194 Status: status.NewInternal(ctx, "InitiateFileDownload: error stating ref:"+req.Ref.String()), 195 }, nil 196 } 197 if statRes.Status.Code != rpc.Code_CODE_OK { 198 if statRes.Status.Code == rpc.Code_CODE_NOT_FOUND { 199 return &provider.InitiateFileDownloadResponse{ 200 Status: status.NewNotFound(ctx, "InitiateFileDownload: file not found"), 201 }, nil 202 } 203 return &provider.InitiateFileDownloadResponse{ 204 Status: status.NewInternal(ctx, "InitiateFileDownload: error stating ref"), 205 }, nil 206 } 207 208 req.Opaque = statRes.Info.Opaque 209 return s.initiateFileDownload(ctx, req) 210 } 211 212 func (s *service) translatePublicRefToCS3Ref(ctx context.Context, ref *provider.Reference) (*provider.Reference, *provider.ResourceInfo, string, error) { 213 log := appctx.GetLogger(ctx) 214 215 info, _, _, token, err := s.extractLinkFromScope(ctx) 216 if err != nil { 217 return nil, nil, "", err 218 } 219 220 var path string 221 switch info.Type { 222 case provider.ResourceType_RESOURCE_TYPE_CONTAINER: 223 // folders point to the folder -> path needs to be added 224 path = utils.MakeRelativePath(ref.Path) 225 case provider.ResourceType_RESOURCE_TYPE_FILE: 226 // files already point to the correct id 227 path = "." 228 default: 229 // TODO: can this happen? 230 // path = utils.MakeRelativePath(relativePath) 231 } 232 233 cs3Ref := &provider.Reference{ 234 ResourceId: info.Id, 235 Path: path, 236 } 237 238 log.Debug(). 239 Interface("sourceRef", ref). 240 Interface("cs3Ref", cs3Ref). 241 Str("tkn", token). 242 Str("originalPath", info.Path). 243 Str("relativePath", path). 244 Msg("translatePublicRefToCS3Ref") 245 return cs3Ref, info, token, nil 246 } 247 248 func (s *service) initiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) { 249 ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 250 switch { 251 case err != nil: 252 return &provider.InitiateFileDownloadResponse{ 253 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 254 }, nil 255 case info.PermissionSet == nil || !info.PermissionSet.InitiateFileDownload: 256 return &provider.InitiateFileDownloadResponse{ 257 Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileDownload permission"), 258 }, nil 259 } 260 dReq := &provider.InitiateFileDownloadRequest{ 261 Ref: ref, 262 } 263 264 gatewayClient, err := s.gatewaySelector.Next() 265 if err != nil { 266 return nil, err 267 } 268 dRes, err := gatewayClient.InitiateFileDownload(ctx, dReq) 269 if err != nil { 270 return &provider.InitiateFileDownloadResponse{ 271 Status: status.NewInternal(ctx, "initiateFileDownload: error calling InitiateFileDownload"), 272 }, nil 273 } 274 275 if dRes.Status.Code != rpc.Code_CODE_OK { 276 return &provider.InitiateFileDownloadResponse{ 277 Status: dRes.Status, 278 }, nil 279 } 280 281 protocols := make([]*provider.FileDownloadProtocol, len(dRes.Protocols)) 282 for p := range dRes.Protocols { 283 if !strings.HasSuffix(dRes.Protocols[p].DownloadEndpoint, "/") { 284 dRes.Protocols[p].DownloadEndpoint += "/" 285 } 286 dRes.Protocols[p].DownloadEndpoint += dRes.Protocols[p].Token 287 288 protocols = append(protocols, &provider.FileDownloadProtocol{ 289 Opaque: dRes.Protocols[p].Opaque, 290 Protocol: dRes.Protocols[p].Protocol, 291 DownloadEndpoint: dRes.Protocols[p].DownloadEndpoint, 292 Expose: true, // the gateway already has encoded the upload endpoint 293 }) 294 } 295 296 return &provider.InitiateFileDownloadResponse{ 297 Opaque: dRes.GetOpaque(), 298 Status: dRes.Status, 299 Protocols: protocols, 300 }, nil 301 } 302 303 func (s *service) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*provider.InitiateFileUploadResponse, error) { 304 cs3Ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 305 switch { 306 case err != nil: 307 return &provider.InitiateFileUploadResponse{ 308 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 309 }, nil 310 case info.PermissionSet == nil || !info.PermissionSet.InitiateFileUpload: 311 return &provider.InitiateFileUploadResponse{ 312 Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileUpload permission"), 313 }, nil 314 } 315 uReq := &provider.InitiateFileUploadRequest{ 316 Ref: cs3Ref, 317 Opaque: req.Opaque, 318 LockId: req.LockId, 319 } 320 321 gatewayClient, err := s.gatewaySelector.Next() 322 if err != nil { 323 return nil, err 324 } 325 uRes, err := gatewayClient.InitiateFileUpload(ctx, uReq) 326 if err != nil { 327 return &provider.InitiateFileUploadResponse{ 328 Status: status.NewInternal(ctx, "InitiateFileUpload: error calling InitiateFileUpload"), 329 }, nil 330 } 331 332 if uRes.Status.Code != rpc.Code_CODE_OK { 333 return &provider.InitiateFileUploadResponse{ 334 Status: uRes.Status, 335 }, nil 336 } 337 338 protocols := make([]*provider.FileUploadProtocol, len(uRes.Protocols)) 339 for p := range uRes.Protocols { 340 if !strings.HasSuffix(uRes.Protocols[p].UploadEndpoint, "/") { 341 uRes.Protocols[p].UploadEndpoint += "/" 342 } 343 uRes.Protocols[p].UploadEndpoint += uRes.Protocols[p].Token 344 345 protocols = append(protocols, &provider.FileUploadProtocol{ 346 Opaque: uRes.Protocols[p].Opaque, 347 Protocol: uRes.Protocols[p].Protocol, 348 UploadEndpoint: uRes.Protocols[p].UploadEndpoint, 349 AvailableChecksums: uRes.Protocols[p].AvailableChecksums, 350 Expose: true, // the gateway already has encoded the upload endpoint 351 }) 352 } 353 354 res := &provider.InitiateFileUploadResponse{ 355 Opaque: uRes.GetOpaque(), 356 Status: uRes.Status, 357 Protocols: protocols, 358 } 359 360 return res, nil 361 } 362 363 func (s *service) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provider.GetPathResponse, error) { 364 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 365 } 366 367 func (s *service) GetHome(ctx context.Context, req *provider.GetHomeRequest) (*provider.GetHomeResponse, error) { 368 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 369 } 370 371 func (s *service) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) { 372 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 373 } 374 375 func (s *service) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { 376 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 377 } 378 379 // ListStorageSpaces returns storage spaces when a public scope is present 380 // in the context. 381 // 382 // On the one hand, it lists a `mountpoint` space that can be used by the 383 // registry to construct a mount path. These spaces have their root 384 // storageid set to 7993447f-687f-490d-875c-ac95e89a62a4 and the 385 // opaqueid set to the link token. 386 // 387 // On the other hand, it lists a `grant` space for the shared resource id, 388 // so id based requests can find the correct storage provider. These spaces 389 // have their root set to the shared resource. 390 func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) { 391 spaceTypes := map[string]struct{}{} 392 var exists = struct{}{} 393 appendTypes := []string{} 394 var spaceID *provider.ResourceId 395 for _, f := range req.Filters { 396 switch f.Type { 397 case provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE: 398 spaceType := f.GetSpaceType() 399 if spaceType == "+mountpoint" || spaceType == "+grant" { 400 appendTypes = append(appendTypes, strings.TrimPrefix(spaceType, "+")) 401 continue 402 } 403 spaceTypes[spaceType] = exists 404 case provider.ListStorageSpacesRequest_Filter_TYPE_ID: 405 resID, err := storagespace.ParseID(f.GetId().GetOpaqueId()) 406 if err != nil { 407 return &provider.ListStorageSpacesResponse{ 408 Status: &rpc.Status{Code: rpc.Code_CODE_INVALID_ARGUMENT, Message: err.Error()}, 409 }, nil 410 } 411 if resID.SpaceId != utils.PublicStorageSpaceID && resID.SpaceId != utils.OCMStorageSpaceID { 412 return &provider.ListStorageSpacesResponse{ 413 // a specific id was requested, return not found instead of empty list 414 Status: &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND}, 415 }, nil 416 } 417 spaceID = &resID 418 } 419 } 420 421 info, share, grantee, token, err := s.extractLinkFromScope(ctx) 422 if err != nil { 423 switch err.(type) { 424 case errtypes.NotFound: 425 // if there is no public scope there are no publicstorage spaces 426 return &provider.ListStorageSpacesResponse{ 427 Status: &rpc.Status{Code: rpc.Code_CODE_OK}, 428 }, nil 429 default: 430 return &provider.ListStorageSpacesResponse{ 431 Status: &rpc.Status{Code: rpc.Code_CODE_INTERNAL}, 432 }, nil 433 } 434 } 435 436 if len(spaceTypes) == 0 { 437 spaceTypes["mountpoint"] = exists 438 } 439 for _, s := range appendTypes { 440 spaceTypes[s] = exists 441 } 442 443 res := &provider.ListStorageSpacesResponse{ 444 Status: status.NewOK(ctx), 445 } 446 for k := range spaceTypes { 447 switch k { 448 case "grant": 449 // when a list storage space with the resourceid of an external 450 // resource is made we may have a grant for it 451 root := info.Id 452 if spaceID != nil && !utils.ResourceIDEqual(spaceID, root) { 453 // none of our business 454 continue 455 } 456 // we know a grant for this resource 457 space := &provider.StorageSpace{ 458 Id: &provider.StorageSpaceId{ 459 OpaqueId: storagespace.FormatResourceID(root), 460 }, 461 SpaceType: "grant", 462 Owner: &userv1beta1.User{Id: grantee}, 463 // the publicstorageprovider keeps track of mount points 464 Root: root, 465 } 466 467 res.StorageSpaces = append(res.StorageSpaces, space) 468 case "mountpoint": 469 root := &provider.ResourceId{ 470 StorageId: utils.PublicStorageProviderID, 471 SpaceId: utils.PublicStorageSpaceID, 472 OpaqueId: token, // the link share has no id, only the token 473 } 474 if ocmShare, ok := share.(*ocm.Share); ok { 475 root.OpaqueId = ocmShare.GetId().GetOpaqueId() 476 } 477 if spaceID != nil { 478 switch { 479 case utils.ResourceIDEqual(spaceID, root): 480 // we have a virtual node 481 case utils.ResourceIDEqual(spaceID, info.Id): 482 // we have a mount point 483 root = info.Id 484 default: 485 // none of our business 486 continue 487 } 488 } 489 space := &provider.StorageSpace{ 490 Id: &provider.StorageSpaceId{ 491 OpaqueId: storagespace.FormatResourceID(root), 492 }, 493 SpaceType: "mountpoint", 494 Owner: &userv1beta1.User{Id: grantee}, // FIXME actually, the mount point belongs to no one? 495 // the publicstorageprovider keeps track of mount points 496 Root: root, 497 } 498 499 res.StorageSpaces = append(res.StorageSpaces, space) 500 } 501 } 502 return res, nil 503 } 504 505 func (s *service) extractLinkFromScope(ctx context.Context) (*provider.ResourceInfo, interface{}, *userv1beta1.UserId, string, error) { 506 scopes, ok := ctxpkg.ContextGetScopes(ctx) 507 if !ok { 508 return nil, nil, nil, "", errtypes.NotFound("No scopes found in context") 509 } 510 for k, v := range scopes { 511 if strings.HasPrefix(k, "ocmshare:") && v.Resource.Decoder == "json" { 512 share := &ocm.Share{} 513 err := utils.UnmarshalJSONToProtoV1(v.Resource.Value, share) 514 if err != nil { 515 return nil, nil, nil, "", errtypes.InternalError("failed to unmarshal ocm share") 516 } 517 518 // the share is minimally populated, we need more than the token 519 // look up complete share 520 info, resolvedShare, err := s.resolveToken(ctx, share) 521 if err != nil { 522 return nil, nil, nil, "", err 523 } 524 return info, resolvedShare, share.Owner, share.Token, nil 525 } else if strings.HasPrefix(k, "publicshare:") && v.Resource.Decoder == "json" { 526 share := &link.PublicShare{} 527 err := utils.UnmarshalJSONToProtoV1(v.Resource.Value, share) 528 if err != nil { 529 return nil, nil, nil, "", errtypes.InternalError("failed to unmarshal public share") 530 } 531 532 // the share is minimally populated, we need more than the token 533 // look up complete share 534 info, resolvedShare, err := s.resolveToken(ctx, share) 535 if err != nil { 536 return nil, nil, nil, "", err 537 } 538 return info, resolvedShare, share.Owner, share.Token, nil 539 } 540 } 541 return nil, nil, nil, "", errtypes.NotFound("No public storage info found in scopes") 542 } 543 func (s *service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { 544 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 545 } 546 547 func (s *service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) { 548 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 549 } 550 551 func (s *service) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) { 552 ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "CreateContainer") 553 defer span.End() 554 555 span.SetAttributes(attribute.KeyValue{ 556 Key: "reference", 557 Value: attribute.StringValue(req.Ref.String()), 558 }) 559 560 cs3Ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 561 switch { 562 case err != nil: 563 return &provider.CreateContainerResponse{ 564 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 565 }, nil 566 case info.PermissionSet == nil || !info.PermissionSet.CreateContainer: 567 return &provider.CreateContainerResponse{ 568 Status: status.NewPermissionDenied(ctx, nil, "share does not grant CreateContainer permission"), 569 }, nil 570 } 571 572 var res *provider.CreateContainerResponse 573 // the call has to be made to the gateway instead of the storage. 574 gatewayClient, err := s.gatewaySelector.Next() 575 if err != nil { 576 return nil, err 577 } 578 res, err = gatewayClient.CreateContainer(ctx, &provider.CreateContainerRequest{ 579 Ref: cs3Ref, 580 }) 581 if err != nil { 582 return &provider.CreateContainerResponse{ 583 Status: status.NewInternal(ctx, "createContainer: error calling CreateContainer for ref:"+req.Ref.String()), 584 }, nil 585 } 586 if res.Status.Code == rpc.Code_CODE_INTERNAL { 587 return res, nil 588 } 589 590 return res, nil 591 } 592 593 func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) { 594 ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 595 if err != nil { 596 return &provider.TouchFileResponse{ 597 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 598 }, nil 599 } 600 gatewayClient, err := s.gatewaySelector.Next() 601 if err != nil { 602 return nil, err 603 } 604 return gatewayClient.TouchFile(ctx, &provider.TouchFileRequest{Opaque: req.Opaque, Ref: ref}) 605 } 606 607 func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { 608 ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Delete") 609 defer span.End() 610 611 span.SetAttributes(attribute.KeyValue{ 612 Key: "reference", 613 Value: attribute.StringValue(req.Ref.String()), 614 }) 615 616 cs3Ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) 617 switch { 618 case err != nil: 619 return &provider.DeleteResponse{ 620 Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err), 621 }, nil 622 case info.PermissionSet == nil || !info.PermissionSet.Delete: 623 return &provider.DeleteResponse{ 624 Status: status.NewPermissionDenied(ctx, nil, "share does not grant Delete permission"), 625 }, nil 626 } 627 628 var res *provider.DeleteResponse 629 // the call has to be made to the gateway instead of the storage. 630 gatewayClient, err := s.gatewaySelector.Next() 631 if err != nil { 632 return nil, err 633 } 634 res, err = gatewayClient.Delete(ctx, &provider.DeleteRequest{ 635 Ref: cs3Ref, 636 }) 637 if err != nil { 638 return &provider.DeleteResponse{ 639 Status: status.NewInternal(ctx, "Delete: error calling Delete for ref:"+req.Ref.String()), 640 }, nil 641 } 642 if res.Status.Code == rpc.Code_CODE_INTERNAL { 643 return res, nil 644 } 645 646 return res, nil 647 } 648 649 func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) { 650 ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Move") 651 defer span.End() 652 653 span.SetAttributes( 654 attribute.KeyValue{ 655 Key: "source", 656 Value: attribute.StringValue(req.Source.String()), 657 }, 658 attribute.KeyValue{ 659 Key: "destination", 660 Value: attribute.StringValue(req.Destination.String()), 661 }, 662 ) 663 664 cs3RefSource, info, tknSource, err := s.translatePublicRefToCS3Ref(ctx, req.Source) 665 switch { 666 case err != nil: 667 return &provider.MoveResponse{ 668 Status: status.NewStatusFromErrType(ctx, "failed to resolve source reference", err), 669 }, nil 670 case info.PermissionSet == nil || !info.PermissionSet.Move: 671 return &provider.MoveResponse{ 672 Status: status.NewPermissionDenied(ctx, nil, "share does not grant Move permission"), 673 }, nil 674 } 675 // FIXME: maybe there's a shortcut possible here using the source path 676 cs3RefDestination, _, tknDest, err := s.translatePublicRefToCS3Ref(ctx, req.Destination) 677 if err != nil { 678 return &provider.MoveResponse{ 679 Status: status.NewStatusFromErrType(ctx, "failed to resolve destination reference", err), 680 }, nil 681 } 682 683 if tknSource != tknDest { 684 return &provider.MoveResponse{ 685 Status: status.NewInvalid(ctx, "Source and destination token must be the same"), 686 }, nil 687 } 688 689 var res *provider.MoveResponse 690 // the call has to be made to the gateway instead of the storage. 691 gatewayClient, err := s.gatewaySelector.Next() 692 if err != nil { 693 return nil, err 694 } 695 res, err = gatewayClient.Move(ctx, &provider.MoveRequest{ 696 Source: cs3RefSource, 697 Destination: cs3RefDestination, 698 }) 699 if err != nil { 700 return &provider.MoveResponse{ 701 Status: status.NewInternal(ctx, "Move: error calling Move for source ref "+req.Source.String()+" to destination ref "+req.Destination.String()), 702 }, nil 703 } 704 if res.Status.Code == rpc.Code_CODE_INTERNAL { 705 return res, nil 706 } 707 708 return res, nil 709 } 710 711 func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) { 712 ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Stat") 713 defer span.End() 714 715 span.SetAttributes( 716 attribute.KeyValue{ 717 Key: "source", 718 Value: attribute.StringValue(req.Ref.String()), 719 }) 720 721 info, share, _, token, err := s.extractLinkFromScope(ctx) 722 if err != nil { 723 switch err.(type) { 724 case errtypes.NotFound: 725 return &provider.StatResponse{ 726 Status: status.NewNotFound(ctx, "share or token not found"), 727 }, nil 728 default: 729 return &provider.StatResponse{ 730 Status: status.NewInternal(ctx, "share or token not found"), 731 }, nil 732 } 733 } 734 735 if info.Type == provider.ResourceType_RESOURCE_TYPE_FILE || req.Ref.Path == "" { 736 res := &provider.StatResponse{ 737 Status: status.NewOK(ctx), 738 Info: info, 739 } 740 s.augmentStatResponse(ctx, res.Info, info, share, token) 741 return res, nil 742 } 743 744 ref := &provider.Reference{ 745 ResourceId: info.Id, 746 Path: utils.MakeRelativePath(req.Ref.Path), 747 } 748 749 gatewayClient, err := s.gatewaySelector.Next() 750 if err != nil { 751 return nil, err 752 } 753 statResponse, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: ref}) 754 if err != nil { 755 return &provider.StatResponse{ 756 Status: status.NewInternal(ctx, "Stat: error calling Stat for ref:"+req.Ref.String()), 757 }, nil 758 } 759 760 s.augmentStatResponse(ctx, statResponse.Info, info, share, token) 761 762 return statResponse, nil 763 } 764 765 func (s *service) augmentStatResponse(ctx context.Context, statInfo *provider.ResourceInfo, shareInfo *provider.ResourceInfo, share interface{}, tkn string) { 766 // prevent leaking internal paths 767 if statInfo != nil { 768 if err := addShare(statInfo, share); err != nil { 769 appctx.GetLogger(ctx).Error().Err(err).Interface("share", share).Interface("info", statInfo).Msg("error when adding share") 770 } 771 772 var sharePath string 773 if shareInfo.Type == provider.ResourceType_RESOURCE_TYPE_FILE { 774 sharePath = path.Base(shareInfo.Path) 775 } else { 776 sharePath = strings.TrimPrefix(statInfo.Path, shareInfo.Path) 777 } 778 779 statInfo.Path = path.Join("/", sharePath) 780 filterPermissions(statInfo.PermissionSet, shareInfo.PermissionSet) 781 } 782 } 783 784 func addShare(i *provider.ResourceInfo, share interface{}) error { 785 if i.Opaque == nil { 786 i.Opaque = &typesv1beta1.Opaque{} 787 } 788 if i.Opaque.Map == nil { 789 i.Opaque.Map = map[string]*typesv1beta1.OpaqueEntry{} 790 } 791 val, err := json.Marshal(share) 792 if err != nil { 793 return err 794 } 795 i.Opaque.Map["link-share"] = &typesv1beta1.OpaqueEntry{Decoder: "json", Value: val} 796 return nil 797 } 798 799 func (s *service) ListContainerStream(req *provider.ListContainerStreamRequest, ss provider.ProviderAPI_ListContainerStreamServer) error { 800 return gstatus.Errorf(codes.Unimplemented, "method not implemented") 801 } 802 803 func (s *service) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { 804 info, share, _, _, err := s.extractLinkFromScope(ctx) 805 if err != nil { 806 switch err.(type) { 807 case errtypes.NotFound: 808 return &provider.ListContainerResponse{ 809 Status: status.NewNotFound(ctx, "share or token not found"), 810 }, nil 811 default: 812 return &provider.ListContainerResponse{ 813 Status: status.NewInternal(ctx, "share or token not found"), 814 }, nil 815 } 816 } 817 if info.PermissionSet == nil || !info.PermissionSet.ListContainer { 818 return &provider.ListContainerResponse{ 819 Status: status.NewPermissionDenied(ctx, nil, "share does not grant ListContainer permission"), 820 }, nil 821 } 822 823 gatewayClient, err := s.gatewaySelector.Next() 824 if err != nil { 825 return nil, err 826 } 827 listContainerR, err := gatewayClient.ListContainer( 828 ctx, 829 &provider.ListContainerRequest{ 830 Ref: &provider.Reference{ 831 ResourceId: info.Id, 832 // prefix relative path with './' to make it a CS3 relative path 833 Path: utils.MakeRelativePath(req.Ref.Path), 834 }, 835 }, 836 ) 837 if err != nil { 838 return &provider.ListContainerResponse{ 839 Status: status.NewInternal(ctx, "ListContainer: error calling ListContainer for ref:"+req.Ref.String()), 840 }, nil 841 } 842 843 for i := range listContainerR.Infos { 844 // FIXME how do we reduce permissions to what is granted by the public link? 845 // only a problem for id based access -> middleware 846 filterPermissions(listContainerR.Infos[i].PermissionSet, info.PermissionSet) 847 if err := addShare(listContainerR.Infos[i], share); err != nil { 848 appctx.GetLogger(ctx).Error().Err(err).Interface("share", share).Interface("info", listContainerR.Infos[i]).Msg("error when adding share") 849 } 850 } 851 852 return listContainerR, nil 853 } 854 855 func filterPermissions(l *provider.ResourcePermissions, r *provider.ResourcePermissions) { 856 l.AddGrant = l.AddGrant && r.AddGrant 857 l.CreateContainer = l.CreateContainer && r.CreateContainer 858 l.Delete = l.Delete && r.Delete 859 l.GetPath = l.GetPath && r.GetPath 860 l.GetQuota = l.GetQuota && r.GetQuota 861 l.InitiateFileDownload = l.InitiateFileDownload && r.InitiateFileDownload 862 l.InitiateFileUpload = l.InitiateFileUpload && r.InitiateFileUpload 863 l.ListContainer = l.ListContainer && r.ListContainer 864 l.ListFileVersions = l.ListFileVersions && r.ListFileVersions 865 l.ListGrants = l.ListGrants && r.ListGrants 866 l.ListRecycle = l.ListRecycle && r.ListRecycle 867 l.Move = l.Move && r.Move 868 l.PurgeRecycle = l.PurgeRecycle && r.PurgeRecycle 869 l.RemoveGrant = l.RemoveGrant && r.RemoveGrant 870 l.RestoreFileVersion = l.RestoreFileVersion && r.RestoreFileVersion 871 l.RestoreRecycleItem = l.RestoreRecycleItem && r.RestoreRecycleItem 872 l.Stat = l.Stat && r.Stat 873 l.UpdateGrant = l.UpdateGrant && r.UpdateGrant 874 } 875 876 func (s *service) ListFileVersions(ctx context.Context, req *provider.ListFileVersionsRequest) (*provider.ListFileVersionsResponse, error) { 877 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 878 } 879 880 func (s *service) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) { 881 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 882 } 883 884 func (s *service) ListRecycleStream(req *provider.ListRecycleStreamRequest, ss provider.ProviderAPI_ListRecycleStreamServer) error { 885 return gstatus.Errorf(codes.Unimplemented, "method not implemented") 886 } 887 888 func (s *service) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) { 889 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 890 } 891 892 func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecycleItemRequest) (*provider.RestoreRecycleItemResponse, error) { 893 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 894 } 895 896 func (s *service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) { 897 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 898 } 899 900 func (s *service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) { 901 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 902 } 903 904 func (s *service) AddGrant(ctx context.Context, req *provider.AddGrantRequest) (*provider.AddGrantResponse, error) { 905 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 906 } 907 908 func (s *service) DenyGrant(ctx context.Context, req *provider.DenyGrantRequest) (*provider.DenyGrantResponse, error) { 909 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 910 } 911 912 func (s *service) CreateReference(ctx context.Context, req *provider.CreateReferenceRequest) (*provider.CreateReferenceResponse, error) { 913 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 914 } 915 916 func (s *service) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) { 917 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 918 } 919 920 func (s *service) UpdateGrant(ctx context.Context, req *provider.UpdateGrantRequest) (*provider.UpdateGrantResponse, error) { 921 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 922 } 923 924 func (s *service) RemoveGrant(ctx context.Context, req *provider.RemoveGrantRequest) (*provider.RemoveGrantResponse, error) { 925 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 926 } 927 928 func (s *service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) (*provider.GetQuotaResponse, error) { 929 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 930 } 931 932 // resolveToken returns the resource info for the publicly shared resource. 933 func (s *service) resolveToken(ctx context.Context, share interface{}) (*provider.ResourceInfo, interface{}, error) { 934 gatewayClient, err := s.gatewaySelector.Next() 935 if err != nil { 936 return nil, nil, err 937 } 938 939 resourceID := &provider.ResourceId{} 940 perms := &provider.ResourcePermissions{} 941 var resolvedShare interface{} 942 switch v := share.(type) { 943 case *link.PublicShare: 944 publicShareResponse, err := gatewayClient.GetPublicShare( 945 ctx, 946 &link.GetPublicShareRequest{ 947 Ref: &link.PublicShareReference{ 948 Spec: &link.PublicShareReference_Token{ 949 Token: v.Token, 950 }, 951 }, 952 Sign: true, 953 }, 954 ) 955 switch { 956 case err != nil: 957 return nil, nil, err 958 case publicShareResponse.Status.Code != rpc.Code_CODE_OK: 959 return nil, nil, errtypes.NewErrtypeFromStatus(publicShareResponse.Status) 960 } 961 resolvedShare = publicShareResponse.GetShare() 962 resourceID = publicShareResponse.GetShare().GetResourceId() 963 perms = publicShareResponse.GetShare().GetPermissions().GetPermissions() 964 case *ocm.Share: 965 gsr, err := gatewayClient.GetOCMShareByToken(ctx, &ocm.GetOCMShareByTokenRequest{ 966 Token: v.Token, 967 }) 968 switch { 969 case err != nil: 970 return nil, nil, err 971 case gsr.Status.Code != rpc.Code_CODE_OK: 972 return nil, nil, errtypes.NewErrtypeFromStatus(gsr.Status) 973 } 974 accessMethods := gsr.GetShare().GetAccessMethods() 975 if len(accessMethods) == 0 { 976 return nil, nil, errtypes.PermissionDenied("failed to get access to the requested resource") 977 } 978 resolvedShare = gsr.GetShare() 979 resourceID = gsr.GetShare().GetResourceId() 980 perms = accessMethods[0].GetWebdavOptions().Permissions 981 } 982 983 sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ 984 Ref: &provider.Reference{ 985 ResourceId: resourceID, 986 }, 987 }) 988 switch { 989 case err != nil: 990 return nil, nil, err 991 case sRes.Status.Code != rpc.Code_CODE_OK: 992 return nil, nil, errtypes.NewErrtypeFromStatus(sRes.Status) 993 } 994 995 // Set permissions 996 sRes.Info.PermissionSet = perms 997 return sRes.Info, resolvedShare, nil 998 }