github.com/cs3org/reva/v2@v2.27.7/pkg/share/manager/memory/memory.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 memory
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    30  	"github.com/cs3org/reva/v2/pkg/share"
    31  	"google.golang.org/genproto/protobuf/field_mask"
    32  
    33  	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    34  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    35  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    36  	typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    37  	"github.com/cs3org/reva/v2/pkg/errtypes"
    38  	"github.com/cs3org/reva/v2/pkg/share/manager/registry"
    39  	"github.com/cs3org/reva/v2/pkg/utils"
    40  )
    41  
    42  var counter uint64
    43  
    44  func init() {
    45  	registry.Register("memory", New)
    46  }
    47  
    48  // New returns a new manager.
    49  func New(c map[string]interface{}) (share.Manager, error) {
    50  	state := map[string]map[*collaboration.ShareId]collaboration.ShareState{}
    51  	mp := map[string]map[*collaboration.ShareId]*provider.Reference{}
    52  	return &manager{
    53  		shareState:      state,
    54  		shareMountPoint: mp,
    55  		lock:            &sync.Mutex{},
    56  	}, nil
    57  }
    58  
    59  type manager struct {
    60  	lock   *sync.Mutex
    61  	shares []*collaboration.Share
    62  	// shareState contains the share state for a user.
    63  	// map["alice"]["share-id"]state.
    64  	shareState map[string]map[*collaboration.ShareId]collaboration.ShareState
    65  	// shareMountPoint contains the mountpoint of a share for a user.
    66  	// map["alice"]["share-id"]reference.
    67  	shareMountPoint map[string]map[*collaboration.ShareId]*provider.Reference
    68  }
    69  
    70  func (m *manager) add(ctx context.Context, s *collaboration.Share) {
    71  	m.lock.Lock()
    72  	defer m.lock.Unlock()
    73  	m.shares = append(m.shares, s)
    74  }
    75  
    76  func (m *manager) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) {
    77  	id := atomic.AddUint64(&counter, 1)
    78  	user := ctxpkg.ContextMustGetUser(ctx)
    79  	now := time.Now().UnixNano()
    80  	ts := &typespb.Timestamp{
    81  		Seconds: uint64(now / 1000000000),
    82  		Nanos:   uint32(now % 1000000000),
    83  	}
    84  
    85  	if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER &&
    86  		(utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) {
    87  		return nil, errtypes.BadRequest("memory: owner/creator and grantee are the same")
    88  	}
    89  
    90  	// check if share already exists.
    91  	key := &collaboration.ShareKey{
    92  		Owner:      md.Owner,
    93  		ResourceId: md.Id,
    94  		Grantee:    g.Grantee,
    95  	}
    96  	_, err := m.getByKey(ctx, key)
    97  	// share already exists
    98  	if err == nil {
    99  		return nil, errtypes.AlreadyExists(key.String())
   100  	}
   101  
   102  	s := &collaboration.Share{
   103  		Id: &collaboration.ShareId{
   104  			OpaqueId: fmt.Sprintf("%d", id),
   105  		},
   106  		ResourceId:  md.Id,
   107  		Permissions: g.Permissions,
   108  		Grantee:     g.Grantee,
   109  		Owner:       md.Owner,
   110  		Creator:     user.Id,
   111  		Ctime:       ts,
   112  		Mtime:       ts,
   113  	}
   114  
   115  	m.add(ctx, s)
   116  	return s, nil
   117  }
   118  
   119  func (m *manager) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) {
   120  	m.lock.Lock()
   121  	defer m.lock.Unlock()
   122  	for _, s := range m.shares {
   123  		if s.GetId().OpaqueId == id.OpaqueId {
   124  			return s, nil
   125  		}
   126  	}
   127  	return nil, errtypes.NotFound(id.String())
   128  }
   129  
   130  func (m *manager) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) {
   131  	m.lock.Lock()
   132  	defer m.lock.Unlock()
   133  	for _, s := range m.shares {
   134  		if (utils.UserEqual(key.Owner, s.Owner) || utils.UserEqual(key.Owner, s.Creator)) &&
   135  			utils.ResourceIDEqual(key.ResourceId, s.ResourceId) && utils.GranteeEqual(key.Grantee, s.Grantee) {
   136  			return s, nil
   137  		}
   138  	}
   139  	return nil, errtypes.NotFound(key.String())
   140  }
   141  
   142  func (m *manager) get(ctx context.Context, ref *collaboration.ShareReference) (s *collaboration.Share, err error) {
   143  	switch {
   144  	case ref.GetId() != nil:
   145  		s, err = m.getByID(ctx, ref.GetId())
   146  	case ref.GetKey() != nil:
   147  		s, err = m.getByKey(ctx, ref.GetKey())
   148  	default:
   149  		err = errtypes.NotFound(ref.String())
   150  	}
   151  
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	// check if we are the owner
   157  	user := ctxpkg.ContextMustGetUser(ctx)
   158  	if share.IsCreatedByUser(s, user) {
   159  		return s, nil
   160  	}
   161  
   162  	// or the grantee
   163  	if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, s.Grantee.GetUserId()) {
   164  		return s, nil
   165  	} else if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
   166  		// check if all user groups match this share; TODO(labkode): filter shares created by us.
   167  		for _, g := range user.Groups {
   168  			if g == s.Grantee.GetGroupId().OpaqueId {
   169  				return s, nil
   170  			}
   171  		}
   172  	}
   173  
   174  	// we return not found to not disclose information
   175  	return nil, errtypes.NotFound(ref.String())
   176  }
   177  
   178  func (m *manager) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) {
   179  	share, err := m.get(ctx, ref)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	return share, nil
   185  }
   186  
   187  func (m *manager) Unshare(ctx context.Context, ref *collaboration.ShareReference) error {
   188  	m.lock.Lock()
   189  	defer m.lock.Unlock()
   190  	user := ctxpkg.ContextMustGetUser(ctx)
   191  	for i, s := range m.shares {
   192  		if sharesEqual(ref, s) {
   193  			if share.IsCreatedByUser(s, user) {
   194  				m.shares[len(m.shares)-1], m.shares[i] = m.shares[i], m.shares[len(m.shares)-1]
   195  				m.shares = m.shares[:len(m.shares)-1]
   196  				return nil
   197  			}
   198  		}
   199  	}
   200  	return errtypes.NotFound(ref.String())
   201  }
   202  
   203  func sharesEqual(ref *collaboration.ShareReference, s *collaboration.Share) bool {
   204  	if ref.GetId() != nil && s.Id != nil {
   205  		if ref.GetId().OpaqueId == s.Id.OpaqueId {
   206  			return true
   207  		}
   208  	} else if ref.GetKey() != nil {
   209  		if (utils.UserEqual(ref.GetKey().Owner, s.Owner) || utils.UserEqual(ref.GetKey().Owner, s.Creator)) &&
   210  			utils.ResourceIDEqual(ref.GetKey().ResourceId, s.ResourceId) && utils.GranteeEqual(ref.GetKey().Grantee, s.Grantee) {
   211  			return true
   212  		}
   213  	}
   214  	return false
   215  }
   216  
   217  func (m *manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) {
   218  	m.lock.Lock()
   219  	defer m.lock.Unlock()
   220  	user := ctxpkg.ContextMustGetUser(ctx)
   221  	var shareRef *collaboration.ShareReference
   222  	if ref != nil {
   223  		shareRef = ref
   224  	} else if updated != nil {
   225  		shareRef = &collaboration.ShareReference{
   226  			Spec: &collaboration.ShareReference_Id{
   227  				Id: updated.Id,
   228  			},
   229  		}
   230  	}
   231  
   232  	for i, s := range m.shares {
   233  		if sharesEqual(shareRef, s) {
   234  			if share.IsCreatedByUser(s, user) {
   235  				now := time.Now().UnixNano()
   236  				if p != nil {
   237  					m.shares[i].Permissions = p
   238  				}
   239  				if fieldMask != nil {
   240  					for _, path := range fieldMask.Paths {
   241  						switch path {
   242  						case "permissions":
   243  							m.shares[i].Permissions = updated.Permissions
   244  						case "expiration":
   245  							m.shares[i].Expiration = updated.Expiration
   246  						default:
   247  							return nil, errtypes.NotSupported("updating " + path + " is not supported")
   248  						}
   249  					}
   250  				}
   251  				m.shares[i].Mtime = &typespb.Timestamp{
   252  					Seconds: uint64(now / 1000000000),
   253  					Nanos:   uint32(now % 1000000000),
   254  				}
   255  				return m.shares[i], nil
   256  			}
   257  		}
   258  	}
   259  	return nil, errtypes.NotFound(ref.String())
   260  }
   261  
   262  func (m *manager) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) {
   263  	var ss []*collaboration.Share
   264  	m.lock.Lock()
   265  	defer m.lock.Unlock()
   266  	user := ctxpkg.ContextMustGetUser(ctx)
   267  	for _, s := range m.shares {
   268  		if share.IsCreatedByUser(s, user) {
   269  			// no filter we return earlier
   270  			if len(filters) == 0 {
   271  				ss = append(ss, s)
   272  				continue
   273  			}
   274  			// check filters
   275  			if share.MatchesFilters(s, filters) {
   276  				ss = append(ss, s)
   277  			}
   278  		}
   279  	}
   280  	return ss, nil
   281  }
   282  
   283  // we list the shares that are targeted to the user in context or to the user groups.
   284  func (m *manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error) {
   285  	var rss []*collaboration.ReceivedShare
   286  	m.lock.Lock()
   287  	defer m.lock.Unlock()
   288  	user := ctxpkg.ContextMustGetUser(ctx)
   289  	if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE {
   290  		// TODO: gateway missing!
   291  		return nil, errors.New("can't use inmem share manager and service accounts")
   292  	}
   293  	for _, s := range m.shares {
   294  		if share.IsCreatedByUser(s, user) || !share.IsGrantedToUser(s, user) {
   295  			// omit shares created by the user or shares the user can't access
   296  			continue
   297  		}
   298  
   299  		if len(filters) == 0 {
   300  			rs := m.convert(ctx, s)
   301  			rss = append(rss, rs)
   302  			continue
   303  		}
   304  
   305  		if share.MatchesFilters(s, filters) {
   306  			rs := m.convert(ctx, s)
   307  			rss = append(rss, rs)
   308  		}
   309  	}
   310  	return rss, nil
   311  }
   312  
   313  // convert must be called in a lock-controlled block.
   314  func (m *manager) convert(ctx context.Context, s *collaboration.Share) *collaboration.ReceivedShare {
   315  	rs := &collaboration.ReceivedShare{
   316  		Share: s,
   317  		State: collaboration.ShareState_SHARE_STATE_PENDING,
   318  	}
   319  	user := ctxpkg.ContextMustGetUser(ctx)
   320  	if v, ok := m.shareState[user.Id.String()]; ok {
   321  		if state, ok := v[s.Id]; ok {
   322  			rs.State = state
   323  		}
   324  	}
   325  	if v, ok := m.shareMountPoint[user.Id.String()]; ok {
   326  		if mp, ok := v[s.Id]; ok {
   327  			rs.MountPoint = mp
   328  		}
   329  	}
   330  	return rs
   331  }
   332  
   333  func (m *manager) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) {
   334  	return m.getReceived(ctx, ref)
   335  }
   336  
   337  func (m *manager) getReceived(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) {
   338  	m.lock.Lock()
   339  	defer m.lock.Unlock()
   340  	user := ctxpkg.ContextMustGetUser(ctx)
   341  	for _, s := range m.shares {
   342  		if sharesEqual(ref, s) {
   343  			if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE || share.IsGrantedToUser(s, user) {
   344  				rs := m.convert(ctx, s)
   345  				return rs, nil
   346  			}
   347  		}
   348  	}
   349  	return nil, errtypes.NotFound(ref.String())
   350  }
   351  
   352  func (m *manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) {
   353  	rs, err := m.getReceived(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}})
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  
   358  	m.lock.Lock()
   359  	defer m.lock.Unlock()
   360  
   361  	for i := range fieldMask.Paths {
   362  		switch fieldMask.Paths[i] {
   363  		case "state":
   364  			rs.State = receivedShare.State
   365  		case "mount_point":
   366  			rs.MountPoint = receivedShare.MountPoint
   367  		case "hidden":
   368  			continue
   369  		default:
   370  			return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported")
   371  		}
   372  	}
   373  
   374  	u := ctxpkg.ContextMustGetUser(ctx)
   375  	uid := u.GetId().String()
   376  	if u.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE {
   377  		uid = forUser.String()
   378  	}
   379  
   380  	// Persist state
   381  	if v, ok := m.shareState[uid]; ok {
   382  		v[rs.Share.Id] = rs.State
   383  		m.shareState[uid] = v
   384  	} else {
   385  		a := map[*collaboration.ShareId]collaboration.ShareState{
   386  			rs.Share.Id: rs.State,
   387  		}
   388  		m.shareState[uid] = a
   389  	}
   390  	// Persist mount point
   391  	if v, ok := m.shareMountPoint[uid]; ok {
   392  		v[rs.Share.Id] = rs.MountPoint
   393  		m.shareMountPoint[uid] = v
   394  	} else {
   395  		a := map[*collaboration.ShareId]*provider.Reference{
   396  			rs.Share.Id: rs.MountPoint,
   397  		}
   398  		m.shareMountPoint[uid] = a
   399  	}
   400  
   401  	return rs, nil
   402  }