diff --git a/changelog/unreleased/ocm-create-list-shares-ocs.md b/changelog/unreleased/ocm-create-list-shares-ocs.md new file mode 100644 index 0000000000..02f366fb26 --- /dev/null +++ b/changelog/unreleased/ocm-create-list-shares-ocs.md @@ -0,0 +1,3 @@ +Enhancement: Create and list OCM shares in OCS layer + +https://github.com/cs3org/reva/pull/3692 \ No newline at end of file diff --git a/go.mod b/go.mod index cd782bd66e..b790b6d909 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/sciencemesh/meshdirectory-web v1.0.4 github.com/sethvargo/go-password v0.2.0 github.com/stretchr/testify v1.8.1 - github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df + github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 github.com/thanhpk/randstr v1.0.4 github.com/tus/tusd v1.10.0 github.com/wk8/go-ordered-map v1.0.0 diff --git a/go.sum b/go.sum index faba1f1292..0973dfc6ec 100644 --- a/go.sum +++ b/go.sum @@ -1120,6 +1120,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df h1:C+J/LwTqP8gRPt1MdSzBNZP0OYuDm5wsmDKgwpLjYzo= github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= +github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyAOu7kJw6O/2jjG2UXLFoBzihqDU9Ofg9M= +github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= diff --git a/internal/grpc/services/ocmcore/ocmcore.go b/internal/grpc/services/ocmcore/ocmcore.go index 7e7425eca7..be4332701a 100644 --- a/internal/grpc/services/ocmcore/ocmcore.go +++ b/internal/grpc/services/ocmcore/ocmcore.go @@ -21,10 +21,12 @@ package ocmcore import ( "context" "fmt" + "time" ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/ocm/share/repository/registry" @@ -110,6 +112,10 @@ func (s *service) CreateOCMCoreShare(ctx context.Context, req *ocmcore.CreateOCM return nil, errtypes.NotSupported("share type not supported") } + now := &typesv1beta1.Timestamp{ + Seconds: uint64(time.Now().Unix()), + } + share, err := s.repo.StoreReceivedShare(ctx, &ocm.ReceivedShare{ ResourceId: &providerpb.ResourceId{ OpaqueId: req.ResourceId, @@ -126,6 +132,8 @@ func (s *service) CreateOCMCoreShare(ctx context.Context, req *ocmcore.CreateOCM Owner: req.Owner, Creator: req.Sender, Protocols: req.Protocols, + Ctime: now, + Mtime: now, Expiration: req.Expiration, State: ocm.ShareState_SHARE_STATE_PENDING, }) diff --git a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go index 508cac88af..88add7619d 100644 --- a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go +++ b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go @@ -51,6 +51,7 @@ type config struct { OCMClientTimeout int `mapstructure:"ocm_timeout"` OCMClientInsecure bool `mapstructure:"ocm_insecure"` GatewaySVC string `mapstructure:"gateway_svc"` + ProviderDomain string `mapstructure:"provider_domain" docs:"The same domain registered in the provider authorizer"` tokenExpiration time.Duration } @@ -174,7 +175,7 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &client.InviteAcceptedRequest{ Token: req.InviteToken.GetToken(), - RecipientProvider: user.GetId().GetIdp(), + RecipientProvider: s.conf.ProviderDomain, UserID: user.GetId().GetOpaqueId(), Email: user.GetMail(), Name: user.GetDisplayName(), diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index 389ef80c7c..d8a537d3e8 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -59,6 +59,7 @@ type config struct { ClientInsecure bool `mapstructure:"client_insecure"` GatewaySVC string `mapstructure:"gatewaysvc"` WebDAVPrefix string `mapstructure:"webdav_prefix"` + ProviderDomain string `mapstructure:"provider_domain" docs:"The same domain registered in the provider authorizer"` } type service struct { @@ -270,11 +271,17 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq } newShareReq := &client.NewShareRequest{ - ShareWith: formatOCMUser(req.Grantee.GetUserId()), - Name: ocmshare.Name, - ResourceID: fmt.Sprintf("%s:%s", req.ResourceId.StorageId, req.ResourceId.OpaqueId), - Owner: formatOCMUser(info.Owner), - Sender: formatOCMUser(user.Id), + ShareWith: formatOCMUser(req.Grantee.GetUserId()), + Name: ocmshare.Name, + ResourceID: fmt.Sprintf("%s:%s", req.ResourceId.StorageId, req.ResourceId.OpaqueId), + Owner: formatOCMUser(&userpb.UserId{ + OpaqueId: info.Owner.OpaqueId, + Idp: s.conf.ProviderDomain, // FIXME: this is not generally true in case of resharing + }), + Sender: formatOCMUser(&userpb.UserId{ + OpaqueId: user.Id.OpaqueId, + Idp: s.conf.ProviderDomain, + }), SenderDisplayName: user.DisplayName, ShareType: "user", ResourceType: getResourceType(info), diff --git a/internal/http/services/owncloud/ocs/config/config.go b/internal/http/services/owncloud/ocs/config/config.go index 2c31992e24..31b1850eb1 100644 --- a/internal/http/services/owncloud/ocs/config/config.go +++ b/internal/http/services/owncloud/ocs/config/config.go @@ -43,6 +43,8 @@ type Config struct { ResourceInfoCacheDrivers map[string]map[string]interface{} `mapstructure:"resource_info_caches"` UserIdentifierCacheTTL int `mapstructure:"user_identifier_cache_ttl"` AllowedLanguages []string `mapstructure:"allowed_languages"` + OCMMountPoint string `mapstructure:"ocm_mount_point"` + ListOCMShares bool `mapstructure:"list_ocm_shares"` } // Init sets sane defaults. diff --git a/internal/http/services/owncloud/ocs/conversions/main.go b/internal/http/services/owncloud/ocs/conversions/main.go index 00efbb2b07..24a50b9246 100644 --- a/internal/http/services/owncloud/ocs/conversions/main.go +++ b/internal/http/services/owncloud/ocs/conversions/main.go @@ -30,8 +30,11 @@ import ( userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/mime" "github.com/cs3org/reva/pkg/publicshare" publicsharemgr "github.com/cs3org/reva/pkg/publicshare/manager/registry" "github.com/cs3org/reva/pkg/user" @@ -173,6 +176,7 @@ type MatchData struct { type MatchValueData struct { ShareType int `json:"shareType" xml:"shareType"` ShareWith string `json:"shareWith" xml:"shareWith"` + ShareWithProvider string `json:"shareWithProvider" xml:"shareWithProvider"` ShareWithAdditionalInfo string `json:"shareWithAdditionalInfo" xml:"shareWithAdditionalInfo"` } @@ -242,6 +246,82 @@ func PublicShare2ShareData(share *link.PublicShare, r *http.Request, publicURL s return sd } +func formatRemoteUser(u *userpb.UserId) string { + return fmt.Sprintf("%s@%s", u.OpaqueId, u.Idp) +} + +func webdavInfo(protocols []*ocm.Protocol) (*ocm.WebDAVProtocol, bool) { + for _, p := range protocols { + if opt, ok := p.Term.(*ocm.Protocol_WebdavOptions); ok { + return opt.WebdavOptions, true + } + } + return nil, false +} + +// ReceivedOCMShare2ShareData converts a cs3 ocm received share into a share data model. +func ReceivedOCMShare2ShareData(share *ocm.ReceivedShare, path string) (*ShareData, error) { + webdav, ok := webdavInfo(share.Protocols) + if !ok { + return nil, errtypes.InternalError("webdav endpoint not in share") + } + + s := &ShareData{ + ID: share.Id.OpaqueId, + UIDOwner: formatRemoteUser(share.Creator), + UIDFileOwner: formatRemoteUser(share.Owner), + ShareWith: share.Grantee.GetUserId().OpaqueId, + Permissions: RoleFromResourcePermissions(webdav.Permissions.Permissions).OCSPermissions(), + ShareType: ShareTypeFederatedCloudShare, + Path: path, + FileTarget: path, + MimeType: mime.Detect(share.ResourceType == provider.ResourceType_RESOURCE_TYPE_CONTAINER, share.Name), + ItemType: ResourceType(share.ResourceType).String(), + ItemSource: path, + STime: share.Ctime.Seconds, + Name: share.Name, + } + + if share.Expiration != nil { + s.Expiration = timestampToExpiration(share.Expiration) + } + return s, nil +} + +func webdavAMInfo(methods []*ocm.AccessMethod) (*ocm.WebDAVAccessMethod, bool) { + for _, a := range methods { + if opt, ok := a.Term.(*ocm.AccessMethod_WebdavOptions); ok { + return opt.WebdavOptions, true + } + } + return nil, false +} + +// OCMShare2ShareData converts a cs3 ocm share into a share data model. +func OCMShare2ShareData(share *ocm.Share) (*ShareData, error) { + webdav, ok := webdavAMInfo(share.AccessMethods) + if !ok { + return nil, errtypes.InternalError("webdav endpoint not in share") + } + + s := &ShareData{ + ID: share.Id.OpaqueId, + UIDOwner: share.Creator.OpaqueId, + UIDFileOwner: share.Owner.OpaqueId, + ShareWith: formatRemoteUser(share.Grantee.GetUserId()), + Permissions: RoleFromResourcePermissions(webdav.Permissions).OCSPermissions(), + ShareType: ShareTypeFederatedCloudShare, + STime: share.Ctime.Seconds, + Name: share.Name, + } + + if share.Expiration != nil { + s.Expiration = timestampToExpiration(share.Expiration) + } + + return s, nil +} + // LocalUserIDToString transforms a cs3api user id into an ocs data model without domain name // TODO ocs uses user names ... so an additional lookup is needed. see mapUserIds(). func LocalUserIDToString(userID *userpb.UserId) string { diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go index 3c06b16aa9..807cad5d84 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go @@ -99,12 +99,23 @@ func (h *Handler) FindSharees(w http.ResponseWriter, r *http.Request) { } func (h *Handler) userAsMatch(u *userpb.User) *conversions.MatchData { + shareWith := u.Username + if shareWith == "" { + shareWith = u.Id.OpaqueId + } + + shareType := conversions.ShareTypeUser + if u.Id.Type == userpb.UserType_USER_TYPE_FEDERATED { + shareType = conversions.ShareTypeFederatedCloudShare + } + return &conversions.MatchData{ Label: u.DisplayName, Value: &conversions.MatchValueData{ - ShareType: int(conversions.ShareTypeUser), + ShareType: int(shareType), // api compatibility with oc10: always use the username - ShareWith: u.Username, + ShareWith: shareWith, + ShareWithProvider: u.Id.Idp, ShareWithAdditionalInfo: h.getAdditionalInfoAttribute(u), }, } 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 5bc18ea496..ab9c6d5c95 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 @@ -19,98 +19,106 @@ package shares import ( + "context" "net/http" + "path/filepath" + "strings" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" + gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1" + providerpb "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/response" + "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/go-chi/chi/v5" + "github.com/pkg/errors" ) -func (h *Handler) createFederatedCloudShare(w http.ResponseWriter, r *http.Request, statInfo *provider.ResourceInfo, role *conversions.Role, roleVal []byte) { - // ctx := r.Context() - - // c, err := pool.GetGatewayServiceClient(pool.Endpoint(h.gatewayAddr)) - // if err != nil { - // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) - // return - // } - - // shareWithUser, shareWithProvider := r.FormValue("shareWithUser"), r.FormValue("shareWithProvider") - // if shareWithUser == "" || shareWithProvider == "" { - // response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "missing shareWith parameters", nil) - // return - // } - - // providerInfoResp, err := c.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ - // Domain: shareWithProvider, - // }) - // if err != nil { - // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc get invite by domain info request", err) - // return - // } - - // remoteUserRes, err := c.GetAcceptedUser(ctx, &invitepb.GetAcceptedUserRequest{ - // RemoteUserId: &userpb.UserId{OpaqueId: shareWithUser, Idp: shareWithProvider, Type: userpb.UserType_USER_TYPE_PRIMARY}, - // }) - // if err != nil { - // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching recipient", err) - // return - // } - // if remoteUserRes.Status.Code != rpc.Code_CODE_OK { - // response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "user not found", err) - // return - // } - - // createShareReq := &ocm.CreateOCMShareRequest{ - // Opaque: &types.Opaque{ - // Map: map[string]*types.OpaqueEntry{ - // /* TODO extend the spec with role names? - // "role": { - // Decoder: "plain", - // Value: []byte(role.Name), - // }, - // */ - // "permissions": { - // Decoder: "plain", - // Value: []byte(strconv.Itoa(int(role.OCSPermissions()))), - // }, - // "name": { - // Decoder: "plain", - // Value: []byte(statInfo.Path), - // }, - // }, - // }, - // ResourceId: statInfo.Id, - // Grant: &ocm.ShareGrant{ - // Grantee: &provider.Grantee{ - // Type: provider.GranteeType_GRANTEE_TYPE_USER, - // Id: &provider.Grantee_UserId{UserId: remoteUserRes.RemoteUser.GetId()}, - // }, - // Permissions: &ocm.SharePermissions{ - // Permissions: role.CS3ResourcePermissions(), - // }, - // }, - // RecipientMeshProvider: providerInfoResp.ProviderInfo, - // } - - // createShareResponse, err := c.CreateOCMShare(ctx, createShareReq) - // if err != nil { - // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc create ocm share request", err) - // return - // } - // if createShareResponse.Status.Code != rpc.Code_CODE_OK { - // if createShareResponse.Status.Code == rpc.Code_CODE_NOT_FOUND { - // response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) - // return - // } - // response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc create ocm share request failed", err) - // return - // } - - // response.WriteOCSSuccess(w, r, "OCM Share created") +func (h *Handler) createFederatedCloudShare(w http.ResponseWriter, r *http.Request, resource *provider.ResourceInfo, role *conversions.Role, roleVal []byte) { + ctx := r.Context() + + c, err := pool.GetGatewayServiceClient(pool.Endpoint(h.gatewayAddr)) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) + return + } + + shareWithUser, shareWithProvider := r.FormValue("shareWithUser"), r.FormValue("shareWithProvider") + if shareWithUser == "" || shareWithProvider == "" { + response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "missing shareWith parameters", nil) + return + } + + providerInfoResp, err := c.GetInfoByDomain(ctx, &providerpb.GetInfoByDomainRequest{ + Domain: shareWithProvider, + }) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc get invite by domain info request", err) + return + } + + if providerInfoResp.Status.Code != rpc.Code_CODE_OK { + // return proper error + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error from provider info response", errors.New(providerInfoResp.Status.Message)) + return + } + + remoteUserRes, err := c.GetAcceptedUser(ctx, &invitepb.GetAcceptedUserRequest{ + RemoteUserId: &userpb.UserId{OpaqueId: shareWithUser, Idp: shareWithProvider, Type: userpb.UserType_USER_TYPE_FEDERATED}, + }) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching recipient", err) + return + } + if remoteUserRes.Status.Code != rpc.Code_CODE_OK { + response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "user not found", err) + return + } + + createShareResponse, err := c.CreateOCMShare(ctx, &ocm.CreateOCMShareRequest{ + ResourceId: resource.Id, + Grantee: &provider.Grantee{ + Type: provider.GranteeType_GRANTEE_TYPE_USER, + Id: &provider.Grantee_UserId{ + UserId: remoteUserRes.RemoteUser.Id, + }, + }, + RecipientMeshProvider: providerInfoResp.ProviderInfo, + AccessMethods: []*ocm.AccessMethod{ + share.NewWebDavAccessMethod(role.CS3ResourcePermissions()), + share.NewWebappAccessMethod(getViewModeFromRole(role)), + }, + }) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc create ocm share request", err) + return + } + if createShareResponse.Status.Code != rpc.Code_CODE_OK { + if createShareResponse.Status.Code == rpc.Code_CODE_NOT_FOUND { + response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) + return + } + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc create ocm share request failed", err) + return + } + + response.WriteOCSSuccess(w, r, "OCM Share created") +} + +func getViewModeFromRole(role *conversions.Role) providerv1beta1.ViewMode { + switch role.Name { + case conversions.RoleViewer: + return providerv1beta1.ViewMode_VIEW_MODE_READ_ONLY + case conversions.RoleEditor: + return providerv1beta1.ViewMode_VIEW_MODE_READ_WRITE + } + return providerv1beta1.ViewMode_VIEW_MODE_INVALID } // GetFederatedShare handles GET requests on /apps/files_sharing/api/v1/shares/remote_shares/{shareid}. @@ -152,23 +160,118 @@ func (h *Handler) GetFederatedShare(w http.ResponseWriter, r *http.Request) { func (h *Handler) ListFederatedShares(w http.ResponseWriter, r *http.Request) { // TODO Implement pagination. // TODO Implement response with HAL schemating - ctx := r.Context() +} - gatewayClient, err := pool.GetGatewayServiceClient(pool.Endpoint(h.gatewayAddr)) +func (h *Handler) listReceivedFederatedShares(ctx context.Context, gw gatewayv1beta1.GatewayAPIClient) ([]*conversions.ShareData, error) { + listRes, err := gw.ListReceivedOCMShares(ctx, &ocm.ListReceivedOCMSharesRequest{}) if err != nil { - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) - return + return nil, err + } + + shares := []*conversions.ShareData{} + for _, s := range listRes.Shares { + sd, err := conversions.ReceivedOCMShare2ShareData(s, h.ocmLocalMount(s)) + if err != nil { + continue + } + h.mapUserIdsReceivedFederatedShare(ctx, gw, sd) + shares = append(shares, sd) + } + return shares, nil +} + +func (h *Handler) ocmLocalMount(share *ocm.ReceivedShare) string { + return filepath.Join("/", h.ocmMountPoint, share.Id.OpaqueId) +} + +func (h *Handler) mapUserIdsReceivedFederatedShare(ctx context.Context, gw gatewayv1beta1.GatewayAPIClient, sd *conversions.ShareData) { + if sd.ShareWith != "" { + user := h.mustGetIdentifiers(ctx, gw, sd.ShareWith, false) + sd.ShareWith = user.Username + sd.ShareWithDisplayname = user.DisplayName + } + + if sd.UIDOwner != "" { + user := h.mustGetRemoteUser(ctx, gw, sd.UIDOwner) + sd.DisplaynameOwner = user.DisplayName + } + + if sd.UIDFileOwner != "" { + user := h.mustGetRemoteUser(ctx, gw, sd.UIDFileOwner) + sd.DisplaynameFileOwner = user.DisplayName + } +} + +func (h *Handler) mapUserIdsFederatedShare(ctx context.Context, gw gatewayv1beta1.GatewayAPIClient, sd *conversions.ShareData) { + if sd.ShareWith != "" { + user := h.mustGetRemoteUser(ctx, gw, sd.ShareWith) + sd.ShareWith = user.Username + sd.ShareWithDisplayname = user.DisplayName } - listOCMSharesResponse, err := gatewayClient.ListOCMShares(ctx, &ocm.ListOCMSharesRequest{}) + if sd.UIDOwner != "" { + user := h.mustGetIdentifiers(ctx, gw, sd.UIDOwner, false) + sd.DisplaynameOwner = user.DisplayName + } + + if sd.UIDFileOwner != "" { + user := h.mustGetIdentifiers(ctx, gw, sd.UIDFileOwner, false) + sd.DisplaynameFileOwner = user.DisplayName + } +} + +func (h *Handler) mustGetRemoteUser(ctx context.Context, gw gatewayv1beta1.GatewayAPIClient, id string) *userIdentifiers { + s := strings.SplitN(id, "@", 2) + opaqueID, idp := s[0], s[1] + userRes, err := gw.GetAcceptedUser(ctx, &invitepb.GetAcceptedUserRequest{ + RemoteUserId: &userpb.UserId{ + Idp: idp, + OpaqueId: opaqueID, + }, + }) if err != nil { - response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc list ocm share request", err) - return + return &userIdentifiers{} } + if userRes.Status.Code != rpc.Code_CODE_OK { + return &userIdentifiers{} + } + + user := userRes.RemoteUser + return &userIdentifiers{ + DisplayName: user.DisplayName, + Username: user.Username, + Mail: user.Mail, + } +} + +func (h *Handler) listOutcomingFederatedShares(ctx context.Context, gw gatewayv1beta1.GatewayAPIClient) ([]*conversions.ShareData, error) { + listRes, err := gw.ListOCMShares(ctx, &ocm.ListOCMSharesRequest{}) + if err != nil { + return nil, err + } + + shares := []*conversions.ShareData{} + for _, s := range listRes.Shares { + sd, err := conversions.OCMShare2ShareData(s) + if err != nil { + continue + } + h.mapUserIdsFederatedShare(ctx, gw, sd) + + info, status, err := h.getResourceInfoByID(ctx, gw, s.ResourceId) + if err != nil { + return nil, err + } + + if status.Code != rpc.Code_CODE_OK { + return nil, err + } - shares := listOCMSharesResponse.GetShares() - if shares == nil { - shares = make([]*ocm.Share, 0) + err = h.addFileInfo(ctx, sd, info) + if err != nil { + return nil, err + } + shares = append(shares, sd) } - response.WriteOCSSuccess(w, r, shares) + return shares, nil } diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go index 58777ebf7b..1553dbb25f 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go @@ -68,10 +68,12 @@ type Handler struct { publicURL string sharePrefix string homeNamespace string + ocmMountPoint string additionalInfoTemplate *template.Template userIdentifierCache *ttlcache.Cache resourceInfoCache cache.ResourceInfoCache resourceInfoCacheTTL time.Duration + listOCMShares bool } // we only cache the minimal set of data instead of the full user metadata. @@ -102,6 +104,8 @@ func (h *Handler) Init(c *config.Config) { h.publicURL = c.Config.Host h.sharePrefix = c.SharePrefix h.homeNamespace = c.HomeNamespace + h.ocmMountPoint = c.OCMMountPoint + h.listOCMShares = c.ListOCMShares h.additionalInfoTemplate, _ = template.New("additionalInfo").Parse(c.AdditionalInfoAttribute) h.resourceInfoCacheTTL = time.Second * time.Duration(c.ResourceInfoCacheTTL) @@ -738,6 +742,17 @@ func (h *Handler) listSharesWithMe(w http.ResponseWriter, r *http.Request) { log.Debug().Msgf("share: %+v", *data) } + if h.listOCMShares { + // include ocm shares in the response + lst, err := h.listReceivedFederatedShares(ctx, client) + if err != nil { + log.Err(err).Msg("error listing received ocm shares") + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error listing received ocm shares", err) + return + } + shares = append(shares, lst...) + } + response.WriteOCSSuccess(w, r, shares) } diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go index 3b65991372..620a7bdaf7 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go @@ -213,6 +213,15 @@ func (h *Handler) listUserShares(r *http.Request, filters []*collaboration.Filte log.Debug().Interface("share", s).Interface("info", info).Interface("shareData", data).Msg("mapped") ocsDataPayload = append(ocsDataPayload, data) } + + if h.listOCMShares { + // include the ocm shares + ocmShares, err := h.listOutcomingFederatedShares(ctx, client) + if err != nil { + return nil, nil, err + } + ocsDataPayload = append(ocsDataPayload, ocmShares...) + } } return ocsDataPayload, nil, nil diff --git a/pkg/ocm/share/repository/sql/conversions.go b/pkg/ocm/share/repository/sql/conversions.go index 716624d87c..1d07f06edb 100644 --- a/pkg/ocm/share/repository/sql/conversions.go +++ b/pkg/ocm/share/repository/sql/conversions.go @@ -19,6 +19,7 @@ package sql import ( + "database/sql" "strconv" "strings" @@ -135,7 +136,7 @@ type dbShare struct { Initiator string Ctime int Mtime int - Expiration int + Expiration sql.NullInt64 ShareType ShareType } @@ -157,7 +158,7 @@ type dbReceivedShare struct { Initiator string Ctime int Mtime int - Expiration int + Expiration sql.NullInt64 Type ShareType State ShareState } @@ -219,9 +220,9 @@ func convertToCS3OCMShare(s *dbShare, am []*ocm.AccessMethod) *ocm.Share { ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: am, } - if s.Expiration != 0 { + if s.Expiration.Valid { share.Expiration = &types.Timestamp{ - Seconds: uint64(s.Expiration), + Seconds: uint64(s.Expiration.Int64), } } return share @@ -258,9 +259,9 @@ func convertToCS3OCMReceivedShare(s *dbReceivedShare, p []*ocm.Protocol) *ocm.Re State: convertToCS3OCMShareState(s.State), Protocols: p, } - if s.Expiration != 0 { + if s.Expiration.Valid { share.Expiration = &types.Timestamp{ - Seconds: uint64(s.Expiration), + Seconds: uint64(s.Expiration.Int64), } } return share diff --git a/pkg/ocm/share/repository/sql/init.sql b/pkg/ocm/share/repository/sql/init.sql index 73f6b3b464..fde3d66353 100644 --- a/pkg/ocm/share/repository/sql/init.sql +++ b/pkg/ocm/share/repository/sql/init.sql @@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS ocm_received_share_protocols ( CREATE TABLE IF NOT EXISTS ocm_protocol_webdav ( ocm_protocol_id INTEGER NOT NULL PRIMARY KEY, uri VARCHAR(255) NOT NULL, - shared_secret VARCHAR(255) NOT NULL, + shared_secret TEXT NOT NULL, permissions INTEGER NOT NULL, FOREIGN KEY (ocm_protocol_id) REFERENCES ocm_received_share_protocols(id) ON DELETE CASCADE ); diff --git a/pkg/ocm/share/repository/sql/sql.go b/pkg/ocm/share/repository/sql/sql.go index 984c1e59f5..957159a7d4 100644 --- a/pkg/ocm/share/repository/sql/sql.go +++ b/pkg/ocm/share/repository/sql/sql.go @@ -99,7 +99,7 @@ func storeWebDAVAccessMethod(tx *sql.Tx, shareID int64, o *ocm.AccessMethod_Webd } func storeWebappAccessMethod(tx *sql.Tx, shareID int64, o *ocm.AccessMethod_WebappOptions) error { - amID, err := storeAccessMethod(tx, shareID, WebDAVAccessMethod) + amID, err := storeAccessMethod(tx, shareID, WebappAccessMethod) if err != nil { return err } @@ -117,7 +117,7 @@ func storeTransferAccessMethod(tx *sql.Tx, shareID int64, _ *ocm.AccessMethod_Tr } func storeAccessMethod(tx *sql.Tx, shareID int64, t AccessMethod) (int64, error) { - query := "INSERT INTO ocm_shares_access_method SET ocm_share_id=?, type=?" + query := "INSERT INTO ocm_shares_access_methods SET ocm_share_id=?, type=?" params := []any{shareID, int(t)} res, err := tx.Exec(query, params...) @@ -259,7 +259,7 @@ func (m *mgr) getByKey(ctx context.Context, user *userpb.User, key *ocm.ShareKey } func (m *mgr) getAccessMethods(ctx context.Context, id int) ([]*ocm.AccessMethod, error) { - query := "SELECT m.type, dav.permissions, app.view_mode FROM ocm_shares_access_method as m LEFT JOIN ocm_access_method_webdav as dav ON m.id=dav.ocm_access_method_id LEFT JOIN ocm_access_method_webapp as app ON m.id=app.ocm_access_method_id WHERE m.ocm_share_id=?" + query := "SELECT m.type, dav.permissions, app.view_mode FROM ocm_shares_access_methods as m LEFT JOIN ocm_access_method_webdav as dav ON m.id=dav.ocm_access_method_id LEFT JOIN ocm_access_method_webapp as app ON m.id=app.ocm_access_method_id WHERE m.ocm_share_id=?" var methods []*ocm.AccessMethod rows, err := m.db.QueryContext(ctx, query, id) @@ -415,7 +415,7 @@ func (m *mgr) getAccessMethodsIds(ctx context.Context, ids []any) (map[string][] return methods, nil } - query := "SELECT m.ocm_share_id, m.type, dav.permissions, app.view_mode FROM ocm_shares_access_method as m LEFT JOIN ocm_access_method_webdav as dav ON m.id=dav.ocm_access_method_id LEFT JOIN ocm_access_method_webapp as app ON m.id=app.ocm_access_method_id WHERE m.ocm_share_id IN " + query := "SELECT m.ocm_share_id, m.type, dav.permissions, app.view_mode FROM ocm_shares_access_methods as m LEFT JOIN ocm_access_method_webdav as dav ON m.id=dav.ocm_access_method_id LEFT JOIN ocm_access_method_webapp as app ON m.id=app.ocm_access_method_id WHERE m.ocm_share_id IN " in := strings.Repeat("?,", len(ids)) query += "(" + in[:len(in)-1] + ")" diff --git a/pkg/ocm/share/repository/sql/sql_test.go b/pkg/ocm/share/repository/sql/sql_test.go index ffafe1ef6b..6a81b2e140 100644 --- a/pkg/ocm/share/repository/sql/sql_test.go +++ b/pkg/ocm/share/repository/sql/sql_test.go @@ -47,7 +47,7 @@ var ( port = 3306 m sync.Mutex // for increasing the port ocmShareTable = "ocm_shares" - ocmAccessMethodTable = "ocm_shares_access_method" + ocmAccessMethodTable = "ocm_shares_access_methods" ocmAMWebDAVTable = "ocm_access_method_webdav" ocmAMWebappTable = "ocm_access_method_webapp" @@ -128,7 +128,7 @@ func createShareTables(ctx *sql.Context, initData []*ocm.Share) map[string]*memo }, "")) tables[ocmShareTable] = tableShares - // ocm_shares_access_method table + // ocm_shares_access_methods table var fkAccessMethods memory.ForeignKeyCollection fkAccessMethods.AddFK(sql.ForeignKeyConstraint{ Columns: []string{"ocm_share_id"}, @@ -349,6 +349,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, }, }, @@ -390,6 +391,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions())}, }, }, @@ -485,6 +487,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), @@ -526,6 +529,7 @@ func TestGetShare(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), @@ -617,6 +621,7 @@ func TestListShares(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), @@ -675,6 +680,7 @@ func TestListShares(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), @@ -691,6 +697,7 @@ func TestListShares(t *testing.T) { Creator: &userpb.UserId{OpaqueId: "einstein"}, Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, + Expiration: &typesv1beta1.Timestamp{}, ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), @@ -748,6 +755,7 @@ func TestListShares(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), }, @@ -811,6 +819,7 @@ func TestListShares(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), }, @@ -942,6 +951,7 @@ func TestListShares(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewEditorRole().CS3ResourcePermissions()), share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), @@ -959,6 +969,7 @@ func TestListShares(t *testing.T) { Ctime: &typesv1beta1.Timestamp{Seconds: 1670859468}, Mtime: &typesv1beta1.Timestamp{Seconds: 1670859468}, ShareType: ocm.ShareType_SHARE_TYPE_USER, + Expiration: &typesv1beta1.Timestamp{}, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), }, @@ -1060,13 +1071,17 @@ func TestStoreShare(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, AccessMethods: []*ocm.AccessMethod{ share.NewWebDavAccessMethod(conversions.NewViewerRole().CS3ResourcePermissions()), + share.NewWebappAccessMethod(appprovider.ViewMode_VIEW_MODE_READ_ONLY), }, }, expected: storeShareExpected{ - shares: []sql.Row{{int64(1), "qwerty", "storage", "resource-id1", "file-name", "richard@cesnet", "einstein", "marie", uint64(1670859468), uint64(1670859468), nil, int8(0)}}, - accessmethods: []sql.Row{{int64(1), int64(1), int8(0)}}, - webdav: []sql.Row{{int64(1), int64(1)}}, - webapp: []sql.Row{}, + shares: []sql.Row{{int64(1), "qwerty", "storage", "resource-id1", "file-name", "richard@cesnet", "einstein", "marie", uint64(1670859468), uint64(1670859468), nil, int8(0)}}, + accessmethods: []sql.Row{ + {int64(1), int64(1), int8(0)}, + {int64(2), int64(1), int8(1)}, + }, + webdav: []sql.Row{{int64(1), int64(1)}}, + webapp: []sql.Row{{int64(2), int8(2)}}, }, }, { @@ -1238,6 +1253,7 @@ func TestGetReceivedShare(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), @@ -1289,6 +1305,7 @@ func TestGetReceivedShare(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_FILE, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), @@ -1394,6 +1411,7 @@ func TestGetReceivedShare(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_FILE, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), @@ -1488,6 +1506,7 @@ func TestListReceviedShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), @@ -1552,6 +1571,7 @@ func TestListReceviedShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_FILE, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), @@ -1572,6 +1592,7 @@ func TestListReceviedShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebappProtocol("https://cernbox.cern.ch/ocm/54321"), }, @@ -1632,6 +1653,7 @@ func TestListReceviedShares(t *testing.T) { ShareType: ocm.ShareType_SHARE_TYPE_USER, State: ocm.ShareState_SHARE_STATE_ACCEPTED, ResourceType: providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER, + Expiration: &typesv1beta1.Timestamp{}, Protocols: []*ocm.Protocol{ share.NewWebDAVProtocol("webdav+https//cernbox.cern.ch/dav/ocm/1", "secret", &ocm.SharePermissions{ Permissions: conversions.NewEditorRole().CS3ResourcePermissions(), diff --git a/pkg/ocm/storage/ocm.go b/pkg/ocm/storage/ocm.go index 3351c68006..2db3eae14c 100644 --- a/pkg/ocm/storage/ocm.go +++ b/pkg/ocm/storage/ocm.go @@ -156,6 +156,11 @@ func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*go return nil, nil, "", err } + endpoint, err = url.PathUnescape(endpoint) + if err != nil { + return nil, nil, "", err + } + // FIXME: it's still not clear from the OCM APIs how to use the shared secret // will use as a token in the bearer authentication as this is the reva implementation c := gowebdav.NewClient(endpoint, "", "") @@ -249,6 +254,9 @@ func (d *driver) GetMD(ctx context.Context, ref *provider.Reference, _ []string) info, err := client.Stat(rel) if err != nil { + if gowebdav.IsErrNotFound(err) { + return nil, errtypes.NotFound(ref.GetPath()) + } return nil, err } diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml index fdf35e50fb..5a0387c893 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-grpc.toml @@ -1,3 +1,6 @@ +[log] +mode = "json" + [shared] gatewaysvc = "{{grpc_address}}" @@ -18,6 +21,7 @@ basic = "{{grpc_address}}" [grpc.services.ocminvitemanager] driver = "json" +provider_domain = "cernbox.cern.ch" [grpc.services.ocminvitemanager.drivers.json] file = "{{invite_token_file}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml index 8fcdd31915..7e3df6ea42 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml @@ -1,3 +1,6 @@ +[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 fdf35e50fb..795855ba9f 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-grpc.toml @@ -1,3 +1,6 @@ +[log] +mode = "json" + [shared] gatewaysvc = "{{grpc_address}}" @@ -18,6 +21,7 @@ basic = "{{grpc_address}}" [grpc.services.ocminvitemanager] driver = "json" +provider_domain = "cesnet.cz" [grpc.services.ocminvitemanager.drivers.json] file = "{{invite_token_file}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml index f07be9f27d..ce607e3648 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml @@ -1,3 +1,6 @@ +[log] +mode = "json" + [shared] gatewaysvc = "{{cesnetgw_address}}"