github.com/cs3org/reva/v2@v2.27.7/pkg/share/manager/owncloudsql/conversions.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
    20  
    21  import (
    22  	"context"
    23  	"strings"
    24  	"time"
    25  
    26  	grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
    27  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    28  	userprovider "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    29  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    30  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    31  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    32  	typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    33  	"github.com/cs3org/reva/v2/pkg/conversions"
    34  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    35  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    36  	"github.com/cs3org/reva/v2/pkg/utils"
    37  	"github.com/jellydator/ttlcache/v2"
    38  )
    39  
    40  // DBShare stores information about user and public shares.
    41  type DBShare struct {
    42  	ID           string
    43  	UIDOwner     string
    44  	UIDInitiator string
    45  	ItemStorage  string
    46  	FileSource   string
    47  	ShareWith    string
    48  	Token        string
    49  	Expiration   string
    50  	Permissions  int
    51  	ShareType    int
    52  	ShareName    string
    53  	STime        int
    54  	FileTarget   string
    55  	RejectedBy   string
    56  	State        int
    57  	Parent       int
    58  }
    59  
    60  // UserConverter describes an interface for converting user ids to names and back
    61  type UserConverter interface {
    62  	UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error)
    63  	UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error)
    64  	GetUser(userid *userpb.UserId) (*userpb.User, error)
    65  }
    66  
    67  // GatewayUserConverter converts usernames and ids using the gateway
    68  type GatewayUserConverter struct {
    69  	gwAddr string
    70  
    71  	IDCache   *ttlcache.Cache
    72  	NameCache *ttlcache.Cache
    73  }
    74  
    75  // NewGatewayUserConverter returns a instance of GatewayUserConverter
    76  func NewGatewayUserConverter(gwAddr string) *GatewayUserConverter {
    77  	IDCache := ttlcache.NewCache()
    78  	_ = IDCache.SetTTL(30 * time.Second)
    79  	IDCache.SkipTTLExtensionOnHit(true)
    80  	NameCache := ttlcache.NewCache()
    81  	_ = NameCache.SetTTL(30 * time.Second)
    82  	NameCache.SkipTTLExtensionOnHit(true)
    83  
    84  	return &GatewayUserConverter{
    85  		gwAddr:    gwAddr,
    86  		IDCache:   IDCache,
    87  		NameCache: NameCache,
    88  	}
    89  }
    90  
    91  // UserIDToUserName converts a user ID to an username
    92  func (c *GatewayUserConverter) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) {
    93  	username, err := c.NameCache.Get(userid.String())
    94  	if err == nil {
    95  		return username.(string), nil
    96  	}
    97  
    98  	gwConn, err := pool.GetGatewayServiceClient(c.gwAddr)
    99  	if err != nil {
   100  		return "", err
   101  	}
   102  	getUserResponse, err := gwConn.GetUser(ctx, &userprovider.GetUserRequest{
   103  		UserId:                 userid,
   104  		SkipFetchingUserGroups: true,
   105  	})
   106  	if err != nil {
   107  		return "", err
   108  	}
   109  	if getUserResponse.Status.Code != rpc.Code_CODE_OK {
   110  		return "", status.NewErrorFromCode(getUserResponse.Status.Code, "gateway")
   111  	}
   112  	_ = c.NameCache.Set(userid.String(), getUserResponse.User.Username)
   113  	return getUserResponse.User.Username, nil
   114  }
   115  
   116  // UserNameToUserID converts a username to an user ID
   117  func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) {
   118  	id, err := c.IDCache.Get(username)
   119  	if err == nil {
   120  		return id.(*userpb.UserId), nil
   121  	}
   122  
   123  	gwConn, err := pool.GetGatewayServiceClient(c.gwAddr)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	getUserResponse, err := gwConn.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{
   128  		Claim:                  "username",
   129  		Value:                  username,
   130  		SkipFetchingUserGroups: true,
   131  	})
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	if getUserResponse.Status.Code != rpc.Code_CODE_OK {
   136  		return nil, status.NewErrorFromCode(getUserResponse.Status.Code, "gateway")
   137  	}
   138  	_ = c.IDCache.Set(username, getUserResponse.User.Id)
   139  	return getUserResponse.User.Id, nil
   140  }
   141  
   142  // GetUser gets the user
   143  func (c *GatewayUserConverter) GetUser(userid *userpb.UserId) (*userpb.User, error) {
   144  	gwc, err := pool.GetGatewayServiceClient(c.gwAddr)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	return utils.GetUser(userid, gwc)
   149  }
   150  
   151  func (m *mgr) formatGrantee(ctx context.Context, g *provider.Grantee) (int, string, error) {
   152  	var granteeType int
   153  	var formattedID string
   154  	switch g.Type {
   155  	case provider.GranteeType_GRANTEE_TYPE_USER:
   156  		granteeType = 0
   157  		var err error
   158  		formattedID, err = m.userConverter.UserIDToUserName(ctx, g.GetUserId())
   159  		if err != nil {
   160  			return 0, "", err
   161  		}
   162  	case provider.GranteeType_GRANTEE_TYPE_GROUP:
   163  		granteeType = 1
   164  		formattedID = formatGroupID(g.GetGroupId())
   165  	default:
   166  		granteeType = -1
   167  	}
   168  	return granteeType, formattedID, nil
   169  }
   170  
   171  func (m *mgr) extractGrantee(ctx context.Context, t int, g string) (*provider.Grantee, error) {
   172  	var grantee provider.Grantee
   173  	switch t {
   174  	case 0:
   175  		userid, err := m.userConverter.UserNameToUserID(ctx, g)
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  		grantee.Type = provider.GranteeType_GRANTEE_TYPE_USER
   180  		grantee.Id = &provider.Grantee_UserId{UserId: userid}
   181  	case 1, 2:
   182  		grantee.Type = provider.GranteeType_GRANTEE_TYPE_GROUP
   183  		grantee.Id = &provider.Grantee_GroupId{GroupId: extractGroupID(g)}
   184  	default:
   185  		grantee.Type = provider.GranteeType_GRANTEE_TYPE_INVALID
   186  	}
   187  	return &grantee, nil
   188  }
   189  
   190  func resourceTypeToItem(r provider.ResourceType) string {
   191  	switch r {
   192  	case provider.ResourceType_RESOURCE_TYPE_FILE:
   193  		return "file"
   194  	case provider.ResourceType_RESOURCE_TYPE_CONTAINER:
   195  		return "folder"
   196  	case provider.ResourceType_RESOURCE_TYPE_REFERENCE:
   197  		return "reference"
   198  	case provider.ResourceType_RESOURCE_TYPE_SYMLINK:
   199  		return "symlink"
   200  	default:
   201  		return ""
   202  	}
   203  }
   204  
   205  func sharePermToInt(p *provider.ResourcePermissions) int {
   206  	return int(conversions.RoleFromResourcePermissions(p, false).OCSPermissions())
   207  }
   208  
   209  func intTosharePerm(p int) (*provider.ResourcePermissions, error) {
   210  	perms, err := conversions.NewPermissions(p)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	return conversions.RoleFromOCSPermissions(perms, nil).CS3ResourcePermissions(), nil
   216  }
   217  
   218  func intToShareState(g int) collaboration.ShareState {
   219  	switch g {
   220  	case 0:
   221  		return collaboration.ShareState_SHARE_STATE_ACCEPTED
   222  	case 1:
   223  		return collaboration.ShareState_SHARE_STATE_PENDING
   224  	case 2:
   225  		return collaboration.ShareState_SHARE_STATE_REJECTED
   226  	default:
   227  		return collaboration.ShareState_SHARE_STATE_INVALID
   228  	}
   229  }
   230  
   231  func formatUserID(u *userpb.UserId) string {
   232  	return u.OpaqueId
   233  }
   234  
   235  func formatGroupID(u *grouppb.GroupId) string {
   236  	return u.OpaqueId
   237  }
   238  
   239  func extractGroupID(u string) *grouppb.GroupId {
   240  	return &grouppb.GroupId{OpaqueId: u}
   241  }
   242  
   243  func (m *mgr) convertToCS3Share(ctx context.Context, s DBShare, storageMountID string) (*collaboration.Share, error) {
   244  	ts := &typespb.Timestamp{
   245  		Seconds: uint64(s.STime),
   246  	}
   247  	permissions, err := intTosharePerm(s.Permissions)
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	grantee, err := m.extractGrantee(ctx, s.ShareType, s.ShareWith)
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	owner, err := m.userConverter.UserNameToUserID(ctx, s.UIDOwner)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	var creator *userpb.UserId
   260  	if s.UIDOwner == s.UIDInitiator {
   261  		creator = owner
   262  	} else {
   263  		creator, err = m.userConverter.UserNameToUserID(ctx, s.UIDOwner)
   264  		if err != nil {
   265  			return nil, err
   266  		}
   267  	}
   268  	return &collaboration.Share{
   269  		Id: &collaboration.ShareId{
   270  			OpaqueId: s.ID,
   271  		},
   272  		ResourceId: &provider.ResourceId{
   273  			SpaceId:  s.ItemStorage,
   274  			OpaqueId: s.FileSource,
   275  		},
   276  		Permissions: &collaboration.SharePermissions{Permissions: permissions},
   277  		Grantee:     grantee,
   278  		Owner:       owner,
   279  		Creator:     creator,
   280  		Ctime:       ts,
   281  		Mtime:       ts,
   282  	}, nil
   283  }
   284  
   285  func (m *mgr) convertToCS3ReceivedShare(ctx context.Context, s DBShare, storageMountID string) (*collaboration.ReceivedShare, error) {
   286  	share, err := m.convertToCS3Share(ctx, s, storageMountID)
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  	var state collaboration.ShareState
   291  	if s.RejectedBy != "" {
   292  		state = collaboration.ShareState_SHARE_STATE_REJECTED
   293  	} else {
   294  		state = intToShareState(s.State)
   295  	}
   296  	return &collaboration.ReceivedShare{
   297  		Share:      share,
   298  		State:      state,
   299  		MountPoint: &provider.Reference{Path: strings.TrimLeft(s.FileTarget, "/")},
   300  	}, nil
   301  }