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