Skip to content

Commit

Permalink
add filter by sharetype in the ocs API (#2050)
Browse files Browse the repository at this point in the history
  • Loading branch information
David Christofas authored Oct 5, 2021
1 parent e15e3b1 commit c40fcc8
Show file tree
Hide file tree
Showing 10 changed files with 586 additions and 170 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/sharetype-filter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add a share types filter to the OCS API

Added a filter to the OCS API to filter the received shares by type.

https://github.com/cs3org/reva/pull/2050
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,36 @@ func (h *Handler) listSharesWithMe(w http.ResponseWriter, r *http.Request) {
}
}

lrsRes, err := client.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{})
filters := []*collaboration.Filter{}
var shareTypes []string
shareTypesParam := r.URL.Query().Get("share_types")
if shareTypesParam != "" {
shareTypes = strings.Split(shareTypesParam, ",")
}
for _, s := range shareTypes {
if s == "" {
continue
}
shareType, err := strconv.Atoi(strings.TrimSpace(s))
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "invalid share type", err)
return
}
switch shareType {
case int(conversions.ShareTypeUser):
filters = append(filters, share.UserGranteeFilter())
case int(conversions.ShareTypeGroup):
filters = append(filters, share.GroupGranteeFilter())
}
}

if len(shareTypes) != 0 && len(filters) == 0 {
// If a share_types filter was set for anything other than user or group shares just return an empty response
response.WriteOCSSuccess(w, r, []*conversions.ShareData{})
return
}

lrsRes, err := client.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{Filters: filters})
if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc ListReceivedShares request", err)
return
Expand Down Expand Up @@ -693,25 +722,47 @@ func (h *Handler) listSharesWithOthers(w http.ResponseWriter, r *http.Request) {
}
}

shareTypes := strings.Split(r.URL.Query().Get("share_types"), ",")
var shareTypes []string
shareTypesParam := r.URL.Query().Get("share_types")
if shareTypesParam != "" {
shareTypes = strings.Split(shareTypesParam, ",")
}

listPublicShares := len(shareTypes) == 0 // if no share_types filter was set we want to list all share by default
listUserShares := len(shareTypes) == 0 // if no share_types filter was set we want to list all share by default
for _, s := range shareTypes {
if s == "" {
continue
}
shareType, err := strconv.Atoi(strings.TrimSpace(s))
if err != nil && s != "" {
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "invalid share type", err)
return
}
if s == "" || shareType == int(conversions.ShareTypeUser) || shareType == int(conversions.ShareTypeGroup) {
userShares, status, err := h.listUserShares(r, filters)
h.logProblems(status, err, "could not listUserShares")
shares = append(shares, userShares...)
}
if s == "" || shareType == int(conversions.ShareTypePublicLink) {
publicShares, status, err := h.listPublicShares(r, linkFilters)
h.logProblems(status, err, "could not listPublicShares")
shares = append(shares, publicShares...)

switch shareType {
case int(conversions.ShareTypeUser):
listUserShares = true
filters = append(filters, share.UserGranteeFilter())
case int(conversions.ShareTypeGroup):
listUserShares = true
filters = append(filters, share.GroupGranteeFilter())
case int(conversions.ShareTypePublicLink):
listPublicShares = true
}
}

if listPublicShares {
publicShares, status, err := h.listPublicShares(r, linkFilters)
h.logProblems(status, err, "could not listPublicShares")
shares = append(shares, publicShares...)
}
if listUserShares {
userShares, status, err := h.listUserShares(r, filters)
h.logProblems(status, err, "could not listUserShares")
shares = append(shares, userShares...)
}

response.WriteOCSSuccess(w, r, shares)
}

Expand Down
114 changes: 96 additions & 18 deletions pkg/cbox/share/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ import (
_ "github.com/go-sql-driver/mysql"
)

const (
shareTypeUser = 0
shareTypeGroup = 1
)

func init() {
registry.Register("sql", New)
}
Expand Down Expand Up @@ -276,19 +281,30 @@ 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, id, stime, permissions, 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, 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, 0, 1}
for _, f := range filters {
if f.Type == collaboration.Filter_TYPE_RESOURCE_ID {
query += " AND (fileid_prefix=? AND item_source=?)"
params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId)
} else if f.Type == collaboration.Filter_TYPE_EXCLUDE_DENIALS {
// TODO this may change once the mapping of permission to share types is completed (cf. pkg/cbox/utils/conversions.go)
query += " AND (permissions > 0)"
WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?)`
params := []interface{}{uid, uid}
var (
filterQuery string
filterParams []interface{}
err error
)
if len(filters) == 0 {
filterQuery += "(share_type=? OR share_type=?)"
params = append(params, shareTypeUser)
params = append(params, shareTypeGroup)
} else {
filterQuery, filterParams, err = translateFilters(filters)
if err != nil {
return nil, err
}
params = append(params, filterParams...)
}

if filterQuery != "" {
query = fmt.Sprintf("%s AND (%s)", query, filterQuery)
}

rows, err := m.db.Query(query, params...)
Expand Down Expand Up @@ -333,13 +349,14 @@ func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.F
query += " AND (share_with=?)"
}

for _, f := range filters {
if f.Type == collaboration.Filter_TYPE_RESOURCE_ID {
query += " AND (fileid_prefix=? AND item_source=?)"
params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId)
} else if f.Type == collaboration.Filter_TYPE_EXCLUDE_DENIALS {
query += " AND (permissions > 0)"
}
filterQuery, filterParams, err := translateFilters(filters)
if err != nil {
return nil, err
}
params = append(params, filterParams...)

if filterQuery != "" {
query = fmt.Sprintf("%s AND (%s)", query, filterQuery)
}

rows, err := m.db.Query(query, params...)
Expand Down Expand Up @@ -476,3 +493,64 @@ func (m *mgr) UpdateReceivedShare(ctx context.Context, ref *collaboration.ShareR
rs.State = f.GetState()
return rs, nil
}

func granteeTypeToShareType(granteeType provider.GranteeType) int {
switch granteeType {
case provider.GranteeType_GRANTEE_TYPE_USER:
return shareTypeUser
case provider.GranteeType_GRANTEE_TYPE_GROUP:
return shareTypeGroup
}
return -1
}

// translateFilters translates the filters to sql queries
func translateFilters(filters []*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 {
switch filterType {
case collaboration.Filter_TYPE_RESOURCE_ID:
filterQuery += "("
for i, f := range filters {
filterQuery += "(fileid_prefix =? AND item_source=?"
params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId)

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

if i != len(filters)-1 {
filterQuery += " OR "
}
}
filterQuery += ")"
case collaboration.Filter_TYPE_EXCLUDE_DENIALS:
// TODO this may change once the mapping of permission to share types is completed (cf. pkg/cbox/utils/conversions.go)
filterQuery += "(permissions > 0)"
default:
return "", nil, fmt.Errorf("filter type is not supported")
}
if filterCounter != len(groupedFilters)-1 {
filterQuery += " AND "
}
filterCounter++
}
return filterQuery, params, nil
}
63 changes: 22 additions & 41 deletions pkg/share/manager/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,21 +268,15 @@ func (m *mgr) get(ctx context.Context, ref *collaboration.ShareReference) (s *co

// check if we are the owner
user := ctxpkg.ContextMustGetUser(ctx)
if utils.UserEqual(user.Id, s.Owner) || utils.UserEqual(user.Id, s.Creator) {
if share.IsCreatedByUser(s, user) {
return s, nil
}

// or the grantee
if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, s.Grantee.GetUserId()) {
if share.IsGrantedToUser(s, user) {
return s, nil
} else if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
// check if all user groups match this share; TODO(labkode): filter shares created by us.
for _, g := range user.Groups {
if g == s.Grantee.GetGroupId().OpaqueId {
return s, nil
}
}
}

// we return not found to not disclose information
return nil, errtypes.NotFound(ref.String())
}
Expand All @@ -302,7 +296,7 @@ func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) er
user := ctxpkg.ContextMustGetUser(ctx)
for i, s := range m.model.Shares {
if sharesEqual(ref, s) {
if utils.UserEqual(user.Id, s.Owner) || utils.UserEqual(user.Id, s.Creator) {
if share.IsCreatedByUser(s, user) {
m.model.Shares[len(m.model.Shares)-1], m.model.Shares[i] = m.model.Shares[i], m.model.Shares[len(m.model.Shares)-1]
m.model.Shares = m.model.Shares[:len(m.model.Shares)-1]
if err := m.model.Save(); err != nil {
Expand Down Expand Up @@ -336,7 +330,7 @@ func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference
user := ctxpkg.ContextMustGetUser(ctx)
for i, s := range m.model.Shares {
if sharesEqual(ref, s) {
if utils.UserEqual(user.Id, s.Owner) || utils.UserEqual(user.Id, s.Creator) {
if share.IsCreatedByUser(s, user) {
now := time.Now().UnixNano()
m.model.Shares[i].Permissions = p
m.model.Shares[i].Mtime = &typespb.Timestamp{
Expand All @@ -360,20 +354,15 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) (
defer m.Unlock()
user := ctxpkg.ContextMustGetUser(ctx)
for _, s := range m.model.Shares {
if utils.UserEqual(user.Id, s.Owner) || utils.UserEqual(user.Id, s.Creator) {
if share.IsCreatedByUser(s, user) {
// no filter we return earlier
if len(filters) == 0 {
ss = append(ss, s)
} else {
// check filters
// TODO(labkode): add the rest of filters.
for _, f := range filters {
if f.Type == collaboration.Filter_TYPE_RESOURCE_ID {
if utils.ResourceIDEqual(s.ResourceId, f.GetResourceId()) {
ss = append(ss, s)
}
}
}
continue
}
// check filters
if share.MatchesFilters(s, filters) {
ss = append(ss, s)
}
}
}
Expand All @@ -387,21 +376,20 @@ func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.F
defer m.Unlock()
user := ctxpkg.ContextMustGetUser(ctx)
for _, s := range m.model.Shares {
if utils.UserEqual(user.Id, s.Owner) || utils.UserEqual(user.Id, s.Creator) {
// omit shares created by me
if share.IsCreatedByUser(s, user) || !share.IsGrantedToUser(s, user) {
// omit shares created by the user or shares the user can't access
continue
}
if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, s.Grantee.GetUserId()) {

if len(filters) == 0 {
rs := m.convert(ctx, s)
rss = append(rss, rs)
continue
}

if share.MatchesFilters(s, filters) {
rs := m.convert(ctx, s)
rss = append(rss, rs)
} else if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
// check if all user groups match this share; TODO(labkode): filter shares created by us.
for _, g := range user.Groups {
if g == s.Grantee.GetGroupId().OpaqueId {
rs := m.convert(ctx, s)
rss = append(rss, rs)
}
}
}
}
return rss, nil
Expand Down Expand Up @@ -432,16 +420,9 @@ func (m *mgr) getReceived(ctx context.Context, ref *collaboration.ShareReference
user := ctxpkg.ContextMustGetUser(ctx)
for _, s := range m.model.Shares {
if sharesEqual(ref, s) {
if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, s.Grantee.GetUserId()) {
if share.IsGrantedToUser(s, user) {
rs := m.convert(ctx, s)
return rs, nil
} else if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
for _, g := range user.Groups {
if s.Grantee.GetGroupId().OpaqueId == g {
rs := m.convert(ctx, s)
return rs, nil
}
}
}
}
}
Expand Down
Loading

0 comments on commit c40fcc8

Please sign in to comment.