github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/nextcloud/nextcloud.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 nextcloud 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "io" 26 "net/http" 27 "net/url" 28 "path" 29 "strings" 30 31 user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 32 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 33 types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 34 "github.com/mitchellh/mapstructure" 35 "github.com/pkg/errors" 36 "github.com/rs/zerolog" 37 38 "github.com/cs3org/reva/v2/pkg/appctx" 39 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 40 "github.com/cs3org/reva/v2/pkg/errtypes" 41 "github.com/cs3org/reva/v2/pkg/events" 42 "github.com/cs3org/reva/v2/pkg/storage" 43 "github.com/cs3org/reva/v2/pkg/storage/fs/registry" 44 ) 45 46 func init() { 47 registry.Register("nextcloud", New) 48 } 49 50 // StorageDriverConfig is the configuration struct for a NextcloudStorageDriver 51 type StorageDriverConfig struct { 52 EndPoint string `mapstructure:"endpoint"` // e.g. "http://nc/apps/sciencemesh/~alice/" 53 SharedSecret string `mapstructure:"shared_secret"` 54 MockHTTP bool `mapstructure:"mock_http"` 55 } 56 57 // StorageDriver implements the storage.FS interface 58 // and connects with a StorageDriver server as its backend 59 type StorageDriver struct { 60 endPoint string 61 sharedSecret string 62 client *http.Client 63 } 64 65 func parseConfig(m map[string]interface{}) (*StorageDriverConfig, error) { 66 c := &StorageDriverConfig{} 67 if err := mapstructure.Decode(m, c); err != nil { 68 err = errors.Wrap(err, "error decoding conf") 69 return nil, err 70 } 71 return c, nil 72 } 73 74 // New returns an implementation to of the storage.FS interface that talks to 75 // a Nextcloud instance over http. 76 func New(m map[string]interface{}, _ events.Stream, _ *zerolog.Logger) (storage.FS, error) { 77 conf, err := parseConfig(m) 78 if err != nil { 79 return nil, err 80 } 81 82 return NewStorageDriver(conf) 83 } 84 85 // NewStorageDriver returns a new NextcloudStorageDriver 86 func NewStorageDriver(c *StorageDriverConfig) (*StorageDriver, error) { 87 var client *http.Client 88 if c.MockHTTP { 89 // called := make([]string, 0) 90 // nextcloudServerMock := GetNextcloudServerMock(&called) 91 // client, _ = TestingHTTPClient(nextcloudServerMock) 92 93 // This is only used by the integration tests: 94 // (unit tests will call SetHTTPClient later): 95 called := make([]string, 0) 96 h := GetNextcloudServerMock(&called) 97 client, _ = TestingHTTPClient(h) 98 // FIXME: defer teardown() 99 } else { 100 if len(c.EndPoint) == 0 { 101 return nil, errors.New("Please specify 'endpoint' in '[grpc.services.storageprovider.drivers.nextcloud]'") 102 } 103 client = &http.Client{} 104 } 105 return &StorageDriver{ 106 endPoint: c.EndPoint, // e.g. "http://nc/apps/sciencemesh/" 107 sharedSecret: c.SharedSecret, 108 client: client, 109 }, nil 110 } 111 112 // Action describes a REST request to forward to the Nextcloud backend 113 type Action struct { 114 verb string 115 argS string 116 } 117 118 func getUser(ctx context.Context) (*user.User, error) { 119 u, ok := ctxpkg.ContextGetUser(ctx) 120 if !ok { 121 err := errors.Wrap(errtypes.UserRequired(""), "nextcloud storage driver: error getting user from ctx") 122 return nil, err 123 } 124 return u, nil 125 } 126 127 // SetHTTPClient sets the HTTP client 128 func (nc *StorageDriver) SetHTTPClient(c *http.Client) { 129 nc.client = c 130 } 131 132 func (nc *StorageDriver) doUpload(ctx context.Context, filePath string, r io.ReadCloser) error { 133 // log := appctx.GetLogger(ctx) 134 user, err := getUser(ctx) 135 if err != nil { 136 return err 137 } 138 // See https://github.com/pondersource/nc-sciencemesh/issues/5 139 // url := nc.endPoint + "~" + user.Username + "/files/" + filePath 140 url := nc.endPoint + "~" + user.Username + "/api/storage/Upload/" + filePath 141 req, err := http.NewRequest(http.MethodPut, url, r) 142 if err != nil { 143 panic(err) 144 } 145 146 req.Header.Set("X-Reva-Secret", nc.sharedSecret) 147 // set the request header Content-Type for the upload 148 // FIXME: get the actual content type from somewhere 149 req.Header.Set("Content-Type", "text/plain") 150 resp, err := nc.client.Do(req) 151 if err != nil { 152 panic(err) 153 } 154 155 defer resp.Body.Close() 156 _, err = io.ReadAll(resp.Body) 157 return err 158 } 159 160 func (nc *StorageDriver) doDownload(ctx context.Context, filePath string) (io.ReadCloser, error) { 161 user, err := getUser(ctx) 162 if err != nil { 163 return nil, err 164 } 165 // See https://github.com/pondersource/nc-sciencemesh/issues/5 166 // url := nc.endPoint + "~" + user.Username + "/files/" + filePath 167 url := nc.endPoint + "~" + user.Username + "/api/storage/Download/" + filePath 168 req, err := http.NewRequest(http.MethodGet, url, strings.NewReader("")) 169 if err != nil { 170 panic(err) 171 } 172 173 resp, err := nc.client.Do(req) 174 if err != nil { 175 panic(err) 176 } 177 if resp.StatusCode != 200 { 178 panic("No 200 response code in download request") 179 } 180 181 return resp.Body, err 182 } 183 184 func (nc *StorageDriver) doDownloadRevision(ctx context.Context, filePath string, key string) (io.ReadCloser, error) { 185 user, err := getUser(ctx) 186 if err != nil { 187 return nil, err 188 } 189 // See https://github.com/pondersource/nc-sciencemesh/issues/5 190 url := nc.endPoint + "~" + user.Username + "/api/storage/DownloadRevision/" + url.QueryEscape(key) + "/" + filePath 191 req, err := http.NewRequest(http.MethodGet, url, strings.NewReader("")) 192 if err != nil { 193 panic(err) 194 } 195 req.Header.Set("X-Reva-Secret", nc.sharedSecret) 196 197 resp, err := nc.client.Do(req) 198 if err != nil { 199 panic(err) 200 } 201 if resp.StatusCode != 200 { 202 panic("No 200 response code in download request") 203 } 204 205 return resp.Body, err 206 } 207 208 func (nc *StorageDriver) do(ctx context.Context, a Action) (int, []byte, error) { 209 log := appctx.GetLogger(ctx) 210 user, err := getUser(ctx) 211 if err != nil { 212 return 0, nil, err 213 } 214 // See https://github.com/cs3org/reva/issues/2377 215 // for discussion of user.Username vs user.Id.OpaqueId 216 url := nc.endPoint + "~" + user.Id.OpaqueId + "/api/storage/" + a.verb 217 log.Info().Msgf("nc.do req %s %s", url, a.argS) 218 req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(a.argS)) 219 if err != nil { 220 return 0, nil, err 221 } 222 req.Header.Set("X-Reva-Secret", nc.sharedSecret) 223 224 req.Header.Set("Content-Type", "application/json") 225 resp, err := nc.client.Do(req) 226 if err != nil { 227 return 0, nil, err 228 } 229 230 defer resp.Body.Close() 231 body, err := io.ReadAll(resp.Body) 232 233 if err != nil { 234 return 0, nil, err 235 } 236 log.Info().Msgf("nc.do res %s %s", url, string(body)) 237 238 return resp.StatusCode, body, nil 239 } 240 241 // GetHome as defined in the storage.FS interface 242 func (nc *StorageDriver) GetHome(ctx context.Context) (string, error) { 243 log := appctx.GetLogger(ctx) 244 log.Info().Msg("GetHome") 245 246 _, respBody, err := nc.do(ctx, Action{"GetHome", ""}) 247 return string(respBody), err 248 } 249 250 // CreateHome as defined in the storage.FS interface 251 func (nc *StorageDriver) CreateHome(ctx context.Context) error { 252 log := appctx.GetLogger(ctx) 253 log.Info().Msg("CreateHome") 254 255 _, _, err := nc.do(ctx, Action{"CreateHome", ""}) 256 return err 257 } 258 259 // CreateDir as defined in the storage.FS interface 260 func (nc *StorageDriver) CreateDir(ctx context.Context, ref *provider.Reference) error { 261 bodyStr, err := json.Marshal(ref) 262 if err != nil { 263 return err 264 } 265 log := appctx.GetLogger(ctx) 266 log.Info().Msgf("CreateDir %s", bodyStr) 267 268 _, _, err = nc.do(ctx, Action{"CreateDir", string(bodyStr)}) 269 return err 270 } 271 272 // TouchFile as defined in the storage.FS interface 273 func (nc *StorageDriver) TouchFile(ctx context.Context, ref *provider.Reference, markprocessing bool, mtime string) error { 274 return fmt.Errorf("unimplemented: TouchFile") 275 } 276 277 // Delete as defined in the storage.FS interface 278 func (nc *StorageDriver) Delete(ctx context.Context, ref *provider.Reference) error { 279 bodyStr, err := json.Marshal(ref) 280 if err != nil { 281 return err 282 } 283 log := appctx.GetLogger(ctx) 284 log.Info().Msgf("Delete %s", bodyStr) 285 286 _, _, err = nc.do(ctx, Action{"Delete", string(bodyStr)}) 287 return err 288 } 289 290 // Move as defined in the storage.FS interface 291 func (nc *StorageDriver) Move(ctx context.Context, oldRef, newRef *provider.Reference) error { 292 type paramsObj struct { 293 OldRef *provider.Reference `json:"oldRef"` 294 NewRef *provider.Reference `json:"newRef"` 295 } 296 bodyObj := ¶msObj{ 297 OldRef: oldRef, 298 NewRef: newRef, 299 } 300 bodyStr, _ := json.Marshal(bodyObj) 301 log := appctx.GetLogger(ctx) 302 log.Info().Msgf("Move %s", bodyStr) 303 304 _, _, err := nc.do(ctx, Action{"Move", string(bodyStr)}) 305 return err 306 } 307 308 // GetMD as defined in the storage.FS interface 309 // TODO forward fieldMask 310 func (nc *StorageDriver) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string, fieldMask []string) (*provider.ResourceInfo, error) { 311 type paramsObj struct { 312 Ref *provider.Reference `json:"ref"` 313 MdKeys []string `json:"mdKeys"` 314 // MetaData provider.ResourceInfo `json:"metaData"` 315 } 316 bodyObj := ¶msObj{ 317 Ref: ref, 318 MdKeys: mdKeys, 319 } 320 bodyStr, _ := json.Marshal(bodyObj) 321 log := appctx.GetLogger(ctx) 322 log.Info().Msgf("GetMD %s", bodyStr) 323 324 status, body, err := nc.do(ctx, Action{"GetMD", string(bodyStr)}) 325 if err != nil { 326 return nil, err 327 } 328 if status == 404 { 329 return nil, errtypes.NotFound("") 330 } 331 var respObj provider.ResourceInfo 332 err = json.Unmarshal(body, &respObj) 333 if err != nil { 334 return nil, err 335 } 336 return &respObj, nil 337 } 338 339 // ListFolder as defined in the storage.FS interface 340 func (nc *StorageDriver) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys, fieldMask []string) ([]*provider.ResourceInfo, error) { 341 type paramsObj struct { 342 Ref *provider.Reference `json:"ref"` 343 MdKeys []string `json:"mdKeys"` 344 } 345 bodyObj := ¶msObj{ 346 Ref: ref, 347 MdKeys: mdKeys, 348 } 349 bodyStr, err := json.Marshal(bodyObj) 350 log := appctx.GetLogger(ctx) 351 log.Info().Msgf("ListFolder %s", bodyStr) 352 if err != nil { 353 return nil, err 354 } 355 status, body, err := nc.do(ctx, Action{"ListFolder", string(bodyStr)}) 356 if err != nil { 357 return nil, err 358 } 359 if status == 404 { 360 return nil, errtypes.NotFound("") 361 } 362 363 var respMapArr []provider.ResourceInfo 364 err = json.Unmarshal(body, &respMapArr) 365 if err != nil { 366 return nil, err 367 } 368 var pointers = make([]*provider.ResourceInfo, len(respMapArr)) 369 for i := 0; i < len(respMapArr); i++ { 370 pointers[i] = &respMapArr[i] 371 } 372 return pointers, err 373 } 374 375 // InitiateUpload as defined in the storage.FS interface 376 func (nc *StorageDriver) InitiateUpload(ctx context.Context, ref *provider.Reference, uploadLength int64, metadata map[string]string) (map[string]string, error) { 377 type paramsObj struct { 378 Ref *provider.Reference `json:"ref"` 379 UploadLength int64 `json:"uploadLength"` 380 Metadata map[string]string `json:"metadata"` 381 } 382 bodyObj := ¶msObj{ 383 Ref: ref, 384 UploadLength: uploadLength, 385 Metadata: metadata, 386 } 387 bodyStr, _ := json.Marshal(bodyObj) 388 log := appctx.GetLogger(ctx) 389 log.Info().Msgf("InitiateUpload %s", bodyStr) 390 391 _, respBody, err := nc.do(ctx, Action{"InitiateUpload", string(bodyStr)}) 392 if err != nil { 393 return nil, err 394 } 395 respMap := make(map[string]string) 396 err = json.Unmarshal(respBody, &respMap) 397 if err != nil { 398 return nil, err 399 } 400 return respMap, err 401 } 402 403 // Upload as defined in the storage.FS interface 404 func (nc *StorageDriver) Upload(ctx context.Context, req storage.UploadRequest, _ storage.UploadFinishedFunc) (*provider.ResourceInfo, error) { 405 err := nc.doUpload(ctx, req.Ref.Path, req.Body) 406 if err != nil { 407 return &provider.ResourceInfo{}, err 408 } 409 410 // return id, etag and mtime 411 ri, err := nc.GetMD(ctx, req.Ref, []string{}, []string{"id", "etag", "mtime"}) 412 if err != nil { 413 return &provider.ResourceInfo{}, err 414 } 415 416 return ri, nil 417 } 418 419 // Download as defined in the storage.FS interface 420 func (nc *StorageDriver) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { 421 md, err := nc.GetMD(ctx, ref, []string{}, nil) 422 if err != nil { 423 return nil, nil, err 424 } 425 if !openReaderfunc(md) { 426 return md, nil, nil 427 } 428 429 reader, err := nc.doDownload(ctx, ref.Path) 430 return md, reader, err 431 } 432 433 // ListRevisions as defined in the storage.FS interface 434 func (nc *StorageDriver) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) { 435 bodyStr, _ := json.Marshal(ref) 436 log := appctx.GetLogger(ctx) 437 log.Info().Msgf("ListRevisions %s", bodyStr) 438 439 _, respBody, err := nc.do(ctx, Action{"ListRevisions", string(bodyStr)}) 440 441 if err != nil { 442 return nil, err 443 } 444 var respMapArr []provider.FileVersion 445 err = json.Unmarshal(respBody, &respMapArr) 446 if err != nil { 447 return nil, err 448 } 449 revs := make([]*provider.FileVersion, len(respMapArr)) 450 for i := 0; i < len(respMapArr); i++ { 451 revs[i] = &respMapArr[i] 452 } 453 return revs, err 454 } 455 456 // DownloadRevision as defined in the storage.FS interface 457 func (nc *StorageDriver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { 458 log := appctx.GetLogger(ctx) 459 log.Info().Msgf("DownloadRevision %s %s", ref.Path, key) 460 461 md, err := nc.GetMD(ctx, ref, []string{}, nil) 462 if err != nil { 463 return nil, nil, err 464 } 465 revs, err := nc.ListRevisions(ctx, ref) 466 if err != nil { 467 return nil, nil, err 468 } 469 var ri *provider.ResourceInfo 470 for _, rev := range revs { 471 if rev.Key == key { 472 473 ri = &provider.ResourceInfo{ 474 // TODO(jfd) we cannot access version content, this will be a problem when trying to fetch version thumbnails 475 // Opaque 476 Type: provider.ResourceType_RESOURCE_TYPE_FILE, 477 Id: &provider.ResourceId{ 478 StorageId: "versions", 479 OpaqueId: md.Id.OpaqueId + "@" + rev.GetKey(), 480 }, 481 // Checksum 482 Etag: rev.Etag, 483 // MimeType 484 Mtime: &types.Timestamp{ 485 Seconds: rev.Mtime, 486 // TODO cs3apis FileVersion should use types.Timestamp instead of uint64 487 }, 488 Path: path.Join("v", rev.Key), 489 // PermissionSet 490 Size: rev.Size, 491 Owner: md.Owner, 492 } 493 } 494 } 495 if ri == nil { 496 return nil, nil, errtypes.NotFound("revision not found") 497 } 498 499 if !openReaderfunc(ri) { 500 return ri, nil, nil 501 } 502 503 readCloser, err := nc.doDownloadRevision(ctx, ref.Path, key) 504 return ri, readCloser, err 505 } 506 507 // RestoreRevision as defined in the storage.FS interface 508 func (nc *StorageDriver) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error { 509 type paramsObj struct { 510 Ref *provider.Reference `json:"ref"` 511 Key string `json:"key"` 512 } 513 bodyObj := ¶msObj{ 514 Ref: ref, 515 Key: key, 516 } 517 bodyStr, _ := json.Marshal(bodyObj) 518 log := appctx.GetLogger(ctx) 519 log.Info().Msgf("RestoreRevision %s", bodyStr) 520 521 _, _, err := nc.do(ctx, Action{"RestoreRevision", string(bodyStr)}) 522 return err 523 } 524 525 // ListRecycle as defined in the storage.FS interface 526 func (nc *StorageDriver) ListRecycle(ctx context.Context, ref *provider.Reference, key string, relativePath string) ([]*provider.RecycleItem, error) { 527 log := appctx.GetLogger(ctx) 528 log.Info().Msg("ListRecycle") 529 type paramsObj struct { 530 Key string `json:"key"` 531 Path string `json:"path"` 532 } 533 bodyObj := ¶msObj{ 534 Key: key, 535 Path: relativePath, 536 } 537 bodyStr, _ := json.Marshal(bodyObj) 538 539 _, respBody, err := nc.do(ctx, Action{"ListRecycle", string(bodyStr)}) 540 541 if err != nil { 542 return nil, err 543 } 544 var respMapArr []provider.RecycleItem 545 err = json.Unmarshal(respBody, &respMapArr) 546 if err != nil { 547 return nil, err 548 } 549 items := make([]*provider.RecycleItem, len(respMapArr)) 550 for i := 0; i < len(respMapArr); i++ { 551 items[i] = &respMapArr[i] 552 } 553 return items, err 554 } 555 556 // RestoreRecycleItem as defined in the storage.FS interface 557 func (nc *StorageDriver) RestoreRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string, restoreRef *provider.Reference) error { 558 type paramsObj struct { 559 Key string `json:"key"` 560 Path string `json:"path"` 561 RestoreRef *provider.Reference `json:"restoreRef"` 562 } 563 bodyObj := ¶msObj{ 564 Key: key, 565 Path: relativePath, 566 RestoreRef: restoreRef, 567 } 568 bodyStr, _ := json.Marshal(bodyObj) 569 570 log := appctx.GetLogger(ctx) 571 log.Info().Msgf("RestoreRecycleItem %s", bodyStr) 572 573 _, _, err := nc.do(ctx, Action{"RestoreRecycleItem", string(bodyStr)}) 574 575 return err 576 } 577 578 // PurgeRecycleItem as defined in the storage.FS interface 579 func (nc *StorageDriver) PurgeRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string) error { 580 type paramsObj struct { 581 Key string `json:"key"` 582 Path string `json:"path"` 583 } 584 bodyObj := ¶msObj{ 585 Key: key, 586 Path: relativePath, 587 } 588 bodyStr, _ := json.Marshal(bodyObj) 589 log := appctx.GetLogger(ctx) 590 log.Info().Msgf("PurgeRecycleItem %s", bodyStr) 591 592 _, _, err := nc.do(ctx, Action{"PurgeRecycleItem", string(bodyStr)}) 593 return err 594 } 595 596 // EmptyRecycle as defined in the storage.FS interface 597 func (nc *StorageDriver) EmptyRecycle(ctx context.Context, ref *provider.Reference) error { 598 log := appctx.GetLogger(ctx) 599 log.Info().Msg("EmptyRecycle") 600 601 _, _, err := nc.do(ctx, Action{"EmptyRecycle", ""}) 602 return err 603 } 604 605 // GetPathByID as defined in the storage.FS interface 606 func (nc *StorageDriver) GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) { 607 bodyStr, _ := json.Marshal(id) 608 _, respBody, err := nc.do(ctx, Action{"GetPathByID", string(bodyStr)}) 609 return string(respBody), err 610 } 611 612 // AddGrant as defined in the storage.FS interface 613 func (nc *StorageDriver) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { 614 type paramsObj struct { 615 Ref *provider.Reference `json:"ref"` 616 G *provider.Grant `json:"g"` 617 } 618 bodyObj := ¶msObj{ 619 Ref: ref, 620 G: g, 621 } 622 bodyStr, _ := json.Marshal(bodyObj) 623 log := appctx.GetLogger(ctx) 624 log.Info().Msgf("AddGrant %s", bodyStr) 625 626 _, _, err := nc.do(ctx, Action{"AddGrant", string(bodyStr)}) 627 return err 628 } 629 630 // DenyGrant as defined in the storage.FS interface 631 func (nc *StorageDriver) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error { 632 type paramsObj struct { 633 Ref *provider.Reference `json:"ref"` 634 G *provider.Grantee `json:"g"` 635 } 636 bodyObj := ¶msObj{ 637 Ref: ref, 638 G: g, 639 } 640 bodyStr, _ := json.Marshal(bodyObj) 641 log := appctx.GetLogger(ctx) 642 log.Info().Msgf("DenyGrant %s", bodyStr) 643 644 _, _, err := nc.do(ctx, Action{"DenyGrant", string(bodyStr)}) 645 return err 646 } 647 648 // RemoveGrant as defined in the storage.FS interface 649 func (nc *StorageDriver) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { 650 type paramsObj struct { 651 Ref *provider.Reference `json:"ref"` 652 G *provider.Grant `json:"g"` 653 } 654 bodyObj := ¶msObj{ 655 Ref: ref, 656 G: g, 657 } 658 bodyStr, _ := json.Marshal(bodyObj) 659 log := appctx.GetLogger(ctx) 660 log.Info().Msgf("RemoveGrant %s", bodyStr) 661 662 _, _, err := nc.do(ctx, Action{"RemoveGrant", string(bodyStr)}) 663 return err 664 } 665 666 // UpdateGrant as defined in the storage.FS interface 667 func (nc *StorageDriver) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { 668 type paramsObj struct { 669 Ref *provider.Reference `json:"ref"` 670 G *provider.Grant `json:"g"` 671 } 672 bodyObj := ¶msObj{ 673 Ref: ref, 674 G: g, 675 } 676 bodyStr, _ := json.Marshal(bodyObj) 677 log := appctx.GetLogger(ctx) 678 log.Info().Msgf("UpdateGrant %s", bodyStr) 679 680 _, _, err := nc.do(ctx, Action{"UpdateGrant", string(bodyStr)}) 681 return err 682 } 683 684 // ListGrants as defined in the storage.FS interface 685 func (nc *StorageDriver) ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) { 686 bodyStr, _ := json.Marshal(ref) 687 log := appctx.GetLogger(ctx) 688 log.Info().Msgf("ListGrants %s", bodyStr) 689 690 _, respBody, err := nc.do(ctx, Action{"ListGrants", string(bodyStr)}) 691 if err != nil { 692 return nil, err 693 } 694 695 // To avoid this error: 696 // json: cannot unmarshal object into Go struct field Grantee.grantee.Id of type providerv1beta1.isGrantee_Id 697 // To test: 698 // bodyStr, _ := json.Marshal(provider.Grant{ 699 // Grantee: &provider.Grantee{ 700 // Type: provider.GranteeType_GRANTEE_TYPE_USER, 701 // Id: &provider.Grantee_UserId{ 702 // UserId: &user.UserId{ 703 // Idp: "some-idp", 704 // OpaqueId: "some-opaque-id", 705 // Type: user.UserType_USER_TYPE_PRIMARY, 706 // }, 707 // }, 708 // }, 709 // Permissions: &provider.ResourcePermissions{}, 710 // }) 711 // JSON example: 712 // [{"grantee":{"Id":{"UserId":{"idp":"some-idp","opaque_id":"some-opaque-id","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true}}] 713 var respMapArr []map[string]interface{} 714 err = json.Unmarshal(respBody, &respMapArr) 715 if err != nil { 716 return nil, err 717 } 718 grants := make([]*provider.Grant, len(respMapArr)) 719 for i := 0; i < len(respMapArr); i++ { 720 granteeMap := respMapArr[i]["grantee"].(map[string]interface{}) 721 granteeIDMap := granteeMap["Id"].(map[string]interface{}) 722 granteeIDUserIDMap := granteeIDMap["UserId"].(map[string]interface{}) 723 724 // if (granteeMap["Id"]) 725 permsMap := respMapArr[i]["permissions"].(map[string]interface{}) 726 grants[i] = &provider.Grant{ 727 Grantee: &provider.Grantee{ 728 Type: provider.GranteeType_GRANTEE_TYPE_USER, // FIXME: support groups too 729 Id: &provider.Grantee_UserId{ 730 UserId: &user.UserId{ 731 Idp: granteeIDUserIDMap["idp"].(string), 732 OpaqueId: granteeIDUserIDMap["opaque_id"].(string), 733 Type: user.UserType(granteeIDUserIDMap["type"].(float64)), 734 }, 735 }, 736 }, 737 Permissions: &provider.ResourcePermissions{ 738 AddGrant: permsMap["add_grant"].(bool), 739 CreateContainer: permsMap["create_container"].(bool), 740 Delete: permsMap["delete"].(bool), 741 GetPath: permsMap["get_path"].(bool), 742 GetQuota: permsMap["get_quota"].(bool), 743 InitiateFileDownload: permsMap["initiate_file_download"].(bool), 744 InitiateFileUpload: permsMap["initiate_file_upload"].(bool), 745 ListGrants: permsMap["list_grants"].(bool), 746 ListContainer: permsMap["list_container"].(bool), 747 ListFileVersions: permsMap["list_file_versions"].(bool), 748 ListRecycle: permsMap["list_recycle"].(bool), 749 Move: permsMap["move"].(bool), 750 RemoveGrant: permsMap["remove_grant"].(bool), 751 PurgeRecycle: permsMap["purge_recycle"].(bool), 752 RestoreFileVersion: permsMap["restore_file_version"].(bool), 753 RestoreRecycleItem: permsMap["restore_recycle_item"].(bool), 754 Stat: permsMap["stat"].(bool), 755 UpdateGrant: permsMap["update_grant"].(bool), 756 }, 757 } 758 } 759 return grants, err 760 } 761 762 // GetQuota as defined in the storage.FS interface 763 func (nc *StorageDriver) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, uint64, error) { 764 log := appctx.GetLogger(ctx) 765 log.Info().Msg("GetQuota") 766 767 _, respBody, err := nc.do(ctx, Action{"GetQuota", ""}) 768 if err != nil { 769 return 0, 0, 0, err 770 } 771 772 var respMap map[string]interface{} 773 err = json.Unmarshal(respBody, &respMap) 774 if err != nil { 775 return 0, 0, 0, err 776 } 777 778 total := uint64(respMap["totalBytes"].(float64)) 779 used := uint64(respMap["usedBytes"].(float64)) 780 remaining := total - used 781 return total, used, remaining, err 782 } 783 784 // CreateReference as defined in the storage.FS interface 785 func (nc *StorageDriver) CreateReference(ctx context.Context, path string, targetURI *url.URL) error { 786 type paramsObj struct { 787 Path string `json:"path"` 788 URL string `json:"url"` 789 } 790 bodyObj := ¶msObj{ 791 Path: path, 792 URL: targetURI.String(), 793 } 794 bodyStr, _ := json.Marshal(bodyObj) 795 796 _, _, err := nc.do(ctx, Action{"CreateReference", string(bodyStr)}) 797 return err 798 } 799 800 // Shutdown as defined in the storage.FS interface 801 func (nc *StorageDriver) Shutdown(ctx context.Context) error { 802 log := appctx.GetLogger(ctx) 803 log.Info().Msg("Shutdown") 804 805 _, _, err := nc.do(ctx, Action{"Shutdown", ""}) 806 return err 807 } 808 809 // SetArbitraryMetadata as defined in the storage.FS interface 810 func (nc *StorageDriver) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error { 811 type paramsObj struct { 812 Ref *provider.Reference `json:"ref"` 813 Md *provider.ArbitraryMetadata `json:"md"` 814 } 815 bodyObj := ¶msObj{ 816 Ref: ref, 817 Md: md, 818 } 819 bodyStr, _ := json.Marshal(bodyObj) 820 log := appctx.GetLogger(ctx) 821 log.Info().Msgf("SetArbitraryMetadata %s", bodyStr) 822 823 _, _, err := nc.do(ctx, Action{"SetArbitraryMetadata", string(bodyStr)}) 824 return err 825 } 826 827 // UnsetArbitraryMetadata as defined in the storage.FS interface 828 func (nc *StorageDriver) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error { 829 type paramsObj struct { 830 Ref *provider.Reference `json:"ref"` 831 Keys []string `json:"keys"` 832 } 833 bodyObj := ¶msObj{ 834 Ref: ref, 835 Keys: keys, 836 } 837 bodyStr, _ := json.Marshal(bodyObj) 838 log := appctx.GetLogger(ctx) 839 log.Info().Msgf("UnsetArbitraryMetadata %s", bodyStr) 840 841 _, _, err := nc.do(ctx, Action{"UnsetArbitraryMetadata", string(bodyStr)}) 842 return err 843 } 844 845 // GetLock returns an existing lock on the given reference 846 func (nc *StorageDriver) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) { 847 return nil, errtypes.NotSupported("unimplemented") 848 } 849 850 // SetLock puts a lock on the given reference 851 func (nc *StorageDriver) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { 852 return errtypes.NotSupported("unimplemented") 853 } 854 855 // RefreshLock refreshes an existing lock on the given reference 856 func (nc *StorageDriver) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock, existingLockID string) error { 857 return errtypes.NotSupported("unimplemented") 858 } 859 860 // Unlock removes an existing lock from the given reference 861 func (nc *StorageDriver) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { 862 return errtypes.NotSupported("unimplemented") 863 } 864 865 // ListStorageSpaces as defined in the storage.FS interface 866 func (nc *StorageDriver) ListStorageSpaces(ctx context.Context, f []*provider.ListStorageSpacesRequest_Filter, unrestricted bool) ([]*provider.StorageSpace, error) { 867 bodyStr, _ := json.Marshal(f) 868 _, respBody, err := nc.do(ctx, Action{"ListStorageSpaces", string(bodyStr)}) 869 if err != nil { 870 return nil, err 871 } 872 873 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1341-L1366 874 var respMapArr []provider.StorageSpace 875 err = json.Unmarshal(respBody, &respMapArr) 876 if err != nil { 877 return nil, err 878 } 879 var spaces = make([]*provider.StorageSpace, len(respMapArr)) 880 for i := 0; i < len(respMapArr); i++ { 881 spaces[i] = &respMapArr[i] 882 } 883 return spaces, err 884 } 885 886 // CreateStorageSpace creates a storage space 887 func (nc *StorageDriver) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { 888 bodyStr, _ := json.Marshal(req) 889 _, respBody, err := nc.do(ctx, Action{"CreateStorageSpace", string(bodyStr)}) 890 if err != nil { 891 return nil, err 892 } 893 var respObj provider.CreateStorageSpaceResponse 894 err = json.Unmarshal(respBody, &respObj) 895 if err != nil { 896 return nil, err 897 } 898 return &respObj, nil 899 } 900 901 // UpdateStorageSpace updates a storage space 902 func (nc *StorageDriver) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { 903 bodyStr, _ := json.Marshal(req) 904 _, respBody, err := nc.do(ctx, Action{"UpdateStorageSpace", string(bodyStr)}) 905 if err != nil { 906 return nil, err 907 } 908 var respObj provider.UpdateStorageSpaceResponse 909 err = json.Unmarshal(respBody, &respObj) 910 if err != nil { 911 return nil, err 912 } 913 return &respObj, nil 914 } 915 916 // DeleteStorageSpace deletes a storage space 917 func (nc *StorageDriver) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) error { 918 bodyStr, _ := json.Marshal(req) 919 _, respBody, err := nc.do(ctx, Action{"DeleteStorageSpace", string(bodyStr)}) 920 if err != nil { 921 return err 922 } 923 var respObj provider.DeleteStorageSpaceResponse 924 err = json.Unmarshal(respBody, &respObj) 925 if err != nil { 926 return err 927 } 928 return nil 929 }