Skip to content

Commit

Permalink
List all shares for project space admins
Browse files Browse the repository at this point in the history
  • Loading branch information
ishank011 committed Jun 9, 2022
1 parent 5b9bd39 commit 0685d91
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 19 deletions.
3 changes: 3 additions & 0 deletions changelog/unreleased/user-share-indicators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Enhancement: Make user share indicators read from the share provider service

https://github.com/cs3org/reva/pull/2946
70 changes: 66 additions & 4 deletions pkg/cbox/publicshare/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,28 @@ import (
"golang.org/x/crypto/bcrypt"

user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
conversions "github.com/cs3org/reva/pkg/cbox/utils"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/publicshare"
"github.com/cs3org/reva/pkg/publicshare/manager/registry"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/sharedconf"
"github.com/cs3org/reva/pkg/utils"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)

const publicShareType = 3
const (
publicShareType = 3

projectInstancesPrefix = "newproject"
projectSpaceGroupsPrefix = "cernbox-project-"
projectSpaceAdminGroupsSuffix = "-admins"
)

func init() {
registry.Register("sql", New)
Expand All @@ -59,6 +68,7 @@ type config struct {
DbHost string `mapstructure:"db_host"`
DbPort int `mapstructure:"db_port"`
DbName string `mapstructure:"db_name"`
GatewaySvc string `mapstructure:"gatewaysvc"`
}

type manager struct {
Expand All @@ -73,6 +83,8 @@ func (c *config) init() {
if c.JanitorRunInterval == 0 {
c.JanitorRunInterval = 3600
}

c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc)
}

func (m *manager) startJanitorRun() {
Expand Down Expand Up @@ -309,11 +321,10 @@ func (m *manager) GetPublicShare(ctx context.Context, u *user.User, ref *link.Pu
}

func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter, md *provider.ResourceInfo, sign bool) ([]*link.PublicShare, error) {
uid := conversions.FormatUserID(u.Id)
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, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?) AND (share_type=?)"
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, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (share_type=?)"
var resourceFilters, ownerFilters, creatorFilters string
var resourceParams, ownerParams, creatorParams []interface{}
params := []interface{}{uid, uid, publicShareType}
params := []interface{}{publicShareType}
for _, f := range filters {
switch f.Type {
case link.ListPublicSharesRequest_Filter_TYPE_RESOURCE_ID:
Expand Down Expand Up @@ -350,6 +361,15 @@ func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []
params = append(params, creatorParams...)
}

uidOwnersQuery, uidOwnersParams, err := m.uidOwnerFilters(ctx, u, filters)
if err != nil {
return nil, err
}
params = append(params, uidOwnersParams...)
if uidOwnersQuery != "" {
query = fmt.Sprintf("%s AND (%s)", query, uidOwnersQuery)
}

rows, err := m.db.Query(query, params...)
if err != nil {
return nil, err
Expand Down Expand Up @@ -476,6 +496,48 @@ func expired(s *link.PublicShare) bool {
return false
}

func (m *manager) uidOwnerFilters(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter) (string, []interface{}, error) {
uid := conversions.FormatUserID(u.Id)

query := "uid_owner=? or uid_initiator=?"
params := []interface{}{uid, uid}

client, err := pool.GetGatewayServiceClient(pool.Endpoint(m.c.GatewaySvc))
if err != nil {
return "", nil, err
}

for _, f := range filters {
if f.Type == link.ListPublicSharesRequest_Filter_TYPE_RESOURCE_ID {
// For shares inside project spaces, if the user is an admin, we try to list all shares created by other admins
if strings.HasPrefix(f.GetResourceId().GetStorageId(), projectInstancesPrefix) {
res, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: f.GetResourceId()}})
if err != nil || res.Status.Code != rpc.Code_CODE_OK {
continue
}

// The path will look like /eos/project/c/cernbox, we need to extract the project name
parts := strings.SplitN(res.Info.Path, "/", 6)
if len(parts) < 5 {
continue
}

adminGroup := projectSpaceGroupsPrefix + parts[4] + projectSpaceAdminGroupsSuffix
for _, g := range u.Groups {
if g == adminGroup {
// User belongs to the admin group, list shares owned by the owner of the project
query += " or uid_owner=?"
params = append(params, res.Info.Owner.GetOpaqueId())
break
}
}
}
}
}

return query, params, nil
}

func hashPassword(password string, cost int) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), cost)
return "1|" + string(bytes), err
Expand Down
90 changes: 75 additions & 15 deletions pkg/cbox/share/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ import (
"strings"
"time"

rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
conversions "github.com/cs3org/reva/pkg/cbox/utils"
ctxpkg "github.com/cs3org/reva/pkg/ctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/share"
"github.com/cs3org/reva/pkg/share/manager/registry"
"github.com/cs3org/reva/pkg/sharedconf"
"github.com/cs3org/reva/pkg/utils"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
Expand All @@ -47,6 +50,10 @@ import (
const (
shareTypeUser = 0
shareTypeGroup = 1

projectInstancesPrefix = "newproject"
projectSpaceGroupsPrefix = "cernbox-project-"
projectSpaceAdminGroupsSuffix = "-admins"
)

func init() {
Expand All @@ -59,6 +66,7 @@ type config struct {
DbHost string `mapstructure:"db_host"`
DbPort int `mapstructure:"db_port"`
DbName string `mapstructure:"db_name"`
GatewaySvc string `mapstructure:"gatewaysvc"`
}

type mgr struct {
Expand Down Expand Up @@ -90,6 +98,7 @@ func parseConfig(m map[string]interface{}) (*config, error) {
if err := mapstructure.Decode(m, c); err != nil {
return nil, err
}
c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc)
return c, nil
}

Expand Down Expand Up @@ -281,16 +290,15 @@ func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference
}

func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) {
uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id)
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=? or uid_initiator=?) AND (share_type=? OR share_type=?)`
params := []interface{}{uid, uid, shareTypeUser, shareTypeGroup}
FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (share_type=? OR share_type=?)`
params := []interface{}{shareTypeUser, shareTypeGroup}

if len(filters) > 0 {
filterQuery, filterParams, err := translateFilters(filters)
groupedFilters := share.GroupFiltersByType(filters)
if len(groupedFilters) > 0 {
filterQuery, filterParams, err := translateFilters(groupedFilters)
if err != nil {
return nil, err
}
Expand All @@ -300,6 +308,15 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) (
}
}

uidOwnersQuery, uidOwnersParams, err := m.uidOwnerFilters(ctx, groupedFilters)
if err != nil {
return nil, err
}
params = append(params, uidOwnersParams...)
if uidOwnersQuery != "" {
query = fmt.Sprintf("%s AND (%s)", query, uidOwnersQuery)
}

rows, err := m.db.Query(query, params...)
if err != nil {
return nil, err
Expand Down Expand Up @@ -342,7 +359,8 @@ func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.F
query += " AND (share_with=? AND share_type = 0)"
}

filterQuery, filterParams, err := translateFilters(filters)
groupedFilters := share.GroupFiltersByType(filters)
filterQuery, filterParams, err := translateFilters(groupedFilters)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -493,6 +511,49 @@ func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.Rece
return rs, nil
}

func (m *mgr) uidOwnerFilters(ctx context.Context, filters map[collaboration.Filter_Type][]*collaboration.Filter) (string, []interface{}, error) {
user := ctxpkg.ContextMustGetUser(ctx)
uid := conversions.FormatUserID(user.Id)

query := "uid_owner=? or uid_initiator=?"
params := []interface{}{uid, uid}

client, err := pool.GetGatewayServiceClient(pool.Endpoint(m.c.GatewaySvc))
if err != nil {
return "", nil, err
}

if resourceFilters, ok := filters[collaboration.Filter_TYPE_RESOURCE_ID]; ok {
for _, f := range resourceFilters {
// For shares inside project spaces, if the user is an admin, we try to list all shares created by other admins
if strings.HasPrefix(f.GetResourceId().GetStorageId(), projectInstancesPrefix) {
res, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: f.GetResourceId()}})
if err != nil || res.Status.Code != rpc.Code_CODE_OK {
continue
}

// The path will look like /eos/project/c/cernbox, we need to extract the project name
parts := strings.SplitN(res.Info.Path, "/", 6)
if len(parts) < 5 {
continue
}

adminGroup := projectSpaceGroupsPrefix + parts[4] + projectSpaceAdminGroupsSuffix
for _, g := range user.Groups {
if g == adminGroup {
// User belongs to the admin group, list shares owned by the owner of the project
query += " or uid_owner=?"
params = append(params, res.Info.Owner.GetOpaqueId())
break
}
}
}
}
}

return query, params, nil
}

func granteeTypeToShareType(granteeType provider.GranteeType) int {
switch granteeType {
case provider.GranteeType_GRANTEE_TYPE_USER:
Expand All @@ -504,38 +565,37 @@ func granteeTypeToShareType(granteeType provider.GranteeType) int {
}

// translateFilters translates the filters to sql queries
func translateFilters(filters []*collaboration.Filter) (string, []interface{}, error) {
func translateFilters(filters map[collaboration.Filter_Type][]*collaboration.Filter) (string, []interface{}, error) {
var (
filterQuery string
params []interface{}
)

groupedFilters := share.GroupFiltersByType(filters)
// If multiple filters of the same type are passed to this function, they need to be combined with the `OR` operator.
// That is why the filters got grouped by type.
// For every given filter type, iterate over the filters and if there are more than one combine them.
// Combine the different filter types using `AND`
var filterCounter = 0
for filterType, filters := range groupedFilters {
for filterType, currFilters := range filters {
switch filterType {
case collaboration.Filter_TYPE_RESOURCE_ID:
filterQuery += "("
for i, f := range filters {
for i, f := range currFilters {
filterQuery += "(fileid_prefix =? AND item_source=?)"
params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId)

if i != len(filters)-1 {
if i != len(currFilters)-1 {
filterQuery += " OR "
}
}
filterQuery += ")"
case collaboration.Filter_TYPE_GRANTEE_TYPE:
filterQuery += "("
for i, f := range filters {
for i, f := range currFilters {
filterQuery += "share_type=?"
params = append(params, granteeTypeToShareType(f.GetGranteeType()))

if i != len(filters)-1 {
if i != len(currFilters)-1 {
filterQuery += " OR "
}
}
Expand All @@ -546,7 +606,7 @@ func translateFilters(filters []*collaboration.Filter) (string, []interface{}, e
default:
return "", nil, fmt.Errorf("filter type is not supported")
}
if filterCounter != len(groupedFilters)-1 {
if filterCounter != len(filters)-1 {
filterQuery += " AND "
}
filterCounter++
Expand Down

0 comments on commit 0685d91

Please sign in to comment.