github.com/cs3org/reva/v2@v2.27.7/pkg/publicshare/manager/owncloudsql/owncloudsql_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 owncloudsql_test 20 21 import ( 22 "context" 23 "database/sql" 24 "os" 25 "strings" 26 "time" 27 28 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 29 link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" 30 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 31 typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 32 "github.com/cs3org/reva/v2/pkg/conversions" 33 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 34 "github.com/cs3org/reva/v2/pkg/publicshare" 35 "github.com/cs3org/reva/v2/pkg/publicshare/manager/owncloudsql" 36 "github.com/cs3org/reva/v2/pkg/share/manager/owncloudsql/mocks" 37 "github.com/cs3org/reva/v2/pkg/utils" 38 "github.com/stretchr/testify/mock" 39 40 _ "github.com/mattn/go-sqlite3" 41 42 . "github.com/onsi/ginkgo/v2" 43 . "github.com/onsi/gomega" 44 ) 45 46 var _ = Describe("SQL manager", func() { 47 var ( 48 m publicshare.Manager 49 user *userpb.User 50 ctx context.Context 51 52 ri *provider.ResourceInfo 53 grant *link.Grant 54 // share *link.PublicShare 55 56 testDbFile *os.File 57 sqldb *sql.DB 58 ) 59 60 AfterEach(func() { 61 os.Remove(testDbFile.Name()) 62 }) 63 64 BeforeEach(func() { 65 var err error 66 testDbFile, err = os.CreateTemp("", "example") 67 Expect(err).ToNot(HaveOccurred()) 68 69 dbData, err := os.ReadFile("test.db") 70 Expect(err).ToNot(HaveOccurred()) 71 72 _, err = testDbFile.Write(dbData) 73 Expect(err).ToNot(HaveOccurred()) 74 err = testDbFile.Close() 75 Expect(err).ToNot(HaveOccurred()) 76 77 sqldb, err = sql.Open("sqlite3", testDbFile.Name()) 78 Expect(err).ToNot(HaveOccurred()) 79 80 userConverter := &mocks.UserConverter{} 81 userConverter.On("UserIDToUserName", mock.Anything, mock.Anything).Return("username", nil) 82 userConverter.On("UserNameToUserID", mock.Anything, mock.Anything).Return( 83 func(_ context.Context, username string) *userpb.UserId { 84 return &userpb.UserId{ 85 OpaqueId: username, 86 } 87 }, 88 nil) 89 m, err = owncloudsql.New("sqlite3", sqldb, owncloudsql.Config{}, userConverter) 90 Expect(err).ToNot(HaveOccurred()) 91 92 user = &userpb.User{ 93 Id: &userpb.UserId{ 94 Idp: "localhost:1111", 95 OpaqueId: "1", 96 }, 97 Username: "username", 98 } 99 ctx = ctxpkg.ContextSetUser(context.Background(), user) 100 101 ri = &provider.ResourceInfo{ 102 Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, 103 Path: "/share1", 104 Id: &provider.ResourceId{OpaqueId: "14"}, // matches the fileid in the oc_filecache 105 Owner: user.Id, 106 PermissionSet: &provider.ResourcePermissions{ 107 Stat: true, 108 }, 109 Size: 10, 110 } 111 grant = &link.Grant{ 112 Permissions: &link.PublicSharePermissions{ 113 Permissions: conversions.NewViewerRole().CS3ResourcePermissions(), 114 }, 115 } 116 }) 117 118 It("creates manager instances", func() { 119 Expect(m).ToNot(BeNil()) 120 }) 121 Describe("CreatePublicShare", func() { 122 It("creates a new share and adds it to the index", func() { 123 link, err := m.CreatePublicShare(ctx, user, ri, grant) 124 Expect(err).ToNot(HaveOccurred()) 125 Expect(link).ToNot(BeNil()) 126 Expect(link.Token).ToNot(Equal("")) 127 Expect(link.PasswordProtected).To(BeFalse()) 128 }) 129 130 It("sets 'PasswordProtected' and stores the password hash if a password is set", func() { 131 grant.Password = "secret123" 132 133 link, err := m.CreatePublicShare(ctx, user, ri, grant) 134 Expect(err).ToNot(HaveOccurred()) 135 Expect(link).ToNot(BeNil()) 136 Expect(link.Token).ToNot(Equal("")) 137 Expect(link.PasswordProtected).To(BeTrue()) 138 // check it is in the db 139 s, err := owncloudsql.GetByToken(sqldb, link.Token) 140 Expect(err).ToNot(HaveOccurred()) 141 Expect(s.ShareWith).To(HavePrefix("1|")) 142 }) 143 144 It("picks up the displayname from the metadata", func() { 145 ri.ArbitraryMetadata = &provider.ArbitraryMetadata{ 146 Metadata: map[string]string{ 147 "name": "metadata name", 148 }, 149 } 150 151 link, err := m.CreatePublicShare(ctx, user, ri, grant) 152 Expect(err).ToNot(HaveOccurred()) 153 Expect(link).ToNot(BeNil()) 154 Expect(link.DisplayName).To(Equal("metadata name")) 155 }) 156 }) 157 158 Context("with an existing share", func() { 159 var ( 160 existingShare *link.PublicShare 161 hashedPassword string 162 ) 163 164 JustBeforeEach(func() { 165 grant.Password = "foo" 166 var err error 167 existingShare, err = m.CreatePublicShare(ctx, user, ri, grant) 168 Expect(err).ToNot(HaveOccurred()) 169 Expect(existingShare).ToNot(BeNil()) 170 171 // read hashed password from db 172 s, err := owncloudsql.GetByToken(sqldb, existingShare.Token) 173 Expect(err).ToNot(HaveOccurred()) 174 hashedPassword = strings.TrimPrefix(s.ShareWith, "1|") 175 }) 176 177 Describe("ListPublicShares", func() { 178 It("lists existing shares", func() { 179 shares, err := m.ListPublicShares(ctx, user, []*link.ListPublicSharesRequest_Filter{}, false) 180 Expect(err).ToNot(HaveOccurred()) 181 Expect(len(shares)).To(Equal(1)) 182 Expect(shares[0].Signature).To(BeNil()) 183 }) 184 185 It("adds a signature", func() { 186 shares, err := m.ListPublicShares(ctx, user, []*link.ListPublicSharesRequest_Filter{}, true) 187 Expect(err).ToNot(HaveOccurred()) 188 Expect(len(shares)).To(Equal(1)) 189 Expect(shares[0].Signature).ToNot(BeNil()) 190 }) 191 192 It("filters by id", func() { 193 shares, err := m.ListPublicShares(ctx, user, []*link.ListPublicSharesRequest_Filter{ 194 publicshare.ResourceIDFilter(&provider.ResourceId{OpaqueId: "UnknownId"}), 195 }, false) 196 Expect(err).ToNot(HaveOccurred()) 197 Expect(len(shares)).To(Equal(0)) 198 }) 199 200 It("filters by storage", func() { 201 shares, err := m.ListPublicShares(ctx, user, []*link.ListPublicSharesRequest_Filter{ 202 publicshare.StorageIDFilter("unknownstorage"), 203 }, false) 204 Expect(err).ToNot(HaveOccurred()) 205 Expect(len(shares)).To(Equal(0)) 206 }) 207 208 Context("when the share has expired", func() { 209 BeforeEach(func() { 210 t := time.Date(2022, time.January, 1, 12, 0, 0, 0, time.UTC) 211 grant.Expiration = &typespb.Timestamp{ 212 Seconds: uint64(t.Unix()), 213 } 214 }) 215 216 It("does not consider the share", func() { 217 shares, err := m.ListPublicShares(ctx, user, []*link.ListPublicSharesRequest_Filter{}, false) 218 Expect(err).ToNot(HaveOccurred()) 219 Expect(len(shares)).To(Equal(0)) 220 }) 221 }) 222 }) 223 224 Describe("GetPublicShare", func() { 225 It("gets the public share by token", func() { 226 returnedShare, err := m.GetPublicShare(ctx, user, &link.PublicShareReference{ 227 Spec: &link.PublicShareReference_Token{ 228 Token: existingShare.Token, 229 }, 230 }, false) 231 Expect(err).ToNot(HaveOccurred()) 232 Expect(returnedShare).ToNot(BeNil()) 233 Expect(returnedShare.Id.OpaqueId).To(Equal(existingShare.Id.OpaqueId)) 234 Expect(returnedShare.Token).To(Equal(existingShare.Token)) 235 }) 236 237 It("gets the public share by id", func() { 238 returnedShare, err := m.GetPublicShare(ctx, user, &link.PublicShareReference{ 239 Spec: &link.PublicShareReference_Id{ 240 Id: &link.PublicShareId{ 241 OpaqueId: existingShare.Id.OpaqueId, 242 }, 243 }, 244 }, false) 245 Expect(err).ToNot(HaveOccurred()) 246 Expect(returnedShare).ToNot(BeNil()) 247 Expect(returnedShare.Signature).To(BeNil()) 248 }) 249 250 It("adds a signature", func() { 251 returnedShare, err := m.GetPublicShare(ctx, user, &link.PublicShareReference{ 252 Spec: &link.PublicShareReference_Id{ 253 Id: &link.PublicShareId{ 254 OpaqueId: existingShare.Id.OpaqueId, 255 }, 256 }, 257 }, true) 258 Expect(err).ToNot(HaveOccurred()) 259 Expect(returnedShare).ToNot(BeNil()) 260 Expect(returnedShare.Signature).ToNot(BeNil()) 261 }) 262 }) 263 264 Describe("RevokePublicShare", func() { 265 var ( 266 existingShare *link.PublicShare 267 ) 268 BeforeEach(func() { 269 grant.Password = "foo" 270 var err error 271 existingShare, err = m.CreatePublicShare(ctx, user, ri, grant) 272 Expect(err).ToNot(HaveOccurred()) 273 Expect(existingShare).ToNot(BeNil()) 274 }) 275 276 It("deletes the share by token", func() { 277 ref := &link.PublicShareReference{ 278 Spec: &link.PublicShareReference_Token{ 279 Token: existingShare.Token, 280 }, 281 } 282 err := m.RevokePublicShare(ctx, user, ref) 283 Expect(err).ToNot(HaveOccurred()) 284 285 returnedShare, err := m.GetPublicShare(ctx, user, &link.PublicShareReference{ 286 Spec: &link.PublicShareReference_Id{ 287 Id: &link.PublicShareId{ 288 OpaqueId: existingShare.Id.OpaqueId, 289 }, 290 }, 291 }, false) 292 Expect(err).To(HaveOccurred()) 293 Expect(returnedShare).To(BeNil()) 294 }) 295 296 It("deletes the share by id", func() { 297 ref := &link.PublicShareReference{ 298 Spec: &link.PublicShareReference_Id{ 299 Id: existingShare.Id, 300 }, 301 } 302 err := m.RevokePublicShare(ctx, user, ref) 303 Expect(err).ToNot(HaveOccurred()) 304 305 returnedShare, err := m.GetPublicShare(ctx, user, &link.PublicShareReference{ 306 Spec: &link.PublicShareReference_Id{ 307 Id: &link.PublicShareId{ 308 OpaqueId: existingShare.Id.OpaqueId, 309 }, 310 }, 311 }, false) 312 Expect(err).To(HaveOccurred()) 313 Expect(returnedShare).To(BeNil()) 314 }) 315 }) 316 317 Describe("GetPublicShareByToken", func() { 318 It("doesn't get the share using a wrong password", func() { 319 auth := &link.PublicShareAuthentication{ 320 Spec: &link.PublicShareAuthentication_Password{ 321 Password: "wroooong", 322 }, 323 } 324 ps, err := m.GetPublicShareByToken(ctx, existingShare.Token, auth, false) 325 Expect(err).To(HaveOccurred()) 326 Expect(ps).To(BeNil()) 327 }) 328 329 It("gets the share using a password", func() { 330 auth := &link.PublicShareAuthentication{ 331 Spec: &link.PublicShareAuthentication_Password{ 332 Password: grant.Password, 333 }, 334 } 335 ps, err := m.GetPublicShareByToken(ctx, existingShare.Token, auth, false) 336 Expect(err).ToNot(HaveOccurred()) 337 Expect(ps).ToNot(BeNil()) 338 }) 339 340 It("gets the share using a signature", func() { 341 err := publicshare.AddSignature(existingShare, hashedPassword) 342 Expect(err).ToNot(HaveOccurred()) 343 auth := &link.PublicShareAuthentication{ 344 Spec: &link.PublicShareAuthentication_Signature{ 345 Signature: existingShare.Signature, 346 }, 347 } 348 ps, err := m.GetPublicShareByToken(ctx, existingShare.Token, auth, false) 349 Expect(err).ToNot(HaveOccurred()) 350 Expect(ps).ToNot(BeNil()) 351 352 }) 353 354 Context("when the share has expired", func() { 355 BeforeEach(func() { 356 t := time.Date(2022, time.January, 1, 12, 0, 0, 0, time.UTC) 357 grant.Expiration = &typespb.Timestamp{ 358 Seconds: uint64(t.Unix()), 359 } 360 }) 361 It("it doesn't consider expired shares", func() { 362 auth := &link.PublicShareAuthentication{ 363 Spec: &link.PublicShareAuthentication_Password{ 364 Password: grant.Password, 365 }, 366 } 367 ps, err := m.GetPublicShareByToken(ctx, existingShare.Token, auth, false) 368 Expect(err).To(HaveOccurred()) 369 Expect(ps).To(BeNil()) 370 }) 371 }) 372 }) 373 374 Describe("UpdatePublicShare", func() { 375 var ( 376 ref *link.PublicShareReference 377 ) 378 379 JustBeforeEach(func() { 380 ref = &link.PublicShareReference{ 381 Spec: &link.PublicShareReference_Token{ 382 Token: existingShare.Token, 383 }, 384 } 385 }) 386 387 It("fails when an invalid reference is given", func() { 388 _, err := m.UpdatePublicShare(ctx, user, &link.UpdatePublicShareRequest{ 389 Ref: &link.PublicShareReference{Spec: &link.PublicShareReference_Id{Id: &link.PublicShareId{OpaqueId: "doesnotexist"}}}, 390 }) 391 Expect(err).To(HaveOccurred()) 392 }) 393 394 It("fails when no valid update request is given", func() { 395 _, err := m.UpdatePublicShare(ctx, user, &link.UpdatePublicShareRequest{ 396 Ref: ref, 397 Update: &link.UpdatePublicShareRequest_Update{}, 398 }) 399 Expect(err).To(HaveOccurred()) 400 }) 401 402 It("updates the display name", func() { 403 ps, err := m.UpdatePublicShare(ctx, user, &link.UpdatePublicShareRequest{ 404 Ref: ref, 405 Update: &link.UpdatePublicShareRequest_Update{ 406 Type: link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME, 407 DisplayName: "new displayname", 408 }, 409 }) 410 Expect(err).ToNot(HaveOccurred()) 411 Expect(ps).ToNot(BeNil()) 412 Expect(ps.DisplayName).To(Equal("new displayname")) 413 414 returnedShare, err := m.GetPublicShare(ctx, user, &link.PublicShareReference{ 415 Spec: &link.PublicShareReference_Id{ 416 Id: &link.PublicShareId{ 417 OpaqueId: existingShare.Id.OpaqueId, 418 }, 419 }, 420 }, false) 421 Expect(err).ToNot(HaveOccurred()) 422 Expect(returnedShare).ToNot(BeNil()) 423 Expect(returnedShare.DisplayName).To(Equal("new displayname")) 424 }) 425 426 It("updates the password", func() { 427 ps, err := m.UpdatePublicShare(ctx, user, &link.UpdatePublicShareRequest{ 428 Ref: ref, 429 Update: &link.UpdatePublicShareRequest_Update{ 430 Type: link.UpdatePublicShareRequest_Update_TYPE_PASSWORD, 431 Grant: &link.Grant{Password: "NewPass"}, 432 }, 433 }) 434 Expect(err).ToNot(HaveOccurred()) 435 Expect(ps).ToNot(BeNil()) 436 437 auth := &link.PublicShareAuthentication{ 438 Spec: &link.PublicShareAuthentication_Password{ 439 Password: "NewPass", 440 }, 441 } 442 ps, err = m.GetPublicShareByToken(ctx, existingShare.Token, auth, false) 443 Expect(err).ToNot(HaveOccurred()) 444 Expect(ps).ToNot(BeNil()) 445 }) 446 447 It("updates the permissions", func() { 448 ps, err := m.UpdatePublicShare(ctx, user, &link.UpdatePublicShareRequest{ 449 Ref: ref, 450 Update: &link.UpdatePublicShareRequest_Update{ 451 Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, 452 Grant: &link.Grant{Permissions: &link.PublicSharePermissions{Permissions: conversions.NewEditorRole().CS3ResourcePermissions()}}, 453 }, 454 }) 455 Expect(err).ToNot(HaveOccurred()) 456 Expect(ps).ToNot(BeNil()) 457 458 returnedShare, err := m.GetPublicShare(ctx, user, ref, false) 459 Expect(err).ToNot(HaveOccurred()) 460 Expect(returnedShare).ToNot(BeNil()) 461 Expect(returnedShare.Permissions.Permissions.Delete).To(BeTrue()) 462 }) 463 464 It("updates the expiration", func() { 465 ts := utils.TimeToTS(time.Now().Add(1 * time.Hour)) 466 ps, err := m.UpdatePublicShare(ctx, user, &link.UpdatePublicShareRequest{ 467 Ref: ref, 468 Update: &link.UpdatePublicShareRequest_Update{ 469 Type: link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION, 470 Grant: &link.Grant{Expiration: ts}, 471 }, 472 }) 473 Expect(err).ToNot(HaveOccurred()) 474 Expect(ps).ToNot(BeNil()) 475 476 returnedShare, err := m.GetPublicShare(ctx, user, ref, false) 477 Expect(err).ToNot(HaveOccurred()) 478 Expect(returnedShare).ToNot(BeNil()) 479 Expect(returnedShare.Expiration.Seconds).To(Equal(ts.Seconds)) 480 481 }) 482 }) 483 }) 484 })