From c2e3998b20f3e1f116810d18bd73b955bb9319f7 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Wed, 12 Feb 2025 17:29:58 +0100 Subject: [PATCH 01/16] ocm: adapt to match OCM 1.2 specs, use discovery in all cases when receiving a share --- .../services/opencloudmesh/ocmd/shares.go | 32 ++++++++++--------- internal/http/services/owncloud/ocdav/dav.go | 2 +- internal/http/services/wellknown/ocm.go | 16 ++++++---- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index 823943a56a..e10fff396c 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -228,7 +228,7 @@ func getCreateShareRequest(r *http.Request) (*createShareRequest, error) { return nil, err } } else { - return nil, errors.New("body request not recognised") + return nil, errors.New("OCM /share request payload not recognised") } // validate the request if err := validate.Struct(req); err != nil { @@ -264,25 +264,27 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro protos := make([]*ocm.Protocol, 0, len(p)) for _, data := range p { ocmProto := data.ToOCMProtocol() - if GetProtocolName(data) == "webdav" && ocmProto.GetWebdavOptions().Uri == "" { - // This is an OCM 1.0 payload with only webdav: we need to resolve the remote URL - remoteRoot, err := discoverOcmWebdavRoot(r) - if err != nil { - return nil, err - } + // Irrespective from the presence of a full `uri` in the payload (deprecated), resolve the remote root + remoteRoot, err := discoverOcmRoot(r, GetProtocolName(data)) + if err != nil { + return nil, err + } + if GetProtocolName(data) == "webdav" { ocmProto.GetWebdavOptions().Uri = filepath.Join(remoteRoot, ocmProto.GetWebdavOptions().SharedSecret) + } else if GetProtocolName(data) == "webapp" { + // ocmProto.GetWebappOptions().Uri = filepath.Join(remoteRoot, ocmProto.GetWebappOptions().SharedSecret) -> this is OCM 1.2 } protos = append(protos, ocmProto) } return protos, nil } -func discoverOcmWebdavRoot(r *http.Request) (string, error) { - // implements the OCM discovery logic to fetch the WebDAV root at the remote host that sent the share, see +func discoverOcmRoot(r *http.Request, proto string) (string, error) { + // implements the OCM discovery logic to fetch the root at the remote host that sent the share for the given proto, see // https://cs3org.github.io/OCM-API/docs.html?branch=v1.1.0&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get ctx := r.Context() log := appctx.GetLogger(ctx) - log.Debug().Str("sender", r.Host).Msg("received OCM 1.0 share, attempting to discover sender endpoint") + log.Debug().Str("sender", r.Host).Msg("received OCM share, attempting to discover sender endpoint") ocmClient := NewClient(time.Duration(10)*time.Second, true) ocmCaps, err := ocmClient.Discover(ctx, r.Host) @@ -291,14 +293,14 @@ func discoverOcmWebdavRoot(r *http.Request) (string, error) { return "", err } for _, t := range ocmCaps.ResourceTypes { - webdavRoot, ok := t.Protocols["webdav"] + protoRoot, ok := t.Protocols[proto] if ok { - // assume the first resourceType that exposes a webdav root is OK to use: as a matter of fact, + // assume the first resourceType that exposes a root is OK to use: as a matter of fact, // no implementation exists yet that exposes multiple resource types with different roots. - return filepath.Join(ocmCaps.Endpoint, webdavRoot), nil + return filepath.Join(ocmCaps.Endpoint, protoRoot), nil } } - log.Warn().Str("sender", r.Host).Interface("response", ocmCaps).Msg("missing webdav root") - return "", errtypes.NotFound("WebDAV root not found on OCM discovery") + log.Warn().Str("sender", r.Host).Interface("response", ocmCaps).Msg("missing root") + return "", errtypes.NotFound(fmt.Sprintf("root not found on OCM discovery for protocol %s", proto)) } diff --git a/internal/http/services/owncloud/ocdav/dav.go b/internal/http/services/owncloud/ocdav/dav.go index b96365f109..86912eeb54 100644 --- a/internal/http/services/owncloud/ocdav/dav.go +++ b/internal/http/services/owncloud/ocdav/dav.go @@ -189,7 +189,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler { } var token, ocmshare string - // OCM v1.1 (OCIS et al.). + // OCM v1.1+ (OCIS et al.). bearer := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") if bearer != "" { // Bearer token is the shared secret, path is /{shareId}/path/to/resource. diff --git a/internal/http/services/wellknown/ocm.go b/internal/http/services/wellknown/ocm.go index f8f4d53612..668f1c2679 100644 --- a/internal/http/services/wellknown/ocm.go +++ b/internal/http/services/wellknown/ocm.go @@ -40,12 +40,13 @@ type OcmProviderConfig struct { } type OcmDiscoveryData struct { - Enabled bool `json:"enabled" xml:"enabled"` - APIVersion string `json:"apiVersion" xml:"apiVersion"` - Endpoint string `json:"endPoint" xml:"endPoint"` - Provider string `json:"provider" xml:"provider"` - ResourceTypes []resourceTypes `json:"resourceTypes" xml:"resourceTypes"` - Capabilities []string `json:"capabilities" xml:"capabilities"` + Enabled bool `json:"enabled" xml:"enabled"` + APIVersion string `json:"apiVersion" xml:"apiVersion"` + Endpoint string `json:"endPoint" xml:"endPoint"` + Provider string `json:"provider" xml:"provider"` + ResourceTypes []resourceTypes `json:"resourceTypes" xml:"resourceTypes"` + Capabilities []string `json:"capabilities" xml:"capabilities"` + InviteAcceptDialog string `json:"inviteAcceptDialog" xml:"inviteAcceptDialog"` } type resourceTypes struct { @@ -124,7 +125,8 @@ func (h *wkocmHandler) init(c *OcmProviderConfig) { Protocols: rtProtos, // expose the protocols as per configuration }} // for now we hardcode the capabilities, as this is currently only advisory - d.Capabilities = []string{"/invite-accepted"} + d.Capabilities = []string{"invites", "webdav-uri", "protocol-object"} + d.InviteAcceptDialog = "/sciencemesh-app/invitations" h.data = d } From a47930ff9533374cf4e18f9e7fd729ba9809240f Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Wed, 12 Feb 2025 17:53:46 +0100 Subject: [PATCH 02/16] ocm: some field renames for OCM 1.2, prior to changing the CS3APIs --- .../ocmshareprovider/ocmshareprovider.go | 4 +-- .../services/opencloudmesh/ocmd/protocols.go | 13 +++---- .../opencloudmesh/ocmd/protocols_test.go | 34 +++++++++---------- .../share/repository/nextcloud/nextcloud.go | 6 ++-- pkg/ocm/share/repository/sql/conversions.go | 4 +-- pkg/ocm/share/repository/sql/sql.go | 4 +-- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index e26e7b02e5..20c6a625c4 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -191,7 +191,7 @@ func (s *service) getWebdavProtocol(share *ocm.Share, m *ocm.AccessMethod_Webdav return &ocmd.WebDAV{ Permissions: perms, - URL: s.webdavURL(share), + URI: s.webdavURL(share), SharedSecret: share.Token, } } @@ -202,7 +202,7 @@ func (s *service) getWebappProtocol(share *ocm.Share) *ocmd.Webapp { panic(err) } return &ocmd.Webapp{ - URITemplate: b.String(), + URI: b.String(), } } diff --git a/internal/http/services/opencloudmesh/ocmd/protocols.go b/internal/http/services/opencloudmesh/ocmd/protocols.go index 08e2ccd127..2b7d96de2e 100644 --- a/internal/http/services/opencloudmesh/ocmd/protocols.go +++ b/internal/http/services/opencloudmesh/ocmd/protocols.go @@ -47,7 +47,7 @@ type Protocol interface { 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"` + URI string `json:"uri" validate:"required"` } // ToOCMProtocol convert the protocol to a ocm Protocol struct. @@ -69,18 +69,19 @@ func (w *WebDAV) ToOCMProtocol() *ocm.Protocol { } } - return ocmshare.NewWebDAVProtocol(w.URL, w.SharedSecret, perms) + return ocmshare.NewWebDAVProtocol(w.URI, w.SharedSecret, perms) } // Webapp contains the parameters for the Webapp protocol. type Webapp struct { - URITemplate string `json:"uriTemplate" validate:"required"` - ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"` + URI string `json:"uri" validate:"required"` + ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"` + SharedSecret string `json:"sharedSecret"` } // ToOCMProtocol convert the protocol to a ocm Protocol struct. func (w *Webapp) ToOCMProtocol() *ocm.Protocol { - return ocmshare.NewWebappProtocol(w.URITemplate, utils.GetAppViewMode(w.ViewMode)) + return ocmshare.NewWebappProtocol(w.URI, utils.GetAppViewMode(w.ViewMode)) } // Datatx contains the parameters for the Datatx protocol. @@ -134,7 +135,7 @@ func (p *Protocols) UnmarshalJSON(data []byte) error { res = &WebDAV{ SharedSecret: ss, Permissions: []string{"read", "write", "share"}, - URL: "", + URI: "", } *p = append(*p, res) } diff --git a/internal/http/services/opencloudmesh/ocmd/protocols_test.go b/internal/http/services/opencloudmesh/ocmd/protocols_test.go index fe45cd8bd3..5c9cbbf1b6 100644 --- a/internal/http/services/opencloudmesh/ocmd/protocols_test.go +++ b/internal/http/services/opencloudmesh/ocmd/protocols_test.go @@ -54,7 +54,7 @@ func TestUnmarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read", "write", "share"}, - URL: "", + URI: "", }, }, }, @@ -64,7 +64,7 @@ func TestUnmarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read", "write"}, - URL: "http://example.org", + URI: "http://example.org", }, }, }, @@ -72,7 +72,7 @@ func TestUnmarshalProtocol(t *testing.T) { raw: `{"name":"multi","options":{},"webapp":{"uriTemplate":"http://example.org/{test}"}}`, expected: []Protocol{ &Webapp{ - URITemplate: "http://example.org/{test}", + URI: "http://example.org/{test}", }, }, }, @@ -92,10 +92,10 @@ func TestUnmarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read", "write"}, - URL: "http://example.org", + URI: "http://example.org", }, &Webapp{ - URITemplate: "http://example.org/{test}", + URI: "http://example.org/{test}", }, &Datatx{ SharedSecret: "secret", @@ -155,7 +155,7 @@ func TestMarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read"}, - URL: "http://example.org", + URI: "http://example.org", }, }, expected: map[string]any{ @@ -164,23 +164,23 @@ func TestMarshalProtocol(t *testing.T) { "webdav": map[string]any{ "sharedSecret": "secret", "permissions": []any{"read"}, - "url": "http://example.org", + "uri": "http://example.org", }, }, }, { in: []Protocol{ &Webapp{ - URITemplate: "http://example.org", - ViewMode: "read", + URI: "http://example.org", + ViewMode: "read", }, }, expected: map[string]any{ "name": "multi", "options": map[string]any{}, "webapp": map[string]any{ - "uriTemplate": "http://example.org", - "viewMode": "read", + "uri": "http://example.org", + "viewMode": "read", }, }, }, @@ -207,11 +207,11 @@ func TestMarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read"}, - URL: "http://example.org", + URI: "http://example.org", }, &Webapp{ - URITemplate: "http://example.org", - ViewMode: "read", + URI: "http://example.org", + ViewMode: "read", }, &Datatx{ SharedSecret: "secret", @@ -225,11 +225,11 @@ func TestMarshalProtocol(t *testing.T) { "webdav": map[string]any{ "sharedSecret": "secret", "permissions": []any{"read"}, - "url": "http://example.org", + "uri": "http://example.org", }, "webapp": map[string]any{ - "uriTemplate": "http://example.org", - "viewMode": "read", + "uri": "http://example.org", + "viewMode": "read", }, "datatx": map[string]any{ "sharedSecret": "secret", diff --git a/pkg/ocm/share/repository/nextcloud/nextcloud.go b/pkg/ocm/share/repository/nextcloud/nextcloud.go index 98d379ca59..2ee9fdf52b 100644 --- a/pkg/ocm/share/repository/nextcloud/nextcloud.go +++ b/pkg/ocm/share/repository/nextcloud/nextcloud.go @@ -97,8 +97,8 @@ type EfssShare struct { Permissions int `json:"permissions" validate:"required"` } `json:"webdav" validate:"required"` WebApp struct { - URITemplate string `json:"uri_template"` - ViewMode string `json:"view_mode"` + URI string `json:"uri_template"` + ViewMode string `json:"view_mode"` } `json:"webapp" validate:"omitempty"` DataTx struct { SourceURI string `json:"source_uri"` @@ -328,7 +328,7 @@ func efssReceivedShareToOcm(resp *ReceivedEfssShare) *ocm.ReceivedShare { Permissions: conversions.RoleFromOCSPermissions(conversions.Permissions(resp.Share.Protocols.WebDAV.Permissions)).CS3ResourcePermissions(), })) if resp.Share.Protocols.WebApp.ViewMode != "" { - proto = append(proto, share.NewWebappProtocol(resp.Share.Protocols.WebApp.URITemplate, utils.GetAppViewMode(resp.Share.Protocols.WebApp.ViewMode))) + proto = append(proto, share.NewWebappProtocol(resp.Share.Protocols.WebApp.URI, utils.GetAppViewMode(resp.Share.Protocols.WebApp.ViewMode))) } if resp.Share.Protocols.DataTx.SourceURI != "" { proto = append(proto, share.NewTransferProtocol(resp.Share.Protocols.DataTx.SourceURI, resp.Share.Token, uint64(resp.Share.Protocols.DataTx.Size))) diff --git a/pkg/ocm/share/repository/sql/conversions.go b/pkg/ocm/share/repository/sql/conversions.go index 54282f4133..51ae4a3606 100644 --- a/pkg/ocm/share/repository/sql/conversions.go +++ b/pkg/ocm/share/repository/sql/conversions.go @@ -169,7 +169,7 @@ type dbProtocol struct { WebDAVURI *string WebDAVSharedSecret *string WebDavPermissions *int - WebappURITemplate *string + WebappURI *string WebappViewMode *int TransferSourceURI *string TransferSharedSecret *string @@ -283,7 +283,7 @@ func convertToCS3Protocol(p *dbProtocol) *ocm.Protocol { Permissions: conversions.RoleFromOCSPermissions(conversions.Permissions(*p.WebDavPermissions)).CS3ResourcePermissions(), }) case WebappProtocol: - return share.NewWebappProtocol(*p.WebappURITemplate, appprovider.ViewMode(*p.WebappViewMode)) + return share.NewWebappProtocol(*p.WebappURI, appprovider.ViewMode(*p.WebappViewMode)) case TransferProtocol: return share.NewTransferProtocol(*p.TransferSourceURI, *p.TransferSharedSecret, uint64(*p.TransferSize)) } diff --git a/pkg/ocm/share/repository/sql/sql.go b/pkg/ocm/share/repository/sql/sql.go index ffdd09be60..81b2d42287 100644 --- a/pkg/ocm/share/repository/sql/sql.go +++ b/pkg/ocm/share/repository/sql/sql.go @@ -707,7 +707,7 @@ func (m *mgr) getProtocolsIds(ctx context.Context, ids []any) (map[string][]*ocm var p dbProtocol for rows.Next() { - if err := rows.Scan(&p.ShareID, &p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURITemplate, &p.WebappViewMode, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil { + if err := rows.Scan(&p.ShareID, &p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURI, &p.WebappViewMode, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil { continue } protocols[p.ShareID] = append(protocols[p.ShareID], convertToCS3Protocol(&p)) @@ -763,7 +763,7 @@ func (m *mgr) getProtocols(ctx context.Context, id int) ([]*ocm.Protocol, error) var p dbProtocol for rows.Next() { - if err := rows.Scan(&p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURITemplate, &p.WebappViewMode, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil { + if err := rows.Scan(&p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURI, &p.WebappViewMode, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil { continue } protocols = append(protocols, convertToCS3Protocol(&p)) From b5824a546139148aaa2e572194fc5ece29f50545 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Thu, 13 Feb 2025 10:34:14 +0100 Subject: [PATCH 03/16] Refactored OCM payloads to remove duplicates --- .../services/opencloudmesh/ocmd/client.go | 67 ++---------------- .../services/opencloudmesh/ocmd/invites.go | 20 +----- .../services/opencloudmesh/ocmd/shares.go | 19 +---- .../ocmd/{protocols.go => specs.go} | 70 +++++++++++++++++-- 4 files changed, 75 insertions(+), 101 deletions(-) rename internal/http/services/opencloudmesh/ocmd/{protocols.go => specs.go} (59%) diff --git a/internal/http/services/opencloudmesh/ocmd/client.go b/internal/http/services/opencloudmesh/ocmd/client.go index 1738957333..fb26a88d11 100644 --- a/internal/http/services/opencloudmesh/ocmd/client.go +++ b/internal/http/services/opencloudmesh/ocmd/client.go @@ -19,7 +19,6 @@ package ocmd import ( - "bytes" "context" "crypto/tls" "encoding/json" @@ -104,37 +103,7 @@ func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*wellknown.O return &disco, nil } -// NewShareRequest contains the parameters for creating a new OCM share. -type NewShareRequest struct { - ShareWith string `json:"shareWith"` - Name string `json:"name"` - Description string `json:"description"` - ProviderID string `json:"providerId"` - Owner string `json:"owner"` - Sender string `json:"sender"` - OwnerDisplayName string `json:"ownerDisplayName"` - SenderDisplayName string `json:"senderDisplayName"` - ShareType string `json:"shareType"` - Expiration uint64 `json:"expiration"` - ResourceType string `json:"resourceType"` - Protocols Protocols `json:"protocol"` -} - -func (r *NewShareRequest) toJSON() (io.Reader, error) { - var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(r); err != nil { - return nil, err - } - return &b, nil -} - -// NewShareResponse is the response returned when creating a new share. -type NewShareResponse struct { - RecipientDisplayName string `json:"recipientDisplayName"` -} - -// NewShare creates a new share. -// https://github.com/cs3org/OCM-API/blob/develop/spec.yaml +// NewShare sends a new OCM share to the remote system. func (c *OCMClient) NewShare(ctx context.Context, endpoint string, r *NewShareRequest) (*NewShareResponse, error) { url, err := url.JoinPath(endpoint, "shares") if err != nil { @@ -182,35 +151,9 @@ func (c *OCMClient) parseNewShareResponse(r *http.Response) (*NewShareResponse, return nil, errtypes.InternalError(string(body)) } -// InviteAcceptedRequest contains the parameters for accepting -// an invitation. -type InviteAcceptedRequest struct { - UserID string `json:"userID"` - Email string `json:"email"` - Name string `json:"name"` - RecipientProvider string `json:"recipientProvider"` - Token string `json:"token"` -} - -// User contains the remote user's information when accepting -// an invitation. -type User struct { - UserID string `json:"userID"` - Email string `json:"email"` - Name string `json:"name"` -} - -func (r *InviteAcceptedRequest) toJSON() (io.Reader, error) { - var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(r); err != nil { - return nil, err - } - return &b, nil -} - -// InviteAccepted informs the sender that the invitation was accepted to start sharing +// InviteAccepted informs the remote end that the invitation was accepted to start sharing // https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1invite-accepted/post -func (c *OCMClient) InviteAccepted(ctx context.Context, endpoint string, r *InviteAcceptedRequest) (*User, error) { +func (c *OCMClient) InviteAccepted(ctx context.Context, endpoint string, r *InviteAcceptedRequest) (*RemoteUser, error) { url, err := url.JoinPath(endpoint, "invite-accepted") if err != nil { return nil, err @@ -236,10 +179,10 @@ func (c *OCMClient) InviteAccepted(ctx context.Context, endpoint string, r *Invi return c.parseInviteAcceptedResponse(resp) } -func (c *OCMClient) parseInviteAcceptedResponse(r *http.Response) (*User, error) { +func (c *OCMClient) parseInviteAcceptedResponse(r *http.Response) (*RemoteUser, error) { switch r.StatusCode { case http.StatusOK: - var u User + var u RemoteUser if err := json.NewDecoder(r.Body).Decode(&u); err != nil { return nil, errors.Wrap(err, "error decoding response body") } diff --git a/internal/http/services/opencloudmesh/ocmd/invites.go b/internal/http/services/opencloudmesh/ocmd/invites.go index 71fe425174..a07015fd24 100644 --- a/internal/http/services/opencloudmesh/ocmd/invites.go +++ b/internal/http/services/opencloudmesh/ocmd/invites.go @@ -49,14 +49,6 @@ func (h *invitesHandler) init(c *config) error { return nil } -type acceptInviteRequest struct { - Token string `json:"token"` - UserID string `json:"userID"` - RecipientProvider string `json:"recipientProvider"` - Name string `json:"name"` - Email string `json:"email"` -} - // AcceptInvite informs avout an accepted invitation so that the users // can initiate the OCM share creation. func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) { @@ -138,7 +130,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) { } } - if err := json.NewEncoder(w).Encode(&user{ + if err := json.NewEncoder(w).Encode(&RemoteUser{ UserID: acceptInviteResponse.UserId.OpaqueId, Email: acceptInviteResponse.Email, Name: acceptInviteResponse.DisplayName, @@ -152,14 +144,8 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) { log.Info().Str("user", fmt.Sprintf("%s@%s", userObj.Id.OpaqueId, userObj.Id.Idp)).Str("token", req.Token).Msg("added to accepted users") } -type user struct { - UserID string `json:"userID"` - Email string `json:"email"` - Name string `json:"name"` -} - -func getAcceptInviteRequest(r *http.Request) (*acceptInviteRequest, error) { - var req acceptInviteRequest +func getAcceptInviteRequest(r *http.Request) (*InviteAcceptedRequest, error) { + var req InviteAcceptedRequest 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 { diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index e10fff396c..43b8849fa9 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -63,21 +63,6 @@ func (h *sharesHandler) init(c *config) error { return nil } -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 - ProviderID string `json:"providerId" 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:"protocol" 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) { @@ -220,8 +205,8 @@ func getIDAndMeshProvider(user string) (string, string, error) { return strings.Join(split[:len(split)-1], "@"), split[len(split)-1], nil } -func getCreateShareRequest(r *http.Request) (*createShareRequest, error) { - var req createShareRequest +func getCreateShareRequest(r *http.Request) (*NewShareRequest, error) { + var req NewShareRequest 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 { diff --git a/internal/http/services/opencloudmesh/ocmd/protocols.go b/internal/http/services/opencloudmesh/ocmd/specs.go similarity index 59% rename from internal/http/services/opencloudmesh/ocmd/protocols.go rename to internal/http/services/opencloudmesh/ocmd/specs.go index 2b7d96de2e..3c784e3581 100644 --- a/internal/http/services/opencloudmesh/ocmd/protocols.go +++ b/internal/http/services/opencloudmesh/ocmd/specs.go @@ -19,9 +19,11 @@ package ocmd import ( + "bytes" "encoding/json" "errors" "fmt" + "io" "reflect" "strings" @@ -31,13 +33,72 @@ import ( utils "github.com/cs3org/reva/pkg/utils" ) -// Protocols is the list of protocols. +// In this file we group the definitions of the OCM payloads according to the official specs +// at https://github.com/cs3org/OCM-API/blob/develop/spec.yaml + +// InviteAcceptedRequest contains the payload of an OCM /invite-accepted request. +// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1invite-accepted/post +type InviteAcceptedRequest struct { + UserID string `json:"userID" validate:"required"` + Email string `json:"email"` + Name string `json:"name"` + RecipientProvider string `json:"recipientProvider"` + Token string `json:"token"` +} + +// RemoteUser contains the remote user's information both when sending an /invite-accepted call and when sending back a response to /invite-accepted +type RemoteUser struct { + UserID string `json:"userID"` + Email string `json:"email"` + Name string `json:"name"` +} + +func (r *InviteAcceptedRequest) toJSON() (io.Reader, error) { + var b bytes.Buffer + if err := json.NewEncoder(&b).Encode(r); err != nil { + return nil, err + } + return &b, nil +} + +// NewShareRequest contains the payload of an OCM /share request. +// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post +type NewShareRequest 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 + ProviderID string `json:"providerId" 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 + Code string `json:"code"` // nonce to be exchanged for a bearer token (not implemented for now) + 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:"protocol" validate:"required"` +} + +func (r *NewShareRequest) toJSON() (io.Reader, error) { + var b bytes.Buffer + if err := json.NewEncoder(&b).Encode(r); err != nil { + return nil, err + } + return &b, nil +} + +// NewShareResponse is the response returned when creating a new share. +type NewShareResponse struct { + RecipientDisplayName string `json:"recipientDisplayName"` +} + +// Protocols is the list of OCM 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 converts the protocol to a OCM `Protocol` struct ToOCMProtocol() *ocm.Protocol } @@ -74,9 +135,8 @@ func (w *WebDAV) ToOCMProtocol() *ocm.Protocol { // Webapp contains the parameters for the Webapp protocol. type Webapp struct { - URI string `json:"uri" validate:"required"` - ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"` - SharedSecret string `json:"sharedSecret"` + URI string `json:"uri" validate:"required"` + ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"` } // ToOCMProtocol convert the protocol to a ocm Protocol struct. From 89a4423baf5c7d75a27e52784a4ca9599a8c43d6 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Thu, 13 Feb 2025 16:26:54 +0100 Subject: [PATCH 04/16] Added ocm 1.2 optional fields (unused for now) --- .../opencloudmesh/ocmd/protocols_test.go | 25 ++++++++++++------- .../http/services/opencloudmesh/ocmd/specs.go | 6 +++-- internal/http/services/wellknown/ocm.go | 4 +-- .../ocm-share/ocm-server-cernbox-grpc.toml | 1 + .../ocm-share/ocm-server-cernbox-http.toml | 4 +++ 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/internal/http/services/opencloudmesh/ocmd/protocols_test.go b/internal/http/services/opencloudmesh/ocmd/protocols_test.go index 5c9cbbf1b6..ee9f9323e9 100644 --- a/internal/http/services/opencloudmesh/ocmd/protocols_test.go +++ b/internal/http/services/opencloudmesh/ocmd/protocols_test.go @@ -59,20 +59,21 @@ func TestUnmarshalProtocol(t *testing.T) { }, }, { - raw: `{"name":"multi","options":{},"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"}}`, + raw: `{"name":"multi","options":{},"webdav":{"sharedSecret":"secret","permissions":["read","write"],"requirements":["req"],"uri":"http://example.org"}}`, expected: []Protocol{ &WebDAV{ SharedSecret: "secret", Permissions: []string{"read", "write"}, + Requirements: []string{"req"}, URI: "http://example.org", }, }, }, { - raw: `{"name":"multi","options":{},"webapp":{"uriTemplate":"http://example.org/{test}"}}`, + raw: `{"name":"multi","options":{},"webapp":{"uri":"http://example.org/test"}}`, expected: []Protocol{ &Webapp{ - URI: "http://example.org/{test}", + URI: "http://example.org/test", }, }, }, @@ -87,7 +88,7 @@ func TestUnmarshalProtocol(t *testing.T) { }, }, { - raw: `{"name":"multi","options":{},"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}}`, + raw: `{"name":"multi","options":{},"webdav":{"sharedSecret":"secret","permissions":["read","write"],"uri":"http://example.org"},"webapp":{"uri":"http://example.org/test"},"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`, expected: []Protocol{ &WebDAV{ SharedSecret: "secret", @@ -95,7 +96,7 @@ func TestUnmarshalProtocol(t *testing.T) { URI: "http://example.org", }, &Webapp{ - URI: "http://example.org/{test}", + URI: "http://example.org/test", }, &Datatx{ SharedSecret: "secret", @@ -155,6 +156,7 @@ func TestMarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read"}, + Requirements: []string{}, URI: "http://example.org", }, }, @@ -164,6 +166,7 @@ func TestMarshalProtocol(t *testing.T) { "webdav": map[string]any{ "sharedSecret": "secret", "permissions": []any{"read"}, + "requirements": []any{}, "uri": "http://example.org", }, }, @@ -179,8 +182,9 @@ func TestMarshalProtocol(t *testing.T) { "name": "multi", "options": map[string]any{}, "webapp": map[string]any{ - "uri": "http://example.org", - "viewMode": "read", + "uri": "http://example.org", + "viewMode": "read", + "sharedSecret": "", }, }, }, @@ -207,6 +211,7 @@ func TestMarshalProtocol(t *testing.T) { &WebDAV{ SharedSecret: "secret", Permissions: []string{"read"}, + Requirements: []string{"req"}, URI: "http://example.org", }, &Webapp{ @@ -225,11 +230,13 @@ func TestMarshalProtocol(t *testing.T) { "webdav": map[string]any{ "sharedSecret": "secret", "permissions": []any{"read"}, + "requirements": []any{"req"}, "uri": "http://example.org", }, "webapp": map[string]any{ - "uri": "http://example.org", - "viewMode": "read", + "uri": "http://example.org", + "viewMode": "read", + "sharedSecret": "", }, "datatx": map[string]any{ "sharedSecret": "secret", diff --git a/internal/http/services/opencloudmesh/ocmd/specs.go b/internal/http/services/opencloudmesh/ocmd/specs.go index 3c784e3581..f3fe6fec00 100644 --- a/internal/http/services/opencloudmesh/ocmd/specs.go +++ b/internal/http/services/opencloudmesh/ocmd/specs.go @@ -108,6 +108,7 @@ type Protocol interface { type WebDAV struct { SharedSecret string `json:"sharedSecret" validate:"required"` Permissions []string `json:"permissions" validate:"required,dive,required,oneof=read write share"` + Requirements []string `json:"requirements"` URI string `json:"uri" validate:"required"` } @@ -135,8 +136,9 @@ func (w *WebDAV) ToOCMProtocol() *ocm.Protocol { // Webapp contains the parameters for the Webapp protocol. type Webapp struct { - URI string `json:"uri" validate:"required"` - ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"` + URI string `json:"uri" validate:"required"` + ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"` + SharedSecret string `json:"sharedSecret"` } // ToOCMProtocol convert the protocol to a ocm Protocol struct. diff --git a/internal/http/services/wellknown/ocm.go b/internal/http/services/wellknown/ocm.go index 668f1c2679..f581fc6e36 100644 --- a/internal/http/services/wellknown/ocm.go +++ b/internal/http/services/wellknown/ocm.go @@ -27,7 +27,7 @@ import ( "github.com/cs3org/reva/pkg/appctx" ) -const OCMAPIVersion = "1.1.0" +const OCMAPIVersion = "1.2.0" type OcmProviderConfig struct { OCMPrefix string `docs:"ocm;The prefix URL where the OCM API is served." mapstructure:"ocm_prefix"` @@ -126,7 +126,7 @@ func (h *wkocmHandler) init(c *OcmProviderConfig) { }} // for now we hardcode the capabilities, as this is currently only advisory d.Capabilities = []string{"invites", "webdav-uri", "protocol-object"} - d.InviteAcceptDialog = "/sciencemesh-app/invitations" + d.InviteAcceptDialog, _ = url.JoinPath(c.Endpoint, "/sciencemesh-app/invitations") h.data = d } diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml index 9e40fe48f6..971339c881 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml @@ -39,6 +39,7 @@ driver = "static" [grpc.services.authregistry.drivers.static.rules] basic = "{{grpc_address}}" +bearer = "{{grpc_address}}" ocmshares = "{{cernboxoutcomingocm_address}}" machine = "{{cernboxmachineauth_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml index 04b6bbe7dd..46e218f9a2 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml @@ -14,6 +14,10 @@ provider_domain = "{{cernboxhttp_address}}" mesh_directory_url = "http://meshdir" smtp_credentials = {} +[http.middlewares.auth] +credential_chain = ["publicshares", "basic", "bearer"] +token_strategy_chain = ["bearer", "header"] + [http.middlewares.cors] [http.middlewares.providerauthorizer] From 0b49ef073dc17b215ed4a48135b04cf4426d0e2f Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Thu, 13 Feb 2025 16:49:20 +0100 Subject: [PATCH 05/16] Enable OCM discovery endpoint for the tests to execute discovery when accepting an OCM share --- .../grpc/fixtures/ocm-server-cernbox-http.toml | 9 +++++++++ .../grpc/fixtures/ocm-server-cesnet-http.toml | 9 +++++++++ .../grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml | 8 ++++++++ .../grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml | 8 ++++++++ 4 files changed, 34 insertions(+) diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml index 775a90f248..0d9e9eeecd 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml @@ -21,3 +21,12 @@ driver = "json" [http.middlewares.providerauthorizer.drivers.json] providers = "fixtures/ocm-providers.demo.json" + +[http.services.wellknown] + +[http.services.wellknown.ocmprovider] +ocm_prefix = "ocm" +provider = "Reva for CERNBox" +endpoint = "http://{{cernboxhttp_address}}" +enable_webapp = true +enable_datatx = true diff --git a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml index 37f3826796..1ebad8107a 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml @@ -21,3 +21,12 @@ driver = "json" [http.middlewares.providerauthorizer.drivers.json] providers = "fixtures/ocm-providers.demo.json" + +[http.services.wellknown] + +[http.services.wellknown.ocmprovider] +ocm_prefix = "ocm" +provider = "Reva for CESNET" +endpoint = "http://{{cesnethttp_address}}" +enable_webapp = true +enable_datatx = true diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml index 46e218f9a2..e1492128f1 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml @@ -35,3 +35,11 @@ driver = "localhome" root = "{{localhome_root}}" [http.services.ocdav] + +[http.services.wellknown] +[http.services.wellknown.ocmprovider] +ocm_prefix = "ocm" +provider = "Reva for CERNBox" +endpoint = "http://{{cernboxhttp_address}}" +enable_webapp = true +enable_datatx = true diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml index dea3c93878..c96e73dbfc 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml @@ -28,3 +28,11 @@ providers = "fixtures/ocm-providers.demo.json" driver = "ocmreceived" [http.services.dataprovider.drivers.ocmreceived] + +[http.services.wellknown] +[http.services.wellknown.ocmprovider] +ocm_prefix = "ocm" +provider = "Reva for CESNET" +endpoint = "http://{{cesnethttp_address}}" +enable_webapp = true +enable_datatx = true From 3e1e65e51eb3f98fc6cb121960c5b2f56d069173 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Fri, 14 Feb 2025 16:27:25 +0100 Subject: [PATCH 06/16] tests: use plain logging, not json --- tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml | 1 - tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml | 1 - tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml | 1 - tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml | 1 - .../grpc/fixtures/ocm-share/cernbox-machine-authprovider.toml | 1 - .../grpc/fixtures/ocm-share/cernbox-webdav-server.toml | 1 - .../fixtures/ocm-share/ocm-cernbox-outcoming-dataserver.toml | 1 - .../grpc/fixtures/ocm-share/ocm-cernbox-outcoming-shares.toml | 3 +-- .../grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml | 1 - .../grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml | 1 - .../grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml | 1 - .../grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml | 1 - .../integration/grpc/fixtures/storageprovider-nextcloud.toml | 2 ++ tests/integration/grpc/fixtures/storageprovider-ocis.toml | 4 +++- tests/integration/grpc/fixtures/storageprovider-owncloud.toml | 2 ++ 15 files changed, 8 insertions(+), 14 deletions(-) diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml index 720c0925bd..3e6f441344 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{grpc_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml index 0d9e9eeecd..c380f61f25 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cernboxgw_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml b/tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml index 77c43a6d2e..729ecc5598 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{grpc_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml index 1ebad8107a..c33ba54fe2 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cesnetgw_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/cernbox-machine-authprovider.toml b/tests/integration/grpc/fixtures/ocm-share/cernbox-machine-authprovider.toml index 03853b9ef9..f27c537c45 100644 --- a/tests/integration/grpc/fixtures/ocm-share/cernbox-machine-authprovider.toml +++ b/tests/integration/grpc/fixtures/ocm-share/cernbox-machine-authprovider.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cernboxgw_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/cernbox-webdav-server.toml b/tests/integration/grpc/fixtures/ocm-share/cernbox-webdav-server.toml index f46772282a..fdc57fa8f0 100644 --- a/tests/integration/grpc/fixtures/ocm-share/cernbox-webdav-server.toml +++ b/tests/integration/grpc/fixtures/ocm-share/cernbox-webdav-server.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cernboxgw_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-dataserver.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-dataserver.toml index a27a86e82e..c868490196 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-dataserver.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-dataserver.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cernboxgw_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-shares.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-shares.toml index 96a3710b04..1545316ae6 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-shares.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-cernbox-outcoming-shares.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cernboxgw_address}}" @@ -19,4 +18,4 @@ machine_secret = "secret" [grpc.services.authprovider] auth_manager = "ocmshares" -[grpc.services.authprovider.auth_managers.ocmshares] \ No newline at end of file +[grpc.services.authprovider.auth_managers.ocmshares] diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml index 971339c881..0b078670d3 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{grpc_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml index e1492128f1..9ea0829b8c 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cernboxgw_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml index 399b310561..2f973ba6c8 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{grpc_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml index c96e73dbfc..3599a83644 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml @@ -1,5 +1,4 @@ [log] -mode = "json" [shared] gatewaysvc = "{{cesnetgw_address}}" diff --git a/tests/integration/grpc/fixtures/storageprovider-nextcloud.toml b/tests/integration/grpc/fixtures/storageprovider-nextcloud.toml index ef85bb5d06..20c5fda65c 100644 --- a/tests/integration/grpc/fixtures/storageprovider-nextcloud.toml +++ b/tests/integration/grpc/fixtures/storageprovider-nextcloud.toml @@ -1,3 +1,5 @@ +[log] + [grpc] address = "{{grpc_address}}" diff --git a/tests/integration/grpc/fixtures/storageprovider-ocis.toml b/tests/integration/grpc/fixtures/storageprovider-ocis.toml index 126a9d9aed..9242b3a8d0 100644 --- a/tests/integration/grpc/fixtures/storageprovider-ocis.toml +++ b/tests/integration/grpc/fixtures/storageprovider-ocis.toml @@ -1,3 +1,5 @@ +[log] + [grpc] address = "{{grpc_address}}" @@ -9,4 +11,4 @@ root = "{{root}}" treetime_accounting = true treesize_accounting = true enable_home = true -userprovidersvc = "localhost:18000" \ No newline at end of file +userprovidersvc = "localhost:18000" diff --git a/tests/integration/grpc/fixtures/storageprovider-owncloud.toml b/tests/integration/grpc/fixtures/storageprovider-owncloud.toml index a65851c005..068fccdc45 100644 --- a/tests/integration/grpc/fixtures/storageprovider-owncloud.toml +++ b/tests/integration/grpc/fixtures/storageprovider-owncloud.toml @@ -1,3 +1,5 @@ +[log] + [grpc] address = "{{grpc_address}}" From 964672672a8e322031cff76dbacdc7476f3e99e9 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Thu, 13 Feb 2025 17:29:12 +0100 Subject: [PATCH 07/16] ocm: fixed discovery --- .../services/opencloudmesh/ocmd/client.go | 49 +++++++++----- .../services/opencloudmesh/ocmd/invites.go | 1 + .../services/opencloudmesh/ocmd/shares.go | 66 +++++++++++++++---- internal/http/services/owncloud/ocdav/dav.go | 1 + 4 files changed, 88 insertions(+), 29 deletions(-) diff --git a/internal/http/services/opencloudmesh/ocmd/client.go b/internal/http/services/opencloudmesh/ocmd/client.go index fb26a88d11..ba1bb3d241 100644 --- a/internal/http/services/opencloudmesh/ocmd/client.go +++ b/internal/http/services/opencloudmesh/ocmd/client.go @@ -70,37 +70,56 @@ func NewClient(timeout time.Duration, insecure bool) *OCMClient { // Discover returns a number of properties used to discover the capabilities offered by a remote cloud storage. // https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*wellknown.OcmDiscoveryData, error) { - url, err := url.JoinPath(endpoint, "/ocm-provider") + log := appctx.GetLogger(ctx) + + remoteurl, _ := url.JoinPath(endpoint, "/.well-known/ocm") + body, err := c.discover(ctx, remoteurl) + if err != nil || len(body) == 0 { + log.Debug().Err(err).Str("sender", remoteurl).Str("response", string(body)).Msg("invalid or empty response, falling back to legacy discovery") + remoteurl, _ := url.JoinPath(endpoint, "/ocm-provider") // legacy discovery endpoint + + body, err = c.discover(ctx, remoteurl) + if err != nil || len(body) == 0 { + log.Warn().Err(err).Str("sender", remoteurl).Str("response", string(body)).Msg("invalid or empty response") + return nil, errtypes.BadRequest("Invalid response on OCM discovery") + } + } + + var disco wellknown.OcmDiscoveryData + err = json.Unmarshal(body, &disco) if err != nil { - return nil, err + log.Warn().Err(err).Str("sender", remoteurl).Str("response", string(body)).Msg("malformed response") + return nil, errtypes.BadRequest("Invalid payload on OCM discovery") } + log.Debug().Str("sender", remoteurl).Any("response", disco).Msg("discovery response") + return &disco, nil +} + +func (c *OCMClient) discover(ctx context.Context, url string) ([]byte, error) { + log := appctx.GetLogger(ctx) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { - return nil, errors.Wrap(err, "error creating request") + return nil, errors.Wrap(err, "error creating OCM discovery request") } req.Header.Set("Content-Type", "application/json") resp, err := c.client.Do(req) if err != nil { - return nil, errors.Wrap(err, "error doing request") + return nil, errors.Wrap(err, "error doing OCM discovery request") } defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err + if resp.StatusCode != http.StatusOK { + log.Warn().Str("sender", url).Any("response", resp).Int("status", resp.StatusCode).Msg("discovery returned") + return nil, errtypes.BadRequest("Remote does not offer a valid OCM discovery endpoint") } - var disco wellknown.OcmDiscoveryData - err = json.Unmarshal(body, &disco) + body, err := io.ReadAll(resp.Body) if err != nil { - log := appctx.GetLogger(ctx) - log.Warn().Str("sender", endpoint).Str("response", string(body)).Msg("malformed response") - return nil, errtypes.InternalError("Invalid payload on OCM discovery") + return nil, errors.Wrap(err, "malformed remote OCM discovery") } - - return &disco, nil + return body, nil } // NewShare sends a new OCM share to the remote system. diff --git a/internal/http/services/opencloudmesh/ocmd/invites.go b/internal/http/services/opencloudmesh/ocmd/invites.go index a07015fd24..adf76304fd 100644 --- a/internal/http/services/opencloudmesh/ocmd/invites.go +++ b/internal/http/services/opencloudmesh/ocmd/invites.go @@ -60,6 +60,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) { reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "missing parameters in request", err) return } + log.Info().Any("req", req).Msg("OCM /invite-accepted request received") if req.Token == "" || req.UserID == "" || req.RecipientProvider == "" { reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipiendProvider must not be null", nil) diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index 43b8849fa9..70af7967f0 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -23,7 +23,7 @@ import ( "fmt" "mime" "net/http" - "path/filepath" + "net/url" "strings" "time" @@ -63,8 +63,7 @@ func (h *sharesHandler) init(c *config) error { return nil } -// CreateShare sends all the informations to the consumer needed to start -// synchronization between the two services. +// CreateShare implements the OCM /shares call. func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { ctx := r.Context() log := appctx.GetLogger(ctx) @@ -73,6 +72,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) return } + log.Info().Any("req", req).Msg("OCM /shares request received") _, meshProvider, err := getIDAndMeshProvider(req.Sender) log.Debug().Msgf("Determined Mesh Provider '%s' from req.Sender '%s'", meshProvider, req.Sender) @@ -161,6 +161,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { } } + log.Info().Any("req", createShareReq).Msg("CreateOCMCoreShare payload") createShareResp, err := h.gatewayClient.CreateOCMCoreShare(ctx, createShareReq) if err != nil { reqres.WriteError(w, r, reqres.APIErrorServerError, "error creating ocm share", err) @@ -210,10 +211,10 @@ func getCreateShareRequest(r *http.Request) (*NewShareRequest, error) { 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 + return nil, errors.Wrap(err, "malformed OCM /shares request") } } else { - return nil, errors.New("OCM /share request payload not recognised") + return nil, errors.New("malformed OCM /shares request payload") } // validate the request if err := validate.Struct(req); err != nil { @@ -249,32 +250,66 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro protos := make([]*ocm.Protocol, 0, len(p)) for _, data := range p { ocmProto := data.ToOCMProtocol() + protocolName := GetProtocolName(data) + var uri string + var isLocalhost bool + + switch protocolName { + case "webdav": + uri = ocmProto.GetWebdavOptions().Uri + isLocalhost = strings.Contains(uri, "localhost") + case "webapp": + uri = ocmProto.GetWebappOptions().UriTemplate + isLocalhost = strings.Contains(uri, "localhost") + } + // Irrespective from the presence of a full `uri` in the payload (deprecated), resolve the remote root - remoteRoot, err := discoverOcmRoot(r, GetProtocolName(data)) + // yet skip this if the remote is localhost (for integration tests) + if isLocalhost { + protos = append(protos, ocmProto) + continue + } + remoteRoot, err := discoverOcmRoot(r, protocolName) if err != nil { return nil, err } - if GetProtocolName(data) == "webdav" { - ocmProto.GetWebdavOptions().Uri = filepath.Join(remoteRoot, ocmProto.GetWebdavOptions().SharedSecret) - } else if GetProtocolName(data) == "webapp" { - // ocmProto.GetWebappOptions().Uri = filepath.Join(remoteRoot, ocmProto.GetWebappOptions().SharedSecret) -> this is OCM 1.2 + uri, _ = url.JoinPath(remoteRoot, uri[strings.LastIndex(uri, "/")+1:]) + + switch protocolName { + case "webdav": + ocmProto.GetWebdavOptions().Uri = uri + case "webapp": + ocmProto.GetWebappOptions().UriTemplate = uri } protos = append(protos, ocmProto) } + return protos, nil } + func discoverOcmRoot(r *http.Request, proto string) (string, error) { // implements the OCM discovery logic to fetch the root at the remote host that sent the share for the given proto, see // https://cs3org.github.io/OCM-API/docs.html?branch=v1.1.0&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get ctx := r.Context() log := appctx.GetLogger(ctx) - log.Debug().Str("sender", r.Host).Msg("received OCM share, attempting to discover sender endpoint") + + // assume the sender host is either given in the usual reverse proxy headers or as RemoteAddr, and that the + // remote end listens on https regardless if the incoming connection got its TLS terminated upstream of us + senderURL := r.Header.Get("X-Real-Ip") + if senderURL == "" { + senderURL = r.Header.Get("X-Forwarded-For") + } + if senderURL == "" { + senderURL = r.RemoteAddr + } + senderURL = "https://" + senderURL[:strings.LastIndex(senderURL, ":")] + log.Debug().Str("sender", senderURL).Msg("received OCM share, attempting to discover sender endpoint") ocmClient := NewClient(time.Duration(10)*time.Second, true) - ocmCaps, err := ocmClient.Discover(ctx, r.Host) + ocmCaps, err := ocmClient.Discover(ctx, senderURL) if err != nil { - log.Warn().Str("sender", r.Host).Err(err).Msg("failed to discover OCM sender") + log.Warn().Str("sender", senderURL).Err(err).Msg("failed to discover OCM sender") return "", err } for _, t := range ocmCaps.ResourceTypes { @@ -282,7 +317,10 @@ func discoverOcmRoot(r *http.Request, proto string) (string, error) { if ok { // assume the first resourceType that exposes a root is OK to use: as a matter of fact, // no implementation exists yet that exposes multiple resource types with different roots. - return filepath.Join(ocmCaps.Endpoint, protoRoot), nil + u, _ := url.Parse(ocmCaps.Endpoint) + u.Path = protoRoot + u.RawQuery = "" + return u.String(), nil } } diff --git a/internal/http/services/owncloud/ocdav/dav.go b/internal/http/services/owncloud/ocdav/dav.go index 86912eeb54..2756312512 100644 --- a/internal/http/services/owncloud/ocdav/dav.go +++ b/internal/http/services/owncloud/ocdav/dav.go @@ -184,6 +184,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler { c, err := pool.GetGatewayServiceClient(pool.Endpoint(s.c.GatewaySvc)) if err != nil { + log.Error().Err(err).Msg("error getting gateway during OCM authentication") w.WriteHeader(http.StatusNotFound) return } From dafb5f321877d9a902cfc4048a8791ec47080e20 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Tue, 18 Feb 2025 08:47:01 +0100 Subject: [PATCH 08/16] Use new ocm 1.2 fields from CS3APIs --- cmd/reva/ocm-share-create.go | 2 +- cmd/reva/ocm-share-update.go | 3 +- go.mod | 2 +- go.sum | 4 +- .../ocmshareprovider/ocmshareprovider.go | 1 + .../services/experimental/sciencemesh/apps.go | 2 +- .../experimental/sciencemesh/share.go | 6 +- .../services/opencloudmesh/ocmd/shares.go | 35 ++--- .../http/services/opencloudmesh/ocmd/specs.go | 6 +- .../handlers/apps/sharing/shares/remote.go | 2 +- internal/http/services/reqres/reqres.go | 4 +- .../share/repository/nextcloud/nextcloud.go | 4 +- .../repository/nextcloud/nextcloud_test.go | 10 +- pkg/ocm/share/repository/sql/conversions.go | 6 +- pkg/ocm/share/repository/sql/sql.go | 2 +- pkg/ocm/share/repository/sql/sql_test.go | 126 +++++++++--------- pkg/ocm/share/utils.go | 14 +- tests/integration/grpc/ocm_share_test.go | 48 ++++++- 18 files changed, 158 insertions(+), 119 deletions(-) diff --git a/cmd/reva/ocm-share-create.go b/cmd/reva/ocm-share-create.go index f2a5078f1b..6e62cce402 100644 --- a/cmd/reva/ocm-share-create.go +++ b/cmd/reva/ocm-share-create.go @@ -197,7 +197,7 @@ func getAccessMethods(webdav, webapp, datatx bool, rol string) ([]*ocm.AccessMet if err != nil { return nil, err } - m = append(m, ocmshare.NewWebDavAccessMethod(perm)) + m = append(m, ocmshare.NewWebDavAccessMethod(perm, []string{})) } if webapp { v, err := getOCMViewMode(rol) diff --git a/cmd/reva/ocm-share-update.go b/cmd/reva/ocm-share-update.go index fdc64a5925..deb20cd529 100644 --- a/cmd/reva/ocm-share-update.go +++ b/cmd/reva/ocm-share-update.go @@ -75,7 +75,8 @@ func ocmShareUpdateCommand() *command { AccessMethods: &ocm.AccessMethod{ Term: &ocm.AccessMethod_WebdavOptions{ WebdavOptions: &ocm.WebDAVAccessMethod{ - Permissions: perm, + Permissions: perm, + Requirements: []string{}, }, }, }, diff --git a/go.mod b/go.mod index 14d60bfb8f..60cc26464a 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/coreos/go-oidc/v3 v3.12.0 github.com/creasty/defaults v1.8.0 github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e - github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1 + github.com/cs3org/go-cs3apis v0.0.0-20250218144737-544dd3919658 github.com/dgraph-io/ristretto v0.2.0 github.com/dolthub/go-mysql-server v0.14.0 github.com/gdexlab/go-render v1.0.1 diff --git a/go.sum b/go.sum index b8bd90c4e2..ef7095d5b4 100644 --- a/go.sum +++ b/go.sum @@ -891,8 +891,8 @@ github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYK github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= 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-20241105092511-3ad35d174fc1 h1:RU6LT6mkD16xZs011+8foU7T3LrPvTTSWeTQ9OgfhkA= -github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ= +github.com/cs3org/go-cs3apis v0.0.0-20250218144737-544dd3919658 h1:CmH7twDuNUrHQXChZMafWjsEp1V47KutJlOAt6FjzGA= +github.com/cs3org/go-cs3apis v0.0.0-20250218144737-544dd3919658/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ= 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= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index 20c6a625c4..60d4f3a0f3 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -191,6 +191,7 @@ func (s *service) getWebdavProtocol(share *ocm.Share, m *ocm.AccessMethod_Webdav return &ocmd.WebDAV{ Permissions: perms, + Requirements: m.WebdavOptions.Requirements, URI: s.webdavURL(share), SharedSecret: share.Token, } diff --git a/internal/http/services/experimental/sciencemesh/apps.go b/internal/http/services/experimental/sciencemesh/apps.go index 461c7ca0a9..2b386cbeeb 100644 --- a/internal/http/services/experimental/sciencemesh/apps.go +++ b/internal/http/services/experimental/sciencemesh/apps.go @@ -121,7 +121,7 @@ func (h *appsHandler) webappTemplate(ctx context.Context, id *ocmpb.ShareId) (st return "", errtypes.BadRequest("share does not contain webapp protocol") } - return webapp.UriTemplate, nil + return webapp.Uri, nil } func getWebappProtocol(protocols []*ocmpb.Protocol) (*ocmpb.WebappProtocol, bool) { diff --git a/internal/http/services/experimental/sciencemesh/share.go b/internal/http/services/experimental/sciencemesh/share.go index fc1113a036..584c9dcfdc 100644 --- a/internal/http/services/experimental/sciencemesh/share.go +++ b/internal/http/services/experimental/sciencemesh/share.go @@ -103,7 +103,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { perm, viewMode := getPermissionsByRole(req.Role) - log.Debug().Msg("calling gatewayClient.CreateOCMShare from sciencemesh/share.go") + log.Debug().Msg("calling gatewayClient.CreateOCMShare") shareRes, err := h.gatewayClient.CreateOCMShare(ctx, &ocm.CreateOCMShareRequest{ ResourceId: statRes.Info.Id, Grantee: &providerpb.Grantee{ @@ -117,11 +117,11 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { }, RecipientMeshProvider: recipientProviderInfo.ProviderInfo, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(perm), + share.NewWebDavAccessMethod(perm, []string{}), share.NewWebappAccessMethod(viewMode), }, }) - log.Debug().Msg("called gatewayClient.CreateOCMShare from sciencemesh/share.go") + log.Debug().Any("response", shareRes).Msg("called gatewayClient.CreateOCMShare") switch { case err != nil: diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index 70af7967f0..34169c2954 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -68,11 +68,11 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { ctx := r.Context() log := appctx.GetLogger(ctx) req, err := getCreateShareRequest(r) + log.Info().Any("req", req).Msg("OCM /shares request received") if err != nil { reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) return } - log.Info().Any("req", req).Msg("OCM /shares request received") _, meshProvider, err := getIDAndMeshProvider(req.Sender) log.Debug().Msgf("Determined Mesh Provider '%s' from req.Sender '%s'", meshProvider, req.Sender) @@ -99,7 +99,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { Provider: &providerInfo, }) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc is provider allowed request", err) + reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc isProviderAllowed request", err) return } if providerAllowedResp.Status.Code != rpc.Code_CODE_OK { @@ -109,7 +109,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { shareWith, _, err := getIDAndMeshProvider(req.ShareWith) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "error with mesh provider", err) return } @@ -127,19 +127,19 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { owner, err := getUserIDFromOCMUser(req.Owner) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "error with remote owner", err) return } sender, err := getUserIDFromOCMUser(req.Sender) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "error with remote sender", err) return } protocols, err := getAndResolveProtocols(req.Protocols, r) if err != nil { - reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil) + reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "error with protocols payload", err) return } @@ -249,23 +249,25 @@ func getOCMShareType(t string) ocm.ShareType { func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, error) { protos := make([]*ocm.Protocol, 0, len(p)) for _, data := range p { + var uri string ocmProto := data.ToOCMProtocol() protocolName := GetProtocolName(data) - var uri string - var isLocalhost bool - switch protocolName { case "webdav": uri = ocmProto.GetWebdavOptions().Uri - isLocalhost = strings.Contains(uri, "localhost") + reqs := ocmProto.GetWebdavOptions().Requirements + if len(reqs) > 0 { + // we currently do not support any kind of requirement + return nil, errtypes.BadRequest(fmt.Sprintf("incoming OCM share with requirements %+v not supported at this endpoint", reqs)) + } case "webapp": - uri = ocmProto.GetWebappOptions().UriTemplate - isLocalhost = strings.Contains(uri, "localhost") + uri = ocmProto.GetWebappOptions().Uri } - // Irrespective from the presence of a full `uri` in the payload (deprecated), resolve the remote root + // Irrespective from the presence of a full `uri` in the payload (deprecated), validate the + // remote is an OCM server and resolve the remote root // yet skip this if the remote is localhost (for integration tests) - if isLocalhost { + if strings.Contains(uri, "localhost") { protos = append(protos, ocmProto) continue } @@ -273,13 +275,13 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro if err != nil { return nil, err } - uri, _ = url.JoinPath(remoteRoot, uri[strings.LastIndex(uri, "/")+1:]) + uri, _ = url.JoinPath(remoteRoot, uri[strings.LastIndex(uri, "/")+1:]) switch protocolName { case "webdav": ocmProto.GetWebdavOptions().Uri = uri case "webapp": - ocmProto.GetWebappOptions().UriTemplate = uri + ocmProto.GetWebappOptions().Uri = uri } protos = append(protos, ocmProto) } @@ -287,7 +289,6 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro return protos, nil } - func discoverOcmRoot(r *http.Request, proto string) (string, error) { // implements the OCM discovery logic to fetch the root at the remote host that sent the share for the given proto, see // https://cs3org.github.io/OCM-API/docs.html?branch=v1.1.0&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get diff --git a/internal/http/services/opencloudmesh/ocmd/specs.go b/internal/http/services/opencloudmesh/ocmd/specs.go index f3fe6fec00..87a5baab45 100644 --- a/internal/http/services/opencloudmesh/ocmd/specs.go +++ b/internal/http/services/opencloudmesh/ocmd/specs.go @@ -98,7 +98,7 @@ type Protocols []Protocol // Protocol represents the way of access the resource // in the OCM share. type Protocol interface { - // ToOCMProtocol converts the protocol to a OCM `Protocol` struct + // ToOCMProtocol converts the protocol to a CS3API OCM `Protocol` struct ToOCMProtocol() *ocm.Protocol } @@ -131,7 +131,7 @@ func (w *WebDAV) ToOCMProtocol() *ocm.Protocol { } } - return ocmshare.NewWebDAVProtocol(w.URI, w.SharedSecret, perms) + return ocmshare.NewWebDAVProtocol(w.URI, w.SharedSecret, perms, w.Requirements) } // Webapp contains the parameters for the Webapp protocol. @@ -226,7 +226,7 @@ func (p Protocols) MarshalJSON() ([]byte, error) { for _, prot := range p { d[GetProtocolName(prot)] = prot } - // fill in the OCM v1.0 properties: for now we only create OCM 1.1 payloads, + // fill in the OCM v1.0 properties: we only create OCM 1.1+ payloads, // irrespective from the capabilities of the remote server. d["name"] = "multi" d["options"] = map[string]any{} 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 711d403a34..3dde33a92c 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 @@ -111,7 +111,7 @@ func (h *Handler) createFederatedCloudShare(w http.ResponseWriter, r *http.Reque }, RecipientMeshProvider: providerInfoResp.ProviderInfo, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(role.CS3ResourcePermissions()), + share.NewWebDavAccessMethod(role.CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(getViewModeFromRole(role)), }, }) diff --git a/internal/http/services/reqres/reqres.go b/internal/http/services/reqres/reqres.go index 404e82543c..d16bc4f625 100644 --- a/internal/http/services/reqres/reqres.go +++ b/internal/http/services/reqres/reqres.go @@ -60,9 +60,7 @@ type APIError struct { // WriteError handles writing error responses. func WriteError(w http.ResponseWriter, r *http.Request, code APIErrorCode, message string, e error) { - if e != nil { - appctx.GetLogger(r.Context()).Error().Err(e).Msg(message) - } + appctx.GetLogger(r.Context()).Error().Err(e).Any("code", code).Str("message", message).Msg("sending back error response") var encoded []byte var err error diff --git a/pkg/ocm/share/repository/nextcloud/nextcloud.go b/pkg/ocm/share/repository/nextcloud/nextcloud.go index 2ee9fdf52b..3ca538462b 100644 --- a/pkg/ocm/share/repository/nextcloud/nextcloud.go +++ b/pkg/ocm/share/repository/nextcloud/nextcloud.go @@ -181,7 +181,7 @@ func (sm *Manager) efssShareToOcm(resp *EfssShare) *ocm.Share { // first generate the map of access methods, assuming WebDAV is always present var am = make([]*ocm.AccessMethod, 0, 3) am = append(am, share.NewWebDavAccessMethod(conversions.RoleFromOCSPermissions( - conversions.Permissions(resp.Protocols.WebDAV.Permissions)).CS3ResourcePermissions())) + conversions.Permissions(resp.Protocols.WebDAV.Permissions)).CS3ResourcePermissions(), []string{})) if resp.Protocols.WebApp.ViewMode != "" { am = append(am, share.NewWebappAccessMethod(utils.GetAppViewMode(resp.Protocols.WebApp.ViewMode))) } @@ -326,7 +326,7 @@ func efssReceivedShareToOcm(resp *ReceivedEfssShare) *ocm.ReceivedShare { var proto = make([]*ocm.Protocol, 0, 3) proto = append(proto, share.NewWebDAVProtocol(resp.Share.Protocols.WebDAV.URI, resp.Share.Token, &ocm.SharePermissions{ Permissions: conversions.RoleFromOCSPermissions(conversions.Permissions(resp.Share.Protocols.WebDAV.Permissions)).CS3ResourcePermissions(), - })) + }, []string{})) if resp.Share.Protocols.WebApp.ViewMode != "" { proto = append(proto, share.NewWebappProtocol(resp.Share.Protocols.WebApp.URI, utils.GetAppViewMode(resp.Share.Protocols.WebApp.ViewMode))) } diff --git a/pkg/ocm/share/repository/nextcloud/nextcloud_test.go b/pkg/ocm/share/repository/nextcloud/nextcloud_test.go index 1273dc944f..3ac7c20f47 100644 --- a/pkg/ocm/share/repository/nextcloud/nextcloud_test.go +++ b/pkg/ocm/share/repository/nextcloud/nextcloud_test.go @@ -283,7 +283,7 @@ var _ = Describe("Nextcloud", func() { OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", }, AccessMethods: []*ocm.AccessMethod{ - ocmshare.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + ocmshare.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), ocmshare.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_WRITE), ocmshare.NewTransferAccessMethod(), }, @@ -422,7 +422,7 @@ var _ = Describe("Nextcloud", func() { }, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - ocmshare.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + ocmshare.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), ocmshare.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_WRITE), ocmshare.NewTransferAccessMethod(), }, @@ -475,7 +475,7 @@ var _ = Describe("Nextcloud", func() { Protocols: []*ocm.Protocol{ ocmshare.NewWebDAVProtocol("webdav-uri", "some-token", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), ocmshare.NewWebappProtocol("app-uri-template", appprovider.ViewMode_VIEW_MODE_READ_WRITE), ocmshare.NewTransferProtocol("source-uri", "some-token", 1), }, @@ -533,7 +533,7 @@ var _ = Describe("Nextcloud", func() { Protocols: []*ocm.Protocol{ ocmshare.NewWebDAVProtocol("webdav-uri", "some-token", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), ocmshare.NewWebappProtocol("app-uri-template", appprovider.ViewMode_VIEW_MODE_READ_WRITE), ocmshare.NewTransferProtocol("source-uri", "some-token", 1), }, @@ -622,7 +622,7 @@ var _ = Describe("Nextcloud", func() { Protocols: []*ocm.Protocol{ ocmshare.NewWebDAVProtocol("webdav-uri", "some-token", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), ocmshare.NewWebappProtocol("app-uri-template", appprovider.ViewMode_VIEW_MODE_READ_WRITE), ocmshare.NewTransferProtocol("source-uri", "some-token", 1), }, diff --git a/pkg/ocm/share/repository/sql/conversions.go b/pkg/ocm/share/repository/sql/conversions.go index 51ae4a3606..c9cd8d4945 100644 --- a/pkg/ocm/share/repository/sql/conversions.go +++ b/pkg/ocm/share/repository/sql/conversions.go @@ -267,7 +267,9 @@ func convertToCS3OCMReceivedShare(s *dbReceivedShare, p []*ocm.Protocol) *ocm.Re func convertToCS3AccessMethod(m *dbAccessMethod) *ocm.AccessMethod { switch m.Type { case WebDAVAccessMethod: - return share.NewWebDavAccessMethod(conversions.RoleFromOCSPermissions(conversions.Permissions(*m.WebDAVPermissions)).CS3ResourcePermissions()) + return share.NewWebDavAccessMethod( + conversions.RoleFromOCSPermissions(conversions.Permissions(*m.WebDAVPermissions)).CS3ResourcePermissions(), + []string{}) // TODO persist requirements case WebappAccessMethod: return share.NewWebappAccessMethod(appprovider.ViewMode(*m.WebAppViewMode)) case TransferAccessMethod: @@ -281,7 +283,7 @@ func convertToCS3Protocol(p *dbProtocol) *ocm.Protocol { case WebDAVProtocol: return share.NewWebDAVProtocol(*p.WebDAVURI, *p.WebDAVSharedSecret, &ocm.SharePermissions{ Permissions: conversions.RoleFromOCSPermissions(conversions.Permissions(*p.WebDavPermissions)).CS3ResourcePermissions(), - }) + }, []string{}) // TODO persist requirements case WebappProtocol: return share.NewWebappProtocol(*p.WebappURI, appprovider.ViewMode(*p.WebappViewMode)) case TransferProtocol: diff --git a/pkg/ocm/share/repository/sql/sql.go b/pkg/ocm/share/repository/sql/sql.go index 81b2d42287..3e4d98300c 100644 --- a/pkg/ocm/share/repository/sql/sql.go +++ b/pkg/ocm/share/repository/sql/sql.go @@ -572,7 +572,7 @@ func storeWebappProtocol(tx *sql.Tx, shareID int64, o *ocm.Protocol_WebappOption } query := "INSERT INTO ocm_protocol_webapp SET ocm_protocol_id=?, uri_template=?, view_mode=?" - params := []any{pID, o.WebappOptions.UriTemplate, o.WebappOptions.ViewMode} + params := []any{pID, o.WebappOptions.Uri, o.WebappOptions.ViewMode} _, err = tx.Exec(query, params...) return err diff --git a/pkg/ocm/share/repository/sql/sql_test.go b/pkg/ocm/share/repository/sql/sql_test.go index 029d89d745..30be2756cc 100644 --- a/pkg/ocm/share/repository/sql/sql_test.go +++ b/pkg/ocm/share/repository/sql/sql_test.go @@ -286,7 +286,7 @@ func createReceivedShareTables(ctx *sql.Context, initData []*ocm.ReceivedShare) must(webdav.Insert(ctx, sql.NewRow(i, prot.WebdavOptions.Uri, prot.WebdavOptions.SharedSecret, int64(conversions.RoleFromResourcePermissions(prot.WebdavOptions.Permissions.Permissions).OCSPermissions())))) case *ocm.Protocol_WebappOptions: must(protocols.Insert(ctx, sql.NewRow(i, mustInt(share.Id.OpaqueId), int8(WebappProtocol)))) - must(webapp.Insert(ctx, sql.NewRow(i, prot.WebappOptions.UriTemplate, int8(prot.WebappOptions.ViewMode)))) + must(webapp.Insert(ctx, sql.NewRow(i, prot.WebappOptions.Uri, int8(prot.WebappOptions.ViewMode)))) case *ocm.Protocol_TransferOptions: must(protocols.Insert(ctx, sql.NewRow(i, mustInt(share.Id.OpaqueId), int8(TransferProtocol)))) must(transfer.Insert(ctx, sql.NewRow(i, prot.TransferOptions.SourceUri, prot.TransferOptions.SharedSecret, int64(prot.TransferOptions.Size)))) @@ -342,7 +342,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, query: &ocm.ShareReference{Spec: &ocm.ShareReference_Id{Id: &ocm.ShareId{OpaqueId: "1"}}}, @@ -359,7 +359,7 @@ func TestGetShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, { @@ -376,7 +376,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, query: &ocm.ShareReference{ @@ -396,7 +396,7 @@ func TestGetShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, { @@ -413,7 +413,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, query: &ocm.ShareReference{ @@ -437,7 +437,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, query: &ocm.ShareReference{ @@ -462,7 +462,7 @@ func TestGetShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, { @@ -479,7 +479,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, query: &ocm.ShareReference{ @@ -508,7 +508,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, - AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, + AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{})}, }, }, query: &ocm.ShareReference{ @@ -538,7 +538,7 @@ func TestGetShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -559,7 +559,7 @@ func TestGetShare(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -580,7 +580,7 @@ func TestGetShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -601,7 +601,7 @@ func TestGetShare(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -671,7 +671,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -693,7 +693,7 @@ func TestListShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -715,7 +715,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -732,7 +732,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -752,7 +752,7 @@ func TestListShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -770,7 +770,7 @@ func TestListShares(t *testing.T) { Expiration: &typesv1beta1.Timestamp{}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -790,7 +790,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -807,7 +807,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -827,7 +827,7 @@ func TestListShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -847,7 +847,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -864,7 +864,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -891,7 +891,7 @@ func TestListShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -911,7 +911,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -928,7 +928,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -958,7 +958,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -975,7 +975,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, { @@ -990,7 +990,7 @@ func TestListShares(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -1023,7 +1023,7 @@ func TestListShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), share.NewTransferAccessMethod(), }, @@ -1041,7 +1041,7 @@ func TestListShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -1140,7 +1140,7 @@ func TestStoreShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1169,7 +1169,7 @@ func TestStoreShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -1184,7 +1184,7 @@ func TestStoreShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, expected: storeShareExpected{ @@ -1218,7 +1218,7 @@ func TestStoreShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, }, @@ -1233,7 +1233,7 @@ func TestStoreShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, }, err: share.ErrShareAlreadyExisting, @@ -1297,7 +1297,7 @@ func TestUpdateShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1686061921}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1330,7 +1330,7 @@ func TestUpdateShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1686061921}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1340,7 +1340,7 @@ func TestUpdateShare(t *testing.T) { fields: []*ocm.UpdateOCMShareRequest_UpdateField{ { Field: &ocm.UpdateOCMShareRequest_UpdateField_AccessMethods{ - AccessMethods: share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + AccessMethods: share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, }, { @@ -1374,7 +1374,7 @@ func TestUpdateShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1686061921}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1411,7 +1411,7 @@ func TestUpdateShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1686061921}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1425,7 +1425,7 @@ func TestUpdateShare(t *testing.T) { fields: []*ocm.UpdateOCMShareRequest_UpdateField{ { Field: &ocm.UpdateOCMShareRequest_UpdateField_AccessMethods{ - AccessMethods: share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + AccessMethods: share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, }, { @@ -1459,7 +1459,7 @@ func TestUpdateShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1686061921}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1484,7 +1484,7 @@ func TestUpdateShare(t *testing.T) { Mtime: &typesv1beta1.Timestamp{Seconds: 1686061921}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, @@ -1498,7 +1498,7 @@ func TestUpdateShare(t *testing.T) { fields: []*ocm.UpdateOCMShareRequest_UpdateField{ { Field: &ocm.UpdateOCMShareRequest_UpdateField_AccessMethods{ - AccessMethods: share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + AccessMethods: share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, }, { @@ -1579,7 +1579,7 @@ func TestGetReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, }, @@ -1601,7 +1601,7 @@ func TestGetReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, }, @@ -1623,7 +1623,7 @@ func TestGetReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, }, @@ -1649,7 +1649,7 @@ func TestGetReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -1673,7 +1673,7 @@ func TestGetReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -1743,7 +1743,7 @@ func TestUpdateReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, }, @@ -1779,7 +1779,7 @@ func TestUpdateReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, }, @@ -1855,7 +1855,7 @@ func TestListReceviedShares(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -1879,7 +1879,7 @@ func TestListReceviedShares(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -1904,7 +1904,7 @@ func TestListReceviedShares(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -1944,7 +1944,7 @@ func TestListReceviedShares(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -1986,7 +1986,7 @@ func TestListReceviedShares(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -2026,7 +2026,7 @@ func TestListReceviedShares(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewWebappProtocol("https://cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), share.NewTransferProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", 10), }, @@ -2106,7 +2106,7 @@ func TestStoreReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, expected: storeReceivedShareExpected{ @@ -2135,7 +2135,7 @@ func TestStoreReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), }, }, }, @@ -2153,7 +2153,7 @@ func TestStoreReceivedShare(t *testing.T) { Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), - }), + }, []string{}), share.NewTransferProtocol("https://transfer.cernbox.cern.ch/ocm/1234", "secret", 100), share.NewWebappProtocol("https://app.cernbox.cern.ch/ocm/1234", appprovider.ViewMode_VIEW_MODE_READ_WRITE), }, diff --git a/pkg/ocm/share/utils.go b/pkg/ocm/share/utils.go index 90e789ab9d..4d26ecf59b 100644 --- a/pkg/ocm/share/utils.go +++ b/pkg/ocm/share/utils.go @@ -25,25 +25,26 @@ import ( ) // NewWebDAVProtocol is an abstraction for creating a WebDAV protocol. -func NewWebDAVProtocol(uri, sharedSecret string, perms *ocm.SharePermissions) *ocm.Protocol { +func NewWebDAVProtocol(uri, sharedSecret string, perms *ocm.SharePermissions, reqs []string) *ocm.Protocol { return &ocm.Protocol{ Term: &ocm.Protocol_WebdavOptions{ WebdavOptions: &ocm.WebDAVProtocol{ Uri: uri, SharedSecret: sharedSecret, Permissions: perms, + Requirements: reqs, }, }, } } // NewWebappProtocol is an abstraction for creating a Webapp protocol. -func NewWebappProtocol(uriTemplate string, viewMode appprovider.ViewMode) *ocm.Protocol { +func NewWebappProtocol(uri string, viewMode appprovider.ViewMode) *ocm.Protocol { return &ocm.Protocol{ Term: &ocm.Protocol_WebappOptions{ WebappOptions: &ocm.WebappProtocol{ - UriTemplate: uriTemplate, - ViewMode: viewMode, + Uri: uri, + ViewMode: viewMode, }, }, } @@ -63,11 +64,12 @@ func NewTransferProtocol(sourceURI, sharedSecret string, size uint64) *ocm.Proto } // NewWebDavAccessMethod is an abstraction for creating a WebDAV access method. -func NewWebDavAccessMethod(perms *provider.ResourcePermissions) *ocm.AccessMethod { +func NewWebDavAccessMethod(perms *provider.ResourcePermissions, reqs []string) *ocm.AccessMethod { return &ocm.AccessMethod{ Term: &ocm.AccessMethod_WebdavOptions{ WebdavOptions: &ocm.WebDAVAccessMethod{ - Permissions: perms, + Permissions: perms, + Requirements: reqs, }, }, } diff --git a/tests/integration/grpc/ocm_share_test.go b/tests/integration/grpc/ocm_share_test.go index a560528179..b572b1b017 100644 --- a/tests/integration/grpc/ocm_share_test.go +++ b/tests/integration/grpc/ocm_share_test.go @@ -197,7 +197,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -278,7 +278,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -374,7 +374,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -477,7 +477,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -626,7 +626,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -643,7 +643,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -668,7 +668,7 @@ var _ = Describe("ocm share", func() { }, }, AccessMethods: []*ocmv1beta1.AccessMethod{ - share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{}), }, RecipientMeshProvider: cesnet.ProviderInfo, }) @@ -677,6 +677,40 @@ var _ = Describe("ocm share", func() { }) }) + Context("einstein creates a share with a requirement that cannot be met", func() { + It("fail with bad request error", func() { + fileToShare := &provider.Reference{ + Path: "/home/file-with-req", + } + By("creating a file") + Expect(helpers.CreateFile(ctxEinstein, cernboxgw, fileToShare.Path, []byte("test"))).To(Succeed()) + + By("share the file with marie") + info, err := stat(ctxEinstein, cernboxgw, fileToShare) + Expect(err).ToNot(HaveOccurred()) + + cesnet, err := cernboxgw.GetInfoByDomain(ctxEinstein, &ocmproviderpb.GetInfoByDomainRequest{ + Domain: "cesnet.cz", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(cesnet.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK)) + + createShareRes, err := cernboxgw.CreateOCMShare(ctxEinstein, &ocmv1beta1.CreateOCMShareRequest{ + ResourceId: info.Id, + Grantee: &provider.Grantee{ + Id: &provider.Grantee_UserId{ + UserId: marie.Id, + }, + }, + AccessMethods: []*ocmv1beta1.AccessMethod{ + share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions(), []string{"unsupported-requirement"}), + }, + RecipientMeshProvider: cesnet.ProviderInfo, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(createShareRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_INVALID_ARGUMENT)) + }) + }) }) }) From b2a1079a79ab4ba6442cade444539702121f83c4 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Thu, 20 Feb 2025 16:58:03 +0100 Subject: [PATCH 09/16] Implemented caching of the webdav clients for OCM, with fallback to v1.0 access --- pkg/ocm/storage/received/ocm.go | 64 ++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/pkg/ocm/storage/received/ocm.go b/pkg/ocm/storage/received/ocm.go index 75a0f38ff6..5710df194a 100644 --- a/pkg/ocm/storage/received/ocm.go +++ b/pkg/ocm/storage/received/ocm.go @@ -26,6 +26,8 @@ import ( "net/url" "path/filepath" "strings" + "sync" + "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" @@ -49,9 +51,17 @@ func init() { registry.Register("ocmreceived", New) } +type cachedClient struct { + client *gowebdav.Client + share *ocmpb.ReceivedShare + expiresAt time.Time +} + type driver struct { c *config gateway gateway.GatewayAPIClient + ccache map[string]*cachedClient + mu sync.Mutex } type config struct { @@ -78,8 +88,9 @@ func New(ctx context.Context, m map[string]interface{}) (storage.FS, error) { d := &driver{ c: &c, gateway: gateway, + ccache: make(map[string]*cachedClient), // this is a cache of webdav clients } - + go d.ccacheCleanupThread() return d, nil } @@ -106,7 +117,6 @@ func shareInfoFromReference(ref *provider.Reference) (*ocmpb.ShareId, string) { } func (d *driver) getWebDAVFromShare(ctx context.Context, shareID *ocmpb.ShareId) (*ocmpb.ReceivedShare, string, string, error) { - // TODO: we may want to cache the share res, err := d.gateway.GetReceivedOCMShare(ctx, &ocmpb.GetReceivedOCMShareRequest{ Ref: &ocmpb.ShareReference{ Spec: &ocmpb.ShareReference_Id{ @@ -143,13 +153,22 @@ func getWebDAVProtocol(protocols []*ocmpb.Protocol) (*ocmpb.WebDAVProtocol, bool } func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*gowebdav.Client, *ocmpb.ReceivedShare, string, error) { + log := appctx.GetLogger(ctx) id, rel := shareInfoFromReference(ref) + // check first if we have a cached webdav client + d.mu.Lock() + defer d.mu.Unlock() + if entry, found := d.ccache[id.OpaqueId]; found { + log.Info().Interface("share", entry.share).Str("rel", rel).Msg("Using cached client to access OCM share") + return entry.client, entry.share, rel, nil + } + + // we don't, build a webdav client share, endpoint, secret, err := d.getWebDAVFromShare(ctx, id) if err != nil { return nil, nil, "", err } - endpoint, err = url.PathUnescape(endpoint) if err != nil { return nil, nil, "", err @@ -158,9 +177,21 @@ func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*go // use the secret as bearer authentication according to OCM v1.1+ c := gowebdav.NewClient(endpoint, "", "") c.SetHeader("Authorization", "Bearer "+secret) + _, err = c.Stat(rel) + if err != nil { + // if we got an error, try to use OCM v1.0 basic auth + log.Info().Str("endpoint", endpoint).Interface("share", share).Str("rel", rel).Str("secret", secret).Err(err).Msg("falling back to OCM v1.0 access") + c.SetHeader("Authorization", "Basic "+secret+":") + } else { + log.Info().Str("endpoint", endpoint).Interface("share", share).Str("rel", rel).Str("secret", secret).Msg("using OCM v1.1 access") + } - log := appctx.GetLogger(ctx) - log.Info().Str("endpoint", endpoint).Interface("share", share).Str("rel", rel).Str("secret", secret).Msg("Accessing OCM share") + // add to cache and return + d.ccache[id.OpaqueId] = &cachedClient{ + client: c, + expiresAt: time.Now().Add(1 * time.Hour), + share: share, + } return c, share, rel, nil } @@ -414,3 +445,26 @@ func (d *driver) CreateStorageSpace(ctx context.Context, req *provider.CreateSto func (d *driver) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { return nil, errtypes.NotSupported("operation not supported") } + +// Cleanup function to remove expired cache entries +func (d *driver) cleanupCache() { + d.mu.Lock() + defer d.mu.Unlock() + + now := time.Now() + for key, entry := range d.ccache { + if now.After(entry.expiresAt) { + delete(d.ccache, key) + } + } +} + +// Periodic cache cleanup goroutine +func (d *driver) ccacheCleanupThread() { + ticker := time.NewTicker(1 * time.Hour) + defer ticker.Stop() + + for range ticker.C { + d.cleanupCache() + } +} From bf0a8d446c05f24751e8cb49e31660bf34c1fb26 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Fri, 14 Feb 2025 22:20:04 +0100 Subject: [PATCH 10/16] changelog --- changelog/unreleased/ocm1.2.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 changelog/unreleased/ocm1.2.md diff --git a/changelog/unreleased/ocm1.2.md b/changelog/unreleased/ocm1.2.md new file mode 100644 index 0000000000..64452b4176 --- /dev/null +++ b/changelog/unreleased/ocm1.2.md @@ -0,0 +1,11 @@ +Enhancement: implement OCM 1.2 + +This PR brings in the implementation of parts of OpenCloudMesh 1.2, including: +* Adopting the new properties of the OCM 1.2 payloads, without implementing any new functionality for now. In particular, any non-empty `requirement` in a share will be rejected (a test was added for that). +* Extending the OCM discovery endpoint. +* Using the remote OCM discovery endpoint to establish the full URL of an incoming remote share, regardless if provided or not. When sending a share, though, we still send a full URL. +* Caching the webdav client used to connect to remote endpoints, with added compatibility to OCM 1.0 remote servers. +* Some refactoring and consolidation of duplicated code. +* Improved logging. + +https://github.com/cs3org/reva/pull/5076 From 719af7c9b7a51fcd4025cc73bdb4c34089a66ef1 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Mon, 3 Mar 2025 10:56:48 +0100 Subject: [PATCH 11/16] Use ttlcache as opposed to custom goroutine --- pkg/ocm/storage/received/ocm.go | 54 ++++++++------------------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/pkg/ocm/storage/received/ocm.go b/pkg/ocm/storage/received/ocm.go index 5710df194a..f036595016 100644 --- a/pkg/ocm/storage/received/ocm.go +++ b/pkg/ocm/storage/received/ocm.go @@ -26,7 +26,6 @@ import ( "net/url" "path/filepath" "strings" - "sync" "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -45,6 +44,7 @@ import ( "github.com/cs3org/reva/pkg/storage/fs/registry" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/studio-b12/gowebdav" + "github.com/ReneKroon/ttlcache/v2" ) func init() { @@ -52,16 +52,14 @@ func init() { } type cachedClient struct { - client *gowebdav.Client - share *ocmpb.ReceivedShare - expiresAt time.Time + client *gowebdav.Client + share *ocmpb.ReceivedShare } type driver struct { c *config gateway gateway.GatewayAPIClient - ccache map[string]*cachedClient - mu sync.Mutex + ccache *ttlcache.Cache } type config struct { @@ -88,9 +86,8 @@ func New(ctx context.Context, m map[string]interface{}) (storage.FS, error) { d := &driver{ c: &c, gateway: gateway, - ccache: make(map[string]*cachedClient), // this is a cache of webdav clients + ccache: ttlcache.NewCache(), // this is a cache of webdav clients } - go d.ccacheCleanupThread() return d, nil } @@ -157,11 +154,10 @@ func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*go id, rel := shareInfoFromReference(ref) // check first if we have a cached webdav client - d.mu.Lock() - defer d.mu.Unlock() - if entry, found := d.ccache[id.OpaqueId]; found { - log.Info().Interface("share", entry.share).Str("rel", rel).Msg("Using cached client to access OCM share") - return entry.client, entry.share, rel, nil + if entry, err := d.ccache.Get(id.OpaqueId); err == nil { + cc := entry.(*cachedClient) + log.Info().Interface("share", cc.share).Str("rel", rel).Msg("accessing OCM share via cached client") + return cc.client, cc.share, rel, nil } // we don't, build a webdav client @@ -187,11 +183,10 @@ func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*go } // add to cache and return - d.ccache[id.OpaqueId] = &cachedClient{ - client: c, - expiresAt: time.Now().Add(1 * time.Hour), - share: share, - } + d.ccache.SetWithTTL(id.OpaqueId, &cachedClient{ + client: c, + share: share, + }, time.Hour) return c, share, rel, nil } @@ -445,26 +440,3 @@ func (d *driver) CreateStorageSpace(ctx context.Context, req *provider.CreateSto func (d *driver) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { return nil, errtypes.NotSupported("operation not supported") } - -// Cleanup function to remove expired cache entries -func (d *driver) cleanupCache() { - d.mu.Lock() - defer d.mu.Unlock() - - now := time.Now() - for key, entry := range d.ccache { - if now.After(entry.expiresAt) { - delete(d.ccache, key) - } - } -} - -// Periodic cache cleanup goroutine -func (d *driver) ccacheCleanupThread() { - ticker := time.NewTicker(1 * time.Hour) - defer ticker.Stop() - - for range ticker.C { - d.cleanupCache() - } -} From b0349036a41049603e21a8dcb4f847e0c03f78b7 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Mon, 3 Mar 2025 10:57:26 +0100 Subject: [PATCH 12/16] Partially reverted logic to access a remote share, see PR comments --- .../http/services/opencloudmesh/ocmd/shares.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index 34169c2954..caddaf956c 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -264,19 +264,27 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro uri = ocmProto.GetWebappOptions().Uri } - // Irrespective from the presence of a full `uri` in the payload (deprecated), validate the - // remote is an OCM server and resolve the remote root - // yet skip this if the remote is localhost (for integration tests) - if strings.Contains(uri, "localhost") { + // If the `uri` contains a hostname, use it as is + u, _ := url.Parse(uri) + if u.Host != "" { protos = append(protos, ocmProto) continue } + // otherwise resolve the hostname using the OCM discovery endpoint remoteRoot, err := discoverOcmRoot(r, protocolName) if err != nil { return nil, err } + if strings.HasPrefix(uri, "/") { + // only take the host from remoteRoot and append the absolute uri + u, _ := url.Parse(remoteRoot) + u.Path = uri + uri = u.String() + } else { + // relative uri + uri, _ = url.JoinPath(remoteRoot, uri) + } - uri, _ = url.JoinPath(remoteRoot, uri[strings.LastIndex(uri, "/")+1:]) switch protocolName { case "webdav": ocmProto.GetWebdavOptions().Uri = uri From 1ebfbef457e228ef902d8ab70d71a1384e412662 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Wed, 5 Mar 2025 18:02:52 +0100 Subject: [PATCH 13/16] Proper implementation using the owner's server address --- .../services/opencloudmesh/ocmd/shares.go | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index caddaf956c..96d78146c0 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -19,6 +19,7 @@ package ocmd import ( + "context" "encoding/json" "fmt" "mime" @@ -137,7 +138,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) { return } - protocols, err := getAndResolveProtocols(req.Protocols, r) + protocols, err := getAndResolveProtocols(ctx, req.Protocols, owner.Idp) if err != nil { reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "error with protocols payload", err) return @@ -246,7 +247,7 @@ func getOCMShareType(t string) ocm.ShareType { } } -func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, error) { +func getAndResolveProtocols(ctx context.Context, p Protocols, ownerServer string) ([]*ocm.Protocol, error) { protos := make([]*ocm.Protocol, 0, len(p)) for _, data := range p { var uri string @@ -270,8 +271,8 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro protos = append(protos, ocmProto) continue } - // otherwise resolve the hostname using the OCM discovery endpoint - remoteRoot, err := discoverOcmRoot(r, protocolName) + // otherwise use as endpoint the owner's server from the payload + remoteRoot, err := discoverOcmRoot(ctx, ownerServer, protocolName) if err != nil { return nil, err } @@ -297,28 +298,15 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro return protos, nil } -func discoverOcmRoot(r *http.Request, proto string) (string, error) { +func discoverOcmRoot(ctx context.Context, ownerServer string, proto string) (string, error) { // implements the OCM discovery logic to fetch the root at the remote host that sent the share for the given proto, see // https://cs3org.github.io/OCM-API/docs.html?branch=v1.1.0&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get - ctx := r.Context() log := appctx.GetLogger(ctx) - // assume the sender host is either given in the usual reverse proxy headers or as RemoteAddr, and that the - // remote end listens on https regardless if the incoming connection got its TLS terminated upstream of us - senderURL := r.Header.Get("X-Real-Ip") - if senderURL == "" { - senderURL = r.Header.Get("X-Forwarded-For") - } - if senderURL == "" { - senderURL = r.RemoteAddr - } - senderURL = "https://" + senderURL[:strings.LastIndex(senderURL, ":")] - log.Debug().Str("sender", senderURL).Msg("received OCM share, attempting to discover sender endpoint") - ocmClient := NewClient(time.Duration(10)*time.Second, true) - ocmCaps, err := ocmClient.Discover(ctx, senderURL) + ocmCaps, err := ocmClient.Discover(ctx, "https://"+ownerServer) if err != nil { - log.Warn().Str("sender", senderURL).Err(err).Msg("failed to discover OCM sender") + log.Warn().Str("sender", ownerServer).Err(err).Msg("failed to discover OCM sender") return "", err } for _, t := range ocmCaps.ResourceTypes { @@ -329,10 +317,11 @@ func discoverOcmRoot(r *http.Request, proto string) (string, error) { u, _ := url.Parse(ocmCaps.Endpoint) u.Path = protoRoot u.RawQuery = "" + log.Debug().Str("sender", ownerServer).Str("proto", proto).Str("URL", u.String()).Msg("resolved protocol URL") return u.String(), nil } } - log.Warn().Str("sender", r.Host).Interface("response", ocmCaps).Msg("missing root") + log.Warn().Str("sender", ownerServer).Interface("response", ocmCaps).Msg("missing protocol root") return "", errtypes.NotFound(fmt.Sprintf("root not found on OCM discovery for protocol %s", proto)) } From d77d5d0ca4ba1027b8c9e25d4f74ea55f9c8df6d Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Tue, 11 Mar 2025 17:11:01 +0100 Subject: [PATCH 14/16] localfs: added logging --- .../http/services/owncloud/ocdav/propfind.go | 2 +- pkg/storage/utils/localfs/localfs.go | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/propfind.go b/internal/http/services/owncloud/ocdav/propfind.go index 0ef6544c00..2f987f4d11 100644 --- a/internal/http/services/owncloud/ocdav/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind.go @@ -263,7 +263,7 @@ func (s *svc) getResourceInfos(ctx context.Context, w http.ResponseWriter, r *ht res, err := client.Stat(ctx, req) if err != nil { - log.Error().Err(err).Interface("req", req).Msg("error sending a grpc stat request") + log.Error().Err(err).Interface("req", req).Msg("error sending a stat request to the gateway") w.WriteHeader(http.StatusInternalServerError) return nil, nil, false } else if res.Status.Code != rpc.Code_CODE_OK { diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index bfb1854aa6..05cdfd07c9 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -912,6 +912,7 @@ func (fs *localfs) moveReferences(ctx context.Context, oldName, newName string) } func (fs *localfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error) { + log := appctx.GetLogger(ctx) fn, err := fs.resolve(ctx, ref) if err != nil { return nil, errors.Wrap(err, "localfs: error resolving ref") @@ -926,6 +927,7 @@ func (fs *localfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys [] fn = fs.wrap(ctx, fn) md, err := os.Stat(fn) if err != nil { + log.Warn().Str("path", fn).Any("md", md).Err(err).Msg("failed stat call in localfs") if os.IsNotExist(err) { return nil, errtypes.NotFound(fn) } @@ -952,6 +954,7 @@ func (fs *localfs) getMDShareFolder(ctx context.Context, p string, mdKeys []stri } func (fs *localfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) ([]*provider.ResourceInfo, error) { + log := appctx.GetLogger(ctx) fn, err := fs.resolve(ctx, ref) if err != nil { return nil, errors.Wrap(err, "localfs: error resolving ref") @@ -960,6 +963,7 @@ func (fs *localfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKe if fn == "/" { homeFiles, err := fs.listFolder(ctx, fn, mdKeys) if err != nil { + log.Warn().Err(err).Msg("failed to execute listFolder for root") return nil, err } if !fs.conf.DisableHome { @@ -973,14 +977,22 @@ func (fs *localfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKe } if fs.isShareFolderRoot(ctx, fn) { - return fs.listShareFolderRoot(ctx, fn, mdKeys) + res, err := fs.listShareFolderRoot(ctx, fn, mdKeys) + if err != nil { + log.Warn().Str("fn", fn).Err(err).Msg("failed to execute listShareFolderRoot") + } + return res, err } if fs.isShareFolderChild(ctx, fn) { return nil, errtypes.PermissionDenied("localfs: error listing folders inside the shared folder, only file references are stored inside") } - return fs.listFolder(ctx, fn, mdKeys) + res, err := fs.listFolder(ctx, fn, mdKeys) + if err != nil { + log.Warn().Str("fn", fn).Err(err).Msg("failed to execute listFolder") + } + return res, err } func (fs *localfs) listFolder(ctx context.Context, fn string, mdKeys []string) ([]*provider.ResourceInfo, error) { From b3431e815a5ef6d6fce901d6831e030cffaed04b Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Fri, 7 Mar 2025 18:20:26 +0100 Subject: [PATCH 15/16] Fixed example to run standalone OCM tests --- examples/cernbox/cernbox.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/cernbox/cernbox.toml b/examples/cernbox/cernbox.toml index 819cabb688..2202518c81 100644 --- a/examples/cernbox/cernbox.toml +++ b/examples/cernbox/cernbox.toml @@ -146,6 +146,8 @@ enable_home_creation = true [grpc.services.storageprovider.drivers.localhome] user_layout = "{{.Username}}" +root = "/revalocalstorage" +share_folder = "/revashares" [[grpc.services.storageprovider]] driver = "ocmoutcoming" @@ -257,6 +259,7 @@ driver = "localhome" [http.services.dataprovider.drivers.localhome] user_layout = "{{.Username}}" +root = "/revalocalstorage" [[http.services.dataprovider]] address = ":443" @@ -406,3 +409,7 @@ debug = true exposed_headers = [] [http.middlewares.log] +level = "debug" + +[http.interceptors.log] +level = "debug" From 6782acfdc77242894a961aaf136a6d05ba6231f4 Mon Sep 17 00:00:00 2001 From: Jesse Geens Date: Wed, 12 Mar 2025 16:24:09 +0100 Subject: [PATCH 16/16] logs for download / upload / move in localfs --- pkg/storage/utils/localfs/localfs.go | 10 ++++++++++ pkg/storage/utils/localfs/upload.go | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 05cdfd07c9..25c28ed55a 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -162,6 +162,7 @@ func getUser(ctx context.Context) (*userpb.User, error) { } func (fs *localfs) wrap(ctx context.Context, p string) string { + log := appctx.GetLogger(ctx) // This is to prevent path traversal. // With this p can't break out of its parent folder p = path.Join("/", p) @@ -175,6 +176,7 @@ func (fs *localfs) wrap(ctx context.Context, p string) string { } else { internal = path.Join(fs.conf.DataDirectory, p) } + log.Debug().Str("old", p).Str("wrapped", internal).Msg("localfs: wrap") return internal } @@ -842,6 +844,8 @@ func (fs *localfs) Delete(ctx context.Context, ref *provider.Reference) error { } func (fs *localfs) Move(ctx context.Context, oldRef, newRef *provider.Reference) error { + log := appctx.GetLogger(ctx) + log.Debug().Any("from", oldRef).Any("to", newRef).Msg("localfs: move") oldName, err := fs.resolve(ctx, oldRef) if err != nil { return errors.Wrap(err, "localfs: error resolving ref") @@ -860,6 +864,7 @@ func (fs *localfs) Move(ctx context.Context, oldRef, newRef *provider.Reference) newName = fs.wrap(ctx, newName) if err := os.Rename(oldName, newName); err != nil { + log.Error().Err(err).Msg("localfs: error moving " + oldName + " to " + newName) return errors.Wrap(err, "localfs: error moving "+oldName+" to "+newName) } @@ -1062,7 +1067,10 @@ func (fs *localfs) listShareFolderRoot(ctx context.Context, home string, mdKeys func (fs *localfs) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { fn, err := fs.resolve(ctx, ref) + log := appctx.GetLogger(ctx) + if err != nil { + log.Error().Err(err).Any("ref", ref).Msg("localfs: error resolving ref") return nil, errors.Wrap(err, "localfs: error resolving ref") } @@ -1074,8 +1082,10 @@ func (fs *localfs) Download(ctx context.Context, ref *provider.Reference) (io.Re r, err := os.Open(fn) if err != nil { if os.IsNotExist(err) { + log.Error().Err(err).Str("path", fn).Msg("localfs: file not found") return nil, errtypes.NotFound(fn) } + log.Error().Err(err).Str("path", fn).Msg("localfs: error opening file") return nil, errors.Wrap(err, "localfs: error reading "+fn) } return r, nil diff --git a/pkg/storage/utils/localfs/upload.go b/pkg/storage/utils/localfs/upload.go index 604cd26b4f..286747c24e 100644 --- a/pkg/storage/utils/localfs/upload.go +++ b/pkg/storage/utils/localfs/upload.go @@ -40,7 +40,9 @@ import ( var defaultFilePerm = os.FileMode(0664) func (fs *localfs) Upload(ctx context.Context, ref *provider.Reference, r io.ReadCloser, metadata map[string]string) error { + log := appctx.GetLogger(ctx) upload, err := fs.GetUpload(ctx, ref.GetPath()) + log.Info().Any("upload", upload).Msg("localfs: upload") if err != nil { return errors.Wrap(err, "localfs: error retrieving upload") } @@ -156,6 +158,7 @@ func (fs *localfs) NewUpload(ctx context.Context, info tusd.FileInfo) (upload tu info.ID = uuid.New().String() binPath, err := fs.getUploadPath(ctx, info.ID) + log.Info().Str("upload-path", binPath).Err(err).Msg("localfs: got upload path") if err != nil { return nil, errors.Wrap(err, "localfs: error resolving upload path") } @@ -307,6 +310,7 @@ func (upload *fileUpload) writeInfo() error { // FinishUpload finishes an upload and moves the file to the internal destination. func (upload *fileUpload) FinishUpload(ctx context.Context) error { + log := appctx.GetLogger(ctx) np := upload.info.Storage["InternalDestination"] // TODO check etag with If-Match header @@ -318,6 +322,7 @@ func (upload *fileUpload) FinishUpload(ctx context.Context) error { //} // if destination exists + log.Info().Str("oldpath", upload.binPath).Str("newpath", np).Msg("localfs: FinishUpload") if _, err := os.Stat(np); err == nil { // create revision if err := upload.fs.archiveRevision(upload.ctx, np); err != nil { @@ -327,6 +332,7 @@ func (upload *fileUpload) FinishUpload(ctx context.Context) error { err := os.Rename(upload.binPath, np) if err != nil { + log.Error().Msg("localfs: Failed to rename in FinishUpload") return err }