Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add filter by sharetype in the ocs API #2050

Merged
merged 3 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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{})
ishank011 marked this conversation as resolved.
Show resolved Hide resolved
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
ishank011 marked this conversation as resolved.
Show resolved Hide resolved
}

// 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