diff --git a/changelog/unreleased/restore-readonly.md b/changelog/unreleased/restore-readonly.md new file mode 100644 index 0000000000..e5b936585e --- /dev/null +++ b/changelog/unreleased/restore-readonly.md @@ -0,0 +1,7 @@ +Bugfix: Logic to restore files to readonly nodes + +This impacts solely the DecomposedFS. Prior to these changes there was no validation when a user tried to restore a file from the trashbin to a share location (i.e any folder under `/Shares`). + +With this patch if the user restoring the resource has write permissions on the share, restore is possible. + +https://github.com/cs3org/reva/pull/1913 diff --git a/pkg/errtypes/errtypes.go b/pkg/errtypes/errtypes.go index 92f88792a1..c897856b6f 100644 --- a/pkg/errtypes/errtypes.go +++ b/pkg/errtypes/errtypes.go @@ -183,7 +183,7 @@ type IsChecksumMismatch interface { } // IsInsufficientStorage is the interface to implement -// to specify that a there is insufficient storage. +// to specify that there is insufficient storage. type IsInsufficientStorage interface { IsInsufficientStorage() } diff --git a/pkg/rgrpc/status/status.go b/pkg/rgrpc/status/status.go index d1cb34bf15..57f9076ba3 100644 --- a/pkg/rgrpc/status/status.go +++ b/pkg/rgrpc/status/status.go @@ -143,6 +143,15 @@ func NewInvalidArg(ctx context.Context, msg string) *rpc.Status { } } +// NewConflict returns a Status with Code_CODE_ABORTED and logs the msg. +func NewConflict(ctx context.Context, err error, msg string) *rpc.Status { + return &rpc.Status{ + Code: rpc.Code_CODE_ABORTED, + Message: msg, + Trace: getTrace(ctx), + } +} + // NewStatusFromErrType returns a status that corresponds to the given errtype func NewStatusFromErrType(ctx context.Context, msg string, err error) *rpc.Status { switch e := err.(type) { diff --git a/pkg/storage/utils/decomposedfs/decomposedfs.go b/pkg/storage/utils/decomposedfs/decomposedfs.go index 7dacfeaad7..3bf1bfb2c8 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -70,7 +70,7 @@ type Tree interface { // CreateReference(ctx context.Context, node *node.Node, targetURI *url.URL) error Move(ctx context.Context, oldNode *node.Node, newNode *node.Node) (err error) Delete(ctx context.Context, node *node.Node) (err error) - RestoreRecycleItemFunc(ctx context.Context, key, trashPath, restorePath string) (*node.Node, func() error, error) // FIXME REFERENCE use ref instead of path + RestoreRecycleItemFunc(ctx context.Context, key, trashPath, restorePath string) (*node.Node, *node.Node, func() error, error) // FIXME REFERENCE use ref instead of path PurgeRecycleItemFunc(ctx context.Context, key, purgePath string) (*node.Node, func() error, error) WriteBlob(key string, reader io.Reader) error @@ -191,7 +191,7 @@ func (fs *Decomposedfs) CreateHome(ctx context.Context) (err error) { if n, err = fs.lu.RootNode(ctx); err != nil { return } - h, err = fs.lu.WalkPath(ctx, n, fs.lu.mustGetUserLayout(ctx), func(ctx context.Context, n *node.Node) error { + h, err = fs.lu.WalkPath(ctx, n, fs.lu.mustGetUserLayout(ctx), false, func(ctx context.Context, n *node.Node) error { if !n.Exists { if err := fs.tp.CreateDir(ctx, n); err != nil { return err @@ -344,7 +344,7 @@ func (fs *Decomposedfs) CreateReference(ctx context.Context, p string, targetURI // create Shares folder if it does not exist var n *node.Node - if n, err = fs.lu.NodeFromPath(ctx, fs.o.ShareFolder); err != nil { + if n, err = fs.lu.NodeFromPath(ctx, fs.o.ShareFolder, false); err != nil { return errtypes.InternalError(err.Error()) } else if !n.Exists { if err = fs.tp.CreateDir(ctx, n); err != nil { diff --git a/pkg/storage/utils/decomposedfs/grants_test.go b/pkg/storage/utils/decomposedfs/grants_test.go index 953b994c55..5c7d4a4640 100644 --- a/pkg/storage/utils/decomposedfs/grants_test.go +++ b/pkg/storage/utils/decomposedfs/grants_test.go @@ -102,7 +102,7 @@ var _ = Describe("Grants", func() { Describe("AddGrant", func() { It("adds grants", func() { - n, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1") + n, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1", false) Expect(err).ToNot(HaveOccurred()) err = env.Fs.AddGrant(env.Ctx, ref, grant) diff --git a/pkg/storage/utils/decomposedfs/lookup.go b/pkg/storage/utils/decomposedfs/lookup.go index bcd9ca72e7..fb85fc1a6c 100644 --- a/pkg/storage/utils/decomposedfs/lookup.go +++ b/pkg/storage/utils/decomposedfs/lookup.go @@ -30,7 +30,9 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/options" + "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/xattrs" "github.com/cs3org/reva/pkg/storage/utils/templates" + "github.com/pkg/xattr" ) // Lookup implements transformations from filepath to node and back @@ -51,7 +53,7 @@ func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference) p := filepath.Clean(ref.Path) if p != "." { // walk the relative path - n, err = lu.WalkPath(ctx, n, p, func(ctx context.Context, n *node.Node) error { + n, err = lu.WalkPath(ctx, n, p, false, func(ctx context.Context, n *node.Node) error { return nil }) if err != nil { @@ -64,7 +66,7 @@ func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference) } if ref.Path != "" { - return lu.NodeFromPath(ctx, ref.GetPath()) + return lu.NodeFromPath(ctx, ref.GetPath(), false) } // reference is invalid @@ -72,7 +74,7 @@ func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference) } // NodeFromPath converts a filename into a Node -func (lu *Lookup) NodeFromPath(ctx context.Context, fn string) (*node.Node, error) { +func (lu *Lookup) NodeFromPath(ctx context.Context, fn string, followReferences bool) (*node.Node, error) { log := appctx.GetLogger(ctx) log.Debug().Interface("fn", fn).Msg("NodeFromPath()") @@ -84,7 +86,7 @@ func (lu *Lookup) NodeFromPath(ctx context.Context, fn string) (*node.Node, erro // TODO collect permissions of the current user on every segment fn = filepath.Clean(fn) if fn != "/" && fn != "." { - n, err = lu.WalkPath(ctx, n, fn, func(ctx context.Context, n *node.Node) error { + n, err = lu.WalkPath(ctx, n, fn, followReferences, func(ctx context.Context, n *node.Node) error { log.Debug().Interface("node", n).Msg("NodeFromPath() walk") return nil }) @@ -139,20 +141,36 @@ func (lu *Lookup) HomeNode(ctx context.Context) (node *node.Node, err error) { if node, err = lu.RootNode(ctx); err != nil { return } - node, err = lu.WalkPath(ctx, node, lu.mustGetUserLayout(ctx), nil) + node, err = lu.WalkPath(ctx, node, lu.mustGetUserLayout(ctx), false, nil) return } -// WalkPath calls n.Child(segment) on every path segment in p starting at the node r -// If a function f is given it will be executed for every segment node, but not the root node r -func (lu *Lookup) WalkPath(ctx context.Context, r *node.Node, p string, f func(ctx context.Context, n *node.Node) error) (*node.Node, error) { +// WalkPath calls n.Child(segment) on every path segment in p starting at the node r. +// If a function f is given it will be executed for every segment node, but not the root node r. +// If followReferences is given the current visited reference node is replaced by the referenced node. +func (lu *Lookup) WalkPath(ctx context.Context, r *node.Node, p string, followReferences bool, f func(ctx context.Context, n *node.Node) error) (*node.Node, error) { segments := strings.Split(strings.Trim(p, "/"), "/") var err error for i := range segments { if r, err = r.Child(ctx, segments[i]); err != nil { return r, err } - // if an intermediate node is missing return not found + + if followReferences { + if attrBytes, err := xattr.Get(r.InternalPath(), xattrs.ReferenceAttr); err == nil { + realNodeID := attrBytes + ref, err := xattrs.ReferenceFromAttr(realNodeID) + if err != nil { + return nil, err + } + + r, err = lu.NodeFromID(ctx, ref.ResourceId) + if err != nil { + return nil, err + } + } + } + if !r.Exists && i < len(segments)-1 { return r, errtypes.NotFound(segments[i]) } diff --git a/pkg/storage/utils/decomposedfs/lookup_test.go b/pkg/storage/utils/decomposedfs/lookup_test.go index e6283abf56..699e3d9d3e 100644 --- a/pkg/storage/utils/decomposedfs/lookup_test.go +++ b/pkg/storage/utils/decomposedfs/lookup_test.go @@ -20,6 +20,7 @@ package decomposedfs_test import ( helpers "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/testhelpers" + "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/xattrs" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -40,11 +41,12 @@ var _ = Describe("Lookup", func() { if env != nil { env.Cleanup() } + }) Describe("Path", func() { It("returns the path including a leading slash", func() { - n, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1") + n, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1", false) Expect(err).ToNot(HaveOccurred()) path, err := env.Lookup.Path(env.Ctx, n) @@ -52,4 +54,15 @@ var _ = Describe("Lookup", func() { Expect(path).To(Equal("/dir1/file1")) }) }) + + Describe("Reference Parsing", func() { + It("parses a valid cs3 reference", func() { + in := []byte("cs3:bede11a0-ea3d-11eb-a78b-bf907adce8ed/c402d01c-ea3d-11eb-a0fc-c32f9d32528f") + ref, err := xattrs.ReferenceFromAttr(in) + + Expect(err).ToNot(HaveOccurred()) + Expect(ref.ResourceId.StorageId).To(Equal("bede11a0-ea3d-11eb-a78b-bf907adce8ed")) + Expect(ref.ResourceId.OpaqueId).To(Equal("c402d01c-ea3d-11eb-a0fc-c32f9d32528f")) + }) + }) }) diff --git a/pkg/storage/utils/decomposedfs/node/node.go b/pkg/storage/utils/decomposedfs/node/node.go index 7233042b88..d9807e3f01 100644 --- a/pkg/storage/utils/decomposedfs/node/node.go +++ b/pkg/storage/utils/decomposedfs/node/node.go @@ -103,6 +103,23 @@ func New(id, parentID, name string, blobsize int64, blobID string, owner *userpb } } +// ChangeOwner sets the owner of n to newOwner +func (n *Node) ChangeOwner(new *userpb.UserId) (err error) { + nodePath := n.InternalPath() + n.owner = new + if err = xattr.Set(nodePath, xattrs.OwnerIDAttr, []byte(new.OpaqueId)); err != nil { + return errors.Wrap(err, "Decomposedfs: could not reset owner id attribute") + } + if err = xattr.Set(nodePath, xattrs.OwnerIDPAttr, []byte(new.Idp)); err != nil { + return errors.Wrap(err, "Decomposedfs: could not reset owner idp attribute") + } + if err = xattr.Set(nodePath, xattrs.OwnerTypeAttr, []byte(utils.UserTypeToString(new.Type))); err != nil { + return errors.Wrap(err, "Decomposedfs: could not reset owner idp attribute") + } + + return +} + // WriteMetadata writes the Node metadata to disk func (n *Node) WriteMetadata(owner *userpb.UserId) (err error) { nodePath := n.InternalPath() diff --git a/pkg/storage/utils/decomposedfs/node/node_test.go b/pkg/storage/utils/decomposedfs/node/node_test.go index a0166bc672..aa8926018f 100644 --- a/pkg/storage/utils/decomposedfs/node/node_test.go +++ b/pkg/storage/utils/decomposedfs/node/node_test.go @@ -64,7 +64,7 @@ var _ = Describe("Node", func() { Describe("ReadNode", func() { It("reads the blobID from the xattrs", func() { - lookupNode, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1") + lookupNode, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1", false) Expect(err).ToNot(HaveOccurred()) n, err := node.ReadNode(env.Ctx, env.Lookup, lookupNode.ID) @@ -75,7 +75,7 @@ var _ = Describe("Node", func() { Describe("WriteMetadata", func() { It("writes all xattrs", func() { - n, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1") + n, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1", false) Expect(err).ToNot(HaveOccurred()) blobsize := 239485734 @@ -90,7 +90,7 @@ var _ = Describe("Node", func() { err = n.WriteMetadata(owner) Expect(err).ToNot(HaveOccurred()) - n2, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1") + n2, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/file1", false) Expect(err).ToNot(HaveOccurred()) Expect(n2.Name).To(Equal("TestName")) Expect(n2.BlobID).To(Equal("TestBlobID")) @@ -100,7 +100,7 @@ var _ = Describe("Node", func() { Describe("Parent", func() { It("returns the parent node", func() { - child, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/subdir1") + child, err := env.Lookup.NodeFromPath(env.Ctx, "/dir1/subdir1", false) Expect(err).ToNot(HaveOccurred()) Expect(child).ToNot(BeNil()) @@ -118,7 +118,7 @@ var _ = Describe("Node", func() { BeforeEach(func() { var err error - parent, err = env.Lookup.NodeFromPath(env.Ctx, "/dir1") + parent, err = env.Lookup.NodeFromPath(env.Ctx, "/dir1", false) Expect(err).ToNot(HaveOccurred()) Expect(parent).ToNot(BeNil()) }) @@ -165,7 +165,7 @@ var _ = Describe("Node", func() { BeforeEach(func() { var err error - n, err = env.Lookup.NodeFromPath(env.Ctx, "dir1/file1") + n, err = env.Lookup.NodeFromPath(env.Ctx, "dir1/file1", false) Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/storage/utils/decomposedfs/recycle.go b/pkg/storage/utils/decomposedfs/recycle.go index f793687fac..46dc1e7c5b 100644 --- a/pkg/storage/utils/decomposedfs/recycle.go +++ b/pkg/storage/utils/decomposedfs/recycle.go @@ -262,7 +262,7 @@ func (fs *Decomposedfs) RestoreRecycleItem(ctx context.Context, key, path string if restoreRef == nil { restoreRef = &provider.Reference{} } - rn, restoreFunc, err := fs.tp.RestoreRecycleItemFunc(ctx, key, path, restoreRef.Path) + rn, p, restoreFunc, err := fs.tp.RestoreRecycleItemFunc(ctx, key, path, restoreRef.Path) if err != nil { return err } @@ -278,6 +278,16 @@ func (fs *Decomposedfs) RestoreRecycleItem(ctx context.Context, key, path string return errtypes.PermissionDenied(key) } + ps, err := fs.p.AssemblePermissions(ctx, p) + if err != nil { + return errtypes.InternalError(err.Error()) + } + + // share receiver cannot restore to a shared resource to which she does not have write permissions. + if !ps.InitiateFileUpload { + return errtypes.PermissionDenied(key) + } + // Run the restore func return restoreFunc() } diff --git a/pkg/storage/utils/decomposedfs/testhelpers/helpers.go b/pkg/storage/utils/decomposedfs/testhelpers/helpers.go index 3d189f1177..5bb8ad3885 100644 --- a/pkg/storage/utils/decomposedfs/testhelpers/helpers.go +++ b/pkg/storage/utils/decomposedfs/testhelpers/helpers.go @@ -150,7 +150,7 @@ func (t *TestEnv) CreateTestDir(name string) (*node.Node, error) { if err != nil { return nil, err } - n, err := t.Lookup.NodeFromPath(t.Ctx, name) + n, err := t.Lookup.NodeFromPath(t.Ctx, name, false) if err != nil { return nil, err } diff --git a/pkg/storage/utils/decomposedfs/tree/tree.go b/pkg/storage/utils/decomposedfs/tree/tree.go index 29fe2fce0a..6d0da24d00 100644 --- a/pkg/storage/utils/decomposedfs/tree/tree.go +++ b/pkg/storage/utils/decomposedfs/tree/tree.go @@ -59,7 +59,7 @@ type Blobstore interface { // PathLookup defines the interface for the lookup component type PathLookup interface { - NodeFromPath(ctx context.Context, fn string) (*node.Node, error) + NodeFromPath(ctx context.Context, fn string, followReferences bool) (*node.Node, error) NodeFromID(ctx context.Context, id *provider.ResourceId) (n *node.Node, err error) RootNode(ctx context.Context) (node *node.Node, err error) HomeOrRootNode(ctx context.Context) (node *node.Node, err error) @@ -446,25 +446,35 @@ func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) { return t.Propagate(ctx, n) } -// RestoreRecycleItemFunc returns a node and a function to restore it from the trash -func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, key, trashPath, restorePath string) (*node.Node, func() error, error) { +// RestoreRecycleItemFunc returns a node and a function to restore it from the trash. +func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, key, trashPath, restorePath string) (*node.Node, *node.Node, func() error, error) { rn, trashItem, deletedNodePath, origin, err := t.readRecycleItem(ctx, key, trashPath) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if restorePath == "" { restorePath = origin } + var target *node.Node + target, err = t.lookup.NodeFromPath(ctx, restorePath, true) + if err != nil { + return nil, nil, nil, err + } + + p, err := target.Parent() + if err != nil { + return nil, nil, nil, err + } + fn := func() error { // link to origin var n *node.Node - n, err = t.lookup.NodeFromPath(ctx, restorePath) + n, err = t.lookup.NodeFromPath(ctx, restorePath, true) if err != nil { return err } - if n.Exists { return errtypes.AlreadyExists("origin already exists") } @@ -486,6 +496,21 @@ func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, key, trashPath, resto } } + // the new node will inherit the permissions of its parent + p, err := n.Parent() + if err != nil { + return err + } + + po, err := p.Owner() + if err != nil { + return err + } + + if err := rn.ChangeOwner(po); err != nil { + return err + } + n.Exists = true // update name attribute if err := xattr.Set(nodePath, xattrs.NameAttr, []byte(n.Name)); err != nil { @@ -505,7 +530,7 @@ func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, key, trashPath, resto } return t.Propagate(ctx, n) } - return rn, fn, nil + return rn, p, fn, nil } // PurgeRecycleItemFunc returns a node and a function to purge it from the trash diff --git a/pkg/storage/utils/decomposedfs/tree/tree_test.go b/pkg/storage/utils/decomposedfs/tree/tree_test.go index d7e41f9f69..af2d987382 100644 --- a/pkg/storage/utils/decomposedfs/tree/tree_test.go +++ b/pkg/storage/utils/decomposedfs/tree/tree_test.go @@ -61,7 +61,7 @@ var _ = Describe("Tree", func() { JustBeforeEach(func() { var err error - n, err = env.Lookup.NodeFromPath(env.Ctx, originalPath) + n, err = env.Lookup.NodeFromPath(env.Ctx, originalPath, false) Expect(err).ToNot(HaveOccurred()) }) @@ -139,33 +139,33 @@ var _ = Describe("Tree", func() { }) It("restores the file to its original location if the targetPath is empty", func() { - _, restoreFunc, err := t.RestoreRecycleItemFunc(env.Ctx, n.ID, "", "") + _, _, restoreFunc, err := t.RestoreRecycleItemFunc(env.Ctx, n.ID, "", "") Expect(err).ToNot(HaveOccurred()) Expect(restoreFunc()).To(Succeed()) - originalNode, err := env.Lookup.NodeFromPath(env.Ctx, originalPath) + originalNode, err := env.Lookup.NodeFromPath(env.Ctx, originalPath, false) Expect(err).ToNot(HaveOccurred()) Expect(originalNode.Exists).To(BeTrue()) }) It("restores files to different locations", func() { - _, restoreFunc, err := t.RestoreRecycleItemFunc(env.Ctx, n.ID, "", "dir1/newLocation") + _, _, restoreFunc, err := t.RestoreRecycleItemFunc(env.Ctx, n.ID, "", "dir1/newLocation") Expect(err).ToNot(HaveOccurred()) Expect(restoreFunc()).To(Succeed()) - newNode, err := env.Lookup.NodeFromPath(env.Ctx, "dir1/newLocation") + newNode, err := env.Lookup.NodeFromPath(env.Ctx, "dir1/newLocation", false) Expect(err).ToNot(HaveOccurred()) Expect(newNode.Exists).To(BeTrue()) - originalNode, err := env.Lookup.NodeFromPath(env.Ctx, originalPath) + originalNode, err := env.Lookup.NodeFromPath(env.Ctx, originalPath, false) Expect(err).ToNot(HaveOccurred()) Expect(originalNode.Exists).To(BeFalse()) }) It("removes the file from the trash", func() { - _, restoreFunc, err := t.RestoreRecycleItemFunc(env.Ctx, n.ID, "", "") + _, _, restoreFunc, err := t.RestoreRecycleItemFunc(env.Ctx, n.ID, "", "") Expect(err).ToNot(HaveOccurred()) Expect(restoreFunc()).To(Succeed()) @@ -184,7 +184,7 @@ var _ = Describe("Tree", func() { JustBeforeEach(func() { var err error - n, err = env.Lookup.NodeFromPath(env.Ctx, "emptydir") + n, err = env.Lookup.NodeFromPath(env.Ctx, "emptydir", false) Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/storage/utils/decomposedfs/upload.go b/pkg/storage/utils/decomposedfs/upload.go index b986ba384d..c810a5d03d 100644 --- a/pkg/storage/utils/decomposedfs/upload.go +++ b/pkg/storage/utils/decomposedfs/upload.go @@ -374,7 +374,7 @@ func (fs *Decomposedfs) lookupNode(ctx context.Context, path string) (*node.Node p = chunkInfo.Path } - n, err := fs.lu.NodeFromPath(ctx, p) + n, err := fs.lu.NodeFromPath(ctx, p, false) if err != nil { return nil, err } diff --git a/pkg/storage/utils/decomposedfs/xattrs/xattrs.go b/pkg/storage/utils/decomposedfs/xattrs/xattrs.go index c36b1eba45..db8296980d 100644 --- a/pkg/storage/utils/decomposedfs/xattrs/xattrs.go +++ b/pkg/storage/utils/decomposedfs/xattrs/xattrs.go @@ -18,6 +18,12 @@ package xattrs +import ( + "strings" + + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" +) + // Declare a list of xattr keys // TODO the below comment is currently copied from the owncloud driver, revisit // Currently,extended file attributes have four separated @@ -74,3 +80,21 @@ const ( UserAcePrefix string = "u:" GroupAcePrefix string = "g:" ) + +// ReferenceFromAttr returns a CS3 reference from xattr of a node. +// Supported formats are: "cs3:storageid/nodeid" +func ReferenceFromAttr(b []byte) (*provider.Reference, error) { + return refFromCS3(b) +} + +// refFromCS3 creates a CS3 reference from a set of bytes. This method should remain private +// and only be called after validation because it can potentially panic. +func refFromCS3(b []byte) (*provider.Reference, error) { + parts := string(b[4:]) + return &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: strings.Split(parts, "/")[0], + OpaqueId: strings.Split(parts, "/")[1], + }, + }, nil +} diff --git a/tests/acceptance/expected-failures-on-OCIS-storage.md b/tests/acceptance/expected-failures-on-OCIS-storage.md index 74da2f08ad..663f2d1711 100644 --- a/tests/acceptance/expected-failures-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-on-OCIS-storage.md @@ -638,13 +638,6 @@ _requires a [CS3 user provisioning api that can update the quota for a user](htt - [apiTrashbin/trashbinSharingToShares.feature:63](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L63) - [apiTrashbin/trashbinSharingToShares.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L64) -#### [Restoring a file to read-only received folder returns incorrect status code](https://github.com/owncloud/ocis/issues/2143) - -- [apiTrashbin/trashbinSharingToShares.feature:82](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L82) -- [apiTrashbin/trashbinSharingToShares.feature:83](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L83) -- [apiTrashbin/trashbinSharingToShares.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L102) -- [apiTrashbin/trashbinSharingToShares.feature:103](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L103) - #### [No way to set default folder for received shares](https://github.com/owncloud/ocis/issues/1327) Scenario Outline: delete a folder when there is a default folder for received shares - [apiWebdavOperations/deleteFolder.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavOperations/deleteFolder.feature#L67) diff --git a/tests/acceptance/expected-failures-on-S3NG-storage.md b/tests/acceptance/expected-failures-on-S3NG-storage.md index b120f7af1d..c793f58a91 100644 --- a/tests/acceptance/expected-failures-on-S3NG-storage.md +++ b/tests/acceptance/expected-failures-on-S3NG-storage.md @@ -626,13 +626,6 @@ _requires a [CS3 user provisioning api that can update the quota for a user](htt - [apiTrashbin/trashbinSharingToShares.feature:63](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L63) - [apiTrashbin/trashbinSharingToShares.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L64) -#### [Restoring a file to read-only received folder returns incorrect status code](https://github.com/owncloud/ocis/issues/2143) - -- [apiTrashbin/trashbinSharingToShares.feature:82](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L82) -- [apiTrashbin/trashbinSharingToShares.feature:83](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L83) -- [apiTrashbin/trashbinSharingToShares.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L102) -- [apiTrashbin/trashbinSharingToShares.feature:103](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinSharingToShares.feature#L103) - #### [No way to set default folder for received shares](https://github.com/owncloud/ocis/issues/1327) Scenario Outline: delete a folder when there is a default folder for received shares - [apiWebdavOperations/deleteFolder.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavOperations/deleteFolder.feature#L67)