fix: [OCM] Specification Compliance (#11773)

* fix: [OCISDEV-450] fix ocm path

* fix: [OCM] Specification Compliance

* update the test

* fix: [OCISDEV-471] OCM: User can't change the federated share permission

* fix tests

* test: fix federated user regex

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>

* test: fix federated user regex

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>

* test: fix federated user regex

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>

* test: adjust test expectations

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>

* latest reva ocm changes

---------

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>
Co-authored-by: Saw-jan <saw.jan.grg3e@gmail.com>
This commit is contained in:
Roman Perekhod
2025-11-10 12:38:10 +01:00
committed by GitHub
parent 0aae8d4035
commit 304c3f1890
17 changed files with 129 additions and 69 deletions

View File

@@ -0,0 +1,5 @@
Bugfix: OCM Specification Compliance
OCM Specification Compliance
https://github.com/owncloud/ocis/pull/11773

5
go.mod
View File

@@ -66,7 +66,7 @@ require (
github.com/open-policy-agent/opa v1.6.0
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20250217093259-fa3804be6c27
github.com/owncloud/reva/v2 v2.0.0-20251017104024-82c22e954c1c
github.com/owncloud/reva/v2 v2.0.0-20251106102926-751223b32d48
github.com/pkg/errors v0.9.1
github.com/pkg/xattr v0.4.12
github.com/prometheus/client_golang v1.23.2
@@ -334,7 +334,6 @@ require (
golang.org/x/sys v0.37.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.37.0 // indirect
golang.org/x/tools/godoc v0.1.0-deprecated // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
@@ -344,7 +343,7 @@ require (
sigs.k8s.io/yaml v1.5.0 // indirect
)
replace github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202
replace github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20251030165916-532350997dde
replace github.com/egirna/icap-client => github.com/kobergj/icap-client v0.0.0-20250116172800-8eaa5022532b

8
go.sum
View File

@@ -578,8 +578,8 @@ github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4O
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kobergj/go-micro/v4 v4.0.0-20250610135441-d0b187215699 h1:3TOdtI6WPyvBB+uCykapjRtQX8vTHMlyhINzR+58B4k=
github.com/kobergj/go-micro/v4 v4.0.0-20250610135441-d0b187215699/go.mod h1:eE/tD53n3KbVrzrWxKLxdkGw45Fg1qaNLWjpJMvIUF4=
github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202 h1:A1xJ2NKgiYFiaHiLl9B5yw/gUBACSs9crDykTS3GuQI=
github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/kobergj/gowebdav v0.0.0-20251030165916-532350997dde h1:HYcp4J4xYe2m9KSUVbTccJb14TpSs+ldCfDFgqsXedI=
github.com/kobergj/gowebdav v0.0.0-20251030165916-532350997dde/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/kobergj/icap-client v0.0.0-20250116172800-8eaa5022532b h1:NBKEgFtIukCreWOEvtrgNQusqosGyhlxzyiHbba1zEI=
github.com/kobergj/icap-client v0.0.0-20250116172800-8eaa5022532b/go.mod h1:HpntrRsQA6RKNXy2Nbr4kVj+NO3OYWpAQUVxeya+3sU=
github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90 h1:pfI8Z5yavO6fU6vDGlWhZ4BgDlvj8c6xB7J57HfTPwA=
@@ -717,8 +717,8 @@ github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HD
github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/owncloud/libre-graph-api-go v1.0.5-0.20250217093259-fa3804be6c27 h1:ID8s5lGBntmrlI6TbDAjTzRyHucn3bVM2wlW+HBplv4=
github.com/owncloud/libre-graph-api-go v1.0.5-0.20250217093259-fa3804be6c27/go.mod h1:+gT+x62AS9u2Farh9wE2uYmgdvTg0MQgsSI62D+xoRg=
github.com/owncloud/reva/v2 v2.0.0-20251017104024-82c22e954c1c h1:8xgW1qAuKNdXgH9P8F4hDI5vU6/VM1PibZhIFj7JSU0=
github.com/owncloud/reva/v2 v2.0.0-20251017104024-82c22e954c1c/go.mod h1:EgvdLWO1ezgdVqewaAP2GIZJfRRggwZItJnnTBxNNrY=
github.com/owncloud/reva/v2 v2.0.0-20251106102926-751223b32d48 h1:RGnvbZNOE1ss3b0BOM8J+bPrk6prfXB0paKnWaDA/Xg=
github.com/owncloud/reva/v2 v2.0.0-20251106102926-751223b32d48/go.mod h1:XenQR69s8JQ5Q4/+/vQbSg6B4+0iM6inYjNxB7SJAhI=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pablodz/inotifywaitgo v0.0.9 h1:njquRbBU7fuwIe5rEvtaniVBjwWzcpdUVptSgzFqZsw=

View File

@@ -6,6 +6,7 @@ import (
"encoding/json"
"io"
"net/http"
"path"
"reflect"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
@@ -544,7 +545,7 @@ func cs3ReceivedOCMSharesToDriveItems(ctx context.Context,
// file shares
resOpaqueID := "/"
if receivedShares[0].GetResourceType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE {
resOpaqueID += receivedShares[0].GetName()
resOpaqueID = path.Join(resOpaqueID, receivedShares[0].GetName())
}
shareStat, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{

View File

@@ -143,7 +143,7 @@ class GraphHelper {
* @return string
*/
public static function getFederatedUserRegex(): string {
return '(?=(.{4})*$)[A-Za-z0-9+/]*={0,2}$';
return self::getUUIDv4Regex() . '@[a-zA-Z-\\\.]+(:\\\d+)?';
}
/**

View File

@@ -64,7 +64,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%local_host_port%"
"const": "%local_base_url%"
},
"issuerAssignedId": {
"type": "string",
@@ -126,7 +126,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%remote_host_port%"
"const": "%remote_base_url%"
},
"issuerAssignedId": {
"type": "string",
@@ -194,7 +194,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%local_host_port%"
"const": "%local_base_url%"
},
"issuerAssignedId": {
"type": "string",
@@ -256,7 +256,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%remote_host_port%"
"const": "%remote_base_url%"
},
"issuerAssignedId": {
"type": "string",
@@ -346,7 +346,7 @@ Feature: search federation users
},
"idp": {
"type": "string",
"const": "%remote_host_port%"
"const": "%remote_base_url%"
},
"mail": {
"type": "string",
@@ -385,7 +385,7 @@ Feature: search federation users
"const": "Alice Hansen"
},
"idp": {
"const": "%local_host_port%"
"const": "%local_base_url%"
},
"mail": {
"pattern": "alice@example.org"
@@ -409,7 +409,7 @@ Feature: search federation users
"const": "Carol King"
},
"idp": {
"const": "%local_host_port%"
"const": "%local_base_url%"
},
"mail": {
"pattern": "carol@example.org"
@@ -480,7 +480,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%local_host_port%"
"const": "%local_base_url%"
},
"issuerAssignedId": {
"type": "string",
@@ -545,7 +545,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%remote_host_port%"
"const": "%remote_base_url%"
},
"issuerAssignedId": {
"type": "string",
@@ -692,7 +692,7 @@ Feature: search federation users
],
"properties": {
"issuer": {
"const": "%remote_host_port%"
"const": "%remote_base_url%"
},
"issuerAssignedId": {
"type": "string",

View File

@@ -207,7 +207,7 @@ func (s *service) UpdateOCMCoreShare(ctx context.Context, req *ocmcore.UpdateOCM
}
fileMask := &fieldmaskpb.FieldMask{Paths: []string{"protocols"}}
user := &userpb.User{Id: ocmuser.RemoteID(&userpb.UserId{OpaqueId: grantee})}
user := &userpb.User{Id: ocmuser.DecodeRemoteUserFederatedID(&userpb.UserId{OpaqueId: grantee})}
_, err := s.repo.UpdateReceivedShare(ctx, user, &ocm.ReceivedShare{
Id: &ocm.ShareId{
OpaqueId: req.GetOcmShareId(),
@@ -234,7 +234,7 @@ func (s *service) DeleteOCMCoreShare(ctx context.Context, req *ocmcore.DeleteOCM
return nil, errtypes.UserRequired("missing remote user id in a metadata")
}
share, err := s.repo.GetReceivedShare(ctx, &userpb.User{Id: ocmuser.RemoteID(&userpb.UserId{OpaqueId: grantee})}, &ocm.ShareReference{
share, err := s.repo.GetReceivedShare(ctx, &userpb.User{Id: ocmuser.DecodeRemoteUserFederatedID(&userpb.UserId{OpaqueId: grantee})}, &ocm.ShareReference{
Spec: &ocm.ShareReference_Id{
Id: &ocm.ShareId{
OpaqueId: req.GetId(),
@@ -245,7 +245,7 @@ func (s *service) DeleteOCMCoreShare(ctx context.Context, req *ocmcore.DeleteOCM
return nil, errtypes.InternalError("unable to get share details")
}
granteeUser := &userpb.User{Id: ocmuser.RemoteID(&userpb.UserId{OpaqueId: grantee})}
granteeUser := &userpb.User{Id: ocmuser.DecodeRemoteUserFederatedID(&userpb.UserId{OpaqueId: grantee})}
err = s.repo.DeleteReceivedShare(ctx, granteeUser, &ocm.ShareReference{
Spec: &ocm.ShareReference_Id{
Id: &ocm.ShareId{
@@ -262,7 +262,7 @@ func (s *service) DeleteOCMCoreShare(ctx context.Context, req *ocmcore.DeleteOCM
if err := events.Publish(ctx, s.eventStream, events.OCMCoreShareDelete{
ShareID: share.Id.OpaqueId,
Sharer: share.GetOwner(),
Grantee: ocmuser.RemoteID(&userpb.UserId{OpaqueId: grantee}),
Grantee: ocmuser.DecodeRemoteUserFederatedID(&userpb.UserId{OpaqueId: grantee}),
ResourceName: share.Name,
CTime: &typespb.Timestamp{Seconds: uint64(time.Now().Unix())},
}); err != nil {

View File

@@ -180,8 +180,8 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite
remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &client.InviteAcceptedRequest{
Token: req.InviteToken.GetToken(),
RecipientProvider: s.conf.ProviderDomain,
// The UserID is only a string here. To not loose the IDP information we use the FederatedID encoding
// i.e. base64(UserID@IDP)
// The UserID is only a string here. To not lose the IDP information we use the FederatedID encoding
// i.e. UserID@IDP
UserID: ocmuser.FederatedID(user.GetId(), "").GetOpaqueId(),
Email: user.GetMail(),
Name: user.GetDisplayName(),
@@ -219,7 +219,7 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite
// we're using the provider domain as the IDP part of the ID
remoteUserID := &userpb.UserId{
Type: userpb.UserType_USER_TYPE_FEDERATED,
Idp: req.GetOriginSystemProvider().Domain,
Idp: ocmuser.NormalizeOCMUserIPD(req.GetOriginSystemProvider().Domain),
OpaqueId: remoteUser.UserID,
}

View File

@@ -325,9 +325,9 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq
// 2.b replace outgoing user ids with ocm user ids
// unpack the federated user id
shareWith := ocmuser.FormatOCMUser(ocmuser.RemoteID(req.GetGrantee().GetUserId()))
shareWith := ocmuser.FormatOCMUser(ocmuser.FederatedID(req.GetGrantee().GetUserId(), ""))
// wrap the local user id in a federated user id
// wrap the local user id in a local federated user id
owner := ocmuser.FormatOCMUser(ocmuser.FederatedID(info.Owner, s.conf.ProviderDomain))
sender := ocmuser.FormatOCMUser(ocmuser.FederatedID(user.Id, s.conf.ProviderDomain))

View File

@@ -60,7 +60,7 @@ type acceptInviteRequest struct {
Email string `json:"email"`
}
// AcceptInvite informs avout an accepted invitation so that the users
// AcceptInvite informs about an accepted invitation so that the users
// can initiate the OCM share creation.
func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@@ -73,7 +73,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
}
if req.Token == "" || req.UserID == "" || req.RecipientProvider == "" {
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipiendProvider must not be null", nil)
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipientProvider must not be null", nil)
return
}
@@ -111,7 +111,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
userObj := &userpb.User{
Id: &userpb.UserId{
OpaqueId: req.UserID,
Idp: req.RecipientProvider,
Idp: ocmuser.NormalizeOCMUserIPD(req.RecipientProvider),
Type: userpb.UserType_USER_TYPE_FEDERATED,
},
Mail: req.Email,

View File

@@ -29,10 +29,10 @@ import (
ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/go-chi/render"
"github.com/owncloud/reva/v2/pkg/appctx"
"github.com/owncloud/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/reva/v2/pkg/utils"
"github.com/go-chi/render"
)
const (
@@ -99,7 +99,6 @@ func (h *notifHandler) Notifications(w http.ResponseWriter, r *http.Request) {
return
}
// TODO(lopresti) this is all to be implemented. For now we just log what we got
log.Debug().Msgf("Received OCM notification: %+v", req)
var status *rpc.Status

View File

@@ -34,11 +34,12 @@ import (
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/go-playground/validator/v10"
"github.com/owncloud/reva/v2/internal/http/services/reqres"
"github.com/owncloud/reva/v2/pkg/appctx"
ocmuser "github.com/owncloud/reva/v2/pkg/ocm/user"
"github.com/owncloud/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/reva/v2/pkg/utils"
"github.com/go-playground/validator/v10"
)
var validate = validator.New()
@@ -124,7 +125,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
return
}
shareWith, _, err := getIDAndMeshProvider(req.ShareWith)
shareWith, _, err := getLocalUserID(req.ShareWith)
if err != nil {
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil)
return
@@ -197,11 +198,28 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
}
func getLocalUserID(user string) (id, provider string, err error) {
idPart, provider, err := getIDAndMeshProvider(user)
if err != nil {
return "", "", err
}
// Handle nested @ in idPart (e.g. "user@idp@provider")
if inner := strings.LastIndex(idPart, "@"); inner != -1 {
id = idPart[:inner]
} else {
id = idPart
}
return id, provider, nil
}
func getUserIDFromOCMUser(user string) (*userpb.UserId, error) {
id, idp, err := getIDAndMeshProvider(user)
if err != nil {
return nil, err
}
idp = ocmuser.NormalizeOCMUserIPD(idp)
return &userpb.UserId{
OpaqueId: id,
Idp: idp,
@@ -210,13 +228,21 @@ func getUserIDFromOCMUser(user string) (*userpb.UserId, error) {
}, nil
}
func getIDAndMeshProvider(user string) (string, string, error) {
// the user is in the form of dimitri@apiwise.nl
split := strings.Split(user, "@")
if len(split) < 2 {
return "", "", errors.New("not in the form <id>@<provider>")
func getIDAndMeshProvider(user string) (id, provider string, err error) {
last := strings.LastIndex(user, "@")
if last == -1 {
return "", "", fmt.Errorf("%s not in the form <id>@<provider>", user)
}
return strings.Join(split[:len(split)-1], "@"), split[len(split)-1], nil
id, provider = user[:last], user[last+1:]
if id == "" {
return "", "", errors.New("id cannot be empty")
}
if provider == "" {
return "", "", errors.New("provider cannot be empty")
}
return id, provider, nil
}
func getCreateShareRequest(r *http.Request) (*createShareRequest, error) {

View File

@@ -226,12 +226,14 @@ func (m *manager) GetRemoteUser(ctx context.Context, initiator *userpb.UserId, r
log := appctx.GetLogger(ctx)
for _, acceptedUser := range m.model.AcceptedUsers[initiator.GetOpaqueId()] {
log.Info().Msgf("looking for '%s' at '%s' - considering '%s' at '%s'",
remoteUserID.OpaqueId,
remoteUserID.Idp,
acceptedUser.Id.GetOpaqueId(),
acceptedUser.Id.GetIdp(),
)
remoteUserID.OpaqueId, remoteUserID.Idp,
acceptedUser.Id.GetOpaqueId(), acceptedUser.Id.GetIdp())
if (acceptedUser.Id.GetOpaqueId() == remoteUserID.OpaqueId) && (remoteUserID.Idp == "" || idpsEqual(acceptedUser.Id.GetIdp(), remoteUserID.Idp)) {
log.Info().Msgf("remote user OpaqueId:'%s' Idp:'%s' matches OpaqueId:'%s' Idp:'%s'",
remoteUserID.OpaqueId, remoteUserID.Idp,
acceptedUser.Id.GetOpaqueId(), acceptedUser.Id.GetIdp())
return acceptedUser, nil
}
}

View File

@@ -173,10 +173,12 @@ func (a *authorizer) IsProviderAllowed(ctx context.Context, pi *ocmprovider.Prov
}
switch {
case !providerAuthorized:
return errtypes.NotFound(pi.GetDomain())
case !a.conf.VerifyRequestHostname:
log.Info().Msg("VerifyRequestHostname is disabled. any provider is allowed")
return nil
case !providerAuthorized:
log.Info().Msg("providerAuthorized is false")
return errtypes.NotFound(pi.GetDomain())
case len(pi.Services) == 0:
return ErrNoIP
}

View File

@@ -1,50 +1,76 @@
package user
import (
"encoding/base64"
"fmt"
"net/url"
"strings"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
)
// FederatedID creates a federated user id by
// FederatedID creates a federated id for local users by
// 1. stripping the protocol from the domain and
// 2. base64 encoding the opaque id with the domain to get a unique identifier that cannot collide with other users
// 2. concatenating the opaque id with the domain to get a unique identifier that cannot collide with other users
func FederatedID(id *userpb.UserId, domain string) *userpb.UserId {
opaqueId := base64.URLEncoding.EncodeToString([]byte(id.OpaqueId + "@" + id.Idp))
return &userpb.UserId{
if domain == "" {
domain = id.Idp
}
// strip protocol from the domain
idp := id.Idp
if u, err := url.Parse(id.Idp); err == nil && u.Host != "" {
idp = u.Host
}
opaqueId := id.OpaqueId
if !strings.Contains(id.OpaqueId, "@") {
opaqueId = id.OpaqueId + "@" + idp
}
u := &userpb.UserId{
Type: userpb.UserType_USER_TYPE_FEDERATED,
Idp: domain,
Idp: NormalizeOCMUserIPD(domain),
OpaqueId: opaqueId,
}
return u
}
// RemoteID creates a remote user id by
// 1. decoding the base64 encoded opaque id
// 2. splitting the opaque id at the last @ to get the opaque id and the domain
func RemoteID(id *userpb.UserId) *userpb.UserId {
// DecodeRemoteUserFederatedID decodes opaque id into remote user's federated id by
// splitting the opaque id at the last @ to get the opaque id and the domain
func DecodeRemoteUserFederatedID(id *userpb.UserId) *userpb.UserId {
remoteId := &userpb.UserId{
Type: userpb.UserType_USER_TYPE_PRIMARY,
Idp: id.Idp,
OpaqueId: id.OpaqueId,
}
bytes, err := base64.URLEncoding.DecodeString(id.GetOpaqueId())
if err != nil {
return remoteId
}
remote := string(bytes)
remote := id.OpaqueId
last := strings.LastIndex(remote, "@")
if last == -1 {
return remoteId
}
remoteId.OpaqueId = remote[:last]
remoteId.Idp = remote[last+1:]
remoteId.Idp = NormalizeOCMUserIPD(remote[last+1:])
return remoteId
}
// FormatOCMUser formats a user id in the form of <opaque-id>@<idp> used by the OCM API in shareWith, owner and creator fields
func FormatOCMUser(u *userpb.UserId) string {
return fmt.Sprintf("%s@%s", u.OpaqueId, u.Idp)
if u.Idp == "" {
return u.OpaqueId
}
// strip protocol from the domain
idp := u.Idp
if u, err := url.Parse(u.Idp); err == nil && u.Host != "" {
idp = u.Host
}
return fmt.Sprintf("%s@%s", u.OpaqueId, idp)
}
// NormalizeOCMUserIPD ensures that the idp has a scheme (https://) prefix if prefix is missing
// to keep the idp consistent across shares and received shares in the OCM share store.
func NormalizeOCMUserIPD(idp string) string {
if strings.Contains(idp, "://") {
return idp
}
return "https://" + idp
}

View File

@@ -32,7 +32,7 @@ func newFile(path, name string, p *propstat) *File {
path = FixSlashes(path)
f.name = filepath.Base(name)
f.path = filepath.Clean(filepath.Join(path, f.name))
f.path = filepath.Clean(filepath.Join(path, name))
f.modified = p.Modified()
f.etag = p.ETag()
f.contentType = p.ContentType()

6
vendor/modules.txt vendored
View File

@@ -1247,7 +1247,7 @@ github.com/orcaman/concurrent-map
# github.com/owncloud/libre-graph-api-go v1.0.5-0.20250217093259-fa3804be6c27
## explicit; go 1.18
github.com/owncloud/libre-graph-api-go
# github.com/owncloud/reva/v2 v2.0.0-20251017104024-82c22e954c1c
# github.com/owncloud/reva/v2 v2.0.0-20251106102926-751223b32d48
## explicit; go 1.24.0
github.com/owncloud/reva/v2/cmd/revad/internal/grace
github.com/owncloud/reva/v2/cmd/revad/runtime
@@ -1852,7 +1852,7 @@ github.com/stretchr/testify/assert
github.com/stretchr/testify/assert/yaml
github.com/stretchr/testify/mock
github.com/stretchr/testify/require
# github.com/studio-b12/gowebdav v0.9.0 => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202
# github.com/studio-b12/gowebdav v0.9.0 => github.com/kobergj/gowebdav v0.0.0-20251030165916-532350997dde
## explicit; go 1.17
github.com/studio-b12/gowebdav
# github.com/tchap/go-patricia/v2 v2.3.3
@@ -2470,7 +2470,7 @@ sigs.k8s.io/yaml
# stash.kopano.io/kgol/rndm v1.1.2
## explicit; go 1.13
stash.kopano.io/kgol/rndm
# github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202
# github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20251030165916-532350997dde
# github.com/egirna/icap-client => github.com/kobergj/icap-client v0.0.0-20250116172800-8eaa5022532b
# github.com/unrolled/secure => github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c
# github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90