github.com/cs3org/reva/v2@v2.27.7/pkg/publicshare/manager/json/json.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 json
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"os"
    26  	"os/signal"
    27  	"strconv"
    28  	"strings"
    29  	"sync"
    30  	"syscall"
    31  	"time"
    32  
    33  	"github.com/rs/zerolog/log"
    34  	"golang.org/x/crypto/bcrypt"
    35  	"google.golang.org/protobuf/proto"
    36  
    37  	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    38  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    39  	link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
    40  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    41  	typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    42  	"github.com/cs3org/reva/v2/pkg/appctx"
    43  	"github.com/cs3org/reva/v2/pkg/errtypes"
    44  	"github.com/cs3org/reva/v2/pkg/publicshare"
    45  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence"
    46  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/cs3"
    47  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/file"
    48  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/memory"
    49  	"github.com/cs3org/reva/v2/pkg/publicshare/manager/registry"
    50  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    51  	"github.com/cs3org/reva/v2/pkg/storage/utils/metadata"
    52  	"github.com/cs3org/reva/v2/pkg/utils"
    53  	"github.com/mitchellh/mapstructure"
    54  	"github.com/pkg/errors"
    55  )
    56  
    57  func init() {
    58  	registry.Register("json", NewFile)
    59  	registry.Register("jsoncs3", NewCS3)
    60  	registry.Register("jsonmemory", NewMemory)
    61  }
    62  
    63  // NewFile returns a new filesystem public shares manager.
    64  func NewFile(c map[string]interface{}) (publicshare.Manager, error) {
    65  	conf := &fileConfig{}
    66  	if err := mapstructure.Decode(c, conf); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	conf.init()
    71  	if conf.File == "" {
    72  		conf.File = "/var/tmp/reva/publicshares"
    73  	}
    74  
    75  	p := file.New(conf.File)
    76  	return New(conf.GatewayAddr, conf.SharePasswordHashCost, conf.JanitorRunInterval, conf.EnableExpiredSharesCleanup, p)
    77  }
    78  
    79  // NewMemory returns a new in-memory public shares manager.
    80  func NewMemory(c map[string]interface{}) (publicshare.Manager, error) {
    81  	conf := &commonConfig{}
    82  	if err := mapstructure.Decode(c, conf); err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	conf.init()
    87  	p := memory.New()
    88  
    89  	return New(conf.GatewayAddr, conf.SharePasswordHashCost, conf.JanitorRunInterval, conf.EnableExpiredSharesCleanup, p)
    90  }
    91  
    92  // NewCS3 returns a new cs3 public shares manager.
    93  func NewCS3(c map[string]interface{}) (publicshare.Manager, error) {
    94  	conf := &cs3Config{}
    95  	if err := mapstructure.Decode(c, conf); err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	conf.init()
   100  
   101  	s, err := metadata.NewCS3Storage(conf.ProviderAddr, conf.ProviderAddr, conf.ServiceUserID, conf.ServiceUserIdp, conf.MachineAuthAPIKey)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	p := cs3.New(s)
   106  
   107  	return New(conf.GatewayAddr, conf.SharePasswordHashCost, conf.JanitorRunInterval, conf.EnableExpiredSharesCleanup, p)
   108  }
   109  
   110  // New returns a new public share manager instance
   111  func New(gwAddr string, pwHashCost, janitorRunInterval int, enableCleanup bool, p persistence.Persistence) (publicshare.Manager, error) {
   112  	m := &manager{
   113  		gatewayAddr:                gwAddr,
   114  		mutex:                      &sync.Mutex{},
   115  		passwordHashCost:           pwHashCost,
   116  		janitorRunInterval:         janitorRunInterval,
   117  		enableExpiredSharesCleanup: enableCleanup,
   118  		persistence:                p,
   119  	}
   120  
   121  	go m.startJanitorRun()
   122  	return m, nil
   123  }
   124  
   125  type commonConfig struct {
   126  	GatewayAddr                string `mapstructure:"gateway_addr"`
   127  	SharePasswordHashCost      int    `mapstructure:"password_hash_cost"`
   128  	JanitorRunInterval         int    `mapstructure:"janitor_run_interval"`
   129  	EnableExpiredSharesCleanup bool   `mapstructure:"enable_expired_shares_cleanup"`
   130  }
   131  
   132  type fileConfig struct {
   133  	commonConfig `mapstructure:",squash"`
   134  
   135  	File string `mapstructure:"file"`
   136  }
   137  
   138  type cs3Config struct {
   139  	commonConfig `mapstructure:",squash"`
   140  
   141  	ProviderAddr      string `mapstructure:"provider_addr"`
   142  	ServiceUserID     string `mapstructure:"service_user_id"`
   143  	ServiceUserIdp    string `mapstructure:"service_user_idp"`
   144  	MachineAuthAPIKey string `mapstructure:"machine_auth_apikey"`
   145  }
   146  
   147  func (c *commonConfig) init() {
   148  	if c.SharePasswordHashCost == 0 {
   149  		c.SharePasswordHashCost = 11
   150  	}
   151  	if c.JanitorRunInterval == 0 {
   152  		c.JanitorRunInterval = 60
   153  	}
   154  }
   155  
   156  type manager struct {
   157  	gatewayAddr string
   158  	mutex       *sync.Mutex
   159  	persistence persistence.Persistence
   160  
   161  	passwordHashCost           int
   162  	janitorRunInterval         int
   163  	enableExpiredSharesCleanup bool
   164  }
   165  
   166  func (m *manager) init() error {
   167  	return m.persistence.Init(context.Background())
   168  }
   169  
   170  func (m *manager) startJanitorRun() {
   171  	if !m.enableExpiredSharesCleanup {
   172  		return
   173  	}
   174  
   175  	ticker := time.NewTicker(time.Duration(m.janitorRunInterval) * time.Second)
   176  	work := make(chan os.Signal, 1)
   177  	signal.Notify(work, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT)
   178  
   179  	for {
   180  		select {
   181  		case <-work:
   182  			return
   183  		case <-ticker.C:
   184  			m.cleanupExpiredShares()
   185  		}
   186  	}
   187  }
   188  
   189  // Dump exports public shares to channels (e.g. during migration)
   190  func (m *manager) Dump(ctx context.Context, shareChan chan<- *publicshare.WithPassword) error {
   191  	log := appctx.GetLogger(ctx)
   192  
   193  	m.mutex.Lock()
   194  	defer m.mutex.Unlock()
   195  
   196  	if err := m.init(); err != nil {
   197  		return err
   198  	}
   199  
   200  	db, err := m.persistence.Read(ctx)
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	for _, v := range db {
   206  		var local publicshare.WithPassword
   207  		if err := utils.UnmarshalJSONToProtoV1([]byte(v.(map[string]interface{})["share"].(string)), &local.PublicShare); err != nil {
   208  			log.Error().Err(err).Msg("error unmarshalling share")
   209  		}
   210  		local.Password = v.(map[string]interface{})["password"].(string)
   211  		shareChan <- &local
   212  	}
   213  
   214  	return nil
   215  }
   216  
   217  // Load imports public shares and received shares from channels (e.g. during migration)
   218  func (m *manager) Load(ctx context.Context, shareChan <-chan *publicshare.WithPassword) error {
   219  	m.mutex.Lock()
   220  	defer m.mutex.Unlock()
   221  
   222  	if err := m.init(); err != nil {
   223  		return err
   224  	}
   225  
   226  	db, err := m.persistence.Read(ctx)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	for ps := range shareChan {
   232  		encShare, err := utils.MarshalProtoV1ToJSON(&ps.PublicShare)
   233  		if err != nil {
   234  			return err
   235  		}
   236  
   237  		db[ps.PublicShare.Id.GetOpaqueId()] = map[string]interface{}{
   238  			"share":    string(encShare),
   239  			"password": ps.Password,
   240  		}
   241  	}
   242  	return m.persistence.Write(ctx, db)
   243  }
   244  
   245  // CreatePublicShare adds a new entry to manager.shares
   246  func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *provider.ResourceInfo, g *link.Grant) (*link.PublicShare, error) {
   247  	id := &link.PublicShareId{
   248  		OpaqueId: utils.RandString(15),
   249  	}
   250  
   251  	tkn := utils.RandString(15)
   252  	now := time.Now().UnixNano()
   253  
   254  	displayName, ok := rInfo.ArbitraryMetadata.Metadata["name"]
   255  	if !ok {
   256  		displayName = tkn
   257  	}
   258  
   259  	quicklink, _ := strconv.ParseBool(rInfo.ArbitraryMetadata.Metadata["quicklink"])
   260  
   261  	var passwordProtected bool
   262  	password := g.Password
   263  	if len(password) > 0 {
   264  		h, err := bcrypt.GenerateFromPassword([]byte(password), m.passwordHashCost)
   265  		if err != nil {
   266  			return nil, errors.Wrap(err, "could not hash share password")
   267  		}
   268  		password = string(h)
   269  		passwordProtected = true
   270  	}
   271  
   272  	createdAt := &typespb.Timestamp{
   273  		Seconds: uint64(now / int64(time.Second)),
   274  		Nanos:   uint32(now % int64(time.Second)),
   275  	}
   276  
   277  	s := &link.PublicShare{
   278  		Id:                id,
   279  		Owner:             rInfo.GetOwner(),
   280  		Creator:           u.Id,
   281  		ResourceId:        rInfo.Id,
   282  		Token:             tkn,
   283  		Permissions:       g.Permissions,
   284  		Ctime:             createdAt,
   285  		Mtime:             createdAt,
   286  		PasswordProtected: passwordProtected,
   287  		Expiration:        g.Expiration,
   288  		DisplayName:       displayName,
   289  		Quicklink:         quicklink,
   290  	}
   291  
   292  	ps := &publicShare{
   293  		Password: password,
   294  	}
   295  	proto.Merge(&ps.PublicShare, s)
   296  
   297  	m.mutex.Lock()
   298  	defer m.mutex.Unlock()
   299  
   300  	if err := m.init(); err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	encShare, err := utils.MarshalProtoV1ToJSON(&ps.PublicShare)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	db, err := m.persistence.Read(ctx)
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  
   314  	if _, ok := db[s.Id.GetOpaqueId()]; !ok {
   315  		db[s.Id.GetOpaqueId()] = map[string]interface{}{
   316  			"share":    string(encShare),
   317  			"password": ps.Password,
   318  		}
   319  	} else {
   320  		return nil, errors.New("key already exists")
   321  	}
   322  
   323  	err = m.persistence.Write(ctx, db)
   324  	if err != nil {
   325  		return nil, err
   326  	}
   327  
   328  	return s, nil
   329  }
   330  
   331  // UpdatePublicShare updates the public share
   332  func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link.UpdatePublicShareRequest) (*link.PublicShare, error) {
   333  	log := appctx.GetLogger(ctx)
   334  	share, err := m.GetPublicShare(ctx, u, req.Ref, false)
   335  	if err != nil {
   336  		return nil, errors.New("ref does not exist")
   337  	}
   338  
   339  	now := time.Now().UnixNano()
   340  	var newPasswordEncoded string
   341  	passwordChanged := false
   342  
   343  	switch req.GetUpdate().GetType() {
   344  	case link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME:
   345  		log.Debug().Str("json", "update display name").Msgf("from: `%v` to `%v`", share.DisplayName, req.Update.GetDisplayName())
   346  		share.DisplayName = req.Update.GetDisplayName()
   347  	case link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS:
   348  		old, _ := json.Marshal(share.Permissions)
   349  		new, _ := json.Marshal(req.Update.GetGrant().Permissions)
   350  
   351  		if req.GetUpdate().GetGrant().GetPassword() != "" {
   352  			passwordChanged = true
   353  			h, err := bcrypt.GenerateFromPassword([]byte(req.Update.GetGrant().Password), m.passwordHashCost)
   354  			if err != nil {
   355  				return nil, errors.Wrap(err, "could not hash share password")
   356  			}
   357  			newPasswordEncoded = string(h)
   358  			share.PasswordProtected = true
   359  		}
   360  
   361  		log.Debug().Str("json", "update grants").Msgf("from: `%v`\nto\n`%v`", old, new)
   362  		share.Permissions = req.Update.GetGrant().GetPermissions()
   363  	case link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION:
   364  		old, _ := json.Marshal(share.Expiration)
   365  		new, _ := json.Marshal(req.Update.GetGrant().Expiration)
   366  		log.Debug().Str("json", "update expiration").Msgf("from: `%v`\nto\n`%v`", old, new)
   367  		share.Expiration = req.Update.GetGrant().Expiration
   368  	case link.UpdatePublicShareRequest_Update_TYPE_PASSWORD:
   369  		passwordChanged = true
   370  		if req.Update.GetGrant().Password == "" {
   371  			share.PasswordProtected = false
   372  			newPasswordEncoded = ""
   373  		} else {
   374  			h, err := bcrypt.GenerateFromPassword([]byte(req.Update.GetGrant().Password), m.passwordHashCost)
   375  			if err != nil {
   376  				return nil, errors.Wrap(err, "could not hash share password")
   377  			}
   378  			newPasswordEncoded = string(h)
   379  			share.PasswordProtected = true
   380  		}
   381  	default:
   382  		return nil, fmt.Errorf("invalid update type: %v", req.GetUpdate().GetType())
   383  	}
   384  
   385  	share.Mtime = &typespb.Timestamp{
   386  		Seconds: uint64(now / int64(time.Second)),
   387  		Nanos:   uint32(now % int64(time.Second)),
   388  	}
   389  
   390  	m.mutex.Lock()
   391  	defer m.mutex.Unlock()
   392  
   393  	if err := m.init(); err != nil {
   394  		return nil, err
   395  	}
   396  
   397  	db, err := m.persistence.Read(ctx)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  
   402  	encShare, err := utils.MarshalProtoV1ToJSON(share)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  
   407  	data, ok := db[share.Id.OpaqueId].(map[string]interface{})
   408  	if !ok {
   409  		data = map[string]interface{}{}
   410  	}
   411  
   412  	if ok && passwordChanged {
   413  		data["password"] = newPasswordEncoded
   414  	}
   415  	data["share"] = string(encShare)
   416  
   417  	db[share.Id.OpaqueId] = data
   418  
   419  	err = m.persistence.Write(ctx, db)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	return share, nil
   425  }
   426  
   427  // GetPublicShare gets a public share either by ID or Token.
   428  func (m *manager) GetPublicShare(ctx context.Context, u *user.User, ref *link.PublicShareReference, sign bool) (*link.PublicShare, error) {
   429  	m.mutex.Lock()
   430  	defer m.mutex.Unlock()
   431  
   432  	if err := m.init(); err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	if ref.GetToken() != "" {
   437  		ps, pw, err := m.getByToken(ctx, ref.GetToken())
   438  		if err != nil {
   439  			return nil, errtypes.NotFound("no shares found by token")
   440  		}
   441  		if ps.PasswordProtected && sign {
   442  			err := publicshare.AddSignature(ps, pw)
   443  			if err != nil {
   444  				return nil, err
   445  			}
   446  		}
   447  		return ps, nil
   448  	}
   449  
   450  	db, err := m.persistence.Read(ctx)
   451  	if err != nil {
   452  		return nil, err
   453  	}
   454  
   455  	for _, v := range db {
   456  		d := v.(map[string]interface{})["share"]
   457  		passDB := v.(map[string]interface{})["password"].(string)
   458  
   459  		var ps link.PublicShare
   460  		if err := utils.UnmarshalJSONToProtoV1([]byte(d.(string)), &ps); err != nil {
   461  			return nil, err
   462  		}
   463  
   464  		if ref.GetId().GetOpaqueId() == ps.Id.OpaqueId {
   465  			if publicshare.IsExpired(&ps) {
   466  				if err := m.revokeExpiredPublicShare(ctx, &ps); err != nil {
   467  					return nil, err
   468  				}
   469  				return nil, errtypes.NotFound("no shares found by id:" + ref.GetId().String())
   470  			}
   471  			if ps.PasswordProtected && sign {
   472  				err := publicshare.AddSignature(&ps, passDB)
   473  				if err != nil {
   474  					return nil, err
   475  				}
   476  			}
   477  			return &ps, nil
   478  		}
   479  
   480  	}
   481  	return nil, errtypes.NotFound("no shares found by id:" + ref.GetId().String())
   482  }
   483  
   484  // ListPublicShares retrieves all the shares on the manager that are valid.
   485  func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter, sign bool) ([]*link.PublicShare, error) {
   486  	m.mutex.Lock()
   487  	defer m.mutex.Unlock()
   488  
   489  	if err := m.init(); err != nil {
   490  		return nil, err
   491  	}
   492  
   493  	log := appctx.GetLogger(ctx)
   494  
   495  	db, err := m.persistence.Read(ctx)
   496  	if err != nil {
   497  		return nil, err
   498  	}
   499  
   500  	client, err := pool.GetGatewayServiceClient(m.gatewayAddr)
   501  	if err != nil {
   502  		return nil, errors.Wrap(err, "failed to list shares")
   503  	}
   504  	cache := make(map[string]struct{})
   505  
   506  	shares := []*link.PublicShare{}
   507  	for _, v := range db {
   508  		var local publicShare
   509  		if err := utils.UnmarshalJSONToProtoV1([]byte(v.(map[string]interface{})["share"].(string)), &local.PublicShare); err != nil {
   510  			return nil, err
   511  		}
   512  
   513  		if publicshare.IsExpired(&local.PublicShare) {
   514  			if err := m.revokeExpiredPublicShare(ctx, &local.PublicShare); err != nil {
   515  				log.Error().Err(err).
   516  					Str("share_token", local.Token).
   517  					Msg("failed to revoke expired public share")
   518  			}
   519  			continue
   520  		}
   521  
   522  		if !publicshare.MatchesFilters(&local.PublicShare, filters) {
   523  			continue
   524  		}
   525  
   526  		key := strings.Join([]string{local.ResourceId.StorageId, local.ResourceId.OpaqueId}, "!")
   527  		if _, hit := cache[key]; !hit && !publicshare.IsCreatedByUser(&local.PublicShare, u) {
   528  			sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: local.ResourceId}})
   529  			if err != nil {
   530  				log.Error().
   531  					Err(err).
   532  					Interface("resource_id", local.ResourceId).
   533  					Msg("ListShares: an error occurred during stat on the resource")
   534  				continue
   535  			}
   536  			if sRes.Status.Code != rpc.Code_CODE_OK {
   537  				if sRes.Status.Code == rpc.Code_CODE_NOT_FOUND {
   538  					log.Debug().
   539  						Str("message", sRes.Status.Message).
   540  						Interface("status", sRes.Status).
   541  						Interface("resource_id", local.ResourceId).
   542  						Msg("ListShares: Resource not found")
   543  					continue
   544  				}
   545  				log.Error().
   546  					Str("message", sRes.Status.Message).
   547  					Interface("status", sRes.Status).
   548  					Interface("resource_id", local.ResourceId).
   549  					Msg("ListShares: could not stat resource")
   550  				continue
   551  			}
   552  			if !sRes.Info.PermissionSet.ListGrants {
   553  				// skip because the user doesn't have the permissions to list
   554  				// shares of this file.
   555  				continue
   556  			}
   557  			cache[key] = struct{}{}
   558  		}
   559  
   560  		if local.PublicShare.PasswordProtected && sign {
   561  			if err := publicshare.AddSignature(&local.PublicShare, local.Password); err != nil {
   562  				return nil, err
   563  			}
   564  		}
   565  
   566  		shares = append(shares, &local.PublicShare)
   567  	}
   568  	return shares, nil
   569  }
   570  
   571  func (m *manager) cleanupExpiredShares() {
   572  	m.mutex.Lock()
   573  	defer m.mutex.Unlock()
   574  
   575  	if err := m.init(); err != nil {
   576  		return
   577  	}
   578  
   579  	db, _ := m.persistence.Read(context.Background())
   580  
   581  	for _, v := range db {
   582  		d := v.(map[string]interface{})["share"]
   583  
   584  		var ps link.PublicShare
   585  		_ = utils.UnmarshalJSONToProtoV1([]byte(d.(string)), &ps)
   586  
   587  		if publicshare.IsExpired(&ps) {
   588  			_ = m.revokeExpiredPublicShare(context.Background(), &ps)
   589  		}
   590  	}
   591  }
   592  
   593  // revokeExpiredPublicShare doesn't have a lock inside, ensure a lock before call
   594  func (m *manager) revokeExpiredPublicShare(ctx context.Context, s *link.PublicShare) error {
   595  	if !m.enableExpiredSharesCleanup {
   596  		return nil
   597  	}
   598  
   599  	err := m.revokePublicShare(ctx, &link.PublicShareReference{
   600  		Spec: &link.PublicShareReference_Id{
   601  			Id: &link.PublicShareId{
   602  				OpaqueId: s.Id.OpaqueId,
   603  			},
   604  		},
   605  	})
   606  	if err != nil {
   607  		log.Err(err).Msg(fmt.Sprintf("publicShareJSONManager: error deleting public share with opaqueId: %s", s.Id.OpaqueId))
   608  		return err
   609  	}
   610  
   611  	return nil
   612  }
   613  
   614  // RevokePublicShare undocumented.
   615  func (m *manager) RevokePublicShare(ctx context.Context, _ *user.User, ref *link.PublicShareReference) error {
   616  	m.mutex.Lock()
   617  	defer m.mutex.Unlock()
   618  
   619  	if err := m.init(); err != nil {
   620  		return err
   621  	}
   622  
   623  	return m.revokePublicShare(ctx, ref)
   624  }
   625  
   626  // revokePublicShare doesn't have a lock inside, ensure a lock before call
   627  func (m *manager) revokePublicShare(ctx context.Context, ref *link.PublicShareReference) error {
   628  	db, err := m.persistence.Read(ctx)
   629  	if err != nil {
   630  		return err
   631  	}
   632  
   633  	switch {
   634  	case ref.GetId() != nil && ref.GetId().OpaqueId != "":
   635  		if _, ok := db[ref.GetId().OpaqueId]; ok {
   636  			delete(db, ref.GetId().OpaqueId)
   637  		} else {
   638  			return errors.New("reference does not exist")
   639  		}
   640  	case ref.GetToken() != "":
   641  		share, _, err := m.getByToken(ctx, ref.GetToken())
   642  		if err != nil {
   643  			return err
   644  		}
   645  		delete(db, share.Id.OpaqueId)
   646  	default:
   647  		return errors.New("reference does not exist")
   648  	}
   649  
   650  	return m.persistence.Write(ctx, db)
   651  }
   652  
   653  // getByToken doesn't have a lock inside, ensure a lock before call
   654  func (m *manager) getByToken(ctx context.Context, token string) (*link.PublicShare, string, error) {
   655  	db, err := m.persistence.Read(ctx)
   656  	if err != nil {
   657  		return nil, "", err
   658  	}
   659  
   660  	for _, v := range db {
   661  		var local link.PublicShare
   662  		if err := utils.UnmarshalJSONToProtoV1([]byte(v.(map[string]interface{})["share"].(string)), &local); err != nil {
   663  			return nil, "", err
   664  		}
   665  
   666  		if local.Token == token {
   667  			passDB := v.(map[string]interface{})["password"].(string)
   668  			return &local, passDB, nil
   669  		}
   670  	}
   671  
   672  	return nil, "", fmt.Errorf("share with token: `%v` not found", token)
   673  }
   674  
   675  // GetPublicShareByToken gets a public share by its opaque token.
   676  func (m *manager) GetPublicShareByToken(ctx context.Context, token string, auth *link.PublicShareAuthentication, sign bool) (*link.PublicShare, error) {
   677  	m.mutex.Lock()
   678  	defer m.mutex.Unlock()
   679  
   680  	if err := m.init(); err != nil {
   681  		return nil, err
   682  	}
   683  
   684  	db, err := m.persistence.Read(ctx)
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	for _, v := range db {
   690  		passDB := v.(map[string]interface{})["password"].(string)
   691  		var local link.PublicShare
   692  		if err := utils.UnmarshalJSONToProtoV1([]byte(v.(map[string]interface{})["share"].(string)), &local); err != nil {
   693  			return nil, err
   694  		}
   695  
   696  		if local.Token == token {
   697  			if publicshare.IsExpired(&local) {
   698  				if err := m.revokeExpiredPublicShare(ctx, &local); err != nil {
   699  					return nil, err
   700  				}
   701  				break
   702  			}
   703  
   704  			if local.PasswordProtected {
   705  				if publicshare.Authenticate(&local, passDB, auth) {
   706  					if sign {
   707  						err := publicshare.AddSignature(&local, passDB)
   708  						if err != nil {
   709  							return nil, err
   710  						}
   711  					}
   712  					return &local, nil
   713  				}
   714  
   715  				return nil, errtypes.InvalidCredentials("json: invalid password")
   716  			}
   717  			return &local, nil
   718  		}
   719  	}
   720  
   721  	return nil, errtypes.NotFound(fmt.Sprintf("share with token: `%v` not found", token))
   722  }
   723  
   724  type publicShare struct {
   725  	link.PublicShare
   726  	Password string `json:"password"`
   727  }