github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/node/permissions.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package node 20 21 import ( 22 "context" 23 "strings" 24 25 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 26 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 27 "github.com/cs3org/reva/v2/pkg/appctx" 28 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 29 "github.com/cs3org/reva/v2/pkg/errtypes" 30 "github.com/cs3org/reva/v2/pkg/utils" 31 "github.com/pkg/errors" 32 ) 33 34 // PermissionFunc should return true when the user has permission to access the node 35 type PermissionFunc func(*Node) bool 36 37 var ( 38 // NoCheck doesn't check permissions, returns true always 39 NoCheck PermissionFunc = func(_ *Node) bool { 40 return true 41 } 42 ) 43 44 // NoPermissions represents an empty set of permissions 45 func NoPermissions() *provider.ResourcePermissions { 46 return &provider.ResourcePermissions{} 47 } 48 49 // ShareFolderPermissions defines permissions for the shared jail 50 func ShareFolderPermissions() *provider.ResourcePermissions { 51 return &provider.ResourcePermissions{ 52 // read permissions 53 ListContainer: true, 54 Stat: true, 55 InitiateFileDownload: true, 56 GetPath: true, 57 GetQuota: true, 58 ListFileVersions: true, 59 } 60 } 61 62 // OwnerPermissions defines permissions for nodes owned by the user 63 func OwnerPermissions() *provider.ResourcePermissions { 64 return &provider.ResourcePermissions{ 65 // all permissions 66 AddGrant: true, 67 CreateContainer: true, 68 Delete: true, 69 GetPath: true, 70 GetQuota: true, 71 InitiateFileDownload: true, 72 InitiateFileUpload: true, 73 ListContainer: true, 74 ListFileVersions: true, 75 ListGrants: true, 76 ListRecycle: true, 77 Move: true, 78 PurgeRecycle: true, 79 RemoveGrant: true, 80 RestoreFileVersion: true, 81 RestoreRecycleItem: true, 82 Stat: true, 83 UpdateGrant: true, 84 DenyGrant: true, 85 } 86 } 87 88 // ServiceAccountPermissions defines the permissions for nodes when requested by a service account 89 func ServiceAccountPermissions() *provider.ResourcePermissions { 90 // TODO: Different permissions for different service accounts 91 return &provider.ResourcePermissions{ 92 Stat: true, 93 ListContainer: true, 94 GetPath: true, // for search index 95 InitiateFileUpload: true, // for personal data export 96 InitiateFileDownload: true, // for full-text-search 97 RemoveGrant: true, // for share expiry 98 ListRecycle: true, // for purge-trash-bin command 99 PurgeRecycle: true, // for purge-trash-bin command 100 RestoreRecycleItem: true, // for cli restore command 101 Delete: true, // for cli restore command with replace option 102 CreateContainer: true, // for space provisioning 103 AddGrant: true, // for initial project space member assignment 104 } 105 } 106 107 // Permissions implements permission checks 108 type Permissions struct { 109 lu PathLookup 110 } 111 112 // NewPermissions returns a new Permissions instance 113 func NewPermissions(lu PathLookup) *Permissions { 114 return &Permissions{ 115 lu: lu, 116 } 117 } 118 119 // AssemblePermissions will assemble the permissions for the current user on the given node, taking into account all parent nodes 120 func (p *Permissions) AssemblePermissions(ctx context.Context, n *Node) (ap *provider.ResourcePermissions, err error) { 121 return p.assemblePermissions(ctx, n, true) 122 } 123 124 // AssembleTrashPermissions will assemble the permissions for the current user on the given node, taking into account all parent nodes 125 func (p *Permissions) AssembleTrashPermissions(ctx context.Context, n *Node) (ap *provider.ResourcePermissions, err error) { 126 return p.assemblePermissions(ctx, n, false) 127 } 128 129 // assemblePermissions will assemble the permissions for the current user on the given node, taking into account all parent nodes 130 func (p *Permissions) assemblePermissions(ctx context.Context, n *Node, failOnTrashedSubtree bool) (ap *provider.ResourcePermissions, err error) { 131 u, ok := ctxpkg.ContextGetUser(ctx) 132 if !ok { 133 return NoPermissions(), nil 134 } 135 136 if u.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { 137 return ServiceAccountPermissions(), nil 138 } 139 140 // are we reading a revision? 141 if strings.Contains(n.ID, RevisionIDDelimiter) { 142 // verify revision key format 143 kp := strings.SplitN(n.ID, RevisionIDDelimiter, 2) 144 if len(kp) != 2 { 145 return NoPermissions(), errtypes.NotFound(n.ID) 146 } 147 // use the actual node for the permission assembly 148 n.ID = kp[0] 149 } 150 151 // determine root 152 rn := n.SpaceRoot 153 cn := n 154 ap = &provider.ResourcePermissions{} 155 156 // for an efficient group lookup convert the list of groups to a map 157 // groups are just strings ... groupnames ... or group ids ??? AAARGH !!! 158 groupsMap := make(map[string]bool, len(u.Groups)) 159 for i := range u.Groups { 160 groupsMap[u.Groups[i]] = true 161 } 162 163 // for all segments, starting at the leaf 164 for cn.ID != rn.ID { 165 if np, accessDenied, err := cn.ReadUserPermissions(ctx, u); err == nil { 166 // check if we have a denial on this node 167 if accessDenied { 168 return np, nil 169 } 170 AddPermissions(ap, np) 171 } else { 172 appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", cn.SpaceID).Str("nodeid", cn.ID).Msg("error reading permissions") 173 // continue with next segment 174 } 175 176 if cn, err = cn.Parent(ctx); err != nil { 177 // We get an error but get a parent, but can not read it from disk (eg. it has been deleted already) 178 if cn != nil { 179 return ap, errors.Wrap(err, "Decomposedfs: error getting parent for node "+cn.ID) 180 } 181 // We do not have a parent, so we assume the next valid parent is the spaceRoot (which must always exist) 182 cn = n.SpaceRoot 183 } 184 if failOnTrashedSubtree && !cn.Exists { 185 return NoPermissions(), errtypes.NotFound(n.ID) 186 } 187 188 } 189 190 // for the root node 191 if np, accessDenied, err := cn.ReadUserPermissions(ctx, u); err == nil { 192 // check if we have a denial on this node 193 if accessDenied { 194 return np, nil 195 } 196 AddPermissions(ap, np) 197 } else { 198 appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", cn.SpaceID).Str("nodeid", cn.ID).Msg("error reading root node permissions") 199 } 200 201 // check if the current user is the owner 202 if utils.UserIDEqual(u.Id, n.Owner()) { 203 return OwnerPermissions(), nil 204 } 205 206 appctx.GetLogger(ctx).Debug().Interface("permissions", ap).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Interface("user", u).Msg("returning agregated permissions") 207 return ap, nil 208 } 209 210 // AddPermissions merges a set of permissions into another 211 // TODO we should use a bitfield for this ... 212 func AddPermissions(l *provider.ResourcePermissions, r *provider.ResourcePermissions) { 213 l.AddGrant = l.AddGrant || r.AddGrant 214 l.CreateContainer = l.CreateContainer || r.CreateContainer 215 l.Delete = l.Delete || r.Delete 216 l.GetPath = l.GetPath || r.GetPath 217 l.GetQuota = l.GetQuota || r.GetQuota 218 l.InitiateFileDownload = l.InitiateFileDownload || r.InitiateFileDownload 219 l.InitiateFileUpload = l.InitiateFileUpload || r.InitiateFileUpload 220 l.ListContainer = l.ListContainer || r.ListContainer 221 l.ListFileVersions = l.ListFileVersions || r.ListFileVersions 222 l.ListGrants = l.ListGrants || r.ListGrants 223 l.ListRecycle = l.ListRecycle || r.ListRecycle 224 l.Move = l.Move || r.Move 225 l.PurgeRecycle = l.PurgeRecycle || r.PurgeRecycle 226 l.RemoveGrant = l.RemoveGrant || r.RemoveGrant 227 l.RestoreFileVersion = l.RestoreFileVersion || r.RestoreFileVersion 228 l.RestoreRecycleItem = l.RestoreRecycleItem || r.RestoreRecycleItem 229 l.Stat = l.Stat || r.Stat 230 l.UpdateGrant = l.UpdateGrant || r.UpdateGrant 231 l.DenyGrant = l.DenyGrant || r.DenyGrant 232 }