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 })