github.com/cs3org/reva/v2@v2.27.7/pkg/cbox/share/sql/sql.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 sql 20 21 import ( 22 "context" 23 "database/sql" 24 "fmt" 25 "path" 26 "strconv" 27 "strings" 28 "time" 29 30 gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 31 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 32 collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 33 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 34 typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 35 conversions "github.com/cs3org/reva/v2/pkg/cbox/utils" 36 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 37 "github.com/cs3org/reva/v2/pkg/errtypes" 38 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 39 "github.com/cs3org/reva/v2/pkg/share" 40 "github.com/cs3org/reva/v2/pkg/share/manager/registry" 41 "github.com/cs3org/reva/v2/pkg/utils" 42 "github.com/mitchellh/mapstructure" 43 "github.com/pkg/errors" 44 "google.golang.org/genproto/protobuf/field_mask" 45 46 // Provides mysql drivers 47 _ "github.com/go-sql-driver/mysql" 48 ) 49 50 const ( 51 shareTypeUser = 0 52 shareTypeGroup = 1 53 ) 54 55 func init() { 56 registry.Register("sql", New) 57 } 58 59 type config struct { 60 DbUsername string `mapstructure:"db_username"` 61 DbPassword string `mapstructure:"db_password"` 62 DbHost string `mapstructure:"db_host"` 63 DbPort int `mapstructure:"db_port"` 64 DbName string `mapstructure:"db_name"` 65 GatewaySvc string `mapstructure:"gatewaysvc"` 66 } 67 68 type mgr struct { 69 c *config 70 db *sql.DB 71 client gatewayv1beta1.GatewayAPIClient 72 } 73 74 // New returns a new share manager. 75 func New(m map[string]interface{}) (share.Manager, error) { 76 c, err := parseConfig(m) 77 if err != nil { 78 err = errors.Wrap(err, "error creating a new manager") 79 return nil, err 80 } 81 82 db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.DbUsername, c.DbPassword, c.DbHost, c.DbPort, c.DbName)) 83 if err != nil { 84 return nil, err 85 } 86 87 gw, err := pool.GetGatewayServiceClient(c.GatewaySvc) 88 if err != nil { 89 return nil, err 90 } 91 92 return &mgr{ 93 c: c, 94 db: db, 95 client: gw, 96 }, nil 97 } 98 99 func parseConfig(m map[string]interface{}) (*config, error) { 100 c := &config{} 101 if err := mapstructure.Decode(m, c); err != nil { 102 return nil, err 103 } 104 return c, nil 105 } 106 107 func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { 108 user := ctxpkg.ContextMustGetUser(ctx) 109 110 // do not allow share to myself or the owner if share is for a user 111 // TODO(labkode): should not this be caught already at the gw level? 112 if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && 113 (utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) { 114 return nil, errors.New("sql: owner/creator and grantee are the same") 115 } 116 117 // check if share already exists. 118 key := &collaboration.ShareKey{ 119 Owner: md.Owner, 120 ResourceId: md.Id, 121 Grantee: g.Grantee, 122 } 123 _, err := m.getByKey(ctx, key) 124 125 // share already exists 126 if err == nil { 127 return nil, errtypes.AlreadyExists(key.String()) 128 } 129 130 now := time.Now().Unix() 131 ts := &typespb.Timestamp{ 132 Seconds: uint64(now), 133 } 134 135 shareType, shareWith := conversions.FormatGrantee(g.Grantee) 136 itemType := conversions.ResourceTypeToItem(md.Type) 137 targetPath := path.Join("/", path.Base(md.Path)) 138 permissions := conversions.SharePermToInt(g.Permissions.Permissions) 139 prefix := md.Id.SpaceId 140 itemSource := md.Id.OpaqueId 141 fileSource, err := strconv.ParseUint(itemSource, 10, 64) 142 if err != nil { 143 // it can be the case that the item source may be a character string 144 // we leave fileSource blank in that case 145 fileSource = 0 146 } 147 148 stmtString := "insert into oc_share set share_type=?,uid_owner=?,uid_initiator=?,item_type=?,fileid_prefix=?,item_source=?,file_source=?,permissions=?,stime=?,share_with=?,file_target=?" 149 stmtValues := []interface{}{shareType, conversions.FormatUserID(md.Owner), conversions.FormatUserID(user.Id), itemType, prefix, itemSource, fileSource, permissions, now, shareWith, targetPath} 150 151 stmt, err := m.db.Prepare(stmtString) 152 if err != nil { 153 return nil, err 154 } 155 result, err := stmt.Exec(stmtValues...) 156 if err != nil { 157 return nil, err 158 } 159 lastID, err := result.LastInsertId() 160 if err != nil { 161 return nil, err 162 } 163 164 return &collaboration.Share{ 165 Id: &collaboration.ShareId{ 166 OpaqueId: strconv.FormatInt(lastID, 10), 167 }, 168 ResourceId: md.Id, 169 Permissions: g.Permissions, 170 Grantee: g.Grantee, 171 Owner: md.Owner, 172 Creator: user.Id, 173 Ctime: ts, 174 Mtime: ts, 175 }, nil 176 } 177 178 func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) { 179 uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id) 180 s := conversions.DBShare{ID: id.OpaqueId} 181 query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND id=? AND (uid_owner=? or uid_initiator=?)" 182 if err := m.db.QueryRow(query, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.STime, &s.Permissions, &s.ShareType); err != nil { 183 if err == sql.ErrNoRows { 184 return nil, errtypes.NotFound(id.OpaqueId) 185 } 186 return nil, err 187 } 188 share, err := conversions.ConvertToCS3Share(ctx, m.client, s) 189 if err != nil { 190 return nil, err 191 } 192 return share, nil 193 } 194 195 func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) { 196 owner := conversions.FormatUserID(key.Owner) 197 uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id) 198 199 s := conversions.DBShare{} 200 shareType, shareWith := conversions.FormatGrantee(key.Grantee) 201 query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, id, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" 202 if err := m.db.QueryRow(query, owner, key.ResourceId.SpaceId, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { 203 if err == sql.ErrNoRows { 204 return nil, errtypes.NotFound(key.String()) 205 } 206 return nil, err 207 } 208 share, err := conversions.ConvertToCS3Share(ctx, m.client, s) 209 if err != nil { 210 return nil, err 211 } 212 return share, nil 213 } 214 215 func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { 216 var s *collaboration.Share 217 var err error 218 switch { 219 case ref.GetId() != nil: 220 s, err = m.getByID(ctx, ref.GetId()) 221 case ref.GetKey() != nil: 222 s, err = m.getByKey(ctx, ref.GetKey()) 223 default: 224 err = errtypes.NotFound(ref.String()) 225 } 226 227 if err != nil { 228 return nil, err 229 } 230 231 return s, nil 232 } 233 234 func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { 235 uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id) 236 var query string 237 params := []interface{}{} 238 switch { 239 case ref.GetId() != nil: 240 query = "delete from oc_share where id=? AND (uid_owner=? or uid_initiator=?)" 241 params = append(params, ref.GetId().OpaqueId, uid, uid) 242 case ref.GetKey() != nil: 243 key := ref.GetKey() 244 shareType, shareWith := conversions.FormatGrantee(key.Grantee) 245 owner := conversions.FormatUserID(key.Owner) 246 query = "delete from oc_share where uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" 247 params = append(params, owner, key.ResourceId.SpaceId, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid) 248 default: 249 return errtypes.NotFound(ref.String()) 250 } 251 252 stmt, err := m.db.Prepare(query) 253 if err != nil { 254 return err 255 } 256 res, err := stmt.Exec(params...) 257 if err != nil { 258 return err 259 } 260 261 rowCnt, err := res.RowsAffected() 262 if err != nil { 263 return err 264 } 265 if rowCnt == 0 { 266 return errtypes.NotFound(ref.String()) 267 } 268 return nil 269 } 270 271 func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) { 272 permissions := conversions.SharePermToInt(p.Permissions) 273 uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id) 274 275 var query string 276 params := []interface{}{} 277 switch { 278 case ref.GetId() != nil: 279 query = "update oc_share set permissions=?,stime=? where id=? AND (uid_owner=? or uid_initiator=?)" 280 params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId, uid, uid) 281 case ref.GetKey() != nil: 282 key := ref.GetKey() 283 shareType, shareWith := conversions.FormatGrantee(key.Grantee) 284 owner := conversions.FormatUserID(key.Owner) 285 query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" 286 params = append(params, permissions, time.Now().Unix(), owner, owner, key.ResourceId.SpaceId, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid) 287 default: 288 return nil, errtypes.NotFound(ref.String()) 289 } 290 291 stmt, err := m.db.Prepare(query) 292 if err != nil { 293 return nil, err 294 } 295 if _, err = stmt.Exec(params...); err != nil { 296 return nil, err 297 } 298 299 return m.GetShare(ctx, ref) 300 } 301 302 func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { 303 uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id) 304 query := `select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, 305 coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, 306 id, stime, permissions, share_type 307 FROM oc_share 308 WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?) AND (share_type=? OR share_type=?)` 309 params := []interface{}{uid, uid, shareTypeUser, shareTypeGroup} 310 311 if len(filters) > 0 { 312 filterQuery, filterParams, err := translateFilters(filters) 313 if err != nil { 314 return nil, err 315 } 316 params = append(params, filterParams...) 317 if filterQuery != "" { 318 query = fmt.Sprintf("%s AND (%s)", query, filterQuery) 319 } 320 } 321 322 rows, err := m.db.Query(query, params...) 323 if err != nil { 324 return nil, err 325 } 326 defer rows.Close() 327 328 var s conversions.DBShare 329 shares := []*collaboration.Share{} 330 for rows.Next() { 331 if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { 332 continue 333 } 334 share, err := conversions.ConvertToCS3Share(ctx, m.client, s) 335 if err != nil { 336 continue 337 } 338 shares = append(shares, share) 339 } 340 if err = rows.Err(); err != nil { 341 return nil, err 342 } 343 344 return shares, nil 345 } 346 347 // we list the shares that are targeted to the user in context or to the user groups. 348 func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, _ *userpb.UserId) ([]*collaboration.ReceivedShare, error) { 349 user := ctxpkg.ContextMustGetUser(ctx) 350 uid := conversions.FormatUserID(user.Id) 351 352 params := []interface{}{uid, uid, uid, uid} 353 for _, v := range user.Groups { 354 params = append(params, v) 355 } 356 357 query := `SELECT coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, 358 coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(file_target, '') as file_target, 359 ts.id, stime, permissions, share_type, coalesce(tr.state, 0) as state 360 FROM oc_share ts LEFT JOIN oc_share_status tr ON (ts.id = tr.id AND tr.recipient = ?) 361 WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner != ? AND uid_initiator != ?)` 362 if len(user.Groups) > 0 { 363 query += " AND ((share_with=? AND share_type = 0) OR (share_type = 1 AND share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))" 364 } else { 365 query += " AND (share_with=? AND share_type = 0)" 366 } 367 368 filterQuery, filterParams, err := translateFilters(filters) 369 if err != nil { 370 return nil, err 371 } 372 params = append(params, filterParams...) 373 374 if filterQuery != "" { 375 query = fmt.Sprintf("%s AND (%s)", query, filterQuery) 376 } 377 378 rows, err := m.db.Query(query, params...) 379 if err != nil { 380 return nil, err 381 } 382 defer rows.Close() 383 384 var s conversions.DBShare 385 shares := []*collaboration.ReceivedShare{} 386 for rows.Next() { 387 if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { 388 continue 389 } 390 share, err := conversions.ConvertToCS3ReceivedShare(ctx, m.client, s) 391 if err != nil { 392 continue 393 } 394 shares = append(shares, share) 395 } 396 if err = rows.Err(); err != nil { 397 return nil, err 398 } 399 400 return shares, nil 401 } 402 403 func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.ReceivedShare, error) { 404 user := ctxpkg.ContextMustGetUser(ctx) 405 uid := conversions.FormatUserID(user.Id) 406 407 params := []interface{}{uid, id.OpaqueId, uid} 408 for _, v := range user.Groups { 409 params = append(params, v) 410 } 411 412 s := conversions.DBShare{ID: id.OpaqueId} 413 query := `select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, 414 coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(file_target, '') as file_target, 415 stime, permissions, share_type, coalesce(tr.state, 0) as state 416 FROM oc_share ts LEFT JOIN oc_share_status tr ON (ts.id = tr.id AND tr.recipient = ?) 417 WHERE (orphan = 0 or orphan IS NULL) AND ts.id=?` 418 if len(user.Groups) > 0 { 419 query += " AND ((share_with=? AND share_type = 0) OR (share_type = 1 AND share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))" 420 } else { 421 query += " AND (share_with=? AND share_type = 0)" 422 } 423 if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.FileTarget, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { 424 if err == sql.ErrNoRows { 425 return nil, errtypes.NotFound(id.OpaqueId) 426 } 427 return nil, err 428 } 429 share, err := conversions.ConvertToCS3ReceivedShare(ctx, m.client, s) 430 if err != nil { 431 return nil, err 432 } 433 return share, nil 434 } 435 436 func (m *mgr) getReceivedByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.ReceivedShare, error) { 437 user := ctxpkg.ContextMustGetUser(ctx) 438 uid := conversions.FormatUserID(user.Id) 439 440 shareType, shareWith := conversions.FormatGrantee(key.Grantee) 441 params := []interface{}{uid, conversions.FormatUserID(key.Owner), key.GetResourceId().SpaceId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith} 442 for _, v := range user.Groups { 443 params = append(params, v) 444 } 445 446 s := conversions.DBShare{} 447 query := `select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, 448 coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(file_target, '') as file_target, 449 ts.id, stime, permissions, share_type, coalesce(tr.state, 0) as state 450 FROM oc_share ts LEFT JOIN oc_share_status tr ON (ts.id = tr.id AND tr.recipient = ?) 451 WHERE (orphan = 0 or orphan IS NULL) AND uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=?` 452 if len(user.Groups) > 0 { 453 query += " AND ((share_with=? AND share_type = 0) OR (share_type = 1 AND share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))" 454 } else { 455 query += " AND (share_with=? AND share_type = 0)" 456 } 457 458 if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { 459 if err == sql.ErrNoRows { 460 return nil, errtypes.NotFound(key.String()) 461 } 462 return nil, err 463 } 464 share, err := conversions.ConvertToCS3ReceivedShare(ctx, m.client, s) 465 if err != nil { 466 return nil, err 467 } 468 return share, nil 469 } 470 471 func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { 472 var s *collaboration.ReceivedShare 473 var err error 474 switch { 475 case ref.GetId() != nil: 476 s, err = m.getReceivedByID(ctx, ref.GetId()) 477 case ref.GetKey() != nil: 478 s, err = m.getReceivedByKey(ctx, ref.GetKey()) 479 default: 480 err = errtypes.NotFound(ref.String()) 481 } 482 483 if err != nil { 484 return nil, err 485 } 486 487 return s, nil 488 489 } 490 491 func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, _ *userpb.UserId) (*collaboration.ReceivedShare, error) { 492 user := ctxpkg.ContextMustGetUser(ctx) 493 494 rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: share.Share.Id}}) 495 if err != nil { 496 return nil, err 497 } 498 499 for i := range fieldMask.Paths { 500 switch fieldMask.Paths[i] { 501 case "state": 502 rs.State = share.State 503 case "mount_point": 504 rs.MountPoint = share.MountPoint 505 default: 506 return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported") 507 } 508 } 509 510 state := 0 511 switch rs.GetState() { 512 case collaboration.ShareState_SHARE_STATE_REJECTED: 513 state = -1 514 case collaboration.ShareState_SHARE_STATE_ACCEPTED: 515 state = 1 516 } 517 518 params := []interface{}{rs.Share.Id.OpaqueId, conversions.FormatUserID(user.Id), state, state} 519 query := "insert into oc_share_status(id, recipient, state) values(?, ?, ?) ON DUPLICATE KEY UPDATE state = ?" 520 521 stmt, err := m.db.Prepare(query) 522 if err != nil { 523 return nil, err 524 } 525 _, err = stmt.Exec(params...) 526 if err != nil { 527 return nil, err 528 } 529 530 return rs, nil 531 } 532 533 func granteeTypeToShareType(granteeType provider.GranteeType) int { 534 switch granteeType { 535 case provider.GranteeType_GRANTEE_TYPE_USER: 536 return shareTypeUser 537 case provider.GranteeType_GRANTEE_TYPE_GROUP: 538 return shareTypeGroup 539 } 540 return -1 541 } 542 543 // translateFilters translates the filters to sql queries 544 func translateFilters(filters []*collaboration.Filter) (string, []interface{}, error) { 545 var ( 546 filterQuery string 547 params []interface{} 548 ) 549 550 groupedFilters := share.GroupFiltersByType(filters) 551 // If multiple filters of the same type are passed to this function, they need to be combined with the `OR` operator. 552 // That is why the filters got grouped by type. 553 // For every given filter type, iterate over the filters and if there are more than one combine them. 554 // Combine the different filter types using `AND` 555 var filterCounter = 0 556 for filterType, filters := range groupedFilters { 557 switch filterType { 558 case collaboration.Filter_TYPE_RESOURCE_ID: 559 filterQuery += "(" 560 for i, f := range filters { 561 filterQuery += "(fileid_prefix =? AND item_source=?)" 562 params = append(params, f.GetResourceId().SpaceId, f.GetResourceId().OpaqueId) 563 564 if i != len(filters)-1 { 565 filterQuery += " OR " 566 } 567 } 568 filterQuery += ")" 569 case collaboration.Filter_TYPE_GRANTEE_TYPE: 570 filterQuery += "(" 571 for i, f := range filters { 572 filterQuery += "share_type=?" 573 params = append(params, granteeTypeToShareType(f.GetGranteeType())) 574 575 if i != len(filters)-1 { 576 filterQuery += " OR " 577 } 578 } 579 filterQuery += ")" 580 case collaboration.Filter_TYPE_EXCLUDE_DENIALS: 581 // TODO this may change once the mapping of permission to share types is completed (cf. pkg/cbox/utils/conversions.go) 582 filterQuery += "(permissions > 0)" 583 default: 584 return "", nil, fmt.Errorf("filter type is not supported") 585 } 586 if filterCounter != len(groupedFilters)-1 { 587 filterQuery += " AND " 588 } 589 filterCounter++ 590 } 591 return filterQuery, params, nil 592 }