diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index b97f25138ed..2edcf4f5d5f 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -43,11 +43,15 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/favorite" "github.com/cs3org/reva/v2/pkg/storage/favorite/registry" "github.com/cs3org/reva/v2/pkg/storage/utils/templates" + "github.com/cs3org/reva/v2/pkg/storagespace" rtrace "github.com/cs3org/reva/v2/pkg/trace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/mitchellh/mapstructure" "github.com/rs/zerolog" "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" ) // name is the Tracer name used to identify this instrumentation library. @@ -390,19 +394,90 @@ func addAccessHeaders(w http.ResponseWriter, r *http.Request) { } } +func authContextForUser(client gatewayv1beta1.GatewayAPIClient, userID *userpb.UserId, machineAuthAPIKey string) (context.Context, error) { + // Get auth + granteeCtx := ctxpkg.ContextSetUser(context.Background(), &userpb.User{Id: userID}) + + authRes, err := client.Authenticate(granteeCtx, &gateway.AuthenticateRequest{ + Type: "machine", + ClientId: "userid:" + userID.OpaqueId, + ClientSecret: machineAuthAPIKey, + }) + if err != nil { + return nil, err + } + if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK { + return nil, errtypes.NewErrtypeFromStatus(authRes.Status) + } + granteeCtx = metadata.AppendToOutgoingContext(granteeCtx, ctxpkg.TokenHeader, authRes.Token) + return granteeCtx, nil +} + +func (s *svc) sspReferenceIsChildOf(ctx context.Context, client gatewayv1beta1.GatewayAPIClient, child, parent *provider.Reference) (bool, error) { + parentStatRes, err := client.Stat(ctx, &provider.StatRequest{Ref: parent}) + if err != nil { + return false, err + } + parentAuthCtx, err := authContextForUser(client, parentStatRes.Info.Owner, s.c.MachineAuthApiKey) + if err != nil { + return false, err + } + parentPathRes, err := client.GetPath(parentAuthCtx, &provider.GetPathRequest{ResourceId: parentStatRes.Info.Id}) + if err != nil { + return false, err + } + + childStatRes, err := client.Stat(ctx, &provider.StatRequest{Ref: child}) + if err != nil { + return false, err + } + if childStatRes.Status.Code == rpc.Code_CODE_NOT_FOUND && utils.IsRelativeReference(child) && child.Path != "." { + childParentRef := &provider.Reference{ + ResourceId: child.ResourceId, + Path: utils.MakeRelativePath(path.Dir(child.Path)), + } + childStatRes, err = client.Stat(ctx, &provider.StatRequest{Ref: childParentRef}) + if err != nil { + return false, err + } + } + childAuthCtx, err := authContextForUser(client, childStatRes.Info.Owner, s.c.MachineAuthApiKey) + if err != nil { + return false, err + } + childPathRes, err := client.GetPath(childAuthCtx, &provider.GetPathRequest{ResourceId: childStatRes.Info.Id}) + if err != nil { + return false, err + } + + cp := childPathRes.Path + pp := parentPathRes.Path + "/" + return strings.HasPrefix(cp, pp), nil +} + func (s *svc) referenceIsChildOf(ctx context.Context, client gatewayv1beta1.GatewayAPIClient, child, parent *provider.Reference) (bool, error) { - if utils.ResourceIDEqual(child.ResourceId, parent.ResourceId) { - return strings.HasPrefix(child.Path, parent.Path+"/"), nil // Relative to the same resource -> compare paths + _, csid := storagespace.SplitStorageID(child.ResourceId.StorageId) + _, psid := storagespace.SplitStorageID(parent.ResourceId.StorageId) + if csid == utils.ShareStorageProviderID || psid == utils.ShareStorageProviderID { + // the sharesstorageprovider needs some special handling + return s.sspReferenceIsChildOf(ctx, client, child, parent) } if child.ResourceId.StorageId != parent.ResourceId.StorageId { return false, nil // Not on the same storage -> not a child } + if utils.ResourceIDEqual(child.ResourceId, parent.ResourceId) { + return strings.HasPrefix(child.Path, parent.Path+"/"), nil // Relative to the same resource -> compare paths + } + // the references are on the same storage but relative to different resources // -> we need to get the path for both resources childPathRes, err := client.GetPath(ctx, &provider.GetPathRequest{ResourceId: child.ResourceId}) if err != nil { + if st, ok := status.FromError(err); ok && st.Code() == codes.Unimplemented { + return false, nil // the storage provider doesn't support GetPath() -> rely on it taking care of recursion issues + } return false, err } parentPathRes, err := client.GetPath(ctx, &provider.GetPathRequest{ResourceId: parent.ResourceId})