github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/node/node_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 node_test
    20  
    21  import (
    22  	"encoding/json"
    23  	"time"
    24  
    25  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    26  	ocsconv "github.com/cs3org/reva/v2/pkg/conversions"
    27  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    28  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    29  	helpers "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/testhelpers"
    30  	"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
    31  	. "github.com/onsi/ginkgo/v2"
    32  	. "github.com/onsi/gomega"
    33  	"github.com/stretchr/testify/mock"
    34  	"google.golang.org/protobuf/testing/protocmp"
    35  )
    36  
    37  var _ = Describe("Node", func() {
    38  	var (
    39  		env *helpers.TestEnv
    40  
    41  		id   string
    42  		name string
    43  	)
    44  
    45  	BeforeEach(func() {
    46  		var err error
    47  		env, err = helpers.NewTestEnv(nil)
    48  		Expect(err).ToNot(HaveOccurred())
    49  
    50  		id = "fooId"
    51  		name = "foo"
    52  	})
    53  
    54  	AfterEach(func() {
    55  		if env != nil {
    56  			env.Cleanup()
    57  		}
    58  	})
    59  
    60  	Describe("New", func() {
    61  		It("generates unique blob ids if none are given", func() {
    62  			n1 := node.New(env.SpaceRootRes.SpaceId, id, "", name, 10, "", provider.ResourceType_RESOURCE_TYPE_FILE, env.Owner.Id, env.Lookup)
    63  			n2 := node.New(env.SpaceRootRes.SpaceId, id, "", name, 10, "", provider.ResourceType_RESOURCE_TYPE_FILE, env.Owner.Id, env.Lookup)
    64  
    65  			Expect(len(n1.BlobID)).To(Equal(36))
    66  			Expect(n1.BlobID).ToNot(Equal(n2.BlobID))
    67  		})
    68  	})
    69  
    70  	Describe("ReadNode", func() {
    71  		It("reads the blobID from the xattrs", func() {
    72  			lookupNode, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{
    73  				ResourceId: env.SpaceRootRes,
    74  				Path:       "./dir1/file1",
    75  			})
    76  			Expect(err).ToNot(HaveOccurred())
    77  
    78  			n, err := node.ReadNode(env.Ctx, env.Lookup, lookupNode.SpaceID, lookupNode.ID, false, nil, false)
    79  			Expect(err).ToNot(HaveOccurred())
    80  			Expect(n.BlobID).To(Equal("file1-blobid"))
    81  		})
    82  	})
    83  
    84  	Describe("WriteMetadata", func() {
    85  		It("writes all xattrs", func() {
    86  			ref := &provider.Reference{
    87  				ResourceId: env.SpaceRootRes,
    88  				Path:       "/dir1/file1",
    89  			}
    90  			n, err := env.Lookup.NodeFromResource(env.Ctx, ref)
    91  			Expect(err).ToNot(HaveOccurred())
    92  
    93  			blobsize := int64(239485734)
    94  			n.Name = "TestName"
    95  			n.BlobID = "TestBlobID"
    96  			n.Blobsize = blobsize
    97  
    98  			err = n.SetXattrs(n.NodeMetadata(env.Ctx), true)
    99  			Expect(err).ToNot(HaveOccurred())
   100  			n2, err := env.Lookup.NodeFromResource(env.Ctx, ref)
   101  			Expect(err).ToNot(HaveOccurred())
   102  			Expect(n2.Name).To(Equal("TestName"))
   103  			Expect(n2.BlobID).To(Equal("TestBlobID"))
   104  			Expect(n2.Blobsize).To(Equal(blobsize))
   105  		})
   106  	})
   107  
   108  	Describe("Parent", func() {
   109  		It("returns the parent node", func() {
   110  			child, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{
   111  				ResourceId: env.SpaceRootRes,
   112  				Path:       "/dir1/subdir1",
   113  			})
   114  			Expect(err).ToNot(HaveOccurred())
   115  			Expect(child).ToNot(BeNil())
   116  
   117  			parent, err := child.Parent(env.Ctx)
   118  			Expect(err).ToNot(HaveOccurred())
   119  			Expect(parent).ToNot(BeNil())
   120  			Expect(parent.ID).To(Equal(child.ParentID))
   121  		})
   122  	})
   123  	Describe("Child", func() {
   124  		var (
   125  			parent *node.Node
   126  		)
   127  
   128  		JustBeforeEach(func() {
   129  			var err error
   130  			parent, err = env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{
   131  				ResourceId: env.SpaceRootRes,
   132  				Path:       "/dir1",
   133  			})
   134  			Expect(err).ToNot(HaveOccurred())
   135  			Expect(parent).ToNot(BeNil())
   136  		})
   137  
   138  		It("returns an empty node if the child does not exist", func() {
   139  			child, err := parent.Child(env.Ctx, "does-not-exist")
   140  			Expect(err).ToNot(HaveOccurred())
   141  			Expect(child).ToNot(BeNil())
   142  			Expect(child.Exists).To(BeFalse())
   143  		})
   144  
   145  		It("returns a directory node with all metadata", func() {
   146  			child, err := parent.Child(env.Ctx, "subdir1")
   147  			Expect(err).ToNot(HaveOccurred())
   148  			Expect(child).ToNot(BeNil())
   149  			Expect(child.Exists).To(BeTrue())
   150  			Expect(child.ParentID).To(Equal(parent.ID))
   151  			Expect(child.Name).To(Equal("subdir1"))
   152  			Expect(child.Blobsize).To(Equal(int64(0)))
   153  		})
   154  
   155  		It("returns a file node with all metadata", func() {
   156  			child, err := parent.Child(env.Ctx, "file1")
   157  			Expect(err).ToNot(HaveOccurred())
   158  			Expect(child).ToNot(BeNil())
   159  			Expect(child.Exists).To(BeTrue())
   160  			Expect(child.ParentID).To(Equal(parent.ID))
   161  			Expect(child.Name).To(Equal("file1"))
   162  			Expect(child.Blobsize).To(Equal(int64(1234)))
   163  		})
   164  
   165  		It("handles broken links including file segments by returning an non-existent node", func() {
   166  			child, err := parent.Child(env.Ctx, "file1/broken")
   167  			Expect(err).ToNot(HaveOccurred())
   168  			Expect(child).ToNot(BeNil())
   169  			Expect(child.Exists).To(BeFalse())
   170  		})
   171  	})
   172  
   173  	Describe("AsResourceInfo", func() {
   174  		var (
   175  			n *node.Node
   176  		)
   177  
   178  		BeforeEach(func() {
   179  			var err error
   180  			n, err = env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{
   181  				ResourceId: env.SpaceRootRes,
   182  				Path:       "dir1/file1",
   183  			})
   184  			Expect(err).ToNot(HaveOccurred())
   185  		})
   186  
   187  		Describe("the Etag field", func() {
   188  			It("is set", func() {
   189  				perms := node.OwnerPermissions()
   190  				ri, err := n.AsResourceInfo(env.Ctx, perms, []string{}, []string{}, false)
   191  				Expect(err).ToNot(HaveOccurred())
   192  				Expect(len(ri.Etag)).To(Equal(34))
   193  			})
   194  
   195  			It("changes when the tmtime is set", func() {
   196  				perms := node.OwnerPermissions()
   197  				ri, err := n.AsResourceInfo(env.Ctx, perms, []string{}, []string{}, false)
   198  				Expect(err).ToNot(HaveOccurred())
   199  				Expect(len(ri.Etag)).To(Equal(34))
   200  				before := ri.Etag
   201  
   202  				tmtime := time.Now()
   203  				Expect(n.SetTMTime(env.Ctx, &tmtime)).To(Succeed())
   204  
   205  				ri, err = n.AsResourceInfo(env.Ctx, perms, []string{}, []string{}, false)
   206  				Expect(err).ToNot(HaveOccurred())
   207  				Expect(len(ri.Etag)).To(Equal(34))
   208  				Expect(ri.Etag).ToNot(Equal(before))
   209  			})
   210  
   211  			It("includes the lock in the Opaque", func() {
   212  				lock := &provider.Lock{
   213  					Type:   provider.LockType_LOCK_TYPE_EXCL,
   214  					User:   env.Owner.Id,
   215  					LockId: "foo",
   216  				}
   217  				err := n.SetLock(env.Ctx, lock)
   218  				Expect(err).ToNot(HaveOccurred())
   219  
   220  				perms := node.OwnerPermissions()
   221  				ri, err := n.AsResourceInfo(env.Ctx, perms, []string{}, []string{}, false)
   222  				Expect(err).ToNot(HaveOccurred())
   223  				Expect(ri.Opaque).ToNot(BeNil())
   224  				Expect(ri.Opaque.Map["lock"]).ToNot(BeNil())
   225  
   226  				storedLock := &provider.Lock{}
   227  				err = json.Unmarshal(ri.Opaque.Map["lock"].Value, storedLock)
   228  				Expect(err).ToNot(HaveOccurred())
   229  				Expect(storedLock).To(BeComparableTo(lock, protocmp.Transform()))
   230  			})
   231  		})
   232  	})
   233  	Describe("Permissions", func() {
   234  		It("Checks the owner permissions on a personal space", func() {
   235  			node1, err := env.Lookup.NodeFromSpaceID(env.Ctx, env.SpaceRootRes.SpaceId)
   236  			Expect(err).ToNot(HaveOccurred())
   237  			perms, _ := node1.PermissionSet(env.Ctx)
   238  			Expect(perms).To(Equal(node.OwnerPermissions()))
   239  		})
   240  		It("Checks the manager permissions on a project space", func() {
   241  			pSpace, err := env.CreateTestStorageSpace("project", &provider.Quota{QuotaMaxBytes: 2000})
   242  			Expect(err).ToNot(HaveOccurred())
   243  			nodePSpace, err := env.Lookup.NodeFromSpaceID(env.Ctx, pSpace.SpaceId)
   244  			Expect(err).ToNot(HaveOccurred())
   245  			u := ctxpkg.ContextMustGetUser(env.Ctx)
   246  			env.Permissions.On("AssemblePermissions", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ResourcePermissions{
   247  				UpdateGrant: true,
   248  				Stat:        true,
   249  			}, nil).Times(1)
   250  			err = env.Fs.UpdateGrant(env.Ctx, &provider.Reference{
   251  				ResourceId: &provider.ResourceId{
   252  					SpaceId:  pSpace.SpaceId,
   253  					OpaqueId: pSpace.OpaqueId,
   254  				},
   255  			}, &provider.Grant{
   256  				Grantee: &provider.Grantee{
   257  					Type: provider.GranteeType_GRANTEE_TYPE_USER,
   258  					Id: &provider.Grantee_UserId{
   259  						UserId: u.Id,
   260  					},
   261  				},
   262  				Permissions: ocsconv.NewManagerRole().CS3ResourcePermissions(),
   263  			})
   264  			Expect(err).ToNot(HaveOccurred())
   265  			perms, _ := nodePSpace.PermissionSet(env.Ctx)
   266  			expected := ocsconv.NewManagerRole().CS3ResourcePermissions()
   267  			Expect(grants.PermissionsEqual(perms, expected)).To(BeTrue())
   268  		})
   269  		It("Checks the Editor permissions on a project space and a denial", func() {
   270  			storageSpace, err := env.CreateTestStorageSpace("project", &provider.Quota{QuotaMaxBytes: 2000})
   271  			Expect(err).ToNot(HaveOccurred())
   272  			u := ctxpkg.ContextMustGetUser(env.Ctx)
   273  			env.Permissions.On("AssemblePermissions", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ResourcePermissions{
   274  				UpdateGrant: true,
   275  				Stat:        true,
   276  			}, nil).Times(1)
   277  			err = env.Fs.UpdateGrant(env.Ctx, &provider.Reference{
   278  				ResourceId: &provider.ResourceId{
   279  					SpaceId:  storageSpace.SpaceId,
   280  					OpaqueId: storageSpace.OpaqueId,
   281  				},
   282  			}, &provider.Grant{
   283  				Grantee: &provider.Grantee{
   284  					Type: provider.GranteeType_GRANTEE_TYPE_USER,
   285  					Id: &provider.Grantee_UserId{
   286  						UserId: u.Id,
   287  					},
   288  				},
   289  				Permissions: ocsconv.NewEditorRole().CS3ResourcePermissions(),
   290  			})
   291  			Expect(err).ToNot(HaveOccurred())
   292  			spaceRoot, err := env.Lookup.NodeFromSpaceID(env.Ctx, storageSpace.SpaceId)
   293  			Expect(err).ToNot(HaveOccurred())
   294  			permissionsActual, _ := spaceRoot.PermissionSet(env.Ctx)
   295  			permissionsExpected := ocsconv.NewEditorRole().CS3ResourcePermissions()
   296  			Expect(grants.PermissionsEqual(permissionsActual, permissionsExpected)).To(BeTrue())
   297  			env.Permissions.On("AssemblePermissions", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ResourcePermissions{
   298  				Stat:            true,
   299  				CreateContainer: true,
   300  			}, nil).Times(1)
   301  			subfolder, err := env.CreateTestDir("subpath", &provider.Reference{
   302  				ResourceId: &provider.ResourceId{
   303  					SpaceId:  storageSpace.SpaceId,
   304  					OpaqueId: storageSpace.OpaqueId,
   305  				},
   306  				Path: ""},
   307  			)
   308  			Expect(err).ToNot(HaveOccurred())
   309  			// adding a denial on the subpath
   310  			env.Permissions.On("AssemblePermissions", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ResourcePermissions{
   311  				DenyGrant: true,
   312  				Stat:      true,
   313  			}, nil).Times(1)
   314  			err = env.Fs.AddGrant(env.Ctx, &provider.Reference{
   315  				ResourceId: &provider.ResourceId{
   316  					SpaceId:  storageSpace.SpaceId,
   317  					OpaqueId: storageSpace.OpaqueId,
   318  				},
   319  				Path: "subpath",
   320  			}, &provider.Grant{
   321  				Grantee: &provider.Grantee{
   322  					Type: provider.GranteeType_GRANTEE_TYPE_USER,
   323  					Id: &provider.Grantee_UserId{
   324  						UserId: u.Id,
   325  					},
   326  				},
   327  				Permissions: ocsconv.NewDeniedRole().CS3ResourcePermissions(),
   328  			})
   329  			Expect(err).ToNot(HaveOccurred())
   330  			// checking that the path "subpath" is denied properly
   331  			subfolder, err = node.ReadNode(env.Ctx, env.Lookup, subfolder.SpaceID, subfolder.ID, false, nil, false)
   332  			Expect(err).ToNot(HaveOccurred())
   333  			subfolderActual, denied := subfolder.PermissionSet(env.Ctx)
   334  			subfolderExpected := ocsconv.NewDeniedRole().CS3ResourcePermissions()
   335  			Expect(grants.PermissionsEqual(subfolderActual, subfolderExpected)).To(BeTrue())
   336  			Expect(denied).To(BeTrue())
   337  		})
   338  	})
   339  
   340  	Describe("SpaceOwnerOrManager", func() {
   341  		It("returns the space owner", func() {
   342  			n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{
   343  				ResourceId: env.SpaceRootRes,
   344  				Path:       "dir1/file1",
   345  			})
   346  			Expect(err).ToNot(HaveOccurred())
   347  
   348  			o := n.SpaceOwnerOrManager(env.Ctx)
   349  			Expect(err).ToNot(HaveOccurred())
   350  			Expect(o).To(BeComparableTo(env.Owner.Id, protocmp.Transform()))
   351  		})
   352  
   353  	})
   354  })