From 689e45db64ed8e8d5517975b5effdec2dee53833 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 1 Mar 2023 09:08:49 +0100 Subject: [PATCH 1/9] create share from sciencemesh service --- .../http/services/sciencemesh/sciencemesh.go | 5 + internal/http/services/sciencemesh/share.go | 201 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 internal/http/services/sciencemesh/share.go diff --git a/internal/http/services/sciencemesh/sciencemesh.go b/internal/http/services/sciencemesh/sciencemesh.go index ec9a235519..256927686b 100644 --- a/internal/http/services/sciencemesh/sciencemesh.go +++ b/internal/http/services/sciencemesh/sciencemesh.go @@ -93,12 +93,17 @@ func (s *svc) routerInit() error { if err := providersHandler.init(s.conf); err != nil { return err } + sharesHandler := new(sharesHandler) + if err := sharesHandler.init(s.conf); err != nil { + return err + } s.router.Get("/generate-invite", tokenHandler.Generate) s.router.Get("/list-invite", tokenHandler.ListInvite) s.router.Post("/accept-invite", tokenHandler.AcceptInvite) s.router.Get("/find-accepted-users", tokenHandler.FindAccepted) s.router.Get("/list-providers", providersHandler.ListProviders) + s.router.Post("/create-share", sharesHandler.CreateShare) return nil } diff --git a/internal/http/services/sciencemesh/share.go b/internal/http/services/sciencemesh/share.go new file mode 100644 index 0000000000..606156683f --- /dev/null +++ b/internal/http/services/sciencemesh/share.go @@ -0,0 +1,201 @@ +// 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 sciencemesh + +import ( + "context" + "encoding/json" + "errors" + "mime" + "net/http" + + appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userv1beta1 "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" + rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + ocmv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/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" + ctxpkg "github.com/cs3org/reva/pkg/ctx" + "github.com/cs3org/reva/pkg/ocm/share" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/go-playground/validator/v10" + "google.golang.org/grpc/metadata" +) + +var validate = validator.New() + +type sharesHandler struct { + gatewayClient gateway.GatewayAPIClient +} + +func (h *sharesHandler) init(c *config) error { + var err error + h.gatewayClient, err = pool.GetGatewayServiceClient(pool.Endpoint(c.GatewaySvc)) + return err +} + +type createShareRequest struct { + SourcePath string `json:"sourcePath" validate:"required"` + TargetPath string `json:"targetPath" validate:"required"` + Type string `json:"type"` + Role string `json:"role" validate:"oneof=viewer editor"` + RecipientUsername string `json:"recipientUsername" validate:"required"` + RecipientHost string `json:"recipientHost" validate:"required"` + // FIXME: the client should not authenticate here + LoginType string `json:"loginType" validate:"required"` + LoginUsername string `json:"loginUsername" validate:"required"` + LoginPassword string `json:"loginPassword" validate:"required"` +} + +// CreateShare creates an OCM share. +func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { + log := appctx.GetLogger(r.Context()) + + req, err := getCreateShareRequest(r) + if err != nil { + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "invalid parameters", err) + return + } + + // FIXME: the client should be already authenticated + res, err := h.gatewayClient.Authenticate(context.Background(), &gateway.AuthenticateRequest{ + Type: req.LoginType, + ClientId: req.LoginUsername, + ClientSecret: req.LoginPassword, + }) + if err != nil { + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "unexpected error", err) + return + } + if res.Status.Code != rpcv1beta1.Code_CODE_OK { + reqres.WriteError(w, r, reqres.APIErrorUnauthenticated, res.Status.Message, errors.New(res.Status.Message)) + return + } + + ctx := r.Context() + ctx = ctxpkg.ContextSetToken(ctx, res.Token) + ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, res.Token) + + statRes, err := h.gatewayClient.Stat(ctx, &providerv1beta1.StatRequest{ + Ref: &providerv1beta1.Reference{ + Path: req.SourcePath, + }, + }) + switch { + case err != nil: + reqres.WriteError(w, r, reqres.APIErrorServerError, "unexpected error", err) + return + case statRes.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND: + reqres.WriteError(w, r, reqres.APIErrorNotFound, statRes.Status.Message, nil) + return + case statRes.Status.Code != rpcv1beta1.Code_CODE_OK: + reqres.WriteError(w, r, reqres.APIErrorServerError, statRes.Status.Message, errors.New(statRes.Status.Message)) + return + } + + recipientProviderInfo, err := h.gatewayClient.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ + Domain: req.RecipientHost, + }) + if err != nil { + reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc get invite by domain info request", err) + return + } + if recipientProviderInfo.Status.Code != rpc.Code_CODE_OK { + reqres.WriteError(w, r, reqres.APIErrorNotFound, recipientProviderInfo.Status.Message, errors.New(recipientProviderInfo.Status.Message)) + return + } + + perm, viewMode := getPermissionsByRole(req.Role) + + shareRes, err := h.gatewayClient.CreateOCMShare(ctx, &ocmv1beta1.CreateOCMShareRequest{ + ResourceId: statRes.Info.Id, + Grantee: &providerv1beta1.Grantee{ + Type: providerv1beta1.GranteeType_GRANTEE_TYPE_USER, + Id: &providerv1beta1.Grantee_UserId{ + UserId: &userv1beta1.UserId{ + Idp: req.RecipientHost, + OpaqueId: req.RecipientUsername, + }, + }, + }, + RecipientMeshProvider: recipientProviderInfo.ProviderInfo, + AccessMethods: []*ocmv1beta1.AccessMethod{ + share.NewWebDavAccessMethod(perm), + share.NewWebappAccessMethod(viewMode), + }, + }) + switch { + case err != nil: + reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc CreateOCMShare", err) + return + case shareRes.Status.Code == rpc.Code_CODE_NOT_FOUND: + reqres.WriteError(w, r, reqres.APIErrorNotFound, shareRes.Status.Message, nil) + return + case shareRes.Status.Code == rpc.Code_CODE_ALREADY_EXISTS: + reqres.WriteError(w, r, reqres.APIErrorAlreadyExist, shareRes.Status.Message, nil) + return + case shareRes.Status.Code != rpc.Code_CODE_OK: + reqres.WriteError(w, r, reqres.APIErrorAlreadyExist, shareRes.Status.Message, errors.New(shareRes.Status.Message)) + return + } + + if err := json.NewEncoder(w).Encode(shareRes); err != nil { + log.Error().Err(err).Msg("error encoding response") + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} + +func getPermissionsByRole(role string) (*providerv1beta1.ResourcePermissions, appprovider.ViewMode) { + switch role { + case "viewer": + return conversions.NewViewerRole().CS3ResourcePermissions(), appprovider.ViewMode_VIEW_MODE_READ_ONLY + case "editor": + return conversions.NewEditorRole().CS3ResourcePermissions(), appprovider.ViewMode_VIEW_MODE_READ_WRITE + } + return nil, 0 +} + +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") + } + // set defaults + if req.Type == "" { + req.Type = "viewer" + } + // validate the request + if err := validate.Struct(req); err != nil { + return nil, err + } + return &req, nil +} From 7c03111bb494ce295b4f6290470a7e10d14e638a Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 1 Mar 2023 09:12:19 +0100 Subject: [PATCH 2/9] make /create-share unprotected --- internal/http/services/sciencemesh/sciencemesh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/services/sciencemesh/sciencemesh.go b/internal/http/services/sciencemesh/sciencemesh.go index 256927686b..3374db4461 100644 --- a/internal/http/services/sciencemesh/sciencemesh.go +++ b/internal/http/services/sciencemesh/sciencemesh.go @@ -113,7 +113,7 @@ func (s *svc) Prefix() string { } func (s *svc) Unprotected() []string { - return nil + return []string{"/create-share"} } func (s *svc) Handler() http.Handler { From 849d39093defbed02235bc32e076cae55e9ea9cb Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 1 Mar 2023 09:13:10 +0100 Subject: [PATCH 3/9] add changelog --- changelog/unreleased/ocm-share-create-sm.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/unreleased/ocm-share-create-sm.md diff --git a/changelog/unreleased/ocm-share-create-sm.md b/changelog/unreleased/ocm-share-create-sm.md new file mode 100644 index 0000000000..ac2e53e05f --- /dev/null +++ b/changelog/unreleased/ocm-share-create-sm.md @@ -0,0 +1,4 @@ +Enhancement: Create OCM share from sciencemesh service + +https://github.com/cs3org/reva/pull/3695 +https://github.com/pondersource/sciencemesh-php/issues/166 \ No newline at end of file From 2495e30f5387fa09b56d1c8b2ae6fd179fd76a78 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 1 Mar 2023 09:15:42 +0100 Subject: [PATCH 4/9] fix linter! --- internal/http/services/sciencemesh/share.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/http/services/sciencemesh/share.go b/internal/http/services/sciencemesh/share.go index 606156683f..fe3be1f2c6 100644 --- a/internal/http/services/sciencemesh/share.go +++ b/internal/http/services/sciencemesh/share.go @@ -30,7 +30,6 @@ import ( userv1beta1 "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" - rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocmv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" @@ -88,7 +87,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "unexpected error", err) return } - if res.Status.Code != rpcv1beta1.Code_CODE_OK { + if res.Status.Code != rpc.Code_CODE_OK { reqres.WriteError(w, r, reqres.APIErrorUnauthenticated, res.Status.Message, errors.New(res.Status.Message)) return } @@ -106,10 +105,10 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { case err != nil: reqres.WriteError(w, r, reqres.APIErrorServerError, "unexpected error", err) return - case statRes.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND: + case statRes.Status.Code == rpc.Code_CODE_NOT_FOUND: reqres.WriteError(w, r, reqres.APIErrorNotFound, statRes.Status.Message, nil) return - case statRes.Status.Code != rpcv1beta1.Code_CODE_OK: + case statRes.Status.Code != rpc.Code_CODE_OK: reqres.WriteError(w, r, reqres.APIErrorServerError, statRes.Status.Message, errors.New(statRes.Status.Message)) return } From 72c9ab1877e3d9e0d1bf81214fd18d8239a82fcc Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte <39946305+gmgigi96@users.noreply.github.com> Date: Thu, 4 May 2023 09:10:26 +0200 Subject: [PATCH 5/9] Update internal/http/services/sciencemesh/share.go Co-authored-by: Giuseppe Lo Presti --- internal/http/services/sciencemesh/share.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/http/services/sciencemesh/share.go b/internal/http/services/sciencemesh/share.go index fe3be1f2c6..aecd007f2b 100644 --- a/internal/http/services/sciencemesh/share.go +++ b/internal/http/services/sciencemesh/share.go @@ -61,10 +61,6 @@ type createShareRequest struct { Role string `json:"role" validate:"oneof=viewer editor"` RecipientUsername string `json:"recipientUsername" validate:"required"` RecipientHost string `json:"recipientHost" validate:"required"` - // FIXME: the client should not authenticate here - LoginType string `json:"loginType" validate:"required"` - LoginUsername string `json:"loginUsername" validate:"required"` - LoginPassword string `json:"loginPassword" validate:"required"` } // CreateShare creates an OCM share. From 98e61fda0e82d730dd0f1211692d75d87d173bc1 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte <39946305+gmgigi96@users.noreply.github.com> Date: Thu, 4 May 2023 09:10:34 +0200 Subject: [PATCH 6/9] Update internal/http/services/sciencemesh/share.go Co-authored-by: Giuseppe Lo Presti --- internal/http/services/sciencemesh/share.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/internal/http/services/sciencemesh/share.go b/internal/http/services/sciencemesh/share.go index aecd007f2b..9d39b9988b 100644 --- a/internal/http/services/sciencemesh/share.go +++ b/internal/http/services/sciencemesh/share.go @@ -73,21 +73,6 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { return } - // FIXME: the client should be already authenticated - res, err := h.gatewayClient.Authenticate(context.Background(), &gateway.AuthenticateRequest{ - Type: req.LoginType, - ClientId: req.LoginUsername, - ClientSecret: req.LoginPassword, - }) - if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "unexpected error", err) - return - } - if res.Status.Code != rpc.Code_CODE_OK { - reqres.WriteError(w, r, reqres.APIErrorUnauthenticated, res.Status.Message, errors.New(res.Status.Message)) - return - } - ctx := r.Context() ctx = ctxpkg.ContextSetToken(ctx, res.Token) ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, res.Token) From 1d7da3a4c6830437610ee0e44a25d99bd0ec8a02 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte <39946305+gmgigi96@users.noreply.github.com> Date: Thu, 4 May 2023 09:10:54 +0200 Subject: [PATCH 7/9] Update internal/http/services/sciencemesh/sciencemesh.go Co-authored-by: Giuseppe Lo Presti --- internal/http/services/sciencemesh/sciencemesh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/services/sciencemesh/sciencemesh.go b/internal/http/services/sciencemesh/sciencemesh.go index 29593288b3..89f49990ee 100644 --- a/internal/http/services/sciencemesh/sciencemesh.go +++ b/internal/http/services/sciencemesh/sciencemesh.go @@ -120,7 +120,7 @@ func (s *svc) Prefix() string { } func (s *svc) Unprotected() []string { - return []string{"/create-share"} + return nil } func (s *svc) Handler() http.Handler { From 471c1632ecc276ae9a8b9565d14fb2c29d98531a Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 4 May 2023 17:12:33 +0200 Subject: [PATCH 8/9] removed old code --- internal/http/services/sciencemesh/share.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/http/services/sciencemesh/share.go b/internal/http/services/sciencemesh/share.go index 9d39b9988b..0e3d81c077 100644 --- a/internal/http/services/sciencemesh/share.go +++ b/internal/http/services/sciencemesh/share.go @@ -19,7 +19,6 @@ package sciencemesh import ( - "context" "encoding/json" "errors" "mime" @@ -35,11 +34,9 @@ import ( "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/internal/http/services/reqres" "github.com/cs3org/reva/pkg/appctx" - ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/go-playground/validator/v10" - "google.golang.org/grpc/metadata" ) var validate = validator.New() @@ -74,8 +71,6 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { } ctx := r.Context() - ctx = ctxpkg.ContextSetToken(ctx, res.Token) - ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, res.Token) statRes, err := h.gatewayClient.Stat(ctx, &providerv1beta1.StatRequest{ Ref: &providerv1beta1.Reference{ From bf0fe4f0763aabf6d5350e3dced1c994db0fa34d Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 4 May 2023 17:13:09 +0200 Subject: [PATCH 9/9] fix import names --- internal/http/services/sciencemesh/share.go | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/http/services/sciencemesh/share.go b/internal/http/services/sciencemesh/share.go index 0e3d81c077..130cbd0505 100644 --- a/internal/http/services/sciencemesh/share.go +++ b/internal/http/services/sciencemesh/share.go @@ -26,11 +26,11 @@ import ( appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/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" - ocmv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" - providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" + providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/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" @@ -72,8 +72,8 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - statRes, err := h.gatewayClient.Stat(ctx, &providerv1beta1.StatRequest{ - Ref: &providerv1beta1.Reference{ + statRes, err := h.gatewayClient.Stat(ctx, &providerpb.StatRequest{ + Ref: &providerpb.Reference{ Path: req.SourcePath, }, }) @@ -103,19 +103,19 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { perm, viewMode := getPermissionsByRole(req.Role) - shareRes, err := h.gatewayClient.CreateOCMShare(ctx, &ocmv1beta1.CreateOCMShareRequest{ + shareRes, err := h.gatewayClient.CreateOCMShare(ctx, &ocm.CreateOCMShareRequest{ ResourceId: statRes.Info.Id, - Grantee: &providerv1beta1.Grantee{ - Type: providerv1beta1.GranteeType_GRANTEE_TYPE_USER, - Id: &providerv1beta1.Grantee_UserId{ - UserId: &userv1beta1.UserId{ + Grantee: &providerpb.Grantee{ + Type: providerpb.GranteeType_GRANTEE_TYPE_USER, + Id: &providerpb.Grantee_UserId{ + UserId: &userpb.UserId{ Idp: req.RecipientHost, OpaqueId: req.RecipientUsername, }, }, }, RecipientMeshProvider: recipientProviderInfo.ProviderInfo, - AccessMethods: []*ocmv1beta1.AccessMethod{ + AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(perm), share.NewWebappAccessMethod(viewMode), }, @@ -144,7 +144,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func getPermissionsByRole(role string) (*providerv1beta1.ResourcePermissions, appprovider.ViewMode) { +func getPermissionsByRole(role string) (*providerpb.ResourcePermissions, appprovider.ViewMode) { switch role { case "viewer": return conversions.NewViewerRole().CS3ResourcePermissions(), appprovider.ViewMode_VIEW_MODE_READ_ONLY