github.com/cs3org/reva/v2@v2.27.7/pkg/publicshare/manager/json/json_test.go (about)

     1  // Copyright 2018-2022 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 json_test
    20  
    21  import (
    22  	"context"
    23  	"os"
    24  	"path/filepath"
    25  	"sync"
    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  	providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    31  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    32  	"github.com/cs3org/reva/v2/pkg/publicshare"
    33  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/json"
    34  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/cs3"
    35  	"github.com/cs3org/reva/v2/pkg/storage/utils/metadata"
    36  	"golang.org/x/crypto/bcrypt"
    37  	"google.golang.org/protobuf/proto"
    38  	"google.golang.org/protobuf/testing/protocmp"
    39  
    40  	. "github.com/onsi/ginkgo/v2"
    41  	. "github.com/onsi/gomega"
    42  )
    43  
    44  var _ = Describe("Json", func() {
    45  	var (
    46  		user1 = &userpb.User{
    47  			Id: &userpb.UserId{
    48  				Idp:      "https://localhost:9200",
    49  				OpaqueId: "admin",
    50  			},
    51  		}
    52  
    53  		sharedResource = &providerv1beta1.ResourceInfo{
    54  			Id: &providerv1beta1.ResourceId{
    55  				StorageId: "storageid",
    56  				OpaqueId:  "opaqueid",
    57  			},
    58  			ArbitraryMetadata: &providerv1beta1.ArbitraryMetadata{
    59  				Metadata: map[string]string{
    60  					"name": "publicshare",
    61  				},
    62  			},
    63  		}
    64  		grant = &link.Grant{
    65  			Permissions: &link.PublicSharePermissions{
    66  				Permissions: &providerv1beta1.ResourcePermissions{
    67  					InitiateFileUpload: false,
    68  				},
    69  			},
    70  		}
    71  
    72  		m       publicshare.Manager
    73  		tmpFile *os.File
    74  		ctx     context.Context
    75  	)
    76  
    77  	Context("with a file persistence layer", func() {
    78  
    79  		BeforeEach(func() {
    80  			var err error
    81  			tmpFile, err = os.CreateTemp("", "reva-unit-test-*.json")
    82  			Expect(err).ToNot(HaveOccurred())
    83  
    84  			config := map[string]interface{}{
    85  				"file":         tmpFile.Name(),
    86  				"gateway_addr": "https://localhost:9200",
    87  			}
    88  			m, err = json.NewFile(config)
    89  			Expect(err).ToNot(HaveOccurred())
    90  
    91  			ctx = ctxpkg.ContextSetUser(context.Background(), user1)
    92  		})
    93  
    94  		AfterEach(func() {
    95  			os.Remove(tmpFile.Name())
    96  		})
    97  
    98  		Describe("Dump", func() {
    99  			JustBeforeEach(func() {
   100  				_, err := m.CreatePublicShare(ctx, user1, sharedResource, &link.Grant{
   101  					Password: "foo",
   102  				})
   103  				Expect(err).ToNot(HaveOccurred())
   104  			})
   105  
   106  			It("dumps all public shares", func() {
   107  				psharesChan := make(chan *publicshare.WithPassword)
   108  				pshares := []*publicshare.WithPassword{}
   109  
   110  				wg := sync.WaitGroup{}
   111  				wg.Add(1)
   112  				go func() {
   113  					for ps := range psharesChan {
   114  						if ps != nil {
   115  							pshares = append(pshares, ps)
   116  						}
   117  					}
   118  					wg.Done()
   119  				}()
   120  				err := m.(publicshare.DumpableManager).Dump(ctx, psharesChan)
   121  				Expect(err).ToNot(HaveOccurred())
   122  				close(psharesChan)
   123  				wg.Wait()
   124  				Eventually(psharesChan).Should(BeClosed())
   125  
   126  				Expect(len(pshares)).To(Equal(1))
   127  				Expect(bcrypt.CompareHashAndPassword([]byte(pshares[0].Password), []byte("foo"))).To(Succeed())
   128  				Expect(pshares[0].PublicShare.Creator).To(BeComparableTo(user1.Id, protocmp.Transform()))
   129  				Expect(pshares[0].PublicShare.ResourceId).To(BeComparableTo(sharedResource.Id, protocmp.Transform()))
   130  			})
   131  		})
   132  
   133  		Describe("Load", func() {
   134  			It("loads shares including state and mountpoint information", func() {
   135  				existingShare, err := m.CreatePublicShare(ctx, user1, sharedResource, &link.Grant{
   136  					Password: "foo",
   137  				})
   138  				Expect(err).ToNot(HaveOccurred())
   139  
   140  				targetManager, err := json.NewMemory(map[string]interface{}{})
   141  				Expect(err).ToNot(HaveOccurred())
   142  
   143  				sharesChan := make(chan *publicshare.WithPassword)
   144  
   145  				wg := sync.WaitGroup{}
   146  				wg.Add(2)
   147  				go func() {
   148  					err := targetManager.(publicshare.LoadableManager).Load(ctx, sharesChan)
   149  					Expect(err).ToNot(HaveOccurred())
   150  					wg.Done()
   151  				}()
   152  				go func() {
   153  					tmpShare := &publicshare.WithPassword{
   154  						Password: "foo",
   155  					}
   156  					proto.Merge(&tmpShare.PublicShare, existingShare)
   157  					sharesChan <- tmpShare
   158  					close(sharesChan)
   159  					wg.Done()
   160  				}()
   161  				wg.Wait()
   162  				Eventually(sharesChan).Should(BeClosed())
   163  
   164  				loadedPublicShare, err := targetManager.GetPublicShare(ctx, user1, &link.PublicShareReference{
   165  					Spec: &link.PublicShareReference_Token{
   166  						Token: existingShare.Token,
   167  					},
   168  				}, false)
   169  				Expect(err).ToNot(HaveOccurred())
   170  				Expect(loadedPublicShare).ToNot(BeNil())
   171  			})
   172  		})
   173  	})
   174  
   175  	Context("with a cs3 persistence layer", func() {
   176  		var (
   177  			tmpdir string
   178  
   179  			storage metadata.Storage
   180  		)
   181  
   182  		BeforeEach(func() {
   183  			var err error
   184  			tmpdir, err = os.MkdirTemp("", "json-publicshare-manager-test")
   185  			Expect(err).ToNot(HaveOccurred())
   186  
   187  			err = os.MkdirAll(tmpdir, 0755)
   188  			Expect(err).ToNot(HaveOccurred())
   189  
   190  			storage, err = metadata.NewDiskStorage(tmpdir)
   191  			Expect(err).ToNot(HaveOccurred())
   192  
   193  			persistence := cs3.New(storage)
   194  			Expect(persistence.Init(context.Background())).To(Succeed())
   195  
   196  			m, err = json.New("https://localhost:9200", 11, 60, false, persistence)
   197  			Expect(err).ToNot(HaveOccurred())
   198  
   199  			ctx = ctxpkg.ContextSetUser(context.Background(), user1)
   200  		})
   201  
   202  		AfterEach(func() {
   203  			if tmpdir != "" {
   204  				os.RemoveAll(tmpdir)
   205  			}
   206  		})
   207  		Describe("CreatePublicShare", func() {
   208  			It("creates public shares", func() {
   209  				ps, err := m.CreatePublicShare(ctx, user1, sharedResource, grant)
   210  				Expect(err).ToNot(HaveOccurred())
   211  				Expect(ps).ToNot(BeNil())
   212  			})
   213  		})
   214  
   215  		Describe("PublicShares", func() {
   216  			It("lists public shares", func() {
   217  				_, err := m.CreatePublicShare(ctx, user1, sharedResource, grant)
   218  				Expect(err).ToNot(HaveOccurred())
   219  
   220  				ps, err := m.ListPublicShares(ctx, user1, []*link.ListPublicSharesRequest_Filter{}, false)
   221  				Expect(err).ToNot(HaveOccurred())
   222  				Expect(len(ps)).To(Equal(1))
   223  				Expect(ps[0].ResourceId).To(Equal(sharedResource.Id))
   224  			})
   225  
   226  			It("picks up shares from the storage", func() {
   227  				_, err := m.CreatePublicShare(ctx, user1, sharedResource, grant)
   228  				Expect(err).ToNot(HaveOccurred())
   229  
   230  				// Reset manager
   231  				p := cs3.New(storage)
   232  				Expect(p.Init(context.Background())).To(Succeed())
   233  
   234  				m, err = json.New("https://localhost:9200", 11, 60, false, p)
   235  				Expect(err).ToNot(HaveOccurred())
   236  
   237  				ps, err := m.ListPublicShares(ctx, user1, []*link.ListPublicSharesRequest_Filter{}, false)
   238  				Expect(err).ToNot(HaveOccurred())
   239  				Expect(len(ps)).To(Equal(1))
   240  				Expect(ps[0].ResourceId).To(Equal(sharedResource.Id))
   241  			})
   242  
   243  			It("refreshes its cache before writing new data", func() {
   244  				_, err := m.CreatePublicShare(ctx, user1, sharedResource, grant)
   245  				Expect(err).ToNot(HaveOccurred())
   246  
   247  				ps, err := m.ListPublicShares(ctx, user1, []*link.ListPublicSharesRequest_Filter{}, false)
   248  				Expect(err).ToNot(HaveOccurred())
   249  				Expect(len(ps)).To(Equal(1))
   250  
   251  				// Purge file on storage and make sure its mtime is newer than the cache
   252  				path := filepath.Join(tmpdir, "publicshares.json")
   253  				Expect(os.WriteFile(path, []byte("{}"), 0x644)).To(Succeed())
   254  				t := time.Now().Add(5 * time.Minute)
   255  				Expect(os.Chtimes(path, t, t)).To(Succeed())
   256  
   257  				_, err = m.CreatePublicShare(ctx, user1, sharedResource, grant)
   258  				Expect(err).ToNot(HaveOccurred())
   259  
   260  				ps, err = m.ListPublicShares(ctx, user1, []*link.ListPublicSharesRequest_Filter{}, false)
   261  				Expect(err).ToNot(HaveOccurred())
   262  				Expect(len(ps)).To(Equal(1)) // Make sure the first created public share is gone
   263  			})
   264  		})
   265  	})
   266  })