Skip to content

Commit

Permalink
add driveAlias (#2623)
Browse files Browse the repository at this point in the history
* add driveAlias

* add unit test

* code cleanup

* use configurable templates for space alias

* add more tests

* add tests with custom alias template
  • Loading branch information
micbar committed Mar 15, 2022
1 parent a24b53d commit d008faf
Show file tree
Hide file tree
Showing 18 changed files with 234 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ build-reva: imports
go build -ldflags ${BUILD_FLAGS} -o ./cmd/reva/reva ./cmd/reva

test: off
go test -coverprofile coverage.out -race $$(go list ./... | grep -v /tests/integration)
go test -v -coverprofile coverage.out -race $$(go list ./... | grep -v /tests/integration)

test-integration: build-ci
cd tests/integration && go test -race ./...
Expand Down
5 changes: 5 additions & 0 deletions changelog/unreleased/space-aliases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add space aliases

Space aliases can be used to resolve spaceIDs in a client.

https://github.com/cs3org/reva/pull/2623
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var _ = Describe("Decomposed", func() {

BeforeEach(func() {
var err error
env, err = testhelpers.NewTestEnv()
env, err = testhelpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())
})

Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/decomposedfs/decomposedfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ var _ = Describe("Decomposed", func() {

JustBeforeEach(func() {
var err error
env, err = helpers.NewTestEnv()
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())

ref = &provider.Reference{
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/decomposedfs/grants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ var _ = Describe("Grants", func() {

JustBeforeEach(func() {
var err error
env, err = helpers.NewTestEnv()
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())

ref = &provider.Reference{
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/decomposedfs/lookup/lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var _ = Describe("Lookup", func() {

JustBeforeEach(func() {
var err error
env, err = helpers.NewTestEnv()
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())
})

Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/decomposedfs/node/locks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var _ = Describe("Node locks", func() {

BeforeEach(func() {
var err error
env, err = helpers.NewTestEnv()
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())

lock = &provider.Lock{
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/decomposedfs/node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var _ = Describe("Node", func() {

BeforeEach(func() {
var err error
env, err = helpers.NewTestEnv()
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())

id = "fooId"
Expand Down
11 changes: 11 additions & 0 deletions pkg/storage/utils/decomposedfs/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ type Options struct {

// permissions service to use when checking permissions
PermissionsSVC string `mapstructure:"permissionssvc"`

PersonalSpaceAliasTemplate string `mapstructure:"personalspacealias_template"`
GeneralSpaceAliasTemplate string `mapstructure:"generalspacealias_template"`
}

// New returns a new Options instance for the given configuration
Expand All @@ -73,5 +76,13 @@ func New(m map[string]interface{}) (*Options, error) {
// c.DataDirectory should never end in / unless it is the root
o.Root = filepath.Clean(o.Root)

if o.PersonalSpaceAliasTemplate == "" {
o.PersonalSpaceAliasTemplate = "{{.SpaceType}}/{{.User.Username}}"
}

if o.GeneralSpaceAliasTemplate == "" {
o.GeneralSpaceAliasTemplate = "{{.SpaceType}}/{{.SpaceName | replace \" \" \"-\" | lower}}"
}

return o, nil
}
20 changes: 10 additions & 10 deletions pkg/storage/utils/decomposedfs/recycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ var _ = Describe("Recycle", func() {

BeforeEach(func() {
var err error
env, err = helpers.NewTestEnv()
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())
})

Context("with sufficient permissions", func() {
When("a user deletes files from the same space", func() {

BeforeEach(func() {
// in this scenario user "u-s-e-r-id" has this permissions:
registerPermissions(env.Permissions, "u-s-e-r-id", &provider.ResourcePermissions{
// in this scenario user "25b69780-5f39-43be-a7ac-a9b9e9fe4230" has this permissions:
registerPermissions(env.Permissions, "25b69780-5f39-43be-a7ac-a9b9e9fe4230", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
Expand Down Expand Up @@ -132,8 +132,8 @@ var _ = Describe("Recycle", func() {
Username: "anotherusername",
})

// in this scenario user "u-s-e-r-id" has this permissions:
registerPermissions(env.Permissions, "u-s-e-r-id", &provider.ResourcePermissions{
// in this scenario user "25b69780-5f39-43be-a7ac-a9b9e9fe4230" has this permissions:
registerPermissions(env.Permissions, "25b69780-5f39-43be-a7ac-a9b9e9fe4230", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
Expand Down Expand Up @@ -245,8 +245,8 @@ var _ = Describe("Recycle", func() {
Expect(err).ToNot(HaveOccurred())
Expect(projectID).ToNot(BeNil())

// in this scenario user "u-s-e-r-id" has this permissions:
registerPermissions(env.Permissions, "u-s-e-r-id", &provider.ResourcePermissions{
// in this scenario user "25b69780-5f39-43be-a7ac-a9b9e9fe4230" has this permissions:
registerPermissions(env.Permissions, "25b69780-5f39-43be-a7ac-a9b9e9fe4230", &provider.ResourcePermissions{
InitiateFileUpload: true,
Delete: true,
ListRecycle: true,
Expand Down Expand Up @@ -317,8 +317,8 @@ var _ = Describe("Recycle", func() {
Username: "readusername",
})

// in this scenario user "u-s-e-r-id" has this permissions:
registerPermissions(env.Permissions, "u-s-e-r-id", &provider.ResourcePermissions{
// in this scenario user "25b69780-5f39-43be-a7ac-a9b9e9fe4230" has this permissions:
registerPermissions(env.Permissions, "25b69780-5f39-43be-a7ac-a9b9e9fe4230", &provider.ResourcePermissions{
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
Expand Down Expand Up @@ -403,7 +403,7 @@ var _ = Describe("Recycle", func() {
})

// in this scenario user "userid" has this permissions:
registerPermissions(env.Permissions, "u-s-e-r-id", &provider.ResourcePermissions{
registerPermissions(env.Permissions, "25b69780-5f39-43be-a7ac-a9b9e9fe4230", &provider.ResourcePermissions{
Delete: true,
ListRecycle: true,
PurgeRecycle: true,
Expand Down
64 changes: 34 additions & 30 deletions pkg/storage/utils/decomposedfs/spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup"
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/xattrs"
"github.com/cs3org/reva/v2/pkg/storage/utils/templates"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/cs3org/reva/v2/pkg/utils/resourceid"
"github.com/google/uuid"
Expand All @@ -62,22 +63,22 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
// "everything is a resource" this is the unique ID for the Space resource.
spaceID := uuid.New().String()
// allow sending a space id
if req.Opaque != nil && req.Opaque.Map != nil {
if e, ok := req.Opaque.Map["spaceid"]; ok && e.Decoder == "plain" {
spaceID = string(e.Value)
}
if reqSpaceID := utils.ReadPlainFromOpaque(req.Opaque, "spaceid"); reqSpaceID != "" {
spaceID = reqSpaceID
}
// allow sending a space description
var description string
if req.Opaque != nil && req.Opaque.Map != nil {
if e, ok := req.Opaque.Map["description"]; ok && e.Decoder == "plain" {
description = string(e.Value)
}
description := utils.ReadPlainFromOpaque(req.Opaque, "description")
// allow sending a spaceAlias
alias := utils.ReadPlainFromOpaque(req.Opaque, "spaceAlias")
u := ctxpkg.ContextMustGetUser(ctx)
if alias == "" {
alias = templates.WithSpacePropertiesAndUser(u, req.Type, req.Name, fs.o.GeneralSpaceAliasTemplate)
}
// TODO enforce a uuid?
// TODO clarify if we want to enforce a single personal storage space or if we want to allow sending the spaceid
if req.Type == spaceTypePersonal {
spaceID = req.GetOwner().GetId().GetOpaqueId()
alias = templates.WithSpacePropertiesAndUser(u, req.Type, req.Name, fs.o.PersonalSpaceAliasTemplate)
}

root, err := node.ReadNode(ctx, fs.lu, spaceID, spaceID)
Expand Down Expand Up @@ -109,7 +110,7 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
return nil, err
}

metadata := make(map[string]string, 3)
metadata := make(map[string]string, 6)

// always enable propagation on the storage space root
// mark the space root node as the end of propagation
Expand All @@ -129,6 +130,10 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
metadata[xattrs.SpaceDescriptionAttr] = description
}

if alias != "" {
metadata[xattrs.SpaceAliasAttr] = alias
}

if err := xattrs.SetMultiple(root.InternalPath(), metadata); err != nil {
return nil, err
}
Expand Down Expand Up @@ -428,17 +433,20 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up
metadata[xattrs.SpaceDescriptionAttr] = string(description.Value)
hasDescription = true
}
if image, ok := space.Opaque.Map["image"]; ok {
imageID := resourceid.OwnCloudResourceIDUnwrap(string(image.Value))
if alias := utils.ReadPlainFromOpaque(space.Opaque, "spaceAlias"); alias != "" {
metadata[xattrs.SpaceAliasAttr] = alias
}
if image := utils.ReadPlainFromOpaque(space.Opaque, "image"); image != "" {
imageID := resourceid.OwnCloudResourceIDUnwrap(image)
if imageID == nil {
return &provider.UpdateStorageSpaceResponse{
Status: &v1beta11.Status{Code: v1beta11.Code_CODE_NOT_FOUND, Message: "decomposedFS: space image resource not found"},
}, nil
}
metadata[xattrs.SpaceImageAttr] = imageID.OpaqueId
}
if readme, ok := space.Opaque.Map["readme"]; ok {
readmeID := resourceid.OwnCloudResourceIDUnwrap(string(readme.Value))
if readme := utils.ReadPlainFromOpaque(space.Opaque, "readme"); readme != "" {
readmeID := resourceid.OwnCloudResourceIDUnwrap(readme)
if readmeID == nil {
return &provider.UpdateStorageSpaceResponse{
Status: &v1beta11.Status{Code: v1beta11.Code_CODE_NOT_FOUND, Message: "decomposedFS: space readme resource not found"},
Expand Down Expand Up @@ -634,10 +642,7 @@ func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, n *node.Node,
}

if n.SpaceRoot.IsDisabled() {
space.Opaque.Map["trashed"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte("trashed"),
}
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "trashed", "trashed")
}

if n.Owner() != nil && n.Owner().OpaqueId != "" {
Expand Down Expand Up @@ -699,24 +704,23 @@ func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, n *node.Node,
}
spaceImage, ok := spaceAttributes[xattrs.SpaceImageAttr]
if ok {
space.Opaque.Map["image"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte(resourceid.OwnCloudResourceIDWrap(&provider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: spaceImage})),
}
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "image", resourceid.OwnCloudResourceIDWrap(
&provider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: spaceImage},
))
}
spaceDescription, ok := spaceAttributes[xattrs.SpaceDescriptionAttr]
if ok {
space.Opaque.Map["description"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte(spaceDescription),
}
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "description", spaceDescription)
}
spaceReadme, ok := spaceAttributes[xattrs.SpaceReadmeAttr]
if ok {
space.Opaque.Map["readme"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte(resourceid.OwnCloudResourceIDWrap(&provider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: spaceReadme})),
}
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "readme", resourceid.OwnCloudResourceIDWrap(
&provider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: spaceReadme},
))
}
spaceAlias, ok := spaceAttributes[xattrs.SpaceAliasAttr]
if ok {
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "spaceAlias", spaceAlias)
}
return space, nil
}
Expand Down
109 changes: 109 additions & 0 deletions pkg/storage/utils/decomposedfs/spaces_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2018-2022 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package decomposedfs_test

import (
permissionsv1beta1 "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
helpers "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/testhelpers"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
)

var _ = Describe("Create Spaces", func() {
var (
env *helpers.TestEnv
)

BeforeEach(func() {
var err error
env, err = helpers.NewTestEnv(nil)
Expect(err).ToNot(HaveOccurred())
env.PermissionsClient.On("CheckPermission", mock.Anything, mock.Anything, mock.Anything).Return(&permissionsv1beta1.CheckPermissionResponse{Status: &rpcv1beta1.Status{Code: rpcv1beta1.Code_CODE_OK}}, nil)
})

AfterEach(func() {
if env != nil {
env.Cleanup()
}
})

Context("during login", func() {
It("space is created", func() {
resp, err := env.Fs.ListStorageSpaces(env.Ctx, nil)
Expect(err).ToNot(HaveOccurred())
Expect(len(resp)).To(Equal(1))
Expect(string(resp[0].Opaque.GetMap()["spaceAlias"].Value)).To(Equal("personal/username"))
Expect(resp[0].Name).To(Equal("username"))
Expect(resp[0].SpaceType).To(Equal("personal"))
})
})
Context("when creating a space", func() {
It("project space is created", func() {
resp, err := env.Fs.CreateStorageSpace(env.Ctx, &provider.CreateStorageSpaceRequest{Name: "Mission to Mars", Type: "project"})
Expect(err).ToNot(HaveOccurred())
Expect(resp.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
Expect(resp.StorageSpace).ToNot(Equal(nil))
Expect(string(resp.StorageSpace.Opaque.Map["spaceAlias"].Value)).To(Equal("project/mission-to-mars"))
Expect(resp.StorageSpace.Name).To(Equal("Mission to Mars"))
Expect(resp.StorageSpace.SpaceType).To(Equal("project"))
})
})
Describe("Create Spaces with custom alias template", func() {
var (
env *helpers.TestEnv
)

BeforeEach(func() {
var err error
env, err = helpers.NewTestEnv(map[string]interface{}{
"personalspacealias_template": "{{.SpaceType}}/{{.Email.Local}}@{{.Email.Domain}}",
"generalspacealias_template": "{{.SpaceType}}:{{.SpaceName | replace \" \" \"-\" | upper}}",
})
Expect(err).ToNot(HaveOccurred())
env.PermissionsClient.On("CheckPermission", mock.Anything, mock.Anything, mock.Anything).Return(&permissionsv1beta1.CheckPermissionResponse{Status: &rpcv1beta1.Status{Code: rpcv1beta1.Code_CODE_OK}}, nil)
})

AfterEach(func() {
if env != nil {
env.Cleanup()
}
})
Context("during login", func() {
It("personal space is created with custom alias", func() {
resp, err := env.Fs.ListStorageSpaces(env.Ctx, nil)
Expect(err).ToNot(HaveOccurred())
Expect(len(resp)).To(Equal(1))
Expect(string(resp[0].Opaque.GetMap()["spaceAlias"].Value)).To(Equal("personal/username@_unknown"))
})
})
Context("creating a space", func() {
It("project space is created with custom alias", func() {
resp, err := env.Fs.CreateStorageSpace(env.Ctx, &provider.CreateStorageSpaceRequest{Name: "Mission to Venus", Type: "project"})
Expect(err).ToNot(HaveOccurred())
Expect(resp.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
Expect(resp.StorageSpace).ToNot(Equal(nil))
Expect(string(resp.StorageSpace.Opaque.Map["spaceAlias"].Value)).To(Equal("project:MISSION-TO-VENUS"))

})
})
})
})
Loading

0 comments on commit d008faf

Please sign in to comment.