github.com/cs3org/reva/v2@v2.27.7/pkg/storage/registry/spaces/spaces_test.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package spaces_test
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  
    26  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    27  	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    28  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    29  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    30  	"github.com/cs3org/reva/v2/pkg/storage"
    31  	"github.com/cs3org/reva/v2/pkg/storage/registry/spaces"
    32  	"github.com/cs3org/reva/v2/pkg/storage/registry/spaces/mocks"
    33  	"github.com/cs3org/reva/v2/pkg/storagespace"
    34  	"github.com/stretchr/testify/mock"
    35  	"google.golang.org/grpc"
    36  
    37  	. "github.com/onsi/ginkgo/v2"
    38  	. "github.com/onsi/gomega"
    39  )
    40  
    41  var _ = Describe("Spaces", func() {
    42  	var (
    43  		handler   storage.Registry
    44  		ctxAlice  context.Context
    45  		fooClient *mocks.StorageProviderClient
    46  		barClient *mocks.StorageProviderClient
    47  		bazClient *mocks.StorageProviderClient
    48  
    49  		rules map[string]interface{}
    50  
    51  		getClientFunc func(addr string) (spaces.StorageProviderClient, error)
    52  
    53  		alice = &userpb.User{
    54  			Id: &userpb.UserId{
    55  				OpaqueId: "alice",
    56  			},
    57  			Username: "alice",
    58  		}
    59  	)
    60  
    61  	BeforeEach(func() {
    62  		fooClient = &mocks.StorageProviderClient{}
    63  		barClient = &mocks.StorageProviderClient{}
    64  		bazClient = &mocks.StorageProviderClient{}
    65  
    66  		fooClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(
    67  			func(_ context.Context, req *provider.ListStorageSpacesRequest, _ ...grpc.CallOption) *provider.ListStorageSpacesResponse {
    68  				rid := &provider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "foospace"}
    69  				spaces := []*provider.StorageSpace{
    70  					{
    71  						Id:        &provider.StorageSpaceId{OpaqueId: storagespace.FormatResourceID(rid)},
    72  						Root:      rid,
    73  						Name:      "Foo space",
    74  						SpaceType: "personal",
    75  						Owner:     alice,
    76  					},
    77  				}
    78  				for _, f := range req.Filters {
    79  					if f.Type == provider.ListStorageSpacesRequest_Filter_TYPE_ID && f.GetId().OpaqueId != "provider-1$foospace!foospace" {
    80  						spaces = []*provider.StorageSpace{}
    81  					}
    82  				}
    83  				return &provider.ListStorageSpacesResponse{
    84  					Status:        &rpcv1beta1.Status{Code: rpcv1beta1.Code_CODE_OK},
    85  					StorageSpaces: spaces,
    86  				}
    87  			}, nil)
    88  		barClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(
    89  			func(_ context.Context, req *provider.ListStorageSpacesRequest, _ ...grpc.CallOption) *provider.ListStorageSpacesResponse {
    90  				rid := &provider.ResourceId{StorageId: "provider-1", SpaceId: "barspace", OpaqueId: "barspace"}
    91  				spaces := []*provider.StorageSpace{
    92  					{
    93  						Id:        &provider.StorageSpaceId{OpaqueId: storagespace.FormatResourceID(rid)},
    94  						Root:      rid,
    95  						Name:      "Bar space",
    96  						SpaceType: "personal",
    97  						Owner:     alice,
    98  					},
    99  				}
   100  				for _, f := range req.Filters {
   101  					if f.Type == provider.ListStorageSpacesRequest_Filter_TYPE_ID && f.GetId().OpaqueId != "barspace!barspace" {
   102  						spaces = []*provider.StorageSpace{}
   103  					}
   104  				}
   105  				return &provider.ListStorageSpacesResponse{
   106  					Status:        &rpcv1beta1.Status{Code: rpcv1beta1.Code_CODE_OK},
   107  					StorageSpaces: spaces,
   108  				}
   109  			}, nil)
   110  		bazClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(
   111  			func(_ context.Context, req *provider.ListStorageSpacesRequest, _ ...grpc.CallOption) *provider.ListStorageSpacesResponse {
   112  				rid1 := &provider.ResourceId{StorageId: "provider-1", SpaceId: "bazspace1", OpaqueId: "bazspace1"}
   113  				rid2 := &provider.ResourceId{StorageId: "provider-1", SpaceId: "bazspace2", OpaqueId: "bazspace2"}
   114  				space1 := &provider.StorageSpace{
   115  					Id:        &provider.StorageSpaceId{OpaqueId: storagespace.FormatResourceID(rid1)},
   116  					Root:      rid1,
   117  					Name:      "Baz space 1",
   118  					SpaceType: "project",
   119  					Owner:     alice,
   120  				}
   121  				space2 := &provider.StorageSpace{
   122  					Id:        &provider.StorageSpaceId{OpaqueId: storagespace.FormatResourceID(rid2)},
   123  					Root:      rid2,
   124  					Name:      "Baz space 2",
   125  					SpaceType: "project",
   126  					Owner:     alice,
   127  				}
   128  				spaces := []*provider.StorageSpace{space1, space2}
   129  				for _, f := range req.Filters {
   130  					if f.Type == provider.ListStorageSpacesRequest_Filter_TYPE_ID {
   131  						if f.GetId().OpaqueId == "provider-1$bazspace1!bazspace2" {
   132  							spaces = []*provider.StorageSpace{space1}
   133  						} else if f.GetId().OpaqueId == "provider-1$bazspace2!bazspace2" {
   134  							spaces = []*provider.StorageSpace{space2}
   135  						} else {
   136  							spaces = []*provider.StorageSpace{}
   137  						}
   138  					}
   139  				}
   140  				return &provider.ListStorageSpacesResponse{
   141  					Status:        &rpcv1beta1.Status{Code: rpcv1beta1.Code_CODE_OK},
   142  					StorageSpaces: spaces,
   143  				}
   144  			}, nil)
   145  
   146  		getClientFunc = func(addr string) (spaces.StorageProviderClient, error) {
   147  			switch addr {
   148  			case "127.0.0.1:13020":
   149  				return fooClient, nil
   150  			case "127.0.0.1:13021":
   151  				return barClient, nil
   152  			case "127.0.0.1:13022":
   153  				return bazClient, nil
   154  			}
   155  			return nil, fmt.Errorf("Nooooo")
   156  		}
   157  
   158  		ctxAlice = ctxpkg.ContextSetUser(context.Background(), alice)
   159  	})
   160  
   161  	JustBeforeEach(func() {
   162  		var err error
   163  		handler, err = spaces.New(rules, getClientFunc)
   164  		Expect(err).ToNot(HaveOccurred())
   165  	})
   166  
   167  	Describe("NewDefault", func() {
   168  		It("returns a new instance", func() {
   169  			_, err := spaces.NewDefault(rules)
   170  			Expect(err).ToNot(HaveOccurred())
   171  		})
   172  	})
   173  
   174  	Describe("New", func() {
   175  		It("uses the path as the pathtemplate if no template is set (e.g. in cases like the publicstorageprovider which returns a single space)", func() {
   176  			rules = map[string]interface{}{
   177  				"providers": map[string]interface{}{
   178  					"127.0.0.1:13020": map[string]interface{}{
   179  						"spaces": map[string]interface{}{
   180  							"personal": map[string]interface{}{
   181  								"mount_point": "/thepath",
   182  							},
   183  						},
   184  					},
   185  				},
   186  			}
   187  
   188  			handler, err := spaces.New(rules, getClientFunc)
   189  			Expect(err).ToNot(HaveOccurred())
   190  
   191  			providers, err := handler.ListProviders(ctxAlice, map[string]string{"path": "/thepath"})
   192  			Expect(err).ToNot(HaveOccurred())
   193  			Expect(len(providers)).To(Equal(1))
   194  			p := providers[0]
   195  			Expect(p.Address).To(Equal("127.0.0.1:13020"))
   196  
   197  			spaces := []*provider.StorageSpace{}
   198  			err = json.Unmarshal(p.Opaque.Map["spaces"].Value, &spaces)
   199  			Expect(err).ToNot(HaveOccurred())
   200  			Expect(len(spaces)).To(Equal(1))
   201  
   202  			Expect(spaces[0].Opaque.Map["path"].Decoder).To(Equal("plain"))
   203  			spacePath := string(spaces[0].Opaque.Map["path"].Value)
   204  			Expect(spacePath).To(Equal("/thepath"))
   205  		})
   206  	})
   207  
   208  	Context("with a simple setup", func() {
   209  		BeforeEach(func() {
   210  			rules = map[string]interface{}{
   211  				"home_provider": "/users/{{.Id.OpaqueId}}",
   212  				"providers": map[string]interface{}{
   213  					"127.0.0.1:13020": map[string]interface{}{
   214  						"spaces": map[string]interface{}{
   215  							"personal": map[string]interface{}{
   216  								"mount_point":   "/users/[a-k]",
   217  								"path_template": "/users/{{.Space.Owner.Username}}",
   218  							},
   219  						},
   220  					},
   221  					"127.0.0.1:13021": map[string]interface{}{
   222  						"spaces": map[string]interface{}{
   223  							"personal": map[string]interface{}{
   224  								"mount_point":   "/users/[l-z]",
   225  								"path_template": "/users/{{.Space.Owner.Username}}",
   226  							},
   227  						},
   228  					},
   229  					"127.0.0.1:13022": map[string]interface{}{
   230  						"spaces": map[string]interface{}{
   231  							"project": map[string]interface{}{
   232  								"mount_point":   "/projects",
   233  								"path_template": "/projects/{{.Space.Name}}",
   234  							},
   235  						},
   236  					},
   237  				},
   238  			}
   239  		})
   240  
   241  		Describe("GetProvider", func() {
   242  			It("returns an error when no provider was found", func() {
   243  				space := &provider.StorageSpace{
   244  					Owner: &userpb.User{
   245  						Username: "bob",
   246  					},
   247  					SpaceType: "somethingfancy",
   248  				}
   249  				p, err := handler.GetProvider(ctxAlice, space)
   250  				Expect(err).To(HaveOccurred())
   251  				Expect(p).To(BeNil())
   252  			})
   253  
   254  			It("filters by space type", func() {
   255  				space := &provider.StorageSpace{
   256  					SpaceType: "personal",
   257  				}
   258  				p, err := handler.GetProvider(ctxAlice, space)
   259  				Expect(err).ToNot(HaveOccurred())
   260  				Expect(p).ToNot(BeNil())
   261  			})
   262  
   263  			It("filters by space type and owner", func() {
   264  				space := &provider.StorageSpace{
   265  					Owner: &userpb.User{
   266  						Username: "alice",
   267  					},
   268  					SpaceType: "personal",
   269  				}
   270  				p, err := handler.GetProvider(ctxAlice, space)
   271  				Expect(err).ToNot(HaveOccurred())
   272  				Expect(p).ToNot(BeNil())
   273  				Expect(p.Address).To(Equal("127.0.0.1:13020"))
   274  
   275  				space = &provider.StorageSpace{
   276  					Owner: &userpb.User{
   277  						Username: "zacharias",
   278  					},
   279  					SpaceType: "personal",
   280  				}
   281  				p, err = handler.GetProvider(ctxAlice, space)
   282  				Expect(err).ToNot(HaveOccurred())
   283  				Expect(p).ToNot(BeNil())
   284  				Expect(p.Address).To(Equal("127.0.0.1:13021"))
   285  			})
   286  		})
   287  
   288  		Describe("ListProviders", func() {
   289  			It("returns all providers when no filter is set", func() {
   290  				filters := map[string]string{}
   291  				providers, err := handler.ListProviders(ctxAlice, filters)
   292  				Expect(err).ToNot(HaveOccurred())
   293  				Expect(len(providers)).To(Equal(3))
   294  			})
   295  
   296  			It("filters by path", func() {
   297  				filters := map[string]string{
   298  					"path": "/projects",
   299  				}
   300  				providers, err := handler.ListProviders(ctxAlice, filters)
   301  				Expect(err).ToNot(HaveOccurred())
   302  				Expect(len(providers)).To(Equal(1))
   303  				p := providers[0]
   304  				Expect(p.Address).To(Equal("127.0.0.1:13022"))
   305  
   306  				spaces := []*provider.StorageSpace{}
   307  				err = json.Unmarshal(p.Opaque.Map["spaces"].Value, &spaces)
   308  				Expect(err).ToNot(HaveOccurred())
   309  				Expect(len(spaces)).To(Equal(2))
   310  
   311  				baz1Found, baz2Found := false, false
   312  				for _, space := range spaces {
   313  					spacePath := string(space.Opaque.Map["path"].Value)
   314  					switch space.Id.OpaqueId {
   315  					case "provider-1$bazspace1!bazspace1":
   316  						baz1Found = true
   317  						Expect(spacePath).To(Equal("/projects/Baz space 1"))
   318  					case "provider-1$bazspace2!bazspace2":
   319  						baz2Found = true
   320  						Expect(spacePath).To(Equal("/projects/Baz space 2"))
   321  					default:
   322  						Fail("unexpected space id")
   323  					}
   324  				}
   325  				Expect(baz1Found).To(BeTrue())
   326  				Expect(baz2Found).To(BeTrue())
   327  			})
   328  
   329  			It("returns an empty list when a non-existent id is given", func() {
   330  				filters := map[string]string{
   331  					"storage_id": "invalid",
   332  					"space_id":   "invalid",
   333  					"opaque_id":  "barspace",
   334  				}
   335  				providers, err := handler.ListProviders(ctxAlice, filters)
   336  				Expect(err).ToNot(HaveOccurred())
   337  				Expect(len(providers)).To(Equal(0))
   338  			})
   339  
   340  			It("filters by id", func() {
   341  				filters := map[string]string{
   342  					"storage_id": "provider-1",
   343  					"space_id":   "foospace",
   344  					"opaque_id":  "foospace",
   345  				}
   346  				providers, err := handler.ListProviders(ctxAlice, filters)
   347  				Expect(err).ToNot(HaveOccurred())
   348  				Expect(len(providers)).To(Equal(1))
   349  				p := providers[0]
   350  				Expect(p.Address).To(Equal("127.0.0.1:13020"))
   351  
   352  				spaces := []*provider.StorageSpace{}
   353  				err = json.Unmarshal(p.Opaque.Map["spaces"].Value, &spaces)
   354  				Expect(err).ToNot(HaveOccurred())
   355  				Expect(len(spaces)).To(Equal(1))
   356  
   357  				Expect(spaces[0].Id.OpaqueId).To(Equal("provider-1$foospace!foospace"))
   358  				Expect(spaces[0].Opaque.Map["path"].Decoder).To(Equal("plain"))
   359  				spacePath := string(spaces[0].Opaque.Map["path"].Value)
   360  				Expect(spacePath).To(Equal("/users/alice"))
   361  
   362  				filters = map[string]string{
   363  					"storage_id": "provider-1",
   364  					"space_id":   "bazspace2",
   365  					"opaque_id":  "bazspace2",
   366  				}
   367  				providers, err = handler.ListProviders(ctxAlice, filters)
   368  				Expect(err).ToNot(HaveOccurred())
   369  				Expect(len(providers)).To(Equal(1))
   370  				p = providers[0]
   371  				Expect(p.Address).To(Equal("127.0.0.1:13022"))
   372  
   373  				err = json.Unmarshal(p.Opaque.Map["spaces"].Value, &spaces)
   374  				Expect(err).ToNot(HaveOccurred())
   375  				Expect(len(spaces)).To(Equal(1))
   376  				Expect(spaces[0].Id.OpaqueId).To(Equal("provider-1$bazspace2!bazspace2"))
   377  				Expect(spaces[0].Opaque.Map["path"].Decoder).To(Equal("plain"))
   378  				spacePath = string(spaces[0].Opaque.Map["path"].Value)
   379  				Expect(spacePath).To(Equal("/projects/Baz space 2"))
   380  			})
   381  		})
   382  	})
   383  
   384  	Context("with a nested setup", func() {
   385  		BeforeEach(func() {
   386  			rules = map[string]interface{}{
   387  				"home_provider": "/users/{{.Id.OpaqueId}}",
   388  				"providers": map[string]interface{}{
   389  					"127.0.0.1:13020": map[string]interface{}{
   390  						"spaces": map[string]interface{}{
   391  							"personal": map[string]interface{}{
   392  								"mount_point":   "/foo",
   393  								"path_template": "/foo",
   394  							},
   395  						},
   396  					},
   397  					"127.0.0.1:13021": map[string]interface{}{
   398  						"spaces": map[string]interface{}{
   399  							"personal": map[string]interface{}{
   400  								"mount_point":   "/foo/bar",
   401  								"path_template": "/foo/bar",
   402  							},
   403  						},
   404  					},
   405  					"127.0.0.1:13022": map[string]interface{}{
   406  						"spaces": map[string]interface{}{
   407  							"project": map[string]interface{}{
   408  								"mount_point":   "/foo/bar/baz",
   409  								"path_template": "/foo/bar/baz",
   410  							},
   411  						},
   412  					},
   413  				},
   414  			}
   415  		})
   416  
   417  		Describe("ListProviders", func() {
   418  			It("includes all spaces below the requested path", func() {
   419  				filters := map[string]string{
   420  					"path": "/foo",
   421  				}
   422  				providers, err := handler.ListProviders(ctxAlice, filters)
   423  				Expect(err).ToNot(HaveOccurred())
   424  				Expect(len(providers)).To(Equal(3))
   425  			})
   426  
   427  			It("includes all spaces below the requested path but not the one above", func() {
   428  				filters := map[string]string{
   429  					"path": "/foo/bar",
   430  				}
   431  				providers, err := handler.ListProviders(ctxAlice, filters)
   432  				Expect(err).ToNot(HaveOccurred())
   433  				Expect(len(providers)).To(Equal(2))
   434  				addresses := []string{}
   435  				for _, p := range providers {
   436  					addresses = append(addresses, p.Address)
   437  				}
   438  				Expect(addresses).To(ConsistOf("127.0.0.1:13021", "127.0.0.1:13022"))
   439  			})
   440  
   441  			It("includes the space for the requested path", func() {
   442  				filters := map[string]string{
   443  					"path": "/foo/bar/baz",
   444  				}
   445  				providers, err := handler.ListProviders(ctxAlice, filters)
   446  				Expect(err).ToNot(HaveOccurred())
   447  				Expect(len(providers)).To(Equal(1))
   448  				Expect(providers[0].Address).To(Equal("127.0.0.1:13022"))
   449  
   450  				filters = map[string]string{
   451  					"path": "/foo/bar/baz/qux",
   452  				}
   453  				providers, err = handler.ListProviders(ctxAlice, filters)
   454  				Expect(err).ToNot(HaveOccurred())
   455  				Expect(len(providers)).To(Equal(1))
   456  				Expect(providers[0].Address).To(Equal("127.0.0.1:13022"))
   457  			})
   458  
   459  			It("includes the space for the requested path", func() {
   460  				filters := map[string]string{
   461  					"path": "/foo/bar/bif",
   462  				}
   463  				providers, err := handler.ListProviders(ctxAlice, filters)
   464  				Expect(err).ToNot(HaveOccurred())
   465  				Expect(len(providers)).To(Equal(1))
   466  				Expect(providers[0].Address).To(Equal("127.0.0.1:13021"))
   467  			})
   468  		})
   469  	})
   470  })