From 94ced01208864b66b21d2c54977c6df548d37974 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte <39946305+gmgigi96@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:01:48 +0100 Subject: [PATCH] Update OCM shares to last version of CS3APIs (#3646) * rewritten new share endpoint according to new ocm specs * add go validator dependency * add new share to ocm client * defined ocm repository * rewrote ocm core * rewrote ocm share provider * removed commented code * fixed app provider with new cs3apis * rewrote ocmshareprovider * fix commands * add share type * add token * add expiration time to ocm shares * refactor * do not set ctime and mtime of share in json driver * set ctime and mtime of ocm share on creation * pass user obj * removed old files * refactored nextcloud ocm share driver * removed unused variable * add commands to get info about ocm share * generate crypto secure random string * fixes in json pkg * fix linting * add randutil pkg * fixes * expose recipient display name * fixes in json parsing * fix function name * add helper functions for tests * fix response when creating new ocm share * fix errors in ocm share provider (still internal error) * improved spawn revad daemons for testing. it's possible now define files with initial content and empty folders used by config files * defined integration tests for ocm shares * fixes in other tests * use fork for go cs3 bindings * fix typo * fix linter * add changelog * fix linter x2 * return correct errors according to specs * add other tests and bugfixes * fix linter * fix cmd for creating ocm shares --- changelog/unreleased/ocm-shares.md | 4 + cmd/reva/arguments.go | 2 +- cmd/reva/main.go | 2 + cmd/reva/ocm-share-create.go | 97 +-- cmd/reva/ocm-share-get-received.go | 74 ++ cmd/reva/ocm-share-get.go | 74 ++ cmd/reva/ocm-share-list-received.go | 10 +- cmd/reva/ocm-share-list.go | 4 +- cmd/reva/ocm-share-update.go | 6 +- cmd/reva/transfer-create.go | 40 +- cmd/revad/runtime/loader.go | 2 +- go.mod | 15 +- go.sum | 40 +- internal/grpc/services/gateway/appprovider.go | 2 +- .../grpc/services/gateway/ocmshareprovider.go | 119 +--- internal/grpc/services/ocmcore/ocmcore.go | 111 +-- .../ocmshareprovider/ocmshareprovider.go | 318 ++++++--- internal/http/services/ocmd/ocm.go | 17 +- internal/http/services/ocmd/protocols.go | 140 ++++ internal/http/services/ocmd/protocols_test.go | 208 ++++++ internal/http/services/ocmd/shares.go | 280 ++++---- .../handlers/apps/sharing/shares/remote.go | 168 +++-- pkg/app/app.go | 2 +- pkg/app/provider/demo/demo.go | 2 +- pkg/app/provider/wopi/wopi.go | 2 +- pkg/ocm/client/client.go | 81 +++ pkg/ocm/share/manager/json/json.go | 600 ---------------- pkg/ocm/share/manager/nextcloud/nextcloud.go | 655 ------------------ pkg/ocm/share/repository/json/json.go | 511 ++++++++++++++ .../{manager => repository}/loader/loader.go | 6 +- .../share/repository/nextcloud/nextcloud.go | 454 ++++++++++++ .../nextcloud/nextcloud_server_mock.go | 2 +- .../nextcloud/nextcloud_suite_test.go | 0 .../nextcloud/nextcloud_test.go | 397 +++-------- .../registry/registry.go | 8 +- pkg/ocm/share/share.go | 37 +- pkg/ocm/share/utils.go | 93 +++ pkg/utils/utils.go | 12 +- tests/helpers/helpers.go | 135 ++++ .../ocm-share/cernbox-webdav-server.toml | 13 + .../ocm-share/ocm-providers.demo.json | 48 ++ .../ocm-share/ocm-server-cernbox-grpc.toml | 70 ++ .../ocm-share/ocm-server-cernbox-http.toml | 30 + .../ocm-share/ocm-server-cesnet-grpc.toml | 58 ++ .../ocm-share/ocm-server-cesnet-http.toml | 20 + .../fixtures/ocm-share/ocm-users.demo.json | 62 ++ tests/integration/grpc/grpc_suite_test.go | 222 +++--- tests/integration/grpc/ocm_invitation_test.go | 2 +- tests/integration/grpc/ocm_share_test.go | 529 ++++++++++++++ .../integration/grpc/storageprovider_test.go | 2 +- tests/integration/grpc/userprovider_test.go | 2 +- 51 files changed, 3550 insertions(+), 2238 deletions(-) create mode 100644 changelog/unreleased/ocm-shares.md create mode 100644 cmd/reva/ocm-share-get-received.go create mode 100644 cmd/reva/ocm-share-get.go create mode 100644 internal/http/services/ocmd/protocols.go create mode 100644 internal/http/services/ocmd/protocols_test.go delete mode 100644 pkg/ocm/share/manager/json/json.go delete mode 100644 pkg/ocm/share/manager/nextcloud/nextcloud.go create mode 100644 pkg/ocm/share/repository/json/json.go rename pkg/ocm/share/{manager => repository}/loader/loader.go (83%) create mode 100644 pkg/ocm/share/repository/nextcloud/nextcloud.go rename pkg/ocm/share/{manager => repository}/nextcloud/nextcloud_server_mock.go (92%) rename pkg/ocm/share/{manager => repository}/nextcloud/nextcloud_suite_test.go (100%) rename pkg/ocm/share/{manager => repository}/nextcloud/nextcloud_test.go (65%) rename pkg/ocm/share/{manager => repository}/registry/registry.go (80%) create mode 100644 pkg/ocm/share/utils.go create mode 100644 tests/integration/grpc/fixtures/ocm-share/cernbox-webdav-server.toml create mode 100644 tests/integration/grpc/fixtures/ocm-share/ocm-providers.demo.json create mode 100644 tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml create mode 100644 tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml create mode 100644 tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml create mode 100644 tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml create mode 100644 tests/integration/grpc/fixtures/ocm-share/ocm-users.demo.json create mode 100644 tests/integration/grpc/ocm_share_test.go diff --git a/changelog/unreleased/ocm-shares.md b/changelog/unreleased/ocm-shares.md new file mode 100644 index 00000000000..dfacec70e28 --- /dev/null +++ b/changelog/unreleased/ocm-shares.md @@ -0,0 +1,4 @@ +Enhancement: Update OCM shares to last version of CS3APIs + +https://github.com/cs3org/reva/pull/3646 +https://github.com/cs3org/cs3apis/pull/199 \ No newline at end of file diff --git a/cmd/reva/arguments.go b/cmd/reva/arguments.go index 85eba57196c..7d1a4a84b09 100644 --- a/cmd/reva/arguments.go +++ b/cmd/reva/arguments.go @@ -138,7 +138,7 @@ func (c *Completer) ocmShareReceivedArgumentCompleter() []prompt.Suggest { } for _, r := range info { - suggests = append(suggests, prompt.Suggest{Text: r.Share.Id.OpaqueId}) + suggests = append(suggests, prompt.Suggest{Text: r.Id.OpaqueId}) } } diff --git a/cmd/reva/main.go b/cmd/reva/main.go index daf7a85851e..03f96278aad 100644 --- a/cmd/reva/main.go +++ b/cmd/reva/main.go @@ -62,8 +62,10 @@ var ( ocmShareListCommand(), ocmShareRemoveCommand(), ocmShareUpdateCommand(), + ocmShareGetCommand(), ocmShareListReceivedCommand(), ocmShareUpdateReceivedCommand(), + ocmShareGetReceivedCommand(), openInAppCommand(), preferencesCommand(), publicShareCreateCommand(), diff --git a/cmd/reva/ocm-share-create.go b/cmd/reva/ocm-share-create.go index cfe61ca9830..1e8fa87a39d 100644 --- a/cmd/reva/ocm-share-create.go +++ b/cmd/reva/ocm-share-create.go @@ -21,17 +21,17 @@ package main import ( "io" "os" - "strconv" "time" + appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1" ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" + ocmshare "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/utils" "github.com/jedib0t/go-pretty/table" "github.com/pkg/errors" @@ -45,10 +45,15 @@ func ocmShareCreateCommand() *command { grantee := cmd.String("grantee", "", "the grantee") idp := cmd.String("idp", "", "the idp of the grantee, default to same idp as the user triggering the action") userType := cmd.String("user-type", "primary", "the type of user account, defaults to primary") - rol := cmd.String("rol", "viewer", "the permission for the share (viewer or editor)") + + webdav := cmd.Bool("webdav", false, "create a share with webdav access") + webapp := cmd.Bool("webapp", false, "create a share for app access") + transfer := cmd.Bool("transfer", false, "create a share for a data transfer") + + rol := cmd.String("rol", "viewer", "the permission for the share (viewer or editor) / applies to webdav and webapp") cmd.ResetFlags = func() { - *grantType, *grantee, *idp, *rol, *userType = "user", "", "", "viewer", "primary" + *grantType, *grantee, *idp, *rol, *userType, *webdav, *webapp, *transfer = "user", "", "", "viewer", "primary", false, false, false } cmd.Action = func(w ...io.Writer) error { @@ -101,40 +106,22 @@ func ocmShareCreateCommand() *command { return formatError(res.Status) } - perm, pint, err := getOCMSharePerm(*rol) + gt := getGrantType(*grantType) + am, err := getAccessMethods(*webdav, *webapp, *transfer, *rol) if err != nil { return err } - gt := getGrantType(*grantType) - grant := &ocm.ShareGrant{ - Permissions: perm, + shareRequest := &ocm.CreateOCMShareRequest{ + ResourceId: res.Info.Id, Grantee: &provider.Grantee{ Type: gt, // For now, we only support user shares. // TODO (ishank011): To be updated once this is decided. - Id: &provider.Grantee_UserId{UserId: u}, - }, - } - - opaqueObj := &types.Opaque{ - Map: map[string]*types.OpaqueEntry{ - "permissions": { - Decoder: "plain", - Value: []byte(strconv.Itoa(pint)), - }, - "name": { - Decoder: "plain", - Value: []byte(res.Info.Path), - }, + Id: &provider.Grantee_UserId{UserId: remoteUserRes.RemoteUser.Id}, }, - } - - shareRequest := &ocm.CreateOCMShareRequest{ - Opaque: opaqueObj, - ResourceId: res.Info.Id, - Grant: grant, RecipientMeshProvider: providerInfo.ProviderInfo, + AccessMethods: am, } shareRes, err := client.CreateOCMShare(ctx, shareRequest) @@ -148,11 +135,12 @@ func ocmShareCreateCommand() *command { t := table.NewWriter() t.SetOutputMirror(os.Stdout) - t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Permissions", "Type", "Grantee.Idp", "Grantee.OpaqueId", "Created", "Updated"}) + t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Type", "Grantee.Idp", "Grantee.OpaqueId", "Created", "Updated"}) + // TODO (gdelmont): expose protocols info s := shareRes.Share t.AppendRows([]table.Row{ - {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), s.Permissions.String(), + {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), s.Grantee.Type.String(), s.Grantee.GetUserId().Idp, s.Grantee.GetUserId().OpaqueId, time.Unix(int64(s.Ctime.Seconds), 0), time.Unix(int64(s.Mtime.Seconds), 0)}, }) @@ -163,15 +151,44 @@ func ocmShareCreateCommand() *command { return cmd } -func getOCMSharePerm(p string) (*ocm.SharePermissions, int, error) { - if p == viewerPermission { - return &ocm.SharePermissions{ - Permissions: conversions.NewViewerRole().CS3ResourcePermissions(), - }, 1, nil - } else if p == editorPermission { - return &ocm.SharePermissions{ - Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }, 15, nil +func getAccessMethods(webdav, webapp, transfer bool, rol string) ([]*ocm.AccessMethod, error) { + var m []*ocm.AccessMethod + if webdav { + perm, err := getOCMSharePerm(rol) + if err != nil { + return nil, err + } + m = append(m, ocmshare.NewWebDavAccessMethod(perm)) + } + if webapp { + v, err := getOCMViewMode(rol) + if err != nil { + return nil, err + } + m = append(m, ocmshare.NewWebappAccessMethod(v)) + } + if transfer { + m = append(m, ocmshare.NewTransferAccessMethod()) + } + return m, nil +} + +func getOCMSharePerm(p string) (*provider.ResourcePermissions, error) { + switch p { + case viewerPermission: + return conversions.NewViewerRole().CS3ResourcePermissions(), nil + case editorPermission: + return conversions.NewEditorRole().CS3ResourcePermissions(), nil + } + return nil, errors.New("invalid rol: " + p) +} + +func getOCMViewMode(p string) (appprovider.ViewMode, error) { + switch p { + case viewerPermission: + return appprovider.ViewMode_VIEW_MODE_READ_ONLY, nil + case editorPermission: + return appprovider.ViewMode_VIEW_MODE_READ_WRITE, nil } - return nil, 0, errors.New("invalid rol: " + p) + return 0, errors.New("invalid rol: " + p) } diff --git a/cmd/reva/ocm-share-get-received.go b/cmd/reva/ocm-share-get-received.go new file mode 100644 index 00000000000..7653edc231d --- /dev/null +++ b/cmd/reva/ocm-share-get-received.go @@ -0,0 +1,74 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package main + +import ( + "errors" + "fmt" + "io" + + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + "github.com/cs3org/reva/pkg/utils" +) + +func ocmShareGetReceivedCommand() *command { + cmd := newCommand("ocm-share-get-received") + cmd.Description = func() string { return "get the info of the OCM share you have received" } + cmd.Usage = func() string { return "Usage: ocm-share-get-received" } + cmd.Action = func(w ...io.Writer) error { + if cmd.NArg() < 1 { + return errors.New("Invalid arguments: " + cmd.Usage()) + } + + ctx := getAuthContext() + client, err := getClient() + if err != nil { + return err + } + + id := cmd.Arg(0) + + shareRes, err := client.GetReceivedOCMShare(ctx, &ocm.GetReceivedOCMShareRequest{ + Ref: &ocm.ShareReference{ + Spec: &ocm.ShareReference_Id{ + Id: &ocm.ShareId{ + OpaqueId: id, + }, + }, + }, + }) + if err != nil { + return err + } + if shareRes.Status.Code != rpc.Code_CODE_OK { + return formatError(shareRes.Status) + } + + d, err := utils.MarshalProtoV1ToJSON(shareRes.Share) + if err != nil { + return err + } + + fmt.Println(string(d)) + + return nil + } + return cmd +} diff --git a/cmd/reva/ocm-share-get.go b/cmd/reva/ocm-share-get.go new file mode 100644 index 00000000000..5c87e78a29b --- /dev/null +++ b/cmd/reva/ocm-share-get.go @@ -0,0 +1,74 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package main + +import ( + "errors" + "fmt" + "io" + + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + "github.com/cs3org/reva/pkg/utils" +) + +func ocmShareGetCommand() *command { + cmd := newCommand("ocm-share-get") + cmd.Description = func() string { return "get the info of the OCM share" } + cmd.Usage = func() string { return "Usage: ocm-share-get" } + cmd.Action = func(w ...io.Writer) error { + if cmd.NArg() < 1 { + return errors.New("Invalid arguments: " + cmd.Usage()) + } + + ctx := getAuthContext() + client, err := getClient() + if err != nil { + return err + } + + id := cmd.Arg(0) + + shareRes, err := client.GetOCMShare(ctx, &ocm.GetOCMShareRequest{ + Ref: &ocm.ShareReference{ + Spec: &ocm.ShareReference_Id{ + Id: &ocm.ShareId{ + OpaqueId: id, + }, + }, + }, + }) + if err != nil { + return err + } + if shareRes.Status.Code != rpc.Code_CODE_OK { + return formatError(shareRes.Status) + } + + d, err := utils.MarshalProtoV1ToJSON(shareRes.Share) + if err != nil { + return err + } + + fmt.Println(string(d)) + + return nil + } + return cmd +} diff --git a/cmd/reva/ocm-share-list-received.go b/cmd/reva/ocm-share-list-received.go index 60f770b7e3b..11d34e68375 100644 --- a/cmd/reva/ocm-share-list-received.go +++ b/cmd/reva/ocm-share-list-received.go @@ -54,14 +54,14 @@ func ocmShareListReceivedCommand() *command { if len(w) == 0 { t := table.NewWriter() t.SetOutputMirror(os.Stdout) - t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Permissions", "Type", + t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Type", "Grantee.Idp", "Grantee.OpaqueId", "Created", "Updated", "State", "ShareType"}) for _, s := range shareRes.Shares { t.AppendRows([]table.Row{ - {s.Share.Id.OpaqueId, s.Share.Owner.Idp, s.Share.Owner.OpaqueId, s.Share.ResourceId.String(), - s.Share.Permissions.String(), s.Share.Grantee.Type.String(), s.Share.Grantee.GetUserId().Idp, - s.Share.Grantee.GetUserId().OpaqueId, time.Unix(int64(s.Share.Ctime.Seconds), 0), - time.Unix(int64(s.Share.Mtime.Seconds), 0), s.State.String(), s.Share.ShareType.String()}, + {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), + s.Grantee.Type.String(), s.Grantee.GetUserId().Idp, + s.Grantee.GetUserId().OpaqueId, time.Unix(int64(s.Ctime.Seconds), 0), + time.Unix(int64(s.Mtime.Seconds), 0), s.State.String(), s.ShareType.String()}, }) } t.Render() diff --git a/cmd/reva/ocm-share-list.go b/cmd/reva/ocm-share-list.go index 5a6ebbce87e..e36d9ac0b22 100644 --- a/cmd/reva/ocm-share-list.go +++ b/cmd/reva/ocm-share-list.go @@ -76,12 +76,12 @@ func ocmShareListCommand() *command { if len(w) == 0 { t := table.NewWriter() t.SetOutputMirror(os.Stdout) - t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Permissions", "Type", + t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Type", "ShareType", "Grantee.Idp", "Grantee.OpaqueId", "Created", "Updated"}) for _, s := range shareRes.Shares { t.AppendRows([]table.Row{ - {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), s.Permissions.String(), + {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), s.Grantee.Type.String(), s.ShareType.String(), s.Grantee.GetUserId().Idp, s.Grantee.GetUserId().OpaqueId, time.Unix(int64(s.Ctime.Seconds), 0), time.Unix(int64(s.Mtime.Seconds), 0)}, }) diff --git a/cmd/reva/ocm-share-update.go b/cmd/reva/ocm-share-update.go index 617ca904f7f..20eda9db08b 100644 --- a/cmd/reva/ocm-share-update.go +++ b/cmd/reva/ocm-share-update.go @@ -54,7 +54,7 @@ func ocmShareUpdateCommand() *command { return err } - perm, _, err := getOCMSharePerm(*rol) + perm, err := getOCMSharePerm(*rol) if err != nil { return err } @@ -69,7 +69,9 @@ func ocmShareUpdateCommand() *command { }, Field: &ocm.UpdateOCMShareRequest_UpdateField{ Field: &ocm.UpdateOCMShareRequest_UpdateField_Permissions{ - Permissions: perm, + Permissions: &ocm.SharePermissions{ + Permissions: perm, + }, }, }, } diff --git a/cmd/reva/transfer-create.go b/cmd/reva/transfer-create.go index c60ba1f2e36..969e3f1bc0b 100644 --- a/cmd/reva/transfer-create.go +++ b/cmd/reva/transfer-create.go @@ -21,7 +21,6 @@ package main import ( "io" "os" - "strconv" "strings" "time" @@ -31,7 +30,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/utils" "github.com/jedib0t/go-pretty/table" "github.com/pkg/errors" @@ -99,42 +98,21 @@ func transferCreateCommand() *command { return err } - resourcePermissions, pint, err := getOCMSharePerm(editorPermission) - if err != nil { - return err - } - gt := provider.GranteeType_GRANTEE_TYPE_USER if strings.ToLower(*granteeType) == "group" { gt = provider.GranteeType_GRANTEE_TYPE_GROUP } createShareReq := &ocm.CreateOCMShareRequest{ - Opaque: &types.Opaque{ - Map: map[string]*types.OpaqueEntry{ - "permissions": { - Decoder: "plain", - Value: []byte(strconv.Itoa(pint)), - }, - "name": { - Decoder: "plain", - Value: []byte(statRes.Info.Path), - }, - "protocol": { - Decoder: "plain", - Value: []byte("datatx"), - }, + Grantee: &provider.Grantee{ + Type: gt, + Id: &provider.Grantee_UserId{ + UserId: u, }, }, ResourceId: statRes.Info.Id, - Grant: &ocm.ShareGrant{ - Grantee: &provider.Grantee{ - Type: gt, - Id: &provider.Grantee_UserId{ - UserId: u, - }, - }, - Permissions: resourcePermissions, + AccessMethods: []*ocm.AccessMethod{ + share.NewTransferAccessMethod(), }, RecipientMeshProvider: providerInfoResp.ProviderInfo, } @@ -152,11 +130,11 @@ func transferCreateCommand() *command { t := table.NewWriter() t.SetOutputMirror(os.Stdout) - t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Permissions", "Type", "Grantee.Idp", "Grantee.OpaqueId", "ShareType", "Created", "Updated"}) + t.AppendHeader(table.Row{"#", "Owner.Idp", "Owner.OpaqueId", "ResourceId", "Type", "Grantee.Idp", "Grantee.OpaqueId", "ShareType", "Created", "Updated"}) s := createShareResponse.Share t.AppendRows([]table.Row{ - {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), s.Permissions.String(), + {s.Id.OpaqueId, s.Owner.Idp, s.Owner.OpaqueId, s.ResourceId.String(), s.Grantee.Type.String(), s.Grantee.GetUserId().Idp, s.Grantee.GetUserId().OpaqueId, s.ShareType.String(), time.Unix(int64(s.Ctime.Seconds), 0), time.Unix(int64(s.Mtime.Seconds), 0)}, }) diff --git a/cmd/revad/runtime/loader.go b/cmd/revad/runtime/loader.go index c34bafa537d..da63b369ce6 100644 --- a/cmd/revad/runtime/loader.go +++ b/cmd/revad/runtime/loader.go @@ -38,7 +38,7 @@ import ( _ "github.com/cs3org/reva/pkg/metrics/driver/loader" _ "github.com/cs3org/reva/pkg/ocm/invite/repository/loader" _ "github.com/cs3org/reva/pkg/ocm/provider/authorizer/loader" - _ "github.com/cs3org/reva/pkg/ocm/share/manager/loader" + _ "github.com/cs3org/reva/pkg/ocm/share/repository/loader" _ "github.com/cs3org/reva/pkg/permission/manager/loader" _ "github.com/cs3org/reva/pkg/preferences/loader" _ "github.com/cs3org/reva/pkg/publicshare/manager/loader" diff --git a/go.mod b/go.mod index 21530e23e83..4dbdff22199 100644 --- a/go.mod +++ b/go.mod @@ -16,13 +16,14 @@ require ( github.com/cheggaaa/pb v1.0.29 github.com/coreos/go-oidc v2.2.1+incompatible github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e - github.com/cs3org/go-cs3apis v0.0.0-20230209081138-33f5a7d81cb2 + github.com/cs3org/go-cs3apis v0.0.0-20230214162720-ac2ceb2ad50e github.com/dgraph-io/ristretto v0.1.1 github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 github.com/gdexlab/go-render v1.0.1 github.com/glpatcern/go-mime v0.0.0-20221026162842-2a8d71ad17a9 github.com/go-chi/chi/v5 v5.0.8 github.com/go-ldap/ldap/v3 v3.4.4 + github.com/go-playground/validator/v10 v10.11.2 github.com/go-sql-driver/mysql v1.6.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/protobuf v1.5.2 @@ -62,14 +63,15 @@ require ( go.opentelemetry.io/otel/exporters/jaeger v1.11.2 go.opentelemetry.io/otel/sdk v1.11.2 go.opentelemetry.io/otel/trace v1.11.2 + go.step.sm/crypto v0.23.2 golang.org/x/crypto v0.5.0 - golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 golang.org/x/sys v0.4.0 golang.org/x/term v0.4.0 golang.org/x/text v0.6.0 - google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 - google.golang.org/grpc v1.51.0 + google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef + google.golang.org/grpc v1.52.0 google.golang.org/protobuf v1.28.1 gotest.tools v2.2.0+incompatible ) @@ -94,6 +96,8 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/strfmt v0.21.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect @@ -103,12 +107,13 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/raft v1.3.11 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect - github.com/huandu/xstrings v1.3.2 // indirect + github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect diff --git a/go.sum b/go.sum index ae3087cd093..8c11b51d7c9 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2Z cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= @@ -55,8 +55,9 @@ cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJW cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= @@ -283,8 +284,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8= github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= -github.com/cs3org/go-cs3apis v0.0.0-20230209081138-33f5a7d81cb2 h1:vJGHFm3lS7LC0XwsSit8ZMqIyE55Op2+X7p1xEH4Vt0= -github.com/cs3org/go-cs3apis v0.0.0-20230209081138-33f5a7d81cb2/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20230214162720-ac2ceb2ad50e h1:w4A601AS6pC+3eHb9XDZe5Ctpi4cFyoFnsed2Yisd8Q= +github.com/cs3org/go-cs3apis v0.0.0-20230214162720-ac2ceb2ad50e/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -396,9 +397,16 @@ github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/ github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -612,8 +620,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= @@ -675,8 +683,8 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -687,6 +695,8 @@ github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE= @@ -921,13 +931,12 @@ github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redblom/go-cs3apis v0.0.0-20230130162347-1c3e4b532eac h1:GsyT76KNkNHftiTKPLY9qRkrU/Nl91G+x9uStQHBVZE= -github.com/redblom/go-cs3apis v0.0.0-20230130162347-1c3e4b532eac/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -961,6 +970,7 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= @@ -1082,6 +1092,8 @@ go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rq go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.step.sm/crypto v0.23.2 h1:XGmQH9Pkpxop47cjYlUhF10L5roPCbu1BCZXopbeW8I= +go.step.sm/crypto v0.23.2/go.mod h1:/IXGz8al8k7u7OV0RTWIi8TRVqO2FMyZVpedV+6Da6U= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -1253,8 +1265,9 @@ golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1659,8 +1672,9 @@ google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+S google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 h1:mmbq5q8M1t7dhkLw320YK4PsOXm6jdnUAkErImaIqOg= google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1698,8 +1712,8 @@ google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/grpc/services/gateway/appprovider.go b/internal/grpc/services/gateway/appprovider.go index 95ed8780091..a6b06bcbde0 100644 --- a/internal/grpc/services/gateway/appprovider.go +++ b/internal/grpc/services/gateway/appprovider.go @@ -195,7 +195,7 @@ func (s *svc) openLocalResources(ctx context.Context, ri *storageprovider.Resour appProviderReq := &providerpb.OpenInAppRequest{ ResourceInfo: ri, - ViewMode: providerpb.OpenInAppRequest_ViewMode(req.ViewMode), + ViewMode: providerpb.ViewMode(req.ViewMode), AccessToken: accessToken, Opaque: req.Opaque, } diff --git a/internal/grpc/services/gateway/ocmshareprovider.go b/internal/grpc/services/gateway/ocmshareprovider.go index 312f259dd1f..c1dbe07f976 100644 --- a/internal/grpc/services/gateway/ocmshareprovider.go +++ b/internal/grpc/services/gateway/ocmshareprovider.go @@ -52,24 +52,6 @@ func (s *svc) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareRequest return nil, errors.Wrap(err, "gateway: error calling CreateShare") } - // if we don't need to commit we return earlier - if !s.c.CommitShareToStorageGrant && !s.c.CommitShareToStorageRef { - return res, nil - } - - // TODO(labkode): if both commits are enabled they could be done concurrently. - if s.c.CommitShareToStorageGrant { - addGrantStatus, err := s.addGrant(ctx, req.ResourceId, req.Grant.Grantee, req.Grant.Permissions.Permissions) - if err != nil { - return nil, errors.Wrap(err, "gateway: error adding OCM grant to storage") - } - if addGrantStatus.Code != rpc.Code_CODE_OK { - return &ocm.CreateOCMShareResponse{ - Status: addGrantStatus, - }, err - } - } - return res, nil } @@ -81,50 +63,11 @@ func (s *svc) RemoveOCMShare(ctx context.Context, req *ocm.RemoveOCMShareRequest }, nil } - // if we need to commit the share, we need the resource it points to. - var share *ocm.Share - if s.c.CommitShareToStorageGrant { - getShareReq := &ocm.GetOCMShareRequest{ - Ref: req.Ref, - } - getShareRes, err := c.GetOCMShare(ctx, getShareReq) - if err != nil { - return nil, errors.Wrap(err, "gateway: error calling GetShare") - } - - if getShareRes.Status.Code != rpc.Code_CODE_OK { - res := &ocm.RemoveOCMShareResponse{ - Status: status.NewInternal(ctx, status.NewErrorFromCode(getShareRes.Status.Code, "gateway"), - "error getting share when committing to the storage"), - } - return res, nil - } - share = getShareRes.Share - } - res, err := c.RemoveOCMShare(ctx, req) if err != nil { return nil, errors.Wrap(err, "gateway: error calling RemoveShare") } - // if we don't need to commit we return earlier - if !s.c.CommitShareToStorageGrant && !s.c.CommitShareToStorageRef { - return res, nil - } - - // TODO(labkode): if both commits are enabled they could be done concurrently. - if s.c.CommitShareToStorageGrant { - removeGrantStatus, err := s.removeGrant(ctx, share.ResourceId, share.Grantee, share.Permissions.Permissions) - if err != nil { - return nil, errors.Wrap(err, "gateway: error removing OCM grant from storage") - } - if removeGrantStatus.Code != rpc.Code_CODE_OK { - return &ocm.RemoveOCMShareResponse{ - Status: removeGrantStatus, - }, err - } - } - return res, nil } @@ -224,11 +167,6 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive }, nil } - // if we don't need to create/delete references then we return early. - if !s.c.CommitShareToStorageGrant && !s.c.CommitShareToStorageRef { - return res, nil - } - // properties are updated in the order they appear in the field mask // when an error occurs the request ends and no further fields are updated for i := range req.UpdateMask.Paths { @@ -239,7 +177,7 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive getShareReq := &ocm.GetReceivedOCMShareRequest{ Ref: &ocm.ShareReference{ Spec: &ocm.ShareReference_Id{ - Id: req.Share.Share.Id, + Id: req.Share.Id, }, }, } @@ -267,8 +205,8 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive panic("gateway: error updating a received share: the share is nil") } - if share.GetShare().ShareType == ocm.Share_SHARE_TYPE_TRANSFER { - srcIdp := share.GetShare().GetOwner().GetIdp() + if isTransferShare(share) { + srcIdp := share.GetOwner().GetIdp() meshProvider, err := s.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ Domain: srcIdp, }) @@ -314,7 +252,7 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive } var srcToken string - srcTokenOpaque, ok := share.GetShare().Grantee.Opaque.Map["token"] + srcTokenOpaque, ok := share.Grantee.Opaque.Map["token"] if !ok { return &ocm.UpdateReceivedOCMShareResponse{ Status: status.NewNotFound(ctx, "token not found"), @@ -330,16 +268,16 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive }, nil } - srcPath := path.Join(srcEndpointPath, share.GetShare().Name) + srcPath := path.Join(srcEndpointPath, share.Name) srcTargetURI := fmt.Sprintf("%s://%s@%s?name=%s", srcEndpointScheme, srcToken, srcServiceHost, srcPath) // get the webdav endpoint of the grantee's idp var granteeIdp string - if share.GetShare().GetGrantee().Type == provider.GranteeType_GRANTEE_TYPE_USER { - granteeIdp = share.GetShare().GetGrantee().GetUserId().Idp + if share.GetGrantee().Type == provider.GranteeType_GRANTEE_TYPE_USER { + granteeIdp = share.GetGrantee().GetUserId().Idp } - if share.GetShare().GetGrantee().Type == provider.GranteeType_GRANTEE_TYPE_GROUP { - granteeIdp = share.GetShare().GetGrantee().GetGroupId().Idp + if share.GetGrantee().Type == provider.GranteeType_GRANTEE_TYPE_GROUP { + granteeIdp = share.GetGrantee().GetGroupId().Idp } destWebdavEndpoint, err := s.getWebdavEndpoint(ctx, granteeIdp) if err != nil { @@ -388,11 +326,11 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive Status: &rpc.Status{Code: rpc.Code_CODE_INTERNAL}, }, nil } - destPath := path.Join(destEndpointPath, homeRes.Path, s.c.DataTransfersFolder, path.Base(share.GetShare().Name)) + destPath := path.Join(destEndpointPath, homeRes.Path, s.c.DataTransfersFolder, path.Base(share.Name)) destTargetURI := fmt.Sprintf("%s://%s@%s?name=%s", destEndpointScheme, destToken, destServiceHost, destPath) shareID := &ocm.ShareId{ - OpaqueId: share.GetShare().GetId().OpaqueId, + OpaqueId: share.GetId().OpaqueId, } req := &datatx.CreateTransferRequest{ SrcTargetUri: srcTargetURI, @@ -417,12 +355,12 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive }, nil } - createRefStatus, err := s.createOCMReference(ctx, share.Share) + createRefStatus, err := s.createOCMReference(ctx, share) return &ocm.UpdateReceivedOCMShareResponse{ Status: createRefStatus, }, err case ocm.ShareState_SHARE_STATE_REJECTED: - s.removeReference(ctx, req.GetShare().GetShare().ResourceId) // error is logged inside removeReference + s.removeReference(ctx, req.GetShare().ResourceId) // error is logged inside removeReference // FIXME we are ignoring an error from removeReference here return res, nil } @@ -439,6 +377,11 @@ func (s *svc) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceive return res, nil } +func isTransferShare(s *ocm.ReceivedShare) bool { + _, ok := getTransferProtocol(s) + return ok +} + func (s *svc) GetReceivedOCMShare(ctx context.Context, req *ocm.GetReceivedOCMShareRequest) (*ocm.GetReceivedOCMShareResponse, error) { c, err := pool.GetOCMShareProviderClient(pool.Endpoint(s.c.OCMShareProviderEndpoint)) if err != nil { @@ -456,21 +399,19 @@ func (s *svc) GetReceivedOCMShare(ctx context.Context, req *ocm.GetReceivedOCMSh return res, nil } -func (s *svc) createOCMReference(ctx context.Context, share *ocm.Share) (*rpc.Status, error) { +func getTransferProtocol(share *ocm.ReceivedShare) (*ocm.TransferProtocol, bool) { + for _, p := range share.Protocols { + if d, ok := p.Term.(*ocm.Protocol_TransferOptions); ok { + return d.TransferOptions, true + } + } + return nil, false +} + +func (s *svc) createOCMReference(ctx context.Context, share *ocm.ReceivedShare) (*rpc.Status, error) { log := appctx.GetLogger(ctx) - var token string - tokenOpaque, ok := share.Grantee.Opaque.Map["token"] - if !ok { - return status.NewNotFound(ctx, "token not found"), nil - } - switch tokenOpaque.Decoder { - case "plain": - token = string(tokenOpaque.Value) - default: - err := errtypes.NotSupported("opaque entry decoder not recognized: " + tokenOpaque.Decoder) - return status.NewInternal(ctx, err, "invalid opaque entry decoder"), nil - } + d, _ := getTransferProtocol(share) homeRes, err := s.GetHome(ctx, &provider.GetHomeRequest{}) if err != nil { @@ -485,7 +426,7 @@ func (s *svc) createOCMReference(ctx context.Context, share *ocm.Share) (*rpc.St // from the main request. refPath = path.Join(homeRes.Path, s.c.ShareFolder, path.Base(share.Name)) // webdav is the scheme, token@host the opaque part and the share name the query of the URL. - targetURI = fmt.Sprintf("webdav://%s@%s?name=%s", token, share.Creator.Idp, share.Name) + targetURI = fmt.Sprintf("webdav://%s@%s?name=%s", d.SharedSecret, share.Creator.Idp, share.Name) log.Info().Msg("mount path will be:" + refPath) createRefReq := &provider.CreateReferenceRequest{ diff --git a/internal/grpc/services/ocmcore/ocmcore.go b/internal/grpc/services/ocmcore/ocmcore.go index adc960cf199..2d258485516 100644 --- a/internal/grpc/services/ocmcore/ocmcore.go +++ b/internal/grpc/services/ocmcore/ocmcore.go @@ -20,16 +20,14 @@ package ocmcore import ( "context" - "encoding/json" "fmt" ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" - provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/ocm/share" - "github.com/cs3org/reva/pkg/ocm/share/manager/registry" + "github.com/cs3org/reva/pkg/ocm/share/repository/registry" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/mitchellh/mapstructure" @@ -48,7 +46,7 @@ type config struct { type service struct { conf *config - sm share.Manager + repo share.Repository } func (c *config) init() { @@ -61,7 +59,7 @@ func (s *service) Register(ss *grpc.Server) { ocmcore.RegisterOcmCoreAPIServer(ss, s) } -func getShareManager(c *config) (share.Manager, error) { +func getShareRepository(c *config) (share.Repository, error) { if f, ok := registry.NewFuncs[c.Driver]; ok { return f(c.Drivers[c.Driver]) } @@ -85,14 +83,14 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { } c.init() - sm, err := getShareManager(c) + repo, err := getShareRepository(c) if err != nil { return nil, err } service := &service{ conf: c, - sm: sm, + repo: repo, } return service, nil @@ -108,91 +106,38 @@ func (s *service) UnprotectedEndpoints() []string { // CreateOCMCoreShare is called when an OCM request comes into this reva instance from. func (s *service) CreateOCMCoreShare(ctx context.Context, req *ocmcore.CreateOCMCoreShareRequest) (*ocmcore.CreateOCMCoreShareResponse, error) { - resource := &provider.ResourceId{ - StorageId: "remote", - OpaqueId: req.Name, + if req.ShareType != ocm.ShareType_SHARE_TYPE_USER { + return nil, errtypes.NotSupported("share type not supported") } - var resourcePermissions *provider.ResourcePermissions - permOpaque, ok := req.Protocol.Opaque.Map["permissions"] - if !ok { - return &ocmcore.CreateOCMCoreShareResponse{ - Status: status.NewInternal(ctx, errtypes.BadRequest("resource permissions not set"), ""), - }, nil - } - switch permOpaque.Decoder { - case "json": - err := json.Unmarshal(permOpaque.Value, &resourcePermissions) - if err != nil { - return &ocmcore.CreateOCMCoreShareResponse{ - Status: status.NewInternal(ctx, err, "error decoding resource permissions"), - }, nil - } - default: - err := errtypes.NotSupported("opaque entry decoder not recognized") - return &ocmcore.CreateOCMCoreShareResponse{ - Status: status.NewInternal(ctx, err, "invalid opaque entry decoder"), - }, nil - } - - var token string - tokenOpaque, ok := req.Protocol.Opaque.Map["token"] - if !ok { - return &ocmcore.CreateOCMCoreShareResponse{ - Status: status.NewInternal(ctx, errtypes.PermissionDenied("token not set"), ""), - }, nil - } - switch tokenOpaque.Decoder { - case "plain": - token = string(tokenOpaque.Value) - default: - err := errtypes.NotSupported("opaque entry decoder not recognized: " + tokenOpaque.Decoder) - return &ocmcore.CreateOCMCoreShareResponse{ - Status: status.NewInternal(ctx, err, "invalid opaque entry decoder"), - }, nil - } - - grant := &ocm.ShareGrant{ - Grantee: &provider.Grantee{ - Type: provider.GranteeType_GRANTEE_TYPE_USER, - // For now, we only support user shares. - // TODO (ishank011): To be updated once this is decided. - Id: &provider.Grantee_UserId{UserId: req.ShareWith}, - // passing this in grant.Grantee.Opaque because ShareGrant itself doesn't have a root opaque. - Opaque: &typespb.Opaque{ - Map: map[string]*typespb.OpaqueEntry{ - "remoteShareId": { - Decoder: "plain", - Value: []byte(req.ProviderId), - }, - }, - }, + share, err := s.repo.StoreReceivedShare(ctx, &ocm.ReceivedShare{ + ResourceId: &providerpb.ResourceId{ + OpaqueId: req.ResourceId, }, - Permissions: &ocm.SharePermissions{ - Permissions: resourcePermissions, + Name: req.Name, + Grantee: &providerpb.Grantee{ + Type: providerpb.GranteeType_GRANTEE_TYPE_USER, + Id: &providerpb.Grantee_UserId{ + UserId: req.ShareWith, + }, }, - } - - var shareType ocm.Share_ShareType - switch req.Protocol.Name { - case "datatx": - shareType = ocm.Share_SHARE_TYPE_TRANSFER - default: - shareType = ocm.Share_SHARE_TYPE_REGULAR - } - - share, err := s.sm.Share(ctx, resource, grant, req.Name, nil, "", req.Owner, token, shareType) - + ShareType: req.ShareType, + Owner: req.Owner, + Creator: req.Sender, + Protocols: req.Protocols, + Expiration: req.Expiration, + State: ocm.ShareState_SHARE_STATE_PENDING, + }) if err != nil { + // TODO: identify errors return &ocmcore.CreateOCMCoreShareResponse{ - Status: status.NewInternal(ctx, err, "error creating ocm core share"), + Status: status.NewInternal(ctx, err, err.Error()), }, nil } - res := &ocmcore.CreateOCMCoreShareResponse{ + return &ocmcore.CreateOCMCoreShareResponse{ Status: status.NewOK(ctx), Id: share.Id.OpaqueId, Created: share.Ctime, - } - return res, nil + }, nil } diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index 51b99a3737e..389ef80c7cf 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -20,13 +20,29 @@ package ocmshareprovider import ( "context" - + "fmt" + "net/url" + "path/filepath" + "time" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/internal/http/services/ocmd" + ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/ocm/client" "github.com/cs3org/reva/pkg/ocm/share" - "github.com/cs3org/reva/pkg/ocm/share/manager/registry" + "github.com/cs3org/reva/pkg/ocm/share/repository/registry" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/sharedconf" + "github.com/cs3org/reva/pkg/utils" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "google.golang.org/grpc" @@ -37,26 +53,37 @@ func init() { } type config struct { - Driver string `mapstructure:"driver"` - Drivers map[string]map[string]interface{} `mapstructure:"drivers"` + Driver string `mapstructure:"driver"` + Drivers map[string]map[string]interface{} `mapstructure:"drivers"` + ClientTimeout int `mapstructure:"client_timeout"` + ClientInsecure bool `mapstructure:"client_insecure"` + GatewaySVC string `mapstructure:"gatewaysvc"` + WebDAVPrefix string `mapstructure:"webdav_prefix"` } type service struct { - conf *config - sm share.Manager + conf *config + repo share.Repository + client *client.OCMClient + gateway gateway.GatewayAPIClient } func (c *config) init() { if c.Driver == "" { c.Driver = "json" } + if c.ClientTimeout == 0 { + c.ClientTimeout = 10 + } + + c.GatewaySVC = sharedconf.GetGatewaySVC(c.GatewaySVC) } func (s *service) Register(ss *grpc.Server) { ocm.RegisterOcmAPIServer(ss, s) } -func getShareManager(c *config) (share.Manager, error) { +func getShareRepository(c *config) (share.Repository, error) { if f, ok := registry.NewFuncs[c.Driver]; ok { return f(c.Drivers[c.Driver]) } @@ -80,14 +107,26 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { } c.init() - sm, err := getShareManager(c) + repo, err := getShareRepository(c) + if err != nil { + return nil, err + } + + client := client.New(&client.Config{ + Timeout: time.Duration(c.ClientTimeout) * time.Second, + Insecure: c.ClientInsecure, + }) + + gateway, err := pool.GetGatewayServiceClient(pool.Endpoint(c.GatewaySVC)) if err != nil { return nil, err } service := &service{ - conf: c, - sm: sm, + conf: c, + repo: repo, + client: client, + gateway: gateway, } return service, nil @@ -98,99 +137,190 @@ func (s *service) Close() error { } func (s *service) UnprotectedEndpoints() []string { - return []string{} + return nil +} + +func getOCMEndpoint(originProvider *ocmprovider.ProviderInfo) (string, error) { + for _, s := range originProvider.Services { + if s.Endpoint.Type.Name == "OCM" { + return s.Endpoint.Path, nil + } + } + return "", errors.New("ocm endpoint not specified for mesh provider") +} + +func formatOCMUser(u *userpb.UserId) string { + return fmt.Sprintf("%s@%s", u.OpaqueId, u.Idp) +} + +func getResourceType(info *providerpb.ResourceInfo) string { + switch info.Type { + case providerpb.ResourceType_RESOURCE_TYPE_FILE: + return "file" + case providerpb.ResourceType_RESOURCE_TYPE_CONTAINER: + return "folder" + } + return "unknown" +} + +func (s *service) webdavURL(ctx context.Context, path string) string { + // the url is in the form of https://cernbox.cern.ch/remote.php/dav/files/gdelmont/eos/user/g/gdelmont + user := ctxpkg.ContextMustGetUser(ctx) + p, err := url.JoinPath(s.conf.WebDAVPrefix, user.Username, path) + if err != nil { + panic(err) + } + return p +} + +func (s *service) getWebdavProtocol(ctx context.Context, info *providerpb.ResourceInfo, m *ocm.AccessMethod_WebdavOptions) *ocmd.WebDAV { + var perms []string + if m.WebdavOptions.Permissions.InitiateFileDownload { + perms = append(perms, "read") + } + if m.WebdavOptions.Permissions.InitiateFileUpload { + perms = append(perms, "write") + } + + return &ocmd.WebDAV{ + SharedSecret: ctxpkg.ContextMustGetToken(ctx), // TODO: change this and use an ocm token + Permissions: perms, + URL: s.webdavURL(ctx, info.Path), // TODO: change this and use an endpoint for ocm + } +} + +func (s *service) getProtocols(ctx context.Context, info *providerpb.ResourceInfo, methods []*ocm.AccessMethod) ocmd.Protocols { + var p ocmd.Protocols + for _, m := range methods { + switch t := m.Term.(type) { + case *ocm.AccessMethod_WebdavOptions: + p = append(p, s.getWebdavProtocol(ctx, info, t)) + case *ocm.AccessMethod_WebappOptions: + // TODO + case *ocm.AccessMethod_TransferOptions: + // TODO + } + } + return p } -// Note: this is for outgoing OCM shares -// This function is used when you for instance -// call `ocm-share-create` in reva-cli. -// For incoming OCM shares from internal/http/services/ocmd/shares.go -// there is the very similar but slightly different function -// CreateOCMCoreShare (the "Core" somehow means "incoming"). -// So make sure to keep in mind the difference between this file for outgoing: -// internal/grpc/services/ocmshareprovider/ocmshareprovider.go -// and the other one for incoming: -// internal/grpc/service/ocmcore/ocmcore.go -// Both functions end up calling the same s.sm.Share function -// on the OCM share manager: -// pkg/ocm/share/manager/{json|nextcloud|...}. func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareRequest) (*ocm.CreateOCMShareResponse, error) { - if req.Opaque == nil { + statRes, err := s.gateway.Stat(ctx, &providerpb.StatRequest{ + Ref: &providerpb.Reference{ + ResourceId: req.ResourceId, + }, + }) + if err != nil { return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, errtypes.BadRequest("can't find resource permissions"), ""), - }, nil + Status: status.NewInternal(ctx, err, err.Error()), + }, err } - var permissions string - permOpaque, ok := req.Opaque.Map["permissions"] - if !ok { + if statRes.Status.Code != rpc.Code_CODE_OK { + if statRes.Status.Code == rpc.Code_CODE_NOT_FOUND { + return &ocm.CreateOCMShareResponse{ + Status: status.NewNotFound(ctx, statRes.Status.Message), + }, nil + } return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, errtypes.BadRequest("resource permissions not set"), ""), + Status: status.NewInternal(ctx, errors.New(statRes.Status.Message), statRes.Status.Message), }, nil } - switch permOpaque.Decoder { - case "plain": - permissions = string(permOpaque.Value) - default: - err := errtypes.NotSupported("opaque entry decoder not recognized: " + permOpaque.Decoder) - return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, err, "invalid opaque entry decoder"), - }, nil + + info := statRes.Info + user := ctxpkg.ContextMustGetUser(ctx) + tkn := utils.RandString(32) + now := time.Now().UnixNano() + ts := &typespb.Timestamp{ + Seconds: uint64(now / 1000000000), + Nanos: uint32(now % 1000000000), } - var name string - nameOpaque, ok := req.Opaque.Map["name"] - if !ok { + ocmshare := &ocm.Share{ + Token: tkn, + Name: filepath.Base(info.Path), + ResourceId: req.ResourceId, + Grantee: req.Grantee, + ShareType: ocm.ShareType_SHARE_TYPE_USER, + Owner: info.Owner, + Creator: user.Id, + Ctime: ts, + Mtime: ts, + Expiration: req.Expiration, + AccessMethods: req.AccessMethods, + } + + ocmshare, err = s.repo.StoreShare(ctx, ocmshare) + if err != nil { + if errors.Is(err, share.ErrShareAlreadyExisting) { + return &ocm.CreateOCMShareResponse{ + Status: status.NewAlreadyExists(ctx, err, "share already exists"), + }, nil + } return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, errtypes.BadRequest("resource name not set"), ""), + Status: status.NewInternal(ctx, err, err.Error()), }, nil } - switch nameOpaque.Decoder { - case "plain": - name = string(nameOpaque.Value) - default: - err := errtypes.NotSupported("opaque entry decoder not recognized: " + nameOpaque.Decoder) + + ocmEndpoint, err := getOCMEndpoint(req.RecipientMeshProvider) + if err != nil { return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, err, "invalid opaque entry decoder"), + Status: status.NewInvalidArg(ctx, "the selected provider does not have an OCM endpoint"), }, nil } - // discover share type - sharetype := ocm.Share_SHARE_TYPE_REGULAR - protocol, ok := req.Opaque.Map["protocol"] - if ok { - switch protocol.Decoder { - case "plain": - if string(protocol.Value) == "datatx" { - sharetype = ocm.Share_SHARE_TYPE_TRANSFER - } - default: - err := errors.New("protocol decoder not recognized") - return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, err, "error creating share"), - }, nil - } + newShareReq := &client.NewShareRequest{ + ShareWith: formatOCMUser(req.Grantee.GetUserId()), + Name: ocmshare.Name, + ResourceID: fmt.Sprintf("%s:%s", req.ResourceId.StorageId, req.ResourceId.OpaqueId), + Owner: formatOCMUser(info.Owner), + Sender: formatOCMUser(user.Id), + SenderDisplayName: user.DisplayName, + ShareType: "user", + ResourceType: getResourceType(info), + Protocols: s.getProtocols(ctx, info, req.AccessMethods), } - var sharedSecret string - share, err := s.sm.Share(ctx, req.ResourceId, req.Grant, name, req.RecipientMeshProvider, permissions, nil, sharedSecret, sharetype) + if req.Expiration != nil { + newShareReq.Expiration = req.Expiration.Seconds + } + newShareRes, err := s.client.NewShare(ctx, ocmEndpoint, newShareReq) if err != nil { - return &ocm.CreateOCMShareResponse{ - Status: status.NewInternal(ctx, err, "error creating share: "+err.Error()), - }, nil + switch { + case errors.Is(err, client.ErrInvalidParameters): + return &ocm.CreateOCMShareResponse{ + Status: status.NewInvalidArg(ctx, err.Error()), + }, nil + case errors.Is(err, client.ErrServiceNotTrusted): + return &ocm.CreateOCMShareResponse{ + Status: status.NewInvalidArg(ctx, err.Error()), + }, nil + default: + return &ocm.CreateOCMShareResponse{ + Status: status.NewInternal(ctx, err, err.Error()), + }, nil + } } res := &ocm.CreateOCMShareResponse{ - Status: status.NewOK(ctx), - Share: share, + Status: status.NewOK(ctx), + Share: ocmshare, + RecipientDisplayName: newShareRes.RecipientDisplayName, } return res, nil } func (s *service) RemoveOCMShare(ctx context.Context, req *ocm.RemoveOCMShareRequest) (*ocm.RemoveOCMShareResponse, error) { - err := s.sm.Unshare(ctx, req.Ref) - if err != nil { + // TODO (gdelmont): notify the remote provider using the /notification ocm endpoint + // https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post + user := ctxpkg.ContextMustGetUser(ctx) + if err := s.repo.DeleteShare(ctx, user, req.Ref); err != nil { + if errors.Is(err, share.ErrShareNotFound) { + return &ocm.RemoveOCMShareResponse{ + Status: status.NewNotFound(ctx, "share does not exist"), + }, nil + } return &ocm.RemoveOCMShareResponse{ Status: status.NewInternal(ctx, err, "error removing share"), }, nil @@ -202,8 +332,14 @@ func (s *service) RemoveOCMShare(ctx context.Context, req *ocm.RemoveOCMShareReq } func (s *service) GetOCMShare(ctx context.Context, req *ocm.GetOCMShareRequest) (*ocm.GetOCMShareResponse, error) { - share, err := s.sm.GetShare(ctx, req.Ref) + user := ctxpkg.ContextMustGetUser(ctx) + ocmshare, err := s.repo.GetShare(ctx, user, req.Ref) if err != nil { + if errors.Is(err, share.ErrShareNotFound) { + return &ocm.GetOCMShareResponse{ + Status: status.NewNotFound(ctx, "share does not exist"), + }, nil + } return &ocm.GetOCMShareResponse{ Status: status.NewInternal(ctx, err, "error getting share"), }, nil @@ -211,12 +347,13 @@ func (s *service) GetOCMShare(ctx context.Context, req *ocm.GetOCMShareRequest) return &ocm.GetOCMShareResponse{ Status: status.NewOK(ctx), - Share: share, + Share: ocmshare, }, nil } func (s *service) ListOCMShares(ctx context.Context, req *ocm.ListOCMSharesRequest) (*ocm.ListOCMSharesResponse, error) { - shares, err := s.sm.ListShares(ctx, req.Filters) // TODO(labkode): add filter to share manager + user := ctxpkg.ContextMustGetUser(ctx) + shares, err := s.repo.ListShares(ctx, user, req.Filters) if err != nil { return &ocm.ListOCMSharesResponse{ Status: status.NewInternal(ctx, err, "error listing shares"), @@ -231,8 +368,14 @@ func (s *service) ListOCMShares(ctx context.Context, req *ocm.ListOCMSharesReque } func (s *service) UpdateOCMShare(ctx context.Context, req *ocm.UpdateOCMShareRequest) (*ocm.UpdateOCMShareResponse, error) { - _, err := s.sm.UpdateShare(ctx, req.Ref, req.Field.GetPermissions()) // TODO(labkode): check what to update + user := ctxpkg.ContextMustGetUser(ctx) + _, err := s.repo.UpdateShare(ctx, user, req.Ref, req.Field.GetPermissions()) // TODO(labkode): check what to update if err != nil { + if errors.Is(err, share.ErrShareNotFound) { + return &ocm.UpdateOCMShareResponse{ + Status: status.NewNotFound(ctx, "share does not exist"), + }, nil + } return &ocm.UpdateOCMShareResponse{ Status: status.NewInternal(ctx, err, "error updating share"), }, nil @@ -245,7 +388,8 @@ func (s *service) UpdateOCMShare(ctx context.Context, req *ocm.UpdateOCMShareReq } func (s *service) ListReceivedOCMShares(ctx context.Context, req *ocm.ListReceivedOCMSharesRequest) (*ocm.ListReceivedOCMSharesResponse, error) { - shares, err := s.sm.ListReceivedShares(ctx) + user := ctxpkg.ContextMustGetUser(ctx) + shares, err := s.repo.ListReceivedShares(ctx, user) if err != nil { return &ocm.ListReceivedOCMSharesResponse{ Status: status.NewInternal(ctx, err, "error listing received shares"), @@ -260,8 +404,14 @@ func (s *service) ListReceivedOCMShares(ctx context.Context, req *ocm.ListReceiv } func (s *service) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateReceivedOCMShareRequest) (*ocm.UpdateReceivedOCMShareResponse, error) { - _, err := s.sm.UpdateReceivedShare(ctx, req.Share, req.UpdateMask) // TODO(labkode): check what to update + user := ctxpkg.ContextMustGetUser(ctx) + _, err := s.repo.UpdateReceivedShare(ctx, user, req.Share, req.UpdateMask) // TODO(labkode): check what to update if err != nil { + if errors.Is(err, share.ErrShareNotFound) { + return &ocm.UpdateReceivedOCMShareResponse{ + Status: status.NewNotFound(ctx, "share does not exist"), + }, nil + } return &ocm.UpdateReceivedOCMShareResponse{ Status: status.NewInternal(ctx, err, "error updating received share"), }, nil @@ -274,8 +424,14 @@ func (s *service) UpdateReceivedOCMShare(ctx context.Context, req *ocm.UpdateRec } func (s *service) GetReceivedOCMShare(ctx context.Context, req *ocm.GetReceivedOCMShareRequest) (*ocm.GetReceivedOCMShareResponse, error) { - share, err := s.sm.GetReceivedShare(ctx, req.Ref) + user := ctxpkg.ContextMustGetUser(ctx) + ocmshare, err := s.repo.GetReceivedShare(ctx, user, req.Ref) if err != nil { + if errors.Is(err, share.ErrShareNotFound) { + return &ocm.GetReceivedOCMShareResponse{ + Status: status.NewNotFound(ctx, "share does not exist"), + }, nil + } return &ocm.GetReceivedOCMShareResponse{ Status: status.NewInternal(ctx, err, "error getting received share"), }, nil @@ -283,7 +439,7 @@ func (s *service) GetReceivedOCMShare(ctx context.Context, req *ocm.GetReceivedO res := &ocm.GetReceivedOCMShareResponse{ Status: status.NewOK(ctx), - Share: share, + Share: ocmshare, } return res, nil } diff --git a/internal/http/services/ocmd/ocm.go b/internal/http/services/ocmd/ocm.go index 2974320a0e4..68e3040b0d3 100644 --- a/internal/http/services/ocmd/ocm.go +++ b/internal/http/services/ocmd/ocm.go @@ -35,12 +35,13 @@ func init() { } type config struct { - SMTPCredentials *smtpclient.SMTPCredentials `mapstructure:"smtp_credentials"` - Prefix string `mapstructure:"prefix"` - Host string `mapstructure:"host"` - GatewaySvc string `mapstructure:"gatewaysvc"` - MeshDirectoryURL string `mapstructure:"mesh_directory_url"` - Config configData `mapstructure:"config"` + SMTPCredentials *smtpclient.SMTPCredentials `mapstructure:"smtp_credentials"` + Prefix string `mapstructure:"prefix"` + Host string `mapstructure:"host"` + GatewaySvc string `mapstructure:"gatewaysvc"` + MeshDirectoryURL string `mapstructure:"mesh_directory_url"` + Config configData `mapstructure:"config"` + ExposeRecipientDisplayName bool `mapstructure:"expose_recipient_display_name"` } func (c *config) init() { @@ -85,7 +86,9 @@ func (s *svc) routerInit() error { invitesHandler := new(invitesHandler) configHandler.init(s.Conf) - sharesHandler.init(s.Conf) + if err := sharesHandler.init(s.Conf); err != nil { + return err + } notificationsHandler.init(s.Conf) if err := invitesHandler.init(s.Conf); err != nil { return err diff --git a/internal/http/services/ocmd/protocols.go b/internal/http/services/ocmd/protocols.go new file mode 100644 index 00000000000..1d76dc5ac68 --- /dev/null +++ b/internal/http/services/ocmd/protocols.go @@ -0,0 +1,140 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package ocmd + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + ocmshare "github.com/cs3org/reva/pkg/ocm/share" +) + +// Protocols is the list of protocols. +type Protocols []Protocol + +// Protocol represents the way of access the resource +// in the OCM share. +type Protocol interface { + // ToOCMProtocol convert the protocol to a ocm Protocol struct + ToOCMProtocol() *ocm.Protocol +} + +// protocols supported by the OCM API + +// WebDAV contains the parameters for the WebDAV protocol. +type WebDAV struct { + SharedSecret string `json:"sharedSecret" validate:"required"` + Permissions []string `json:"permissions" validate:"required,dive,required,oneof=read write share"` + URL string `json:"url" validate:"required"` +} + +// ToOCMProtocol convert the protocol to a ocm Protocol struct. +func (w *WebDAV) ToOCMProtocol() *ocm.Protocol { + perms := &ocm.SharePermissions{ + Permissions: &providerv1beta1.ResourcePermissions{}, + } + for _, p := range w.Permissions { + switch p { + case "read": + perms.Permissions.GetPath = true + perms.Permissions.InitiateFileDownload = true + perms.Permissions.ListContainer = true + perms.Permissions.Stat = true + case "write": + perms.Permissions.InitiateFileUpload = true + case "share": + perms.Reshare = true + } + } + + return ocmshare.NewWebDAVProtocol(w.URL, w.SharedSecret, perms) +} + +// Webapp contains the parameters for the Webapp protocol. +type Webapp struct { + URITemplate string `json:"uriTemplate" validate:"required"` +} + +// ToOCMProtocol convert the protocol to a ocm Protocol struct. +func (w *Webapp) ToOCMProtocol() *ocm.Protocol { + return ocmshare.NewWebappProtocol(w.URITemplate) +} + +// Datatx contains the parameters for the Datatx protocol. +type Datatx struct { + SharedSecret string `json:"sharedSecret" validate:"required"` + SourceURI string `json:"srcUri" validate:"required"` + Size uint64 `json:"size" validate:"required"` +} + +// ToOCMProtocol convert the protocol to a ocm Protocol struct. +func (w *Datatx) ToOCMProtocol() *ocm.Protocol { + return ocmshare.NewTransferProtocol(w.SourceURI, w.SharedSecret, w.Size) +} + +var protocolImpl = map[string]reflect.Type{ + "webdav": reflect.TypeOf(WebDAV{}), + "webapp": reflect.TypeOf(Webapp{}), + "datatx": reflect.TypeOf(Datatx{}), +} + +// UnmarshalJSON implements the Unmarshaler interface. +func (p *Protocols) UnmarshalJSON(data []byte) error { + var prot map[string]json.RawMessage + if err := json.Unmarshal(data, &prot); err != nil { + return err + } + + *p = []Protocol{} + + for name, d := range prot { + var res Protocol + ctype, ok := protocolImpl[name] + if !ok { + return fmt.Errorf("protocol %s not recognised", name) + } + + res = reflect.New(ctype).Interface().(Protocol) + if err := json.Unmarshal(d, &res); err != nil { + return err + } + + *p = append(*p, res) + } + return nil +} + +// MarshalJSON implements the Marshaler interface. +func (p Protocols) MarshalJSON() ([]byte, error) { + d := make(map[string]Protocol) + for _, prot := range p { + d[getProtocolName(prot)] = prot + } + return json.Marshal(d) +} + +func getProtocolName(p Protocol) string { + n := reflect.TypeOf(p).String() + s := strings.Split(n, ".") + return strings.ToLower(s[len(s)-1]) +} diff --git a/internal/http/services/ocmd/protocols_test.go b/internal/http/services/ocmd/protocols_test.go new file mode 100644 index 00000000000..f6bbecb6cd2 --- /dev/null +++ b/internal/http/services/ocmd/protocols_test.go @@ -0,0 +1,208 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package ocmd + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/gdexlab/go-render/render" +) + +func TestUnmarshalProtocol(t *testing.T) { + tests := []struct { + raw string + expected Protocols + err string + }{ + { + raw: "{}", + expected: []Protocol{}, + }, + { + raw: `{"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"}}`, + expected: []Protocol{ + &WebDAV{ + SharedSecret: "secret", + Permissions: []string{"read", "write"}, + URL: "http://example.org", + }, + }, + }, + { + raw: `{"webapp":{"uriTemplate":"http://example.org/{test}"}}`, + expected: []Protocol{ + &Webapp{ + URITemplate: "http://example.org/{test}", + }, + }, + }, + { + raw: `{"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`, + expected: []Protocol{ + &Datatx{ + SharedSecret: "secret", + SourceURI: "http://example.org", + Size: 10, + }, + }, + }, + { + raw: `{"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"},"webapp":{"uriTemplate":"http://example.org/{test}"},"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`, + expected: []Protocol{ + &WebDAV{ + SharedSecret: "secret", + Permissions: []string{"read", "write"}, + URL: "http://example.org", + }, + &Webapp{ + URITemplate: "http://example.org/{test}", + }, + &Datatx{ + SharedSecret: "secret", + SourceURI: "http://example.org", + Size: 10, + }, + }, + }, + { + raw: `{"not_existing":{}}`, + err: "protocol not_existing not recognised", + }, + } + + for _, tt := range tests { + var got Protocols + err := json.Unmarshal([]byte(tt.raw), &got) + if err != nil && err.Error() != tt.err { + t.Fatalf("not expected error. got=%+v expected=%+v", err, tt.err) + } + + if tt.err == "" { + if !reflect.DeepEqual(got, tt.expected) { + t.Fatalf("result does not match with expected. got=%+v expected=%+v", render.AsCode(got), render.AsCode(tt.expected)) + } + } + } +} + +func TestMarshalProtocol(t *testing.T) { + tests := []struct { + in Protocols + expected map[string]map[string]any + }{ + { + in: []Protocol{}, + expected: map[string]map[string]any{}, + }, + { + in: []Protocol{ + &WebDAV{ + SharedSecret: "secret", + Permissions: []string{"read"}, + URL: "http://example.org", + }, + }, + expected: map[string]map[string]any{ + "webdav": { + "sharedSecret": "secret", + "permissions": []any{"read"}, + "url": "http://example.org", + }, + }, + }, + { + in: []Protocol{ + &Webapp{ + URITemplate: "http://example.org", + }, + }, + expected: map[string]map[string]any{ + "webapp": { + "uriTemplate": "http://example.org", + }, + }, + }, + { + in: []Protocol{ + &Datatx{ + SharedSecret: "secret", + SourceURI: "http://example.org/source", + Size: 10, + }, + }, + expected: map[string]map[string]any{ + "datatx": { + "sharedSecret": "secret", + "srcUri": "http://example.org/source", + "size": float64(10), + }, + }, + }, + { + in: []Protocol{ + &WebDAV{ + SharedSecret: "secret", + Permissions: []string{"read"}, + URL: "http://example.org", + }, + &Webapp{ + URITemplate: "http://example.org", + }, + &Datatx{ + SharedSecret: "secret", + SourceURI: "http://example.org/source", + Size: 10, + }, + }, + expected: map[string]map[string]any{ + "webdav": { + "sharedSecret": "secret", + "permissions": []any{"read"}, + "url": "http://example.org", + }, + "webapp": { + "uriTemplate": "http://example.org", + }, + "datatx": { + "sharedSecret": "secret", + "srcUri": "http://example.org/source", + "size": float64(10), + }, + }, + }, + } + + for _, tt := range tests { + d, err := json.Marshal(tt.in) + if err != nil { + t.Fatal("not expected error", err) + } + + var got map[string]map[string]any + if err := json.Unmarshal(d, &got); err != nil { + t.Fatal("not expected error", err) + } + + if !reflect.DeepEqual(tt.expected, got) { + t.Fatalf("result does not match with expected. got=%+v expected=%+v", render.AsCode(got), render.AsCode(tt.expected)) + } + } +} diff --git a/internal/http/services/ocmd/shares.go b/internal/http/services/ocmd/shares.go index a4dbd9699b0..e8dec02fede 100644 --- a/internal/http/services/ocmd/shares.go +++ b/internal/http/services/ocmd/shares.go @@ -22,89 +22,72 @@ import ( "encoding/json" "errors" "fmt" - "io" - "math" "mime" "net/http" - "reflect" "strings" - "time" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1" ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" - types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/internal/http/services/reqres" - "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/utils" + "github.com/go-playground/validator/v10" ) +var validate = validator.New() + type sharesHandler struct { - gatewayAddr string + gatewayClient gateway.GatewayAPIClient + exposeRecipientDisplayName bool +} + +func (h *sharesHandler) init(c *config) error { + var err error + h.gatewayClient, err = pool.GetGatewayServiceClient(pool.Endpoint(c.GatewaySvc)) + if err != nil { + return err + } + h.exposeRecipientDisplayName = c.ExposeRecipientDisplayName + return nil } -func (h *sharesHandler) init(c *config) { - h.gatewayAddr = c.GatewaySvc +type createShareRequest struct { + ShareWith string `json:"shareWith" validate:"required"` // identifier of the recipient of the share + Name string `json:"name" validate:"required"` // name of the resource + Description string `json:"description"` // (optional) description of the resource + ResourceID string `json:"resourceId" validate:"required"` // unique identifier of the resource at provider side + Owner string `json:"owner" validate:"required"` // unique identifier of the owner at provider side + Sender string `json:"sender" validate:"required"` // unique indentifier of the user who wants to share the resource at provider side + OwnerDisplayName string `json:"ownerDisplayName"` // display name of the owner of the resource + SenderDisplayName string `json:"senderDisplayName"` // dispay name of the user who wants to share the resource + ShareType string `json:"shareType" validate:"required,oneof=user group"` // recipient share type (user or group) + ResourceType string `json:"resourceType" validate:"required,oneof=file folder"` + Expiration uint64 `json:"expiration"` + Protocols Protocols `json:"protocols" validate:"required"` } // CreateShare sends all the informations to the consumer needed to start // synchronization between the two services. func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - log := appctx.GetLogger(ctx) - contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) - var shareWith, meshProvider, resource, providerID, owner string - var protocol map[string]interface{} - if err == nil && contentType == "application/json" { - defer r.Body.Close() - reqBody, err := io.ReadAll(r.Body) - if err == nil { - reqMap := make(map[string]interface{}) - err = json.Unmarshal(reqBody, &reqMap) - if err == nil { - meshProvider = reqMap["meshProvider"].(string) // FIXME: get this from sharedBy string? - shareWith, protocol = reqMap["shareWith"].(string), reqMap["protocol"].(map[string]interface{}) - resource, owner = reqMap["name"].(string), reqMap["owner"].(string) - // Note that if an OCM request were to go directly from a Nextcloud server - // to a Reva server, it will (incorrectly) sends an integer provider_id instead a string one. - // This doesn't happen when using the sciencemesh-nextcloud app, but in order to make the OCM - // test suite pass, this code works around that: - if reflect.ValueOf(reqMap["providerId"]).Kind() == reflect.Float64 { - providerID = fmt.Sprintf("%d", int(math.Round(reqMap["providerId"].(float64)))) - } else { - providerID = reqMap["providerId"].(string) - } - } else { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "could not parse json request body", nil) - } - } - } else { - var protocolJSON string - shareWith, protocolJSON, meshProvider = r.FormValue("shareWith"), r.FormValue("protocol"), r.FormValue("meshProvider") - resource, providerID, owner = r.FormValue("name"), r.FormValue("providerId"), r.FormValue("owner") - err = json.Unmarshal([]byte(protocolJSON), &protocol) - if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "invalid protocol parameters", nil) - } - } - if resource == "" || providerID == "" || owner == "" { - msg := fmt.Sprintf("missing details about resource to be shared (resource='%s', providerID='%s', owner='%s", resource, providerID, owner) - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, msg, nil) - return - } - if shareWith == "" || protocol["name"] == "" || meshProvider == "" { - msg := fmt.Sprintf("missing request parameters (shareWith='%s', protocol.name='%s', meshProvider='%s'", shareWith, protocol["name"], meshProvider) - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, msg, nil) + req, err := getCreateShareRequest(r) + if err != nil { + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) return } - gatewayClient, err := pool.GetGatewayServiceClient(pool.Endpoint(h.gatewayAddr)) + _, meshProvider, err := getIDAndMeshProvider(req.Sender) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorServerError, "error getting storage grpc client", err) + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) return } @@ -122,7 +105,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { }, } - providerAllowedResp, err := gatewayClient.IsProviderAllowed(ctx, &ocmprovider.IsProviderAllowedRequest{ + providerAllowedResp, err := h.gatewayClient.IsProviderAllowed(ctx, &ocmprovider.IsProviderAllowedRequest{ Provider: &providerInfo, }) if err != nil { @@ -134,9 +117,14 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { return } - var shareWithParts = strings.Split(shareWith, "@") - userRes, err := gatewayClient.GetUser(ctx, &userpb.GetUserRequest{ - UserId: &userpb.UserId{OpaqueId: shareWithParts[0]}, SkipFetchingUserGroups: true, + shareWith, _, err := getIDAndMeshProvider(req.ShareWith) + if err != nil { + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) + return + } + + userRes, err := h.gatewayClient.GetUser(ctx, &userpb.GetUserRequest{ + UserId: &userpb.UserId{OpaqueId: shareWith}, SkipFetchingUserGroups: true, }) if err != nil { reqres.WriteError(w, r, reqres.APIErrorServerError, "error searching recipient", err) @@ -147,105 +135,119 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { return } - var permissions conversions.Permissions - var token string - options, ok := protocol["options"].(map[string]interface{}) - if !ok { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "protocol: webdav token not provided", nil) + owner, err := getUserIDFromOCMUser(req.Owner) + if err != nil { + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) return } - token, ok = options["sharedSecret"].(string) - if !ok { - token, ok = options["token"].(string) - if !ok { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "protocol: webdav token not provided", nil) - return - } - } - var role *conversions.Role - pval, ok := options["permissions"].(int) - if !ok { - role = conversions.NewViewerRole() - } else { - permissions, err = conversions.NewPermissions(pval) - if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) - return - } - role = conversions.RoleFromOCSPermissions(permissions) - } - - val, err := json.Marshal(role.CS3ResourcePermissions()) + sender, err := getUserIDFromOCMUser(req.Sender) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorServerError, "could not encode role", nil) + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) return } - ownerParts := strings.Split(owner, "@") - if len(ownerParts) != 2 { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "owner should be opaqueId@webDAVHost", nil) - } - ownerID := &userpb.UserId{ - OpaqueId: ownerParts[0], - Idp: ownerParts[1], - Type: userpb.UserType_USER_TYPE_PRIMARY, - } createShareReq := &ocmcore.CreateOCMCoreShareRequest{ - Name: resource, - ProviderId: providerID, - Owner: ownerID, - ShareWith: userRes.User.GetId(), - Protocol: &ocmcore.Protocol{ - Name: protocol["name"].(string), - Opaque: &types.Opaque{ - Map: map[string]*types.OpaqueEntry{ - "permissions": { - Decoder: "json", - Value: val, - }, - "token": { - Decoder: "plain", - Value: []byte(token), - }, - }, - }, - }, + Description: req.Description, + Name: req.Name, + ResourceId: req.ResourceID, + Owner: owner, + Sender: sender, + ShareWith: userRes.User.Id, + ResourceType: getResourceTypeFromOCMRequest(req.ResourceType), + ShareType: getOCMShareType(req.ShareType), + Protocols: getProtocols(req.Protocols), + } + + if req.Expiration != 0 { + createShareReq.Expiration = &types.Timestamp{ + Seconds: req.Expiration, + } } - createShareResponse, err := gatewayClient.CreateOCMCoreShare(ctx, createShareReq) + + createShareResp, err := h.gatewayClient.CreateOCMCoreShare(ctx, createShareReq) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc create ocm core share request", err) + reqres.WriteError(w, r, reqres.APIErrorServerError, "error creating ocm share", err) return } - if createShareResponse.Status.Code != rpc.Code_CODE_OK { - if createShareResponse.Status.Code == rpc.Code_CODE_NOT_FOUND { - reqres.WriteError(w, r, reqres.APIErrorNotFound, "not found", nil) - return - } - reqres.WriteError(w, r, reqres.APIErrorServerError, "grpc create ocm core share request failed", errors.New(createShareResponse.Status.Message)) + + if userRes.Status.Code != rpc.Code_CODE_OK { + // TODO: define errors in the cs3apis + reqres.WriteError(w, r, reqres.APIErrorServerError, "error creating ocm share", errors.New(createShareResp.Status.Message)) return } - timeCreated := createShareResponse.Created - jsonOut, err := json.Marshal( - map[string]string{ - "id": createShareResponse.Id, - "createdAt": time.Unix(int64(timeCreated.Seconds), int64(timeCreated.Nanos)).String(), - }, - ) - if err != nil { - reqres.WriteError(w, r, reqres.APIErrorServerError, "error marshalling share data", err) - return + response := map[string]any{} + + if h.exposeRecipientDisplayName { + response["recipientDisplayName"] = userRes.User.DisplayName } + _ = json.NewEncoder(w).Encode(response) w.WriteHeader(http.StatusCreated) - w.Header().Set("Content-Type", "application/json") +} - _, err = w.Write(jsonOut) +func getUserIDFromOCMUser(user string) (*userpb.UserId, error) { + id, idp, err := getIDAndMeshProvider(user) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorServerError, "error writing shares data", err) - return + return nil, err + } + return &userpb.UserId{ + OpaqueId: id, + Idp: idp, + // the remote user is a federated account for the local reva + Type: userpb.UserType_USER_TYPE_FEDERATED, + }, nil +} + +func getIDAndMeshProvider(user string) (string, string, error) { + // the user is in the form of dimitri@apiwise.nl + split := strings.Split(user, "@") + if len(split) < 2 { + return "", "", errors.New("not in the form @") } + return strings.Join(split[:len(split)-1], "@"), split[len(split)-1], nil +} - log.Info().Msg("Share created.") +func getCreateShareRequest(r *http.Request) (*createShareRequest, error) { + var req createShareRequest + contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err == nil && contentType == "application/json" { + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, err + } + } else { + return nil, errors.New("body request not recognised") + } + // validate the request + if err := validate.Struct(req); err != nil { + return nil, err + } + return &req, nil +} + +func getResourceTypeFromOCMRequest(t string) providerpb.ResourceType { + switch t { + case "file": + return providerpb.ResourceType_RESOURCE_TYPE_FILE + case "folder": + return providerpb.ResourceType_RESOURCE_TYPE_CONTAINER + default: + return providerpb.ResourceType_RESOURCE_TYPE_INVALID + } +} + +func getOCMShareType(t string) ocm.ShareType { + if t == "user" { + return ocm.ShareType_SHARE_TYPE_USER + } + return ocm.ShareType_SHARE_TYPE_GROUP +} + +func getProtocols(p Protocols) []*ocm.Protocol { + prot := make([]*ocm.Protocol, 0, len(p)) + for _, data := range p { + prot = append(prot, data.ToOCMProtocol()) + } + return prot } diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go index f35830c5c04..5bc18ea4964 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go @@ -20,15 +20,9 @@ package shares import ( "net/http" - "strconv" - userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1" - ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" - rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/response" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" @@ -36,87 +30,87 @@ import ( ) func (h *Handler) createFederatedCloudShare(w http.ResponseWriter, r *http.Request, statInfo *provider.ResourceInfo, role *conversions.Role, roleVal []byte) { - ctx := r.Context() - - c, err := pool.GetGatewayServiceClient(pool.Endpoint(h.gatewayAddr)) - if err != nil { - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) - return - } - - shareWithUser, shareWithProvider := r.FormValue("shareWithUser"), r.FormValue("shareWithProvider") - if shareWithUser == "" || shareWithProvider == "" { - response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "missing shareWith parameters", nil) - return - } - - providerInfoResp, err := c.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ - Domain: shareWithProvider, - }) - if err != nil { - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc get invite by domain info request", err) - return - } - - remoteUserRes, err := c.GetAcceptedUser(ctx, &invitepb.GetAcceptedUserRequest{ - RemoteUserId: &userpb.UserId{OpaqueId: shareWithUser, Idp: shareWithProvider, Type: userpb.UserType_USER_TYPE_PRIMARY}, - }) - if err != nil { - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching recipient", err) - return - } - if remoteUserRes.Status.Code != rpc.Code_CODE_OK { - response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "user not found", err) - return - } - - createShareReq := &ocm.CreateOCMShareRequest{ - Opaque: &types.Opaque{ - Map: map[string]*types.OpaqueEntry{ - /* TODO extend the spec with role names? - "role": { - Decoder: "plain", - Value: []byte(role.Name), - }, - */ - "permissions": { - Decoder: "plain", - Value: []byte(strconv.Itoa(int(role.OCSPermissions()))), - }, - "name": { - Decoder: "plain", - Value: []byte(statInfo.Path), - }, - }, - }, - ResourceId: statInfo.Id, - Grant: &ocm.ShareGrant{ - Grantee: &provider.Grantee{ - Type: provider.GranteeType_GRANTEE_TYPE_USER, - Id: &provider.Grantee_UserId{UserId: remoteUserRes.RemoteUser.GetId()}, - }, - Permissions: &ocm.SharePermissions{ - Permissions: role.CS3ResourcePermissions(), - }, - }, - RecipientMeshProvider: providerInfoResp.ProviderInfo, - } - - createShareResponse, err := c.CreateOCMShare(ctx, createShareReq) - if err != nil { - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc create ocm share request", err) - return - } - if createShareResponse.Status.Code != rpc.Code_CODE_OK { - if createShareResponse.Status.Code == rpc.Code_CODE_NOT_FOUND { - response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) - return - } - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc create ocm share request failed", err) - return - } - - response.WriteOCSSuccess(w, r, "OCM Share created") + // ctx := r.Context() + + // c, err := pool.GetGatewayServiceClient(pool.Endpoint(h.gatewayAddr)) + // if err != nil { + // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) + // return + // } + + // shareWithUser, shareWithProvider := r.FormValue("shareWithUser"), r.FormValue("shareWithProvider") + // if shareWithUser == "" || shareWithProvider == "" { + // response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "missing shareWith parameters", nil) + // return + // } + + // providerInfoResp, err := c.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ + // Domain: shareWithProvider, + // }) + // if err != nil { + // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc get invite by domain info request", err) + // return + // } + + // remoteUserRes, err := c.GetAcceptedUser(ctx, &invitepb.GetAcceptedUserRequest{ + // RemoteUserId: &userpb.UserId{OpaqueId: shareWithUser, Idp: shareWithProvider, Type: userpb.UserType_USER_TYPE_PRIMARY}, + // }) + // if err != nil { + // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching recipient", err) + // return + // } + // if remoteUserRes.Status.Code != rpc.Code_CODE_OK { + // response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "user not found", err) + // return + // } + + // createShareReq := &ocm.CreateOCMShareRequest{ + // Opaque: &types.Opaque{ + // Map: map[string]*types.OpaqueEntry{ + // /* TODO extend the spec with role names? + // "role": { + // Decoder: "plain", + // Value: []byte(role.Name), + // }, + // */ + // "permissions": { + // Decoder: "plain", + // Value: []byte(strconv.Itoa(int(role.OCSPermissions()))), + // }, + // "name": { + // Decoder: "plain", + // Value: []byte(statInfo.Path), + // }, + // }, + // }, + // ResourceId: statInfo.Id, + // Grant: &ocm.ShareGrant{ + // Grantee: &provider.Grantee{ + // Type: provider.GranteeType_GRANTEE_TYPE_USER, + // Id: &provider.Grantee_UserId{UserId: remoteUserRes.RemoteUser.GetId()}, + // }, + // Permissions: &ocm.SharePermissions{ + // Permissions: role.CS3ResourcePermissions(), + // }, + // }, + // RecipientMeshProvider: providerInfoResp.ProviderInfo, + // } + + // createShareResponse, err := c.CreateOCMShare(ctx, createShareReq) + // if err != nil { + // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc create ocm share request", err) + // return + // } + // if createShareResponse.Status.Code != rpc.Code_CODE_OK { + // if createShareResponse.Status.Code == rpc.Code_CODE_NOT_FOUND { + // response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) + // return + // } + // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc create ocm share request failed", err) + // return + // } + + // response.WriteOCSSuccess(w, r, "OCM Share created") } // GetFederatedShare handles GET requests on /apps/files_sharing/api/v1/shares/remote_shares/{shareid}. diff --git a/pkg/app/app.go b/pkg/app/app.go index 32a4e34dbe1..da2720c15da 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -41,6 +41,6 @@ type Registry interface { // Provider is the interface that application providers implement // for interacting with external apps that serve the requested resource. type Provider interface { - GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string, opaqueMap map[string]*typespb.OpaqueEntry, language string) (*appprovider.OpenInAppURL, error) + GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.ViewMode, token string, opaqueMap map[string]*typespb.OpaqueEntry, language string) (*appprovider.OpenInAppURL, error) GetAppProviderInfo(ctx context.Context) (*registry.ProviderInfo, error) } diff --git a/pkg/app/provider/demo/demo.go b/pkg/app/provider/demo/demo.go index e10d7fa3c48..344f7d88833 100644 --- a/pkg/app/provider/demo/demo.go +++ b/pkg/app/provider/demo/demo.go @@ -40,7 +40,7 @@ type demoProvider struct { iframeUIProvider string } -func (p *demoProvider) GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string, opaqueMap map[string]*typespb.OpaqueEntry, language string) (*appprovider.OpenInAppURL, error) { +func (p *demoProvider) GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.ViewMode, token string, opaqueMap map[string]*typespb.OpaqueEntry, language string) (*appprovider.OpenInAppURL, error) { url := fmt.Sprintf("