diff --git a/changelog/unreleased/public-share-owner.md b/changelog/unreleased/public-share-owner.md new file mode 100644 index 0000000000..cf0e372e85 --- /dev/null +++ b/changelog/unreleased/public-share-owner.md @@ -0,0 +1,3 @@ +Bugfix: Fixes registration and naming of services + +https://github.com/cs3org/reva/pull/4287 \ No newline at end of file diff --git a/internal/http/services/ocmd/ocm.go b/internal/http/services/ocmd/ocm.go index 13c901539a..f848bb6a86 100644 --- a/internal/http/services/ocmd/ocm.go +++ b/internal/http/services/ocmd/ocm.go @@ -30,7 +30,7 @@ import ( ) func init() { - global.Register("ocmd", New) + global.Register("ocm", New) } type config struct { diff --git a/pkg/preferences/loader/loader.go b/pkg/preferences/loader/loader.go index 1d0620c4a5..708427987b 100644 --- a/pkg/preferences/loader/loader.go +++ b/pkg/preferences/loader/loader.go @@ -21,5 +21,6 @@ package loader import ( // Load preferences drivers. _ "github.com/cs3org/reva/pkg/preferences/memory" + _ "github.com/cs3org/reva/pkg/preferences/sql" // Add your own here. ) diff --git a/pkg/share/manager/loader/loader.go b/pkg/share/manager/loader/loader.go index e80b82788f..daa37bd113 100644 --- a/pkg/share/manager/loader/loader.go +++ b/pkg/share/manager/loader/loader.go @@ -22,6 +22,5 @@ import ( // Load core share manager drivers. _ "github.com/cs3org/reva/pkg/share/manager/json" _ "github.com/cs3org/reva/pkg/share/manager/memory" - _ "github.com/cs3org/reva/pkg/share/manager/sql" // Add your own here. ) diff --git a/pkg/share/manager/sql/conversions.go b/pkg/share/manager/sql/conversions.go deleted file mode 100644 index a09cae34e5..0000000000 --- a/pkg/share/manager/sql/conversions.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql - -import ( - "context" - - grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" - userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - 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/internal/http/services/owncloud/ocs/conversions" - "github.com/cs3org/reva/pkg/rgrpc/status" - "github.com/cs3org/reva/pkg/rgrpc/todo/pool" -) - -//go:generate mockery -name UserConverter - -// DBShare stores information about user and public shares. -type DBShare struct { - ID string - UIDOwner string - UIDInitiator string - ItemStorage string - ItemSource string - ShareWith string - Token string - Expiration string - Permissions int - ShareType int - ShareName string - STime int - FileTarget string - RejectedBy string - State int -} - -// UserConverter describes an interface for converting user ids to names and back. -type UserConverter interface { - UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) - UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) -} - -// GatewayUserConverter converts usernames and ids using the gateway. -type GatewayUserConverter struct { - gwAddr string -} - -// NewGatewayUserConverter returns a instance of GatewayUserConverter. -func NewGatewayUserConverter(gwAddr string) *GatewayUserConverter { - return &GatewayUserConverter{ - gwAddr: gwAddr, - } -} - -// UserIDToUserName converts a user ID to an username. -func (c *GatewayUserConverter) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) { - gwConn, err := pool.GetGatewayServiceClient(pool.Endpoint(c.gwAddr)) - if err != nil { - return "", err - } - getUserResponse, err := gwConn.GetUser(ctx, &userpb.GetUserRequest{ - UserId: userid, - SkipFetchingUserGroups: true, - }) - if err != nil { - return "", err - } - if getUserResponse.Status.Code != rpc.Code_CODE_OK { - return "", status.NewErrorFromCode(getUserResponse.Status.Code, "gateway") - } - return getUserResponse.User.Username, nil -} - -// UserNameToUserID converts a username to an user ID. -func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) { - gwConn, err := pool.GetGatewayServiceClient(pool.Endpoint(c.gwAddr)) - if err != nil { - return nil, err - } - getUserResponse, err := gwConn.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{ - Claim: "username", - Value: username, - SkipFetchingUserGroups: true, - }) - if err != nil { - return nil, err - } - if getUserResponse.Status.Code != rpc.Code_CODE_OK { - return nil, status.NewErrorFromCode(getUserResponse.Status.Code, "gateway") - } - return getUserResponse.User.Id, nil -} - -func (m *mgr) formatGrantee(ctx context.Context, g *provider.Grantee) (int, string, error) { - var granteeType int - var formattedID string - switch g.Type { - case provider.GranteeType_GRANTEE_TYPE_USER: - granteeType = 0 - var err error - formattedID, err = m.userConverter.UserIDToUserName(ctx, g.GetUserId()) - if err != nil { - return 0, "", err - } - case provider.GranteeType_GRANTEE_TYPE_GROUP: - granteeType = 1 - formattedID = formatGroupID(g.GetGroupId()) - default: - granteeType = -1 - } - return granteeType, formattedID, nil -} - -func (m *mgr) extractGrantee(ctx context.Context, t int, g string) (*provider.Grantee, error) { - var grantee provider.Grantee - switch t { - case 0: - userid, err := m.userConverter.UserNameToUserID(ctx, g) - if err != nil { - return nil, err - } - grantee.Type = provider.GranteeType_GRANTEE_TYPE_USER - grantee.Id = &provider.Grantee_UserId{UserId: userid} - case 1: - grantee.Type = provider.GranteeType_GRANTEE_TYPE_GROUP - grantee.Id = &provider.Grantee_GroupId{GroupId: extractGroupID(g)} - default: - grantee.Type = provider.GranteeType_GRANTEE_TYPE_INVALID - } - return &grantee, nil -} - -func resourceTypeToItem(r provider.ResourceType) string { - switch r { - case provider.ResourceType_RESOURCE_TYPE_FILE: - return "file" - case provider.ResourceType_RESOURCE_TYPE_CONTAINER: - return "folder" - case provider.ResourceType_RESOURCE_TYPE_REFERENCE: - return "reference" - case provider.ResourceType_RESOURCE_TYPE_SYMLINK: - return "symlink" - default: - return "" - } -} - -func sharePermToInt(p *provider.ResourcePermissions) int { - return int(conversions.RoleFromResourcePermissions(p).OCSPermissions()) -} - -func intTosharePerm(p int) (*provider.ResourcePermissions, error) { - perms, err := conversions.NewPermissions(p) - if err != nil { - return nil, err - } - - return conversions.RoleFromOCSPermissions(perms).CS3ResourcePermissions(), nil -} - -func intToShareState(g int) collaboration.ShareState { - switch g { - case 0: - return collaboration.ShareState_SHARE_STATE_ACCEPTED - case 1: - return collaboration.ShareState_SHARE_STATE_PENDING - case 2: - return collaboration.ShareState_SHARE_STATE_REJECTED - default: - return collaboration.ShareState_SHARE_STATE_INVALID - } -} - -func formatUserID(u *userpb.UserId) string { - return u.OpaqueId -} - -func formatGroupID(u *grouppb.GroupId) string { - return u.OpaqueId -} - -func extractGroupID(u string) *grouppb.GroupId { - return &grouppb.GroupId{OpaqueId: u} -} - -func (m *mgr) convertToCS3Share(ctx context.Context, s DBShare, storageMountID string) (*collaboration.Share, error) { - ts := &typespb.Timestamp{ - Seconds: uint64(s.STime), - } - permissions, err := intTosharePerm(s.Permissions) - if err != nil { - return nil, err - } - grantee, err := m.extractGrantee(ctx, s.ShareType, s.ShareWith) - if err != nil { - return nil, err - } - owner, err := m.userConverter.UserNameToUserID(ctx, s.UIDOwner) - if err != nil { - return nil, err - } - var creator *userpb.UserId - if s.UIDOwner == s.UIDInitiator { - creator = owner - } else { - creator, err = m.userConverter.UserNameToUserID(ctx, s.UIDOwner) - if err != nil { - return nil, err - } - } - return &collaboration.Share{ - Id: &collaboration.ShareId{ - OpaqueId: s.ID, - }, - ResourceId: &provider.ResourceId{ - StorageId: storageMountID + "!" + s.ItemStorage, - OpaqueId: s.ItemSource, - }, - Permissions: &collaboration.SharePermissions{Permissions: permissions}, - Grantee: grantee, - Owner: owner, - Creator: creator, - Ctime: ts, - Mtime: ts, - }, nil -} - -func (m *mgr) convertToCS3ReceivedShare(ctx context.Context, s DBShare, storageMountID string) (*collaboration.ReceivedShare, error) { - share, err := m.convertToCS3Share(ctx, s, storageMountID) - if err != nil { - return nil, err - } - var state collaboration.ShareState - if s.RejectedBy != "" { - state = collaboration.ShareState_SHARE_STATE_REJECTED - } else { - state = intToShareState(s.State) - } - return &collaboration.ReceivedShare{ - Share: share, - State: state, - }, nil -} diff --git a/pkg/share/manager/sql/mocks/UserConverter.go b/pkg/share/manager/sql/mocks/UserConverter.go deleted file mode 100644 index 48da015a80..0000000000 --- a/pkg/share/manager/sql/mocks/UserConverter.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - - userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" -) - -// UserConverter is an autogenerated mock type for the UserConverter type -type UserConverter struct { - mock.Mock -} - -// UserIDToUserName provides a mock function with given fields: ctx, userid -func (_m *UserConverter) UserIDToUserName(ctx context.Context, userid *userv1beta1.UserId) (string, error) { - ret := _m.Called(ctx, userid) - - var r0 string - if rf, ok := ret.Get(0).(func(context.Context, *userv1beta1.UserId) string); ok { - r0 = rf(ctx, userid) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *userv1beta1.UserId) error); ok { - r1 = rf(ctx, userid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UserNameToUserID provides a mock function with given fields: ctx, username -func (_m *UserConverter) UserNameToUserID(ctx context.Context, username string) (*userv1beta1.UserId, error) { - ret := _m.Called(ctx, username) - - var r0 *userv1beta1.UserId - if rf, ok := ret.Get(0).(func(context.Context, string) *userv1beta1.UserId); ok { - r0 = rf(ctx, username) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*userv1beta1.UserId) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, username) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/pkg/share/manager/sql/sql.go b/pkg/share/manager/sql/sql.go deleted file mode 100644 index 2c156fd713..0000000000 --- a/pkg/share/manager/sql/sql.go +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql - -import ( - "context" - "database/sql" - "fmt" - "path" - "strconv" - "strings" - "time" - - 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" - "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/errtypes" - "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/cs3org/reva/pkg/utils/cfg" - - // Provides mysql drivers. - _ "github.com/go-sql-driver/mysql" - "github.com/pkg/errors" - "google.golang.org/genproto/protobuf/field_mask" -) - -const ( - shareTypeUser = 0 - shareTypeGroup = 1 -) - -func init() { - registry.Register("oc10-sql", NewMysql) -} - -type config struct { - GatewayAddr string `mapstructure:"gatewaysvc"` - StorageMountID string `mapstructure:"storage_mount_id"` - DBUsername string `mapstructure:"db_username"` - DBPassword string `mapstructure:"db_password"` - DBHost string `mapstructure:"db_host"` - DBPort int `mapstructure:"db_port"` - DBName string `mapstructure:"db_name"` -} - -func (c *config) ApplyDefaults() { - c.GatewayAddr = sharedconf.GetGatewaySVC(c.GatewayAddr) -} - -type mgr struct { - driver string - db *sql.DB - storageMountID string - userConverter UserConverter -} - -// NewMysql returns a new share manager connection to a mysql database. -func NewMysql(ctx context.Context, m map[string]interface{}) (share.Manager, error) { - var c config - if err := cfg.Decode(m, &c); err != nil { - return nil, err - } - - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.DBUsername, c.DBPassword, c.DBHost, c.DBPort, c.DBName)) - if err != nil { - return nil, err - } - - userConverter := NewGatewayUserConverter(c.GatewayAddr) - - return New("mysql", db, c.StorageMountID, userConverter) -} - -// New returns a new Cache instance connecting to the given sql.DB. -func New(driver string, db *sql.DB, storageMountID string, userConverter UserConverter) (share.Manager, error) { - return &mgr{ - driver: driver, - db: db, - storageMountID: storageMountID, - userConverter: userConverter, - }, nil -} - -func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { - user := appctx.ContextMustGetUser(ctx) - - // do not allow share to myself or the owner if share is for a user - // TODO(labkode): should not this be caught already at the gw level? - if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && - (utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) { - return nil, errors.New("sql: owner/creator and grantee are the same") - } - - // check if share already exists. - key := &collaboration.ShareKey{ - Owner: md.Owner, - ResourceId: md.Id, - Grantee: g.Grantee, - } - _, err := m.getByKey(ctx, key) - - // share already exists - if err == nil { - return nil, errtypes.AlreadyExists(key.String()) - } - - now := time.Now().Unix() - ts := &typespb.Timestamp{ - Seconds: uint64(now), - } - - owner, err := m.userConverter.UserIDToUserName(ctx, md.Owner) - if err != nil { - return nil, err - } - shareType, shareWith, err := m.formatGrantee(ctx, g.Grantee) - if err != nil { - return nil, err - } - itemType := resourceTypeToItem(md.Type) - targetPath := path.Join("/", path.Base(md.Path)) - permissions := sharePermToInt(g.Permissions.Permissions) - itemSource := md.Id.OpaqueId - fileSource, err := strconv.ParseUint(itemSource, 10, 64) - if err != nil { - // it can be the case that the item source may be a character string - // we leave fileSource blank in that case - fileSource = 0 - } - - stmtString := "INSERT INTO oc_share (share_type,uid_owner,uid_initiator,item_type,item_source,file_source,permissions,stime,share_with,file_target) VALUES (?,?,?,?,?,?,?,?,?,?)" - stmtValues := []interface{}{shareType, owner, user.Username, itemType, itemSource, fileSource, permissions, now, shareWith, targetPath} - - stmt, err := m.db.Prepare(stmtString) - if err != nil { - return nil, err - } - result, err := stmt.Exec(stmtValues...) - if err != nil { - return nil, err - } - lastID, err := result.LastInsertId() - if err != nil { - return nil, err - } - - return &collaboration.Share{ - Id: &collaboration.ShareId{ - OpaqueId: strconv.FormatInt(lastID, 10), - }, - ResourceId: md.Id, - Permissions: g.Permissions, - Grantee: g.Grantee, - Owner: md.Owner, - Creator: user.Id, - Ctime: ts, - Mtime: ts, - }, nil -} - -func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { - var s *collaboration.Share - var err error - switch { - case ref.GetId() != nil: - s, err = m.getByID(ctx, ref.GetId()) - case ref.GetKey() != nil: - s, err = m.getByKey(ctx, ref.GetKey()) - default: - err = errtypes.NotFound(ref.String()) - } - - if err != nil { - return nil, err - } - - return s, nil -} - -func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { - uid := appctx.ContextMustGetUser(ctx).Username - var query string - params := []interface{}{} - switch { - case ref.GetId() != nil: - query = "DELETE FROM oc_share where id=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, ref.GetId().OpaqueId, uid, uid) - case ref.GetKey() != nil: - key := ref.GetKey() - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return err - } - owner := formatUserID(key.Owner) - query = "DELETE FROM oc_share WHERE uid_owner=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid) - default: - return errtypes.NotFound(ref.String()) - } - - stmt, err := m.db.Prepare(query) - if err != nil { - return err - } - res, err := stmt.Exec(params...) - if err != nil { - return err - } - - rowCnt, err := res.RowsAffected() - if err != nil { - return err - } - if rowCnt == 0 { - return errtypes.NotFound(ref.String()) - } - return nil -} - -func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) { - permissions := sharePermToInt(p.Permissions) - uid := appctx.ContextMustGetUser(ctx).Username - - var query string - params := []interface{}{} - switch { - case ref.GetId() != nil: - query = "update oc_share set permissions=?,stime=? where id=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId, uid, uid) - case ref.GetKey() != nil: - key := ref.GetKey() - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return nil, err - } - owner := formatUserID(key.Owner) - query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, permissions, time.Now().Unix(), owner, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid) - default: - return nil, errtypes.NotFound(ref.String()) - } - - stmt, err := m.db.Prepare(query) - if err != nil { - return nil, err - } - if _, err = stmt.Exec(params...); err != nil { - return nil, err - } - - return m.GetShare(ctx, ref) -} - -func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { - uid := appctx.ContextMustGetUser(ctx).Username - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE (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...) - if err != nil { - return nil, err - } - defer rows.Close() - - var s DBShare - shares := []*collaboration.Share{} - for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { - continue - } - share, err := m.convertToCS3Share(ctx, s, m.storageMountID) - if err != nil { - return nil, err - } - shares = append(shares, share) - } - if err = rows.Err(); err != nil { - return nil, err - } - - return shares, nil -} - -// we list the shares that are targeted to the user in context or to the user groups. -func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { - user := appctx.ContextMustGetUser(ctx) - uid := user.Username - - params := []interface{}{uid, uid, uid} - for _, v := range user.Groups { - params = append(params, v) - } - - homeConcat := "" - if m.driver == "mysql" { // mysql upsert - homeConcat = "storages.id = CONCAT('home::', ts.uid_owner)" - } else { // sqlite3 upsert - homeConcat = "storages.id = 'home::' || ts.uid_owner" - } - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, ts.id, stime, permissions, share_type, accepted, storages.numeric_id FROM oc_share ts LEFT JOIN oc_storages storages ON " + homeConcat + " WHERE (uid_owner != ? AND uid_initiator != ?) " - if len(user.Groups) > 0 { - query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" - } else { - query += "AND (share_with=?)" - } - - 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...) - if err != nil { - return nil, err - } - defer rows.Close() - - var s DBShare - shares := []*collaboration.ReceivedShare{} - for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State, &s.ItemStorage); err != nil { - continue - } - share, err := m.convertToCS3ReceivedShare(ctx, s, m.storageMountID) - if err != nil { - return nil, err - } - shares = append(shares, share) - } - if err = rows.Err(); err != nil { - return nil, err - } - - return shares, nil -} - -func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { - var s *collaboration.ReceivedShare - var err error - switch { - case ref.GetId() != nil: - s, err = m.getReceivedByID(ctx, ref.GetId()) - case ref.GetKey() != nil: - s, err = m.getReceivedByKey(ctx, ref.GetKey()) - default: - err = errtypes.NotFound(ref.String()) - } - - if err != nil { - return nil, err - } - - return s, nil -} - -func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { - rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: share.Share.Id}}) - if err != nil { - return nil, err - } - - for i := range fieldMask.Paths { - switch fieldMask.Paths[i] { - case "state": - rs.State = share.State - // TODO case "mount_point": - default: - return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported") - } - } - - var queryAccept string - switch rs.GetState() { - case collaboration.ShareState_SHARE_STATE_REJECTED: - queryAccept = "update oc_share set accepted=2 where id=?" - case collaboration.ShareState_SHARE_STATE_ACCEPTED: - queryAccept = "update oc_share set accepted=0 where id=?" - } - - if queryAccept != "" { - stmt, err := m.db.Prepare(queryAccept) - if err != nil { - return nil, err - } - _, err = stmt.Exec(rs.Share.Id.OpaqueId) - if err != nil { - return nil, err - } - } - - return rs, nil -} - -func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) { - uid := appctx.ContextMustGetUser(ctx).Username - s := DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, stime, permissions, share_type FROM oc_share WHERE id=? AND (uid_owner=? or uid_initiator=?)" - if err := m.db.QueryRow(query, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.STime, &s.Permissions, &s.ShareType); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(id.OpaqueId) - } - return nil, err - } - return m.convertToCS3Share(ctx, s, m.storageMountID) -} - -func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) { - owner, err := m.userConverter.UserIDToUserName(ctx, key.Owner) - if err != nil { - return nil, err - } - uid := appctx.ContextMustGetUser(ctx).Username - - s := DBShare{} - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return nil, err - } - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE uid_owner=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" - if err = m.db.QueryRow(query, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(key.String()) - } - return nil, err - } - return m.convertToCS3Share(ctx, s, m.storageMountID) -} - -func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.ReceivedShare, error) { - user := appctx.ContextMustGetUser(ctx) - uid := user.Username - - params := []interface{}{id.OpaqueId, uid} - for _, v := range user.Groups { - params = append(params, v) - } - - s := DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, stime, permissions, share_type, accepted FROM oc_share ts WHERE ts.id=? " - if len(user.Groups) > 0 { - query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" - } else { - query += "AND (share_with=?)" - } - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(id.OpaqueId) - } - return nil, err - } - return m.convertToCS3ReceivedShare(ctx, s, m.storageMountID) -} - -func (m *mgr) getReceivedByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.ReceivedShare, error) { - user := appctx.ContextMustGetUser(ctx) - uid := user.Username - - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return nil, err - } - params := []interface{}{uid, formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith} - for _, v := range user.Groups { - params = append(params, v) - } - - s := DBShare{} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, ts.id, stime, permissions, share_type, accepted FROM oc_share ts WHERE uid_owner=? AND item_source=? AND share_type=? AND share_with=? " - if len(user.Groups) > 0 { - query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" - } else { - query += "AND (share_with=?)" - } - - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(key.String()) - } - return nil, err - } - return m.convertToCS3ReceivedShare(ctx, s, m.storageMountID) -} - -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 += "item_source=?" - params = append(params, 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 -} diff --git a/pkg/share/manager/sql/sql_suite_test.go b/pkg/share/manager/sql/sql_suite_test.go deleted file mode 100644 index e3c6fbd8e9..0000000000 --- a/pkg/share/manager/sql/sql_suite_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestSql(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Sql Suite") -} diff --git a/pkg/share/manager/sql/sql_test.go b/pkg/share/manager/sql/sql_test.go deleted file mode 100644 index 176e75eb66..0000000000 --- a/pkg/share/manager/sql/sql_test.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql_test - -import ( - "context" - "database/sql" - "os" - - user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" - provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/share" - sqlmanager "github.com/cs3org/reva/pkg/share/manager/sql" - mocks "github.com/cs3org/reva/pkg/share/manager/sql/mocks" - _ "github.com/mattn/go-sqlite3" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" - "google.golang.org/protobuf/types/known/fieldmaskpb" -) - -var _ = Describe("SQL manager", func() { - var ( - mgr share.Manager - ctx context.Context - testDBFile *os.File - - loginAs = func(user *user.User) { - ctx = appctx.ContextSetUser(context.Background(), user) - } - admin = &user.User{ - Id: &user.UserId{ - Idp: "idp", - OpaqueId: "userid", - Type: user.UserType_USER_TYPE_PRIMARY, - }, - Username: "admin", - } - otherUser = &user.User{ - Id: &user.UserId{ - Idp: "idp", - OpaqueId: "userid", - Type: user.UserType_USER_TYPE_PRIMARY, - }, - Username: "einstein", - } - - shareRef = &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{ - Id: &collaboration.ShareId{ - OpaqueId: "1", - }, - }} - ) - - AfterEach(func() { - os.Remove(testDBFile.Name()) - }) - - BeforeEach(func() { - var err error - testDBFile, err = os.CreateTemp("", "example") - Expect(err).ToNot(HaveOccurred()) - - dbData, err := os.ReadFile("test.db") - Expect(err).ToNot(HaveOccurred()) - - _, err = testDBFile.Write(dbData) - Expect(err).ToNot(HaveOccurred()) - err = testDBFile.Close() - Expect(err).ToNot(HaveOccurred()) - - sqldb, err := sql.Open("sqlite3", testDBFile.Name()) - Expect(err).ToNot(HaveOccurred()) - - userConverter := &mocks.UserConverter{} - userConverter.On("UserIDToUserName", mock.Anything, mock.Anything).Return("username", nil) - userConverter.On("UserNameToUserID", mock.Anything, mock.Anything).Return( - func(_ context.Context, username string) *user.UserId { - return &user.UserId{ - OpaqueId: username, - } - }, - func(_ context.Context, username string) error { return nil }) - mgr, err = sqlmanager.New("sqlite3", sqldb, "abcde", userConverter) - Expect(err).ToNot(HaveOccurred()) - - loginAs(admin) - }) - - It("creates manager instances", func() { - Expect(mgr).ToNot(BeNil()) - }) - - Describe("GetShare", func() { - It("returns the share", func() { - share, err := mgr.GetShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - }) - - It("returns an error if the share does not exis", func() { - share, err := mgr.GetShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{ - Id: &collaboration.ShareId{ - OpaqueId: "2", - }, - }}) - Expect(err).To(HaveOccurred()) - Expect(share).To(BeNil()) - }) - }) - - Describe("Share", func() { - It("creates a share", func() { - grant := &collaboration.ShareGrant{ - Grantee: &provider.Grantee{ - Type: provider.GranteeType_GRANTEE_TYPE_USER, - Id: &provider.Grantee_UserId{UserId: &user.UserId{ - OpaqueId: "someone", - }}, - }, - Permissions: &collaboration.SharePermissions{ - Permissions: &provider.ResourcePermissions{ - GetPath: true, - InitiateFileDownload: true, - ListFileVersions: true, - ListContainer: true, - Stat: true, - }, - }, - } - info := &provider.ResourceInfo{ - Id: &provider.ResourceId{ - StorageId: "/", - OpaqueId: "something", - }, - } - share, err := mgr.Share(ctx, info, grant) - - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.Id.OpaqueId).To(Equal("2")) - }) - }) - - Describe("ListShares", func() { - It("lists shares", func() { - shares, err := mgr.ListShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(1)) - - shares, err = mgr.ListShares(ctx, []*collaboration.Filter{ - share.ResourceIDFilter(&provider.ResourceId{ - StorageId: "/", - OpaqueId: "somethingElse", - }), - }) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(0)) - }) - }) - - Describe("ListReceivedShares", func() { - It("lists received shares", func() { - loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(1)) - }) - }) - - Describe("GetReceivedShare", func() { - It("returns the received share", func() { - loginAs(otherUser) - share, err := mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - }) - }) - - Describe("UpdateReceivedShare", func() { - It("returns an error when no valid field is set in the mask", func() { - loginAs(otherUser) - - share, err := mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_ACCEPTED)) - - share.State = collaboration.ShareState_SHARE_STATE_REJECTED - _, err = mgr.UpdateReceivedShare(ctx, share, &fieldmaskpb.FieldMask{Paths: []string{"foo"}}) - Expect(err).To(HaveOccurred()) - }) - - It("updates the state when the state is set in the mask", func() { - loginAs(otherUser) - - share, err := mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_ACCEPTED)) - - share.State = collaboration.ShareState_SHARE_STATE_REJECTED - share, err = mgr.UpdateReceivedShare(ctx, share, &fieldmaskpb.FieldMask{Paths: []string{"state"}}) - Expect(err).ToNot(HaveOccurred()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_REJECTED)) - - share, err = mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_REJECTED)) - }) - }) - - Describe("Unshare", func() { - It("deletes shares", func() { - loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(1)) - - loginAs(admin) - err = mgr.Unshare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{ - Id: &collaboration.ShareId{ - OpaqueId: shares[0].Share.Id.OpaqueId, - }, - }}) - Expect(err).ToNot(HaveOccurred()) - - loginAs(otherUser) - shares, err = mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(0)) - }) - }) - - Describe("UpdateShare", func() { - It("updates permissions", func() { - share, err := mgr.GetShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share.Permissions.Permissions.Delete).To(BeTrue()) - - share, err = mgr.UpdateShare(ctx, shareRef, &collaboration.SharePermissions{ - Permissions: &provider.ResourcePermissions{ - InitiateFileUpload: true, - RestoreFileVersion: true, - RestoreRecycleItem: true, - }}) - Expect(err).ToNot(HaveOccurred()) - Expect(share.Permissions.Permissions.Delete).To(BeFalse()) - - share, err = mgr.GetShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share.Permissions.Permissions.Delete).To(BeFalse()) - }) - }) -}) diff --git a/pkg/share/manager/sql/test.db b/pkg/share/manager/sql/test.db deleted file mode 100644 index fba76fdcc4..0000000000 Binary files a/pkg/share/manager/sql/test.db and /dev/null differ diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml index ed9d667478..775a90f248 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cernboxgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cernboxhttp_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml index a4ee3efa9a..37f3826796 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cesnetgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cesnethttp_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml index 03a8a0b89b..04b6bbe7dd 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cernboxgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cernboxhttp_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml index 6a12f8eb99..dea3c93878 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cesnetgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cesnethttp_address}}"