Initial QSfera import
This commit is contained in:
+480
@@ -0,0 +1,480 @@
|
||||
// Copyright 2018-2021 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 conversions sits between CS3 type definitions and OCS API Responses
|
||||
package conversions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/mime"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/publicshare"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/user"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
|
||||
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
publicsharemgr "github.com/opencloud-eu/reva/v2/pkg/publicshare/manager/registry"
|
||||
usermgr "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
// ShareTypeUser refers to user shares
|
||||
ShareTypeUser ShareType = 0
|
||||
|
||||
// ShareTypePublicLink refers to public link shares
|
||||
ShareTypePublicLink ShareType = 3
|
||||
|
||||
// ShareTypeGroup represents a group share
|
||||
ShareTypeGroup ShareType = 1
|
||||
|
||||
// ShareTypeFederatedCloudShare represents a federated share
|
||||
ShareTypeFederatedCloudShare ShareType = 6
|
||||
|
||||
// ShareTypeSpaceMembershipUser represents an action regarding user type space members
|
||||
ShareTypeSpaceMembershipUser ShareType = 7
|
||||
|
||||
// ShareTypeSpaceMembershipGroup represents an action regarding group type space members
|
||||
ShareTypeSpaceMembershipGroup ShareType = 8
|
||||
|
||||
// ShareWithUserTypeUser represents a normal user
|
||||
ShareWithUserTypeUser ShareWithUserType = 0
|
||||
|
||||
// ShareWithUserTypeGuest represents a guest user
|
||||
ShareWithUserTypeGuest ShareWithUserType = 1
|
||||
|
||||
// The datetime format of ISO8601
|
||||
_iso8601 = "2006-01-02T15:04:05Z0700"
|
||||
)
|
||||
|
||||
// ResourceType indicates the OCS type of the resource
|
||||
type ResourceType int
|
||||
|
||||
func (rt ResourceType) String() (s string) {
|
||||
switch rt {
|
||||
case 0:
|
||||
s = "invalid"
|
||||
case 1:
|
||||
s = "file"
|
||||
case 2:
|
||||
s = "folder"
|
||||
case 3:
|
||||
s = "reference"
|
||||
default:
|
||||
s = "invalid"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ShareType denotes a type of share
|
||||
type ShareType int
|
||||
|
||||
// ShareWithUserType denotes a type of user
|
||||
type ShareWithUserType int
|
||||
|
||||
// ShareData represents https://doc.owncloud.com/server/developer_manual/core/ocs-share-api.html#response-attributes-1
|
||||
type ShareData struct {
|
||||
// TODO int?
|
||||
ID string `json:"id" xml:"id"`
|
||||
// The share’s type
|
||||
ShareType ShareType `json:"share_type" xml:"share_type"`
|
||||
// The username of the owner of the share.
|
||||
UIDOwner string `json:"uid_owner" xml:"uid_owner"`
|
||||
// The display name of the owner of the share.
|
||||
DisplaynameOwner string `json:"displayname_owner" xml:"displayname_owner"`
|
||||
// Additional info to identify the share owner, eg. the email or username
|
||||
AdditionalInfoOwner string `json:"additional_info_owner" xml:"additional_info_owner"`
|
||||
// The permission attribute set on the file.
|
||||
// TODO(jfd) change the default to read only
|
||||
Permissions Permissions `json:"permissions" xml:"permissions"`
|
||||
// The UNIX timestamp when the share was created.
|
||||
STime uint64 `json:"stime" xml:"stime"`
|
||||
// ?
|
||||
Parent string `json:"parent" xml:"parent"`
|
||||
// The UNIX timestamp when the share expires.
|
||||
Expiration string `json:"expiration" xml:"expiration"`
|
||||
// The public link to the item being shared.
|
||||
Token string `json:"token" xml:"token"`
|
||||
// The unique id of the user that owns the file or folder being shared.
|
||||
UIDFileOwner string `json:"uid_file_owner" xml:"uid_file_owner"`
|
||||
// The display name of the user that owns the file or folder being shared.
|
||||
DisplaynameFileOwner string `json:"displayname_file_owner" xml:"displayname_file_owner"`
|
||||
// Additional info to identify the file owner, eg. the email or username
|
||||
AdditionalInfoFileOwner string `json:"additional_info_file_owner" xml:"additional_info_file_owner"`
|
||||
// share state, 0 = accepted, 1 = pending, 2 = declined
|
||||
State int `json:"state" xml:"state"`
|
||||
// The path to the shared file or folder.
|
||||
Path string `json:"path" xml:"path"`
|
||||
// The type of the object being shared. This can be one of 'file' or 'folder'.
|
||||
ItemType string `json:"item_type" xml:"item_type"`
|
||||
// The RFC2045-compliant mimetype of the file.
|
||||
MimeType string `json:"mimetype" xml:"mimetype"`
|
||||
// The space ID of the original file location
|
||||
SpaceID string `json:"space_id" xml:"space_id"`
|
||||
// The space alias of the original file location
|
||||
SpaceAlias string `json:"space_alias" xml:"space_alias"`
|
||||
StorageID string `json:"storage_id" xml:"storage_id"`
|
||||
Storage uint64 `json:"storage" xml:"storage"`
|
||||
// The unique node id of the item being shared.
|
||||
ItemSource string `json:"item_source" xml:"item_source"`
|
||||
// The unique node id of the item being shared. For legacy reasons item_source and file_source attributes have the same value.
|
||||
FileSource string `json:"file_source" xml:"file_source"`
|
||||
// The unique node id of the parent node of the item being shared.
|
||||
FileParent string `json:"file_parent" xml:"file_parent"`
|
||||
// The basename of the shared file.
|
||||
FileTarget string `json:"file_target" xml:"file_target"`
|
||||
// The uid of the share recipient. This is either
|
||||
// - a GID (group id) if it is being shared with a group or
|
||||
// - a UID (user id) if the share is shared with a user.
|
||||
// - a password for public links
|
||||
ShareWith string `json:"share_with,omitempty" xml:"share_with,omitempty"`
|
||||
// The type of user
|
||||
// - 0 = normal user
|
||||
// - 1 = guest account
|
||||
ShareWithUserType ShareWithUserType `json:"share_with_user_type" xml:"share_with_user_type"`
|
||||
// The display name of the share recipient
|
||||
ShareWithDisplayname string `json:"share_with_displayname,omitempty" xml:"share_with_displayname,omitempty"`
|
||||
// Additional info to identify the share recipient, eg. the email or username
|
||||
ShareWithAdditionalInfo string `json:"share_with_additional_info" xml:"share_with_additional_info"`
|
||||
// Whether the recipient was notified, by mail, about the share being shared with them.
|
||||
MailSend int `json:"mail_send" xml:"mail_send"`
|
||||
// Name of the public share
|
||||
Name string `json:"name" xml:"name"`
|
||||
// URL of the public share
|
||||
URL string `json:"url,omitempty" xml:"url,omitempty"`
|
||||
// Attributes associated
|
||||
Attributes string `json:"attributes,omitempty" xml:"attributes,omitempty"`
|
||||
// Quicklink indicates if the link is the quicklink
|
||||
Quicklink bool `json:"quicklink,omitempty" xml:"quicklink,omitempty"`
|
||||
// PasswordProtected represents a public share is password protected
|
||||
// PasswordProtected bool `json:"password_protected,omitempty" xml:"password_protected,omitempty"`
|
||||
Hidden bool `json:"hidden" xml:"hidden"`
|
||||
}
|
||||
|
||||
// ShareeData holds share recipient search results
|
||||
type ShareeData struct {
|
||||
Exact *ExactMatchesData `json:"exact" xml:"exact"`
|
||||
Users []*MatchData `json:"users" xml:"users>element"`
|
||||
Groups []*MatchData `json:"groups" xml:"groups>element"`
|
||||
Remotes []*MatchData `json:"remotes" xml:"remotes>element"`
|
||||
}
|
||||
|
||||
// TokenInfo holds token information
|
||||
type TokenInfo struct {
|
||||
// for all callers
|
||||
Token string `json:"token" xml:"token"`
|
||||
LinkURL string `json:"link_url" xml:"link_url"`
|
||||
PasswordProtected bool `json:"password_protected" xml:"password_protected"`
|
||||
Aliaslink bool `json:"alias_link" xml:"alias_link"`
|
||||
|
||||
// if not password protected
|
||||
ID string `json:"id" xml:"id"`
|
||||
StorageID string `json:"storage_id" xml:"storage_id"`
|
||||
SpaceID string `json:"space_id" xml:"space_id"`
|
||||
OpaqueID string `json:"opaque_id" xml:"opaque_id"`
|
||||
Path string `json:"path" xml:"path"`
|
||||
|
||||
// if native access
|
||||
SpacePath string `json:"space_path" xml:"space_path"`
|
||||
SpaceAlias string `json:"space_alias" xml:"space_alias"`
|
||||
SpaceURL string `json:"space_url" xml:"space_url"`
|
||||
SpaceType string `json:"space_type" xml:"space_type"`
|
||||
}
|
||||
|
||||
// ExactMatchesData hold exact matches
|
||||
type ExactMatchesData struct {
|
||||
Users []*MatchData `json:"users" xml:"users>element"`
|
||||
Groups []*MatchData `json:"groups" xml:"groups>element"`
|
||||
Remotes []*MatchData `json:"remotes" xml:"remotes>element"`
|
||||
}
|
||||
|
||||
// MatchData describes a single match
|
||||
type MatchData struct {
|
||||
Label string `json:"label" xml:"label,omitempty"`
|
||||
Value *MatchValueData `json:"value" xml:"value"`
|
||||
}
|
||||
|
||||
// MatchValueData holds the type and actual value
|
||||
type MatchValueData struct {
|
||||
ShareType int `json:"shareType" xml:"shareType"`
|
||||
ShareWith string `json:"shareWith" xml:"shareWith"`
|
||||
ShareWithProvider string `json:"shareWithProvider" xml:"shareWithProvider"`
|
||||
ShareWithAdditionalInfo string `json:"shareWithAdditionalInfo" xml:"shareWithAdditionalInfo,omitempty"`
|
||||
UserType int `json:"userType" xml:"userType"`
|
||||
}
|
||||
|
||||
// CS3Share2ShareData converts a cs3api user share into shareData data model
|
||||
func CS3Share2ShareData(ctx context.Context, share *collaboration.Share) *ShareData {
|
||||
sd := &ShareData{
|
||||
// share.permissions are mapped below
|
||||
// Displaynames are added later
|
||||
UIDOwner: LocalUserIDToString(share.GetCreator()),
|
||||
UIDFileOwner: LocalUserIDToString(share.GetOwner()),
|
||||
}
|
||||
|
||||
switch share.Grantee.Type {
|
||||
case provider.GranteeType_GRANTEE_TYPE_USER:
|
||||
sd.ShareType = ShareTypeUser
|
||||
sd.ShareWith = LocalUserIDToString(share.Grantee.GetUserId())
|
||||
shareType := share.GetGrantee().GetUserId().GetType()
|
||||
if shareType == userpb.UserType_USER_TYPE_LIGHTWEIGHT || shareType == userpb.UserType_USER_TYPE_GUEST {
|
||||
sd.ShareWithUserType = ShareWithUserTypeGuest
|
||||
} else {
|
||||
sd.ShareWithUserType = ShareWithUserTypeUser
|
||||
}
|
||||
case provider.GranteeType_GRANTEE_TYPE_GROUP:
|
||||
sd.ShareType = ShareTypeGroup
|
||||
sd.ShareWith = LocalGroupIDToString(share.Grantee.GetGroupId())
|
||||
}
|
||||
|
||||
if share.Id != nil {
|
||||
sd.ID = share.Id.OpaqueId
|
||||
}
|
||||
if share.GetPermissions().GetPermissions() != nil {
|
||||
sd.Permissions = RoleFromResourcePermissions(share.GetPermissions().GetPermissions(), false).OCSPermissions()
|
||||
}
|
||||
if share.Ctime != nil {
|
||||
sd.STime = share.Ctime.Seconds // TODO CS3 api birth time = btime
|
||||
}
|
||||
|
||||
if share.Expiration != nil {
|
||||
expiration := time.Unix(int64(share.Expiration.Seconds), int64(share.Expiration.Nanos))
|
||||
sd.Expiration = expiration.Format(_iso8601)
|
||||
}
|
||||
|
||||
return sd
|
||||
}
|
||||
|
||||
// PublicShare2ShareData converts a cs3api public share into shareData data model
|
||||
func PublicShare2ShareData(share *link.PublicShare, r *http.Request, publicURL string) *ShareData {
|
||||
sd := &ShareData{
|
||||
// share.permissions are mapped below
|
||||
// Displaynames are added later
|
||||
ShareType: ShareTypePublicLink,
|
||||
Token: share.Token,
|
||||
Name: share.DisplayName,
|
||||
MailSend: 0,
|
||||
URL: publicURL + path.Join("/", "s/"+share.Token),
|
||||
UIDOwner: LocalUserIDToString(share.Creator),
|
||||
UIDFileOwner: LocalUserIDToString(share.Owner),
|
||||
Quicklink: share.Quicklink,
|
||||
}
|
||||
if share.Id != nil {
|
||||
sd.ID = share.Id.OpaqueId
|
||||
}
|
||||
|
||||
if s := share.GetPermissions().GetPermissions(); s != nil {
|
||||
sd.Permissions = RoleFromResourcePermissions(share.GetPermissions().GetPermissions(), true).OCSPermissions()
|
||||
}
|
||||
|
||||
if share.Expiration != nil {
|
||||
sd.Expiration = timestampToExpiration(share.Expiration)
|
||||
}
|
||||
if share.Ctime != nil {
|
||||
sd.STime = share.Ctime.Seconds // TODO CS3 api birth time = btime
|
||||
}
|
||||
|
||||
// hide password
|
||||
if share.PasswordProtected {
|
||||
sd.ShareWith = "***redacted***"
|
||||
sd.ShareWithDisplayname = "***redacted***"
|
||||
}
|
||||
|
||||
return sd
|
||||
}
|
||||
|
||||
func formatRemoteUser(u *userpb.UserId) string {
|
||||
return fmt.Sprintf("%s@%s", u.OpaqueId, u.Idp)
|
||||
}
|
||||
|
||||
func webdavInfo(protocols []*ocm.Protocol) (*ocm.WebDAVProtocol, bool) {
|
||||
for _, p := range protocols {
|
||||
if opt, ok := p.Term.(*ocm.Protocol_WebdavOptions); ok {
|
||||
return opt.WebdavOptions, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ReceivedOCMShare2ShareData converts a cs3 ocm received share into a share data model.
|
||||
func ReceivedOCMShare2ShareData(share *ocm.ReceivedShare, path string) (*ShareData, error) {
|
||||
webdav, ok := webdavInfo(share.Protocols)
|
||||
if !ok {
|
||||
return nil, errtypes.InternalError("webdav endpoint not in share")
|
||||
}
|
||||
|
||||
opaqueid := base64.StdEncoding.EncodeToString([]byte("/"))
|
||||
|
||||
shareTarget := filepath.Join("/Shares", share.Name)
|
||||
|
||||
s := &ShareData{
|
||||
ID: share.Id.OpaqueId,
|
||||
UIDOwner: formatRemoteUser(share.Creator),
|
||||
UIDFileOwner: formatRemoteUser(share.Owner),
|
||||
ShareWith: share.Grantee.GetUserId().OpaqueId,
|
||||
Permissions: RoleFromResourcePermissions(webdav.GetPermissions().GetPermissions(), false).OCSPermissions(),
|
||||
ShareType: ShareTypeFederatedCloudShare,
|
||||
Path: shareTarget,
|
||||
FileTarget: shareTarget,
|
||||
MimeType: mime.Detect(share.ResourceType == provider.ResourceType_RESOURCE_TYPE_CONTAINER, share.Name), //nolint:staticcheck // we will update our ocm implementation later
|
||||
ItemType: ResourceType(share.ResourceType).String(), //nolint:staticcheck // we will update our ocm implementation later
|
||||
ItemSource: storagespace.FormatResourceID(&provider.ResourceId{
|
||||
StorageId: utils.OCMStorageProviderID,
|
||||
SpaceId: share.Id.OpaqueId,
|
||||
OpaqueId: opaqueid,
|
||||
}),
|
||||
STime: share.Ctime.Seconds,
|
||||
Name: share.Name,
|
||||
SpaceID: storagespace.FormatStorageID(utils.OCMStorageProviderID, share.Id.OpaqueId),
|
||||
}
|
||||
|
||||
if share.Expiration != nil {
|
||||
s.Expiration = timestampToExpiration(share.Expiration)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func webdavAMInfo(methods []*ocm.AccessMethod) (*ocm.WebDAVAccessMethod, bool) {
|
||||
for _, a := range methods {
|
||||
if opt, ok := a.Term.(*ocm.AccessMethod_WebdavOptions); ok {
|
||||
return opt.WebdavOptions, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// OCMShare2ShareData converts a cs3 ocm share into a share data model.
|
||||
func OCMShare2ShareData(share *ocm.Share) (*ShareData, error) {
|
||||
webdav, ok := webdavAMInfo(share.AccessMethods)
|
||||
if !ok {
|
||||
return nil, errtypes.InternalError("webdav endpoint not in share")
|
||||
}
|
||||
|
||||
s := &ShareData{
|
||||
ID: share.Id.OpaqueId,
|
||||
UIDOwner: share.Creator.OpaqueId,
|
||||
UIDFileOwner: share.Owner.OpaqueId,
|
||||
ShareWith: formatRemoteUser(share.Grantee.GetUserId()),
|
||||
Permissions: RoleFromResourcePermissions(webdav.GetPermissions(), false).OCSPermissions(),
|
||||
ShareType: ShareTypeFederatedCloudShare,
|
||||
STime: share.Ctime.Seconds,
|
||||
Name: share.Name,
|
||||
}
|
||||
|
||||
if share.Expiration != nil {
|
||||
s.Expiration = timestampToExpiration(share.Expiration)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// LocalUserIDToString transforms a cs3api user id into an ocs data model without domain name
|
||||
// TODO ocs uses user names ... so an additional lookup is needed. see mapUserIds()
|
||||
func LocalUserIDToString(userID *userpb.UserId) string {
|
||||
if userID == nil || userID.OpaqueId == "" {
|
||||
return ""
|
||||
}
|
||||
return userID.OpaqueId
|
||||
}
|
||||
|
||||
// LocalGroupIDToString transforms a cs3api group id into an ocs data model without domain name
|
||||
func LocalGroupIDToString(groupID *grouppb.GroupId) string {
|
||||
if groupID == nil || groupID.OpaqueId == "" {
|
||||
return ""
|
||||
}
|
||||
return groupID.OpaqueId
|
||||
}
|
||||
|
||||
// GetUserManager returns a connection to a user share manager
|
||||
func GetUserManager(manager string, m map[string]map[string]interface{}) (user.Manager, error) {
|
||||
if f, ok := usermgr.NewFuncs[manager]; ok {
|
||||
return f(m[manager])
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("driver %s not found for user manager", manager)
|
||||
}
|
||||
|
||||
// GetPublicShareManager returns a connection to a public share manager
|
||||
func GetPublicShareManager(manager string, m map[string]map[string]interface{}) (publicshare.Manager, error) {
|
||||
if f, ok := publicsharemgr.NewFuncs[manager]; ok {
|
||||
return f(m[manager])
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("driver %s not found for public shares manager", manager)
|
||||
}
|
||||
|
||||
// timestamp is assumed to be UTC ... just human readable ...
|
||||
// FIXME and ambiguous / error prone because there is no time zone ...
|
||||
func timestampToExpiration(t *types.Timestamp) string {
|
||||
return time.Unix(int64(t.Seconds), int64(t.Nanos)).UTC().Format("2006-01-02 15:05:05")
|
||||
}
|
||||
|
||||
// ParseTimestamp tries to parse the ocs expiry into a CS3 Timestamp
|
||||
func ParseTimestamp(timestampString string) (*types.Timestamp, error) {
|
||||
parsedTime, err := time.Parse("2006-01-02T15:04:05Z0700", timestampString)
|
||||
if err != nil {
|
||||
parsedTime, err = time.Parse("2006-01-02", timestampString)
|
||||
if err == nil {
|
||||
// the link needs to be valid for the whole day
|
||||
parsedTime = parsedTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("datetime format invalid: %v, %s", timestampString, err.Error())
|
||||
}
|
||||
final := parsedTime.UnixNano()
|
||||
|
||||
return &types.Timestamp{
|
||||
Seconds: uint64(final / 1000000000),
|
||||
Nanos: uint32(final % 1000000000),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UserTypeString returns human readable strings for various user types
|
||||
func UserTypeString(userType userpb.UserType) string {
|
||||
switch userType {
|
||||
case userpb.UserType_USER_TYPE_PRIMARY:
|
||||
return "primary"
|
||||
case userpb.UserType_USER_TYPE_SECONDARY:
|
||||
return "secondary"
|
||||
case userpb.UserType_USER_TYPE_SERVICE:
|
||||
return "service"
|
||||
case userpb.UserType_USER_TYPE_APPLICATION:
|
||||
return "application"
|
||||
case userpb.UserType_USER_TYPE_GUEST:
|
||||
return "guest"
|
||||
case userpb.UserType_USER_TYPE_FEDERATED:
|
||||
return "federated"
|
||||
case userpb.UserType_USER_TYPE_LIGHTWEIGHT:
|
||||
return "lightweight"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright 2020 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 conversions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Permissions reflects the CRUD permissions used in the OCS sharing API
|
||||
type Permissions uint
|
||||
|
||||
const (
|
||||
// PermissionInvalid represents an invalid permission
|
||||
PermissionInvalid Permissions = 0
|
||||
// PermissionRead grants read permissions on a resource
|
||||
PermissionRead Permissions = 1 << (iota - 1)
|
||||
// PermissionWrite grants write permissions on a resource
|
||||
PermissionWrite
|
||||
// PermissionCreate grants create permissions on a resource
|
||||
PermissionCreate
|
||||
// PermissionDelete grants delete permissions on a resource
|
||||
PermissionDelete
|
||||
// PermissionShare grants share permissions on a resource
|
||||
PermissionShare
|
||||
// PermissionAll grants all permissions on a resource
|
||||
PermissionAll Permissions = (1 << (iota - 1)) - 1
|
||||
// PermissionMaxInput is to be used within value range checks
|
||||
PermissionMaxInput = PermissionAll
|
||||
// PermissionMinInput is to be used within value range checks
|
||||
PermissionMinInput = PermissionRead
|
||||
// PermissionsNone is to be used to deny access on a resource
|
||||
PermissionsNone = 64
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPermissionNotInRange defines a permission specific error.
|
||||
ErrPermissionNotInRange = fmt.Errorf("the provided permission is not between %d and %d", PermissionMinInput, PermissionMaxInput)
|
||||
// ErrZeroPermission defines a permission specific error
|
||||
ErrZeroPermission = errors.New("permission is zero")
|
||||
)
|
||||
|
||||
// NewPermissions creates a new Permissions instance.
|
||||
// The value must be in the valid range.
|
||||
func NewPermissions(val int) (Permissions, error) {
|
||||
if val == int(PermissionInvalid) {
|
||||
return PermissionInvalid, ErrZeroPermission
|
||||
} else if val < int(PermissionInvalid) || int(PermissionMaxInput) < val {
|
||||
return PermissionInvalid, ErrPermissionNotInRange
|
||||
}
|
||||
return Permissions(val), nil
|
||||
}
|
||||
|
||||
// Contain tests if the permissions contain another one.
|
||||
func (p Permissions) Contain(other Permissions) bool {
|
||||
return p&other == other
|
||||
}
|
||||
|
||||
func (p Permissions) String() string {
|
||||
return strconv.FormatUint(uint64(p), 10)
|
||||
}
|
||||
+651
@@ -0,0 +1,651 @@
|
||||
// Copyright 2018-2021 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 conversions sits between CS3 type definitions and OCS API Responses
|
||||
package conversions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/grants"
|
||||
)
|
||||
|
||||
// Role is a set of ocs permissions and cs3 resource permissions under a common name.
|
||||
type Role struct {
|
||||
Name string
|
||||
cS3ResourcePermissions *provider.ResourcePermissions
|
||||
ocsPermissions Permissions
|
||||
}
|
||||
|
||||
const (
|
||||
// RoleViewer grants non-editor role on a resource.
|
||||
RoleViewer = "viewer"
|
||||
// RoleViewerListGrants grants non-editor role on a resource.
|
||||
RoleViewerListGrants = "viewer-list-grants"
|
||||
// RoleSpaceViewer grants non-editor role on a space.
|
||||
RoleSpaceViewer = "spaceviewer"
|
||||
// RoleEditor grants editor permission on a resource, including folders.
|
||||
RoleEditor = "editor"
|
||||
// RoleEditorListGrants grants editor permission on a resource, including folders.
|
||||
RoleEditorListGrants = "editor-list-grants"
|
||||
// RoleSpaceEditor grants editor permission on a space.
|
||||
RoleSpaceEditor = "spaceeditor"
|
||||
// RoleSpaceEditorWithoutVersions grants editor permission without list/restore versions on a space.
|
||||
RoleSpaceEditorWithoutVersions = "spaceeditor-without-versions"
|
||||
// RoleFileEditor grants editor permission on a single file.
|
||||
RoleFileEditor = "file-editor"
|
||||
// RoleFileEditorListGrants grants editor permission on a single file.
|
||||
RoleFileEditorListGrants = "file-editor-list-grants"
|
||||
// RoleCoowner grants co-owner permissions on a resource.
|
||||
RoleCoowner = "coowner"
|
||||
// RoleEditorLite grants permission to upload and download to a resource.
|
||||
RoleEditorLite = "editor-lite"
|
||||
// RoleUploader grants uploader permission to upload onto a resource (no download).
|
||||
RoleUploader = "uploader"
|
||||
// RoleManager grants manager permissions on a resource. Semantically equivalent to co-owner.
|
||||
RoleManager = "manager"
|
||||
// RoleSecureViewer grants secure view permissions on a resource or space.
|
||||
RoleSecureViewer = "secure-viewer"
|
||||
|
||||
// RoleUnknown is used for unknown roles.
|
||||
RoleUnknown = "unknown"
|
||||
// RoleLegacy provides backwards compatibility.
|
||||
RoleLegacy = "legacy"
|
||||
// RoleDenied grants no permission at all on a resource
|
||||
RoleDenied = "denied"
|
||||
)
|
||||
|
||||
// CS3ResourcePermissions for the role
|
||||
func (r *Role) CS3ResourcePermissions() *provider.ResourcePermissions {
|
||||
return r.cS3ResourcePermissions
|
||||
}
|
||||
|
||||
// OCSPermissions for the role
|
||||
func (r *Role) OCSPermissions() Permissions {
|
||||
return r.ocsPermissions
|
||||
}
|
||||
|
||||
// WebDAVPermissions returns the webdav permissions used in propfinds, eg. "WCKDNVR"
|
||||
/*
|
||||
from https://github.com/owncloud/core/blob/10715e2b1c85fc3855a38d2b1fe4426b5e3efbad/apps/dav/lib/Files/PublicFiles/SharedNodeTrait.php#L196-L215
|
||||
|
||||
$p = '';
|
||||
if ($node->isDeletable() && $this->checkSharePermissions(Constants::PERMISSION_DELETE)) {
|
||||
$p .= 'D';
|
||||
}
|
||||
if ($node->isUpdateable() && $this->checkSharePermissions(Constants::PERMISSION_UPDATE)) {
|
||||
$p .= 'NV'; // Renameable, Moveable
|
||||
}
|
||||
if ($node->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
|
||||
if ($node->isUpdateable() && $this->checkSharePermissions(Constants::PERMISSION_UPDATE)) {
|
||||
$p .= 'W';
|
||||
}
|
||||
} else {
|
||||
if ($node->isCreatable() && $this->checkSharePermissions(Constants::PERMISSION_CREATE)) {
|
||||
$p .= 'CK';
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
// D = delete
|
||||
// NV = update (renameable moveable)
|
||||
// W = update (files only)
|
||||
// CK = create (folders only)
|
||||
// S = Shared
|
||||
// R = Shareable
|
||||
// M = Mounted
|
||||
// Z = Deniable (NEW)
|
||||
// P = Purge from trashbin
|
||||
// X = SecureViewable
|
||||
func (r *Role) WebDAVPermissions(isDir, isShared, isMountpoint, isPublic bool) string {
|
||||
var b strings.Builder
|
||||
if !isPublic && isShared {
|
||||
fmt.Fprintf(&b, "S")
|
||||
}
|
||||
if r.ocsPermissions.Contain(PermissionShare) {
|
||||
fmt.Fprintf(&b, "R")
|
||||
}
|
||||
if !isPublic && isMountpoint {
|
||||
fmt.Fprintf(&b, "M")
|
||||
}
|
||||
if r.ocsPermissions.Contain(PermissionDelete) {
|
||||
fmt.Fprintf(&b, "D") // TODO oc10 shows received shares as deletable
|
||||
}
|
||||
if r.ocsPermissions.Contain(PermissionWrite) {
|
||||
// Single file public link shares cannot be renamed
|
||||
if !isPublic || (isPublic && r.cS3ResourcePermissions != nil && r.cS3ResourcePermissions.Move) {
|
||||
fmt.Fprintf(&b, "NV")
|
||||
}
|
||||
if !isDir {
|
||||
fmt.Fprintf(&b, "W")
|
||||
}
|
||||
}
|
||||
if isDir && r.ocsPermissions.Contain(PermissionCreate) {
|
||||
fmt.Fprintf(&b, "CK")
|
||||
}
|
||||
|
||||
if r.CS3ResourcePermissions().DenyGrant {
|
||||
fmt.Fprintf(&b, "Z")
|
||||
}
|
||||
|
||||
if r.CS3ResourcePermissions().PurgeRecycle {
|
||||
fmt.Fprintf(&b, "P")
|
||||
}
|
||||
|
||||
if r.Name == RoleSecureViewer {
|
||||
fmt.Fprintf(&b, "X")
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// RoleFromName creates a role from the name
|
||||
func RoleFromName(name string) *Role {
|
||||
switch name {
|
||||
case RoleDenied:
|
||||
return NewDeniedRole()
|
||||
case RoleViewer:
|
||||
return NewViewerRole()
|
||||
case RoleViewerListGrants:
|
||||
return NewViewerListGrantsRole()
|
||||
case RoleSpaceViewer:
|
||||
return NewSpaceViewerRole()
|
||||
case RoleEditor:
|
||||
return NewEditorRole()
|
||||
case RoleEditorListGrants:
|
||||
return NewEditorListGrantsRole()
|
||||
case RoleSpaceEditor:
|
||||
return NewSpaceEditorRole()
|
||||
case RoleFileEditor:
|
||||
return NewFileEditorRole()
|
||||
case RoleFileEditorListGrants:
|
||||
return NewFileEditorListGrantsRole()
|
||||
case RoleUploader:
|
||||
return NewUploaderRole()
|
||||
case RoleManager:
|
||||
return NewManagerRole()
|
||||
case RoleSecureViewer:
|
||||
return NewSecureViewerRole()
|
||||
case RoleCoowner:
|
||||
return NewCoownerRole()
|
||||
default:
|
||||
return NewUnknownRole()
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnknownRole creates an unknown role. An Unknown role has no permissions over a cs3 resource nor any ocs endpoint.
|
||||
func NewUnknownRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleUnknown,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{},
|
||||
ocsPermissions: PermissionInvalid,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDeniedRole creates a fully denied role
|
||||
func NewDeniedRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleDenied,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{},
|
||||
ocsPermissions: PermissionsNone,
|
||||
}
|
||||
}
|
||||
|
||||
// NewViewerRole creates a viewer role. `sharing` indicates if sharing permission should be added
|
||||
func NewViewerRole() *Role {
|
||||
p := PermissionRead
|
||||
return &Role{
|
||||
Name: RoleViewer,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
ListContainer: true,
|
||||
ListRecycle: true,
|
||||
Stat: true,
|
||||
},
|
||||
ocsPermissions: p,
|
||||
}
|
||||
}
|
||||
|
||||
// NewViewerListGrantsRole creates a viewer role. `sharing` indicates if sharing permission should be added
|
||||
func NewViewerListGrantsRole() *Role {
|
||||
role := NewViewerRole()
|
||||
role.cS3ResourcePermissions.ListGrants = true
|
||||
return role
|
||||
}
|
||||
|
||||
// NewSpaceViewerRole creates a spaceviewer role
|
||||
func NewSpaceViewerRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleSpaceViewer,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
ListContainer: true,
|
||||
ListGrants: true,
|
||||
ListRecycle: true,
|
||||
Stat: true,
|
||||
},
|
||||
ocsPermissions: PermissionRead,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEditorRole creates an editor role. `sharing` indicates if sharing permission should be added
|
||||
func NewEditorRole() *Role {
|
||||
p := PermissionRead | PermissionCreate | PermissionWrite | PermissionDelete
|
||||
return &Role{
|
||||
Name: RoleEditor,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
CreateContainer: true,
|
||||
Delete: true,
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
InitiateFileUpload: true,
|
||||
ListContainer: true,
|
||||
ListRecycle: true,
|
||||
Move: true,
|
||||
RestoreRecycleItem: true,
|
||||
Stat: true,
|
||||
},
|
||||
ocsPermissions: p,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEditorListGrantsRole creates an editor role. `sharing` indicates if sharing permission should be added
|
||||
func NewEditorListGrantsRole() *Role {
|
||||
role := NewEditorRole()
|
||||
role.cS3ResourcePermissions.ListGrants = true
|
||||
return role
|
||||
}
|
||||
|
||||
// NewSpaceEditorRole creates an editor role
|
||||
func NewSpaceEditorRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleSpaceEditor,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
CreateContainer: true,
|
||||
Delete: true,
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
InitiateFileUpload: true,
|
||||
ListContainer: true,
|
||||
ListFileVersions: true,
|
||||
ListGrants: true,
|
||||
ListRecycle: true,
|
||||
Move: true,
|
||||
RestoreFileVersion: true,
|
||||
RestoreRecycleItem: true,
|
||||
Stat: true,
|
||||
},
|
||||
ocsPermissions: PermissionRead | PermissionCreate | PermissionWrite | PermissionDelete,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSpaceEditorWithoutVersionsRole creates an editor without list/restore versions role
|
||||
func NewSpaceEditorWithoutVersionsRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleSpaceEditorWithoutVersions,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
CreateContainer: true,
|
||||
Delete: true,
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
InitiateFileUpload: true,
|
||||
ListContainer: true,
|
||||
ListGrants: true,
|
||||
ListRecycle: true,
|
||||
Move: true,
|
||||
RestoreRecycleItem: true,
|
||||
Stat: true,
|
||||
},
|
||||
ocsPermissions: PermissionRead | PermissionCreate | PermissionWrite | PermissionDelete,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileEditorRole creates a file-editor role
|
||||
func NewFileEditorRole() *Role {
|
||||
p := PermissionRead | PermissionWrite
|
||||
return &Role{
|
||||
Name: RoleEditor,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
ListContainer: true,
|
||||
ListRecycle: true,
|
||||
Stat: true,
|
||||
InitiateFileUpload: true,
|
||||
RestoreRecycleItem: true,
|
||||
},
|
||||
ocsPermissions: p,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileEditorListGrantsRole creates a file-editor role
|
||||
func NewFileEditorListGrantsRole() *Role {
|
||||
role := NewFileEditorRole()
|
||||
role.cS3ResourcePermissions.ListGrants = true
|
||||
return role
|
||||
}
|
||||
|
||||
// NewCoownerRole creates a coowner role.
|
||||
func NewCoownerRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleCoowner,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
ListGrants: true,
|
||||
ListContainer: true,
|
||||
ListFileVersions: true,
|
||||
ListRecycle: true,
|
||||
Stat: true,
|
||||
InitiateFileUpload: true,
|
||||
RestoreFileVersion: true,
|
||||
RestoreRecycleItem: true,
|
||||
CreateContainer: true,
|
||||
Delete: true,
|
||||
Move: true,
|
||||
PurgeRecycle: true,
|
||||
AddGrant: true,
|
||||
UpdateGrant: true,
|
||||
RemoveGrant: true,
|
||||
},
|
||||
ocsPermissions: PermissionAll,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEditorLiteRole creates an editor-lite role
|
||||
func NewEditorLiteRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleEditorLite,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
Stat: true,
|
||||
GetPath: true,
|
||||
CreateContainer: true,
|
||||
InitiateFileUpload: true,
|
||||
InitiateFileDownload: true,
|
||||
ListContainer: true,
|
||||
Move: true,
|
||||
},
|
||||
ocsPermissions: PermissionCreate,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUploaderRole creates an uploader role with no download permissions
|
||||
func NewUploaderRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleUploader,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
Stat: true,
|
||||
GetPath: true,
|
||||
CreateContainer: true,
|
||||
InitiateFileUpload: true,
|
||||
},
|
||||
ocsPermissions: PermissionCreate,
|
||||
}
|
||||
}
|
||||
|
||||
// NewNoneRole creates a role with no permissions
|
||||
func NewNoneRole() *Role {
|
||||
return &Role{
|
||||
Name: "none",
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{},
|
||||
ocsPermissions: PermissionInvalid,
|
||||
}
|
||||
}
|
||||
|
||||
// NewManagerRole creates an manager role
|
||||
func NewManagerRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleManager,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
GetPath: true,
|
||||
GetQuota: true,
|
||||
InitiateFileDownload: true,
|
||||
ListGrants: true,
|
||||
ListContainer: true,
|
||||
ListFileVersions: true,
|
||||
ListRecycle: true,
|
||||
Stat: true,
|
||||
InitiateFileUpload: true,
|
||||
RestoreFileVersion: true,
|
||||
RestoreRecycleItem: true,
|
||||
Move: true,
|
||||
CreateContainer: true,
|
||||
Delete: true,
|
||||
PurgeRecycle: true,
|
||||
|
||||
// these permissions only make sense to enforce them in the root of the storage space.
|
||||
AddGrant: true, // managers can add users to the space
|
||||
RemoveGrant: true, // managers can remove users from the space
|
||||
UpdateGrant: true,
|
||||
DenyGrant: true, // managers can deny access to sub folders
|
||||
},
|
||||
ocsPermissions: PermissionAll,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSecureViewerRole creates a secure viewer role
|
||||
func NewSecureViewerRole() *Role {
|
||||
return &Role{
|
||||
Name: RoleSecureViewer,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{
|
||||
GetPath: true,
|
||||
ListContainer: true,
|
||||
Stat: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RoleFromOCSPermissions tries to map ocs permissions to a role
|
||||
// TODO: rethink using this. ocs permissions cannot be assigned 1:1 to roles
|
||||
func RoleFromOCSPermissions(p Permissions, ri *provider.ResourceInfo) *Role {
|
||||
switch {
|
||||
// Invalid
|
||||
case p == PermissionInvalid:
|
||||
return NewNoneRole()
|
||||
// Uploader
|
||||
case p == PermissionCreate:
|
||||
return NewUploaderRole()
|
||||
// Viewer/SpaceViewer
|
||||
case p == PermissionRead:
|
||||
if isSpaceRoot(ri) {
|
||||
return NewSpaceViewerRole()
|
||||
}
|
||||
return NewViewerRole()
|
||||
// Editor/SpaceEditor
|
||||
case p.Contain(PermissionRead) && p.Contain(PermissionWrite) && p.Contain(PermissionCreate) && p.Contain(PermissionDelete) && !p.Contain(PermissionShare):
|
||||
if isSpaceRoot(ri) {
|
||||
return NewSpaceEditorRole()
|
||||
}
|
||||
|
||||
return NewEditorRole()
|
||||
// Custom
|
||||
default:
|
||||
return NewLegacyRoleFromOCSPermissions(p)
|
||||
}
|
||||
}
|
||||
|
||||
func isSpaceRoot(ri *provider.ResourceInfo) bool {
|
||||
if ri == nil {
|
||||
return false
|
||||
}
|
||||
if ri.Type != provider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.GetId().GetOpaqueId() != ri.GetSpace().GetRoot().GetOpaqueId() ||
|
||||
ri.GetId().GetSpaceId() != ri.GetSpace().GetRoot().GetSpaceId() ||
|
||||
ri.GetId().GetStorageId() != ri.GetSpace().GetRoot().GetStorageId() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NewLegacyRoleFromOCSPermissions tries to map a legacy combination of ocs permissions to cs3 resource permissions as a legacy role
|
||||
func NewLegacyRoleFromOCSPermissions(p Permissions) *Role {
|
||||
r := &Role{
|
||||
Name: RoleLegacy, // TODO custom role?
|
||||
ocsPermissions: p,
|
||||
cS3ResourcePermissions: &provider.ResourcePermissions{},
|
||||
}
|
||||
if p.Contain(PermissionRead) {
|
||||
r.cS3ResourcePermissions.ListContainer = true
|
||||
// r.cS3ResourcePermissions.ListGrants = true
|
||||
r.cS3ResourcePermissions.ListRecycle = true
|
||||
r.cS3ResourcePermissions.Stat = true
|
||||
r.cS3ResourcePermissions.GetPath = true
|
||||
r.cS3ResourcePermissions.GetQuota = true
|
||||
r.cS3ResourcePermissions.InitiateFileDownload = true
|
||||
}
|
||||
if p.Contain(PermissionWrite) {
|
||||
r.cS3ResourcePermissions.InitiateFileUpload = true
|
||||
r.cS3ResourcePermissions.RestoreRecycleItem = true
|
||||
}
|
||||
if p.Contain(PermissionCreate) {
|
||||
r.cS3ResourcePermissions.Stat = true
|
||||
r.cS3ResourcePermissions.CreateContainer = true
|
||||
// FIXME permissions mismatch: double check ocs create vs update file
|
||||
// - if the file exists the ocs api needs to check update permission,
|
||||
// - if the file does not exist the ocs api needs to check update permission
|
||||
r.cS3ResourcePermissions.InitiateFileUpload = true
|
||||
if p.Contain(PermissionWrite) {
|
||||
r.cS3ResourcePermissions.Move = true // TODO move only when create and write?
|
||||
}
|
||||
}
|
||||
if p.Contain(PermissionDelete) {
|
||||
r.cS3ResourcePermissions.Delete = true
|
||||
}
|
||||
if p.Contain(PermissionShare) {
|
||||
r.cS3ResourcePermissions.AddGrant = true
|
||||
// r.cS3ResourcePermissions.RemoveGrant = true // TODO when are you able to unshare / delete
|
||||
// r.cS3ResourcePermissions.UpdateGrant = true
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// RoleFromResourcePermissions tries to map cs3 resource permissions to a role
|
||||
// It needs to know whether this is a link or not, because empty permissions on links mean "INTERNAL LINK"
|
||||
// while empty permissions on other resources mean "DENIAL". Obviously this is not optimal.
|
||||
func RoleFromResourcePermissions(rp *provider.ResourcePermissions, islink bool) *Role {
|
||||
r := &Role{
|
||||
Name: RoleUnknown,
|
||||
ocsPermissions: PermissionInvalid,
|
||||
cS3ResourcePermissions: rp,
|
||||
}
|
||||
if rp == nil {
|
||||
return r
|
||||
}
|
||||
if grants.PermissionsEqual(rp, &provider.ResourcePermissions{}) {
|
||||
if !islink {
|
||||
r.ocsPermissions = PermissionsNone
|
||||
r.Name = RoleDenied
|
||||
}
|
||||
return r
|
||||
}
|
||||
if rp.ListContainer &&
|
||||
rp.ListRecycle &&
|
||||
rp.Stat &&
|
||||
rp.GetPath &&
|
||||
rp.GetQuota &&
|
||||
rp.InitiateFileDownload {
|
||||
r.ocsPermissions |= PermissionRead
|
||||
}
|
||||
if rp.InitiateFileUpload &&
|
||||
rp.RestoreRecycleItem {
|
||||
r.ocsPermissions |= PermissionWrite
|
||||
}
|
||||
if rp.Stat &&
|
||||
rp.CreateContainer &&
|
||||
rp.InitiateFileUpload {
|
||||
r.ocsPermissions |= PermissionCreate
|
||||
}
|
||||
if rp.Delete {
|
||||
r.ocsPermissions |= PermissionDelete
|
||||
}
|
||||
if rp.AddGrant {
|
||||
r.ocsPermissions |= PermissionShare
|
||||
}
|
||||
|
||||
if r.ocsPermissions.Contain(PermissionRead) {
|
||||
if r.ocsPermissions.Contain(PermissionWrite) && r.ocsPermissions.Contain(PermissionCreate) && r.ocsPermissions.Contain(PermissionDelete) && r.ocsPermissions.Contain(PermissionShare) {
|
||||
r.Name = RoleEditor
|
||||
if rp.ListGrants {
|
||||
r.Name = RoleEditorListGrants
|
||||
}
|
||||
if rp.RemoveGrant {
|
||||
r.Name = RoleManager
|
||||
}
|
||||
return r // editor or manager
|
||||
}
|
||||
if r.ocsPermissions == PermissionRead|PermissionShare {
|
||||
r.Name = RoleViewer
|
||||
if rp.ListGrants {
|
||||
r.Name = RoleViewerListGrants
|
||||
}
|
||||
return r
|
||||
}
|
||||
} else if rp.Stat && rp.GetPath && rp.ListContainer && !rp.InitiateFileUpload && !rp.Delete && !rp.AddGrant {
|
||||
r.Name = RoleSecureViewer
|
||||
return r
|
||||
}
|
||||
if r.ocsPermissions == PermissionCreate {
|
||||
if rp.GetPath && rp.InitiateFileDownload && rp.ListContainer && rp.Move {
|
||||
r.Name = RoleEditorLite
|
||||
return r
|
||||
}
|
||||
r.Name = RoleUploader
|
||||
return r
|
||||
}
|
||||
r.Name = RoleLegacy
|
||||
// at this point other ocs permissions may have been mapped.
|
||||
// TODO what about even more granular cs3 permissions?, eg. only stat
|
||||
return r
|
||||
}
|
||||
|
||||
// SufficientCS3Permissions returns true if the `existing` permissions contain the `requested` permissions
|
||||
func SufficientCS3Permissions(existing, requested *provider.ResourcePermissions) bool {
|
||||
if existing == nil || requested == nil {
|
||||
return false
|
||||
}
|
||||
// empty permissions represent a denial
|
||||
if grants.PermissionsEqual(requested, &provider.ResourcePermissions{}) {
|
||||
return existing.DenyGrant
|
||||
}
|
||||
|
||||
numFields := existing.ProtoReflect().Descriptor().Fields().Len()
|
||||
for i := 0; i < numFields; i++ {
|
||||
field := existing.ProtoReflect().Descriptor().Fields().Get(i)
|
||||
existingPermission := existing.ProtoReflect().Get(field).Bool()
|
||||
requestedPermission := requested.ProtoReflect().Get(field).Bool()
|
||||
// every requested permission needs to exist for the creator
|
||||
if requestedPermission && !existingPermission {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user