Skip to content

Commit

Permalink
[tests-only] Recycle and restore (#2389)
Browse files Browse the repository at this point in the history
* simplify permissions in recycle unit tests

Signed-off-by: jkoberg <[email protected]>

* add tests regarding recycle-restore

Signed-off-by: jkoberg <[email protected]>
  • Loading branch information
kobergj authored Dec 21, 2021
1 parent 9e4af06 commit 0557b8b
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 61 deletions.
246 changes: 187 additions & 59 deletions pkg/storage/utils/decomposedfs/recycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
ctxpkg "github.com/cs3org/reva/pkg/ctx"
"github.com/cs3org/reva/pkg/storage/utils/decomposedfs/mocks"
helpers "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/testhelpers"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
Expand All @@ -43,12 +44,21 @@ var _ = Describe("Recycle", func() {
})

Context("with sufficient permissions", func() {
BeforeEach(func() {
})

When("a user deletes files from the same space", func() {

BeforeEach(func() {
// in this scenario user "userid" has this permissions:
registerPermissions(env.Permissions, "userid", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
RestoreRecycleItem: true,
GetQuota: true,
})
})

JustBeforeEach(func() {
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(2)
err := env.Fs.Delete(env.Ctx, &provider.Reference{
ResourceId: env.SpaceRootRes,
Path: "/dir1/file1",
Expand All @@ -63,22 +73,20 @@ var _ = Describe("Recycle", func() {
})

It("they are stored in the same trashbin", func() {
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(1)
items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(2))
})

It("they do not count towards the quota anymore", func() {
env.Permissions.On("AssemblePermissions", mock.Anything, mock.Anything).Return(provider.ResourcePermissions{GetQuota: true}, nil).Times(1)
_, used, err := env.Fs.GetQuota(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes})
Expect(err).ToNot(HaveOccurred())
Expect(used).To(Equal(uint64(0)))
})

It("they can be permanently deleted by this user", func() {
// mock call to blobstore
env.Blobstore.On("Delete", mock.Anything).Return(nil).Times(2)
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(4)

items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expand All @@ -94,6 +102,21 @@ var _ = Describe("Recycle", func() {
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(0))
})

It("they can be restored", func() {
env.Blobstore.On("Delete", mock.Anything).Return(nil).Times(2)

items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(2))

err = env.Fs.RestoreRecycleItem(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, items[0].Key, "/", nil)
Expect(err).ToNot(HaveOccurred())

items, err = env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(1))
})
})

When("two users delete files from the same space", func() {
Expand All @@ -108,10 +131,27 @@ var _ = Describe("Recycle", func() {
},
Username: "anotherusername",
})

// in this scenario user "userid" has this permissions:
registerPermissions(env.Permissions, "userid", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
RestoreRecycleItem: true,
})

// and user "anotheruserid" has the same permissions:
registerPermissions(env.Permissions, "anotheruserid", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
RestoreRecycleItem: true,
})
})

JustBeforeEach(func() {
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(2)
err := env.Fs.Delete(env.Ctx, &provider.Reference{
ResourceId: env.SpaceRootRes,
Path: "/dir1/file1",
Expand All @@ -127,7 +167,6 @@ var _ = Describe("Recycle", func() {
})

It("they are stored in the same trashbin (for both users)", func() {
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(2)
itemsA, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(itemsA)).To(Equal(2))
Expand All @@ -141,7 +180,6 @@ var _ = Describe("Recycle", func() {

It("they can be permanently deleted by the other user", func() {
env.Blobstore.On("Delete", mock.Anything).Return(nil).Times(2)
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(4)

items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expand All @@ -168,18 +206,56 @@ var _ = Describe("Recycle", func() {
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(0))
})

It("they can be restored by the other user", func() {
env.Blobstore.On("Delete", mock.Anything).Return(nil).Times(2)

items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(2))

// pick correct ctx
var ctx1, ctx2 context.Context
switch items[0].Type {
case provider.ResourceType_RESOURCE_TYPE_FILE:
ctx1 = env.Ctx
ctx2 = ctx
case provider.ResourceType_RESOURCE_TYPE_CONTAINER:
ctx1 = ctx
ctx2 = env.Ctx
}

err = env.Fs.RestoreRecycleItem(ctx1, &provider.Reference{ResourceId: env.SpaceRootRes}, items[0].Key, "/", nil)
Expect(err).ToNot(HaveOccurred())

err = env.Fs.RestoreRecycleItem(ctx2, &provider.Reference{ResourceId: env.SpaceRootRes}, items[1].Key, "/", nil)
Expect(err).ToNot(HaveOccurred())

items, err = env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(0))
})
})

When("a user deletes files from different spaces", func() {
BeforeEach(func() {
var err error
projectID, err = env.CreateTestStorageSpace("project")
projectID, err = env.CreateTestStorageSpace("project", &provider.Quota{QuotaMaxBytes: 2000})
Expect(err).ToNot(HaveOccurred())
Expect(projectID).ToNot(BeNil())

// in this scenario user "userid" has this permissions:
registerPermissions(env.Permissions, "userid", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
RestoreRecycleItem: true,
GetQuota: true,
})
})

JustBeforeEach(func() {
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(2)
err := env.Fs.Delete(env.Ctx, &provider.Reference{
ResourceId: env.SpaceRootRes,
Path: "/dir1/file1",
Expand All @@ -194,7 +270,6 @@ var _ = Describe("Recycle", func() {
})

It("they are stored in different trashbins", func() {
env.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(2)
items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(1))
Expand All @@ -207,6 +282,25 @@ var _ = Describe("Recycle", func() {

Expect(recycled1).ToNot(Equal(recycled2))
})

It("they can excess the spaces quota if restored", func() {
items, err := env.Fs.ListRecycle(env.Ctx, &provider.Reference{ResourceId: projectID}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(1))

// use up 2000 byte quota
_, err = env.CreateTestFile("largefile", "largefile-blobid", 2000, projectID.OpaqueId)
Expect(err).ToNot(HaveOccurred())

err = env.Fs.RestoreRecycleItem(env.Ctx, &provider.Reference{ResourceId: projectID}, items[0].Key, "/", nil)
Expect(err).ToNot(HaveOccurred())

max, used, err := env.Fs.GetQuota(env.Ctx, &provider.Reference{ResourceId: projectID})
Expect(err).ToNot(HaveOccurred())
Expect(max).To(Equal(uint64(2000)))
Expect(used).To(Equal(uint64(3234)))
})

})
})
Context("with insufficient permissions", func() {
Expand All @@ -222,38 +316,20 @@ var _ = Describe("Recycle", func() {
Username: "readusername",
})

// need user with access ...
env.Permissions.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == "userid"
}),
mock.Anything,
mock.Anything,
).Return(true, nil)

// and user with read access ...
env.Permissions.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == "readuserid"
}),
mock.Anything,
mock.MatchedBy(func(f func(*provider.ResourcePermissions) bool) bool {
return f(&provider.ResourcePermissions{ListRecycle: true})
}),
).Return(true, nil)
env.Permissions.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == "readuserid"
}),
mock.Anything,
mock.MatchedBy(func(f func(*provider.ResourcePermissions) bool) bool {
return f(&provider.ResourcePermissions{
PurgeRecycle: true,
Delete: true,
})
}),
).Return(false, nil)
// in this scenario user "userid" has this permissions:
registerPermissions(env.Permissions, "userid", &provider.ResourcePermissions{
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
RestoreRecycleItem: true,
})

// and user "readuserid" has this permissions:
registerPermissions(env.Permissions, "readuserid", &provider.ResourcePermissions{
ListRecycle: true,
})
})

It("can list the trashbin", func() {
err := env.Fs.Delete(env.Ctx, &provider.Reference{
ResourceId: env.SpaceRootRes,
Expand Down Expand Up @@ -294,6 +370,22 @@ var _ = Describe("Recycle", func() {
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("permission denied"))
})

It("cannot restore files from trashbin", func() {
err := env.Fs.Delete(env.Ctx, &provider.Reference{
ResourceId: env.SpaceRootRes,
Path: "/dir1/file1",
})
Expect(err).ToNot(HaveOccurred())

items, err := env.Fs.ListRecycle(ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, "", "/")
Expect(err).ToNot(HaveOccurred())
Expect(len(items)).To(Equal(1))

err = env.Fs.RestoreRecycleItem(ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, items[0].Key, "/", nil)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("permission denied"))
})
})
})

Expand All @@ -308,23 +400,20 @@ var _ = Describe("Recycle", func() {
},
Username: "mrhacker",
})
env.Permissions.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == "userid"
}),
mock.Anything,
mock.Anything,
).Return(true, nil)
env.Permissions.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == "hacker"
}),
mock.Anything,
mock.Anything,
).Return(false, nil)

// in this scenario user "userid" has this permissions:
registerPermissions(env.Permissions, "userid", &provider.ResourcePermissions{
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
RestoreRecycleItem: true,
})

// and user "hacker" has no permissions:
registerPermissions(env.Permissions, "hacker", &provider.ResourcePermissions{})
})

It("cannot delete, list or purge", func() {
It("cannot delete, list, purge or restore", func() {
err := env.Fs.Delete(ctx, &provider.Reference{
ResourceId: env.SpaceRootRes,
Path: "/dir1/file1",
Expand All @@ -349,6 +438,45 @@ var _ = Describe("Recycle", func() {
err = env.Fs.PurgeRecycleItem(ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, items[0].Key, "/")
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("permission denied"))

err = env.Fs.RestoreRecycleItem(ctx, &provider.Reference{ResourceId: env.SpaceRootRes}, items[0].Key, "/", nil)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("permission denied"))
})
})
})

func registerPermissions(m *mocks.PermissionsChecker, uid string, exp *provider.ResourcePermissions) {
// add positives
m.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return uid == "" || ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == uid
}),
mock.Anything,
mock.MatchedBy(func(r func(*provider.ResourcePermissions) bool) bool {
return exp == nil || r(exp)
}),
).Return(true, nil)

// add negatives
if exp != nil {
m.On("HasPermission",
mock.MatchedBy(func(ctx context.Context) bool {
return uid == "" || ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == uid
}),
mock.Anything,
mock.Anything,
).Return(false, nil)
}

p := provider.ResourcePermissions{}
if exp != nil {
p = *exp
}
m.On("AssemblePermissions",
mock.MatchedBy(func(ctx context.Context) bool {
return uid == "" || ctxpkg.ContextMustGetUser(ctx).Id.OpaqueId == uid
}),
mock.Anything,
).Return(p, nil)
}
5 changes: 3 additions & 2 deletions pkg/storage/utils/decomposedfs/testhelpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func NewTestEnv() (*TestEnv, error) {
Ctx: ctx,
}

env.SpaceRootRes, err = env.CreateTestStorageSpace("personal")
env.SpaceRootRes, err = env.CreateTestStorageSpace("personal", nil)
return env, err
}

Expand Down Expand Up @@ -171,11 +171,12 @@ func (t *TestEnv) CreateTestFile(name, blobID string, blobSize int64, parentID s
// /dir1/
// /dir1/file1
// /dir1/subdir1
func (t *TestEnv) CreateTestStorageSpace(typ string) (*providerv1beta1.ResourceId, error) {
func (t *TestEnv) CreateTestStorageSpace(typ string, quota *providerv1beta1.Quota) (*providerv1beta1.ResourceId, error) {
t.Permissions.On("HasPermission", mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Times(1) // Permissions required for setup below
space, err := t.Fs.CreateStorageSpace(t.Ctx, &providerv1beta1.CreateStorageSpaceRequest{
Owner: t.Owner,
Type: typ,
Quota: quota,
})
if err != nil {
return nil, err
Expand Down

0 comments on commit 0557b8b

Please sign in to comment.