diff --git a/Makefile b/Makefile index 3a59bea137..1de6e3e747 100644 --- a/Makefile +++ b/Makefile @@ -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 ./... diff --git a/changelog/unreleased/space-aliases.md b/changelog/unreleased/space-aliases.md new file mode 100644 index 0000000000..3e01e0b102 --- /dev/null +++ b/changelog/unreleased/space-aliases.md @@ -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 diff --git a/pkg/storage/utils/decomposedfs/decomposedfs_concurrency_test.go b/pkg/storage/utils/decomposedfs/decomposedfs_concurrency_test.go index 14c3499224..7bc6fe1cb8 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs_concurrency_test.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs_concurrency_test.go @@ -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()) }) diff --git a/pkg/storage/utils/decomposedfs/decomposedfs_test.go b/pkg/storage/utils/decomposedfs/decomposedfs_test.go index 63dd901be6..d456628b1a 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs_test.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs_test.go @@ -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{ diff --git a/pkg/storage/utils/decomposedfs/grants_test.go b/pkg/storage/utils/decomposedfs/grants_test.go index 980470e449..13f9f9ac2e 100644 --- a/pkg/storage/utils/decomposedfs/grants_test.go +++ b/pkg/storage/utils/decomposedfs/grants_test.go @@ -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{ diff --git a/pkg/storage/utils/decomposedfs/lookup/lookup_test.go b/pkg/storage/utils/decomposedfs/lookup/lookup_test.go index 6598d7c283..aacdcc1789 100644 --- a/pkg/storage/utils/decomposedfs/lookup/lookup_test.go +++ b/pkg/storage/utils/decomposedfs/lookup/lookup_test.go @@ -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()) }) diff --git a/pkg/storage/utils/decomposedfs/node/locks_test.go b/pkg/storage/utils/decomposedfs/node/locks_test.go index ec6ab2d5b3..f11878953e 100644 --- a/pkg/storage/utils/decomposedfs/node/locks_test.go +++ b/pkg/storage/utils/decomposedfs/node/locks_test.go @@ -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{ diff --git a/pkg/storage/utils/decomposedfs/node/node_test.go b/pkg/storage/utils/decomposedfs/node/node_test.go index 0f58a451d3..c653ba6b26 100644 --- a/pkg/storage/utils/decomposedfs/node/node_test.go +++ b/pkg/storage/utils/decomposedfs/node/node_test.go @@ -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" diff --git a/pkg/storage/utils/decomposedfs/options/options.go b/pkg/storage/utils/decomposedfs/options/options.go index 765c0c0c53..16811837a7 100644 --- a/pkg/storage/utils/decomposedfs/options/options.go +++ b/pkg/storage/utils/decomposedfs/options/options.go @@ -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 @@ -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 } diff --git a/pkg/storage/utils/decomposedfs/recycle_test.go b/pkg/storage/utils/decomposedfs/recycle_test.go index 6fdcec20e1..e5e6a3b2d8 100644 --- a/pkg/storage/utils/decomposedfs/recycle_test.go +++ b/pkg/storage/utils/decomposedfs/recycle_test.go @@ -39,7 +39,7 @@ var _ = Describe("Recycle", func() { BeforeEach(func() { var err error - env, err = helpers.NewTestEnv() + env, err = helpers.NewTestEnv(nil) Expect(err).ToNot(HaveOccurred()) }) @@ -47,8 +47,8 @@ var _ = Describe("Recycle", 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, @@ -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, @@ -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, @@ -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, @@ -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, diff --git a/pkg/storage/utils/decomposedfs/spaces.go b/pkg/storage/utils/decomposedfs/spaces.go index 651c758798..2421db8afe 100644 --- a/pkg/storage/utils/decomposedfs/spaces.go +++ b/pkg/storage/utils/decomposedfs/spaces.go @@ -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" @@ -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) @@ -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 @@ -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 } @@ -428,8 +433,11 @@ 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"}, @@ -437,8 +445,8 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up } 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"}, @@ -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 != "" { @@ -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 } diff --git a/pkg/storage/utils/decomposedfs/spaces_test.go b/pkg/storage/utils/decomposedfs/spaces_test.go new file mode 100644 index 0000000000..d68bcac767 --- /dev/null +++ b/pkg/storage/utils/decomposedfs/spaces_test.go @@ -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")) + + }) + }) + }) +}) diff --git a/pkg/storage/utils/decomposedfs/testhelpers/helpers.go b/pkg/storage/utils/decomposedfs/testhelpers/helpers.go index 8b2c5fbec2..9581766619 100644 --- a/pkg/storage/utils/decomposedfs/testhelpers/helpers.go +++ b/pkg/storage/utils/decomposedfs/testhelpers/helpers.go @@ -64,20 +64,27 @@ type TestEnv struct { // /dir1/ // /dir1/file1 // /dir1/subdir1/ -func NewTestEnv() (*TestEnv, error) { +// +// The default config can be overridden by providing the strings to override +// via map as a parameter +func NewTestEnv(config map[string]interface{}) (*TestEnv, error) { tmpRoot, err := helpers.TempDir("reva-unit-tests-*-root") if err != nil { return nil, err } - - config := map[string]interface{}{ + defaultConfig := map[string]interface{}{ "root": tmpRoot, "treetime_accounting": true, "treesize_accounting": true, "share_folder": "/Shares", "user_layout": "{{.Id.OpaqueId}}", } - o, err := options.New(config) + // make it possible to override single config values + for k, v := range config { + defaultConfig[k] = v + } + + o, err := options.New(defaultConfig) if err != nil { return nil, err } @@ -85,7 +92,7 @@ func NewTestEnv() (*TestEnv, error) { owner := &userpb.User{ Id: &userpb.UserId{ Idp: "idp", - OpaqueId: "u-s-e-r-id", + OpaqueId: "25b69780-5f39-43be-a7ac-a9b9e9fe4230", Type: userpb.UserType_USER_TYPE_PRIMARY, }, Username: "username", diff --git a/pkg/storage/utils/decomposedfs/tree/tree_test.go b/pkg/storage/utils/decomposedfs/tree/tree_test.go index 896126cd1c..9486372637 100644 --- a/pkg/storage/utils/decomposedfs/tree/tree_test.go +++ b/pkg/storage/utils/decomposedfs/tree/tree_test.go @@ -45,7 +45,7 @@ var _ = Describe("Tree", func() { JustBeforeEach(func() { var err error - env, err = helpers.NewTestEnv() + env, err = helpers.NewTestEnv(nil) Expect(err).ToNot(HaveOccurred()) t = env.Tree }) diff --git a/pkg/storage/utils/decomposedfs/xattrs/xattrs.go b/pkg/storage/utils/decomposedfs/xattrs/xattrs.go index 65dd18ad9e..664b9bcb32 100644 --- a/pkg/storage/utils/decomposedfs/xattrs/xattrs.go +++ b/pkg/storage/utils/decomposedfs/xattrs/xattrs.go @@ -96,6 +96,7 @@ const ( SpaceDescriptionAttr string = OcisPrefix + "space.description" SpaceReadmeAttr string = OcisPrefix + "space.readme" SpaceImageAttr string = OcisPrefix + "space.image" + SpaceAliasAttr string = OcisPrefix + "space.alias" UserAcePrefix string = "u:" GroupAcePrefix string = "g:" diff --git a/pkg/storage/utils/templates/templates.go b/pkg/storage/utils/templates/templates.go index 2741073cc6..61c3e5d22e 100644 --- a/pkg/storage/utils/templates/templates.go +++ b/pkg/storage/utils/templates/templates.go @@ -43,6 +43,14 @@ type UserData struct { Email EmailData } +// SpaceData contains the templace placeholders for a space. +// For example {{.SpaceName}} {{.SpaceType}} or {{.User.Id.OpaqueId}} +type SpaceData struct { + *UserData + SpaceType string + SpaceName string +} + // EmailData contains mail data // split into local and domain part. // It is extracted from splitting the username by @. @@ -69,8 +77,30 @@ func WithUser(u *userpb.User, tpl string) string { return b.String() } +// WithSpacePropertiesAndUser generates a layout based on user data and a space type. +func WithSpacePropertiesAndUser(u *userpb.User, spaceType string, spaceName string, tpl string) string { + tpl = clean(tpl) + sd := newSpaceData(u, spaceType, spaceName) + // compile given template tpl + t, err := template.New("tpl").Funcs(sprig.TxtFuncMap()).Parse(tpl) + if err != nil { + err := errors.Wrap(err, fmt.Sprintf("error parsing template: spaceanduser_template:%+v tpl:%s", sd, tpl)) + panic(err) + } + b := bytes.Buffer{} + if err := t.Execute(&b, sd); err != nil { + err := errors.Wrap(err, fmt.Sprintf("error executing template: spaceanduser_template:%+v tpl:%s", sd, tpl)) + panic(err) + } + return b.String() +} + func newUserData(u *userpb.User) *UserData { usernameSplit := strings.Split(u.Username, "@") + if u.Mail != "" { + usernameSplit = strings.Split(u.Mail, "@") + } + if len(usernameSplit) == 1 { usernameSplit = append(usernameSplit, "_unknown") } @@ -88,6 +118,16 @@ func newUserData(u *userpb.User) *UserData { return ut } +func newSpaceData(u *userpb.User, st string, n string) *SpaceData { + userData := newUserData(u) + sd := &SpaceData{ + userData, + st, + n, + } + return sd +} + func clean(a string) string { return path.Clean(a) } diff --git a/tests/oc-integration-tests/drone/storage-users-ocis.toml b/tests/oc-integration-tests/drone/storage-users-ocis.toml index ed175d2850..d076b92bf1 100644 --- a/tests/oc-integration-tests/drone/storage-users-ocis.toml +++ b/tests/oc-integration-tests/drone/storage-users-ocis.toml @@ -23,6 +23,8 @@ root = "/drone/src/tmp/reva/data" treetime_accounting = true treesize_accounting = true permissionssvc = "localhost:10000" +personalspacealias_template = "{{.SpaceType}}/{{.User.Username}}" +generalspacealias_template = "{{.SpaceType}}/{{.SpaceName | replace \" \" \"-\" | lower}}" # we have a locally running dataprovider [http] diff --git a/tests/oc-integration-tests/local/storage-users.toml b/tests/oc-integration-tests/local/storage-users.toml index 0c50bcb3d8..a270504b28 100644 --- a/tests/oc-integration-tests/local/storage-users.toml +++ b/tests/oc-integration-tests/local/storage-users.toml @@ -27,6 +27,8 @@ root = "/var/tmp/reva/data" treetime_accounting = true treesize_accounting = true permissionssvc = "localhost:10000" +personalspacealias_template = "{{.SpaceType}}/{{.User.Username}}" +generalspacealias_template = "{{.SpaceType}}/{{.SpaceName | replace \" \" \"-\" | lower}}" # we have a locally running dataprovider [http]