249 lines
9.5 KiB
Go
249 lines
9.5 KiB
Go
// Copyright 2018-2021 CERN
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// In applying this license, CERN does not waive the privileges and immunities
|
|
// granted to it by virtue of its status as an Intergovernmental Organization
|
|
// or submit itself to any jurisdiction.
|
|
|
|
package share
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
|
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
|
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
|
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
|
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/grants"
|
|
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
|
"google.golang.org/genproto/protobuf/field_mask"
|
|
)
|
|
|
|
const (
|
|
// NoState can be used to signal the filter matching functions to ignore the share state.
|
|
NoState collaboration.ShareState = -1
|
|
)
|
|
|
|
// Metadata contains Metadata for a share
|
|
type Metadata struct {
|
|
ETag string
|
|
Mtime *types.Timestamp
|
|
}
|
|
|
|
// Manager is the interface that manipulates shares.
|
|
type Manager interface {
|
|
// Create a new share in fn with the given acl.
|
|
Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error)
|
|
|
|
// GetShare gets the information for a share by the given ref.
|
|
GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error)
|
|
|
|
// Unshare deletes the share pointed by ref.
|
|
Unshare(ctx context.Context, ref *collaboration.ShareReference) error
|
|
|
|
// UpdateShare updates the mode of the given share.
|
|
UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error)
|
|
|
|
// ListShares returns the shares created by the user. If md is provided is not nil,
|
|
// it returns only shares attached to the given resource.
|
|
ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error)
|
|
|
|
// ListReceivedShares returns the list of shares the user has access to. `forUser` parameter for service accounts only
|
|
ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error)
|
|
|
|
// GetReceivedShare returns the information for a received share.
|
|
GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error)
|
|
|
|
// UpdateReceivedShare updates the received share with share state.`forUser` parameter for service accounts only
|
|
UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error)
|
|
}
|
|
|
|
// ReceivedShareWithUser holds the relevant information for representing a received share of a user
|
|
type ReceivedShareWithUser struct {
|
|
UserID *userv1beta1.UserId
|
|
ReceivedShare *collaboration.ReceivedShare
|
|
}
|
|
|
|
// DumpableManager defines a share manager which supports dumping its contents
|
|
type DumpableManager interface {
|
|
Dump(ctx context.Context, shareChan chan<- *collaboration.Share, receivedShareChan chan<- ReceivedShareWithUser) error
|
|
}
|
|
|
|
// LoadableManager defines a share manager which supports loading contents from a dump
|
|
type LoadableManager interface {
|
|
Load(ctx context.Context, shareChan <-chan *collaboration.Share, receivedShareChan <-chan ReceivedShareWithUser) error
|
|
}
|
|
|
|
// GroupGranteeFilter is an abstraction for creating filter by grantee type group.
|
|
func GroupGranteeFilter() *collaboration.Filter {
|
|
return &collaboration.Filter{
|
|
Type: collaboration.Filter_TYPE_GRANTEE_TYPE,
|
|
Term: &collaboration.Filter_GranteeType{
|
|
GranteeType: provider.GranteeType_GRANTEE_TYPE_GROUP,
|
|
},
|
|
}
|
|
}
|
|
|
|
// UserGranteeFilter is an abstraction for creating filter by grantee type user.
|
|
func UserGranteeFilter() *collaboration.Filter {
|
|
return &collaboration.Filter{
|
|
Type: collaboration.Filter_TYPE_GRANTEE_TYPE,
|
|
Term: &collaboration.Filter_GranteeType{
|
|
GranteeType: provider.GranteeType_GRANTEE_TYPE_USER,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ResourceIDFilter is an abstraction for creating filter by resource id.
|
|
func ResourceIDFilter(id *provider.ResourceId) *collaboration.Filter {
|
|
return &collaboration.Filter{
|
|
Type: collaboration.Filter_TYPE_RESOURCE_ID,
|
|
Term: &collaboration.Filter_ResourceId{
|
|
ResourceId: id,
|
|
},
|
|
}
|
|
}
|
|
|
|
// SpaceIDFilter is an abstraction for creating filter by space id.
|
|
func SpaceIDFilter(id string) *collaboration.Filter {
|
|
return &collaboration.Filter{
|
|
Type: collaboration.Filter_TYPE_SPACE_ID,
|
|
Term: &collaboration.Filter_SpaceId{
|
|
SpaceId: id,
|
|
},
|
|
}
|
|
}
|
|
|
|
// StateFilter is an abstraction for creating filter by share state.
|
|
func StateFilter(state collaboration.ShareState) *collaboration.Filter {
|
|
return &collaboration.Filter{
|
|
Type: collaboration.Filter_TYPE_STATE,
|
|
Term: &collaboration.Filter_State{
|
|
State: state,
|
|
},
|
|
}
|
|
}
|
|
|
|
// IsCreatedByUser checks if the user is the owner or creator of the share.
|
|
func IsCreatedByUser(share *collaboration.Share, user *userv1beta1.User) bool {
|
|
return utils.UserEqual(user.Id, share.Owner) || utils.UserEqual(user.Id, share.Creator)
|
|
}
|
|
|
|
// IsGrantedToUser checks if the user is a grantee of the share. Either by a user grant or by a group grant.
|
|
func IsGrantedToUser(share *collaboration.Share, user *userv1beta1.User) bool {
|
|
if share.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, share.Grantee.GetUserId()) {
|
|
return true
|
|
}
|
|
if share.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
|
|
// check if any of the user's group is the grantee of the share
|
|
for _, g := range user.Groups {
|
|
if g == share.Grantee.GetGroupId().OpaqueId {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// MatchesFilter tests if the share passes the filter.
|
|
func MatchesFilter(share *collaboration.Share, state collaboration.ShareState, filter *collaboration.Filter) bool {
|
|
switch filter.Type {
|
|
case collaboration.Filter_TYPE_RESOURCE_ID:
|
|
return utils.ResourceIDEqual(share.ResourceId, filter.GetResourceId())
|
|
case collaboration.Filter_TYPE_GRANTEE_TYPE:
|
|
return share.Grantee.Type == filter.GetGranteeType()
|
|
case collaboration.Filter_TYPE_EXCLUDE_DENIALS:
|
|
// This filter type is used to filter out "denial shares". These are currently implemented by having the permission "0".
|
|
// I.e. if the permission is 0 we don't want to show it.
|
|
return !grants.PermissionsEqual(share.Permissions.Permissions, &provider.ResourcePermissions{})
|
|
case collaboration.Filter_TYPE_SPACE_ID:
|
|
return share.ResourceId.SpaceId == filter.GetSpaceId()
|
|
case collaboration.Filter_TYPE_STATE:
|
|
return state == filter.GetState()
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// MatchesAnyFilter checks if the share passes at least one of the given filters.
|
|
func MatchesAnyFilter(share *collaboration.Share, state collaboration.ShareState, filters []*collaboration.Filter) bool {
|
|
for _, f := range filters {
|
|
if MatchesFilter(share, state, f) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// MatchesFilters checks if the share passes the given filters.
|
|
// Filters of the same type form a disjuntion, a logical OR. Filters of separate type form a conjunction, a logical AND.
|
|
// Here is an example:
|
|
// (resource_id=1 OR resource_id=2) AND (grantee_type=USER OR grantee_type=GROUP)
|
|
func MatchesFilters(share *collaboration.Share, filters []*collaboration.Filter) bool {
|
|
if len(filters) == 0 {
|
|
return true
|
|
}
|
|
grouped := GroupFiltersByType(filters)
|
|
for _, f := range grouped {
|
|
if !MatchesAnyFilter(share, NoState, f) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// MatchesFiltersWithState checks if the share passes the given filters.
|
|
// This can check filter by share state
|
|
// Filters of the same type form a disjuntion, a logical OR. Filters of separate type form a conjunction, a logical AND.
|
|
// Here is an example:
|
|
// (resource_id=1 OR resource_id=2) AND (grantee_type=USER OR grantee_type=GROUP)
|
|
func MatchesFiltersWithState(share *collaboration.Share, state collaboration.ShareState, filters []*collaboration.Filter) bool {
|
|
if len(filters) == 0 {
|
|
return true
|
|
}
|
|
grouped := GroupFiltersByType(filters)
|
|
for _, f := range grouped {
|
|
if !MatchesAnyFilter(share, state, f) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// GroupFiltersByType groups the given filters and returns a map using the filter type as the key.
|
|
func GroupFiltersByType(filters []*collaboration.Filter) map[collaboration.Filter_Type][]*collaboration.Filter {
|
|
grouped := make(map[collaboration.Filter_Type][]*collaboration.Filter)
|
|
for _, f := range filters {
|
|
grouped[f.Type] = append(grouped[f.Type], f)
|
|
}
|
|
return grouped
|
|
}
|
|
|
|
// FilterFiltersByType returns a slice of filters by a given type.
|
|
// If no filter with the given type exists within the filters, then an
|
|
// empty slice is returned.
|
|
func FilterFiltersByType(f []*collaboration.Filter, t collaboration.Filter_Type) []*collaboration.Filter {
|
|
return GroupFiltersByType(f)[t]
|
|
}
|
|
|
|
// IsExpired tests whether a share is expired
|
|
func IsExpired(s *collaboration.Share) bool {
|
|
if e := s.GetExpiration(); e != nil {
|
|
expiration := time.Unix(int64(e.Seconds), int64(e.Nanos))
|
|
return expiration.Before(time.Now())
|
|
}
|
|
return false
|
|
}
|