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 }