forked from CenterForOpenScience/osf.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpermissions.py
More file actions
369 lines (282 loc) · 13.3 KB
/
permissions.py
File metadata and controls
369 lines (282 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
from rest_framework import permissions
from rest_framework import exceptions
from addons.base.models import BaseAddonSettings
from osf.models import (
AbstractNode,
Contributor,
DraftNode,
DraftRegistration,
Institution,
Node,
NodeRelation,
OSFGroup,
OSFUser,
Preprint,
PrivateLink,
)
from osf.utils import permissions as osf_permissions
from api.base.utils import get_user_auth, is_deprecated, assert_resource_type
class ContributorOrPublic(permissions.BasePermission):
acceptable_models = (AbstractNode, NodeRelation, Preprint, DraftRegistration)
def has_object_permission(self, request, view, obj):
from api.nodes.views import NodeStorageProvider
if isinstance(obj, BaseAddonSettings):
obj = obj.owner
if isinstance(obj, NodeStorageProvider):
obj = obj.node
if isinstance(obj, DraftNode):
obj = obj.registered_draft.first()
if isinstance(obj, dict):
obj = obj.get('self', None)
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if request.method in permissions.SAFE_METHODS:
return obj.is_public or obj.can_view(auth)
else:
return obj.can_edit(auth)
class IsPublic(permissions.BasePermission):
acceptable_models = (AbstractNode,)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
return obj.is_public or obj.can_view(auth)
class MustBePublic(permissions.BasePermission):
"""
Only public nodes (even for contributors)
"""
acceptable_models = (AbstractNode,)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
return obj.is_public
class IsAdminContributor(permissions.BasePermission):
"""
Use on API views where the requesting user needs to be an
admin contributor to make changes. Admin group membership
is not sufficient.
"""
acceptable_models = (AbstractNode, DraftRegistration)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if request.method in permissions.SAFE_METHODS:
return obj.has_permission(auth.user, osf_permissions.ADMIN)
else:
return obj.is_admin_contributor(auth.user)
class EditIfPublic(permissions.BasePermission):
acceptable_models = (AbstractNode,)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
if request.method not in permissions.SAFE_METHODS:
return obj.is_public
return True
class IsAdmin(permissions.BasePermission):
acceptable_models = (AbstractNode, PrivateLink)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
if isinstance(obj, PrivateLink):
obj = view.get_node()
auth = get_user_auth(request)
return obj.has_permission(auth.user, osf_permissions.ADMIN)
class AdminDeletePermissions(permissions.BasePermission):
acceptable_models = (AbstractNode, DraftRegistration)
def has_object_permission(self, request, view, obj):
"""
Admin perms are required to delete a node
"""
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if request.method == 'DELETE':
return obj.has_permission(auth.user, osf_permissions.ADMIN)
return True
class IsContributorOrGroupMember(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
assert isinstance(obj, AbstractNode), f'obj must be an Node, got {obj}'
auth = get_user_auth(request)
if request.method in permissions.SAFE_METHODS:
return obj.is_contributor_or_group_member(auth.user)
else:
return obj.has_permission(auth.user, osf_permissions.WRITE)
class AdminOrWriteContributor(permissions.BasePermission):
acceptable_models = (AbstractNode, OSFUser, Institution, BaseAddonSettings, DraftRegistration)
def has_object_permission(self, request, view, obj):
if isinstance(obj, dict) and 'self' in obj:
obj = obj['self']
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if request.method in permissions.SAFE_METHODS:
return obj.is_public or obj.can_view(auth)
return obj.has_permission(auth.user, osf_permissions.ADMIN) or obj.has_permission(auth.user, osf_permissions.WRITE)
class AdminOrPublic(permissions.BasePermission):
acceptable_models = (AbstractNode, OSFUser, Institution, BaseAddonSettings, DraftRegistration)
def has_object_permission(self, request, view, obj):
if isinstance(obj, dict) and 'self' in obj:
obj = obj['self']
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if request.method in permissions.SAFE_METHODS:
return obj.is_public or obj.can_view(auth)
else:
return obj.has_permission(auth.user, osf_permissions.ADMIN)
class AdminContributorOrPublic(permissions.BasePermission):
acceptable_models = (AbstractNode, DraftRegistration)
def has_object_permission(self, request, view, obj):
"""
To make changes, user must be an admin contributor. Admin group membership is not sufficient.
"""
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
if request.method in permissions.SAFE_METHODS:
return obj.is_public or obj.can_view(auth)
else:
return obj.is_admin_contributor(auth.user)
class ExcludeWithdrawals(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if isinstance(obj, Node):
node = obj
else:
context = request.parser_context['kwargs']
node = AbstractNode.load(context[view.node_lookup_url_kwarg])
if node.is_retracted:
return False
return True
class ReadOnlyIfWithdrawn(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if isinstance(obj, Node):
node = obj
else:
context = request.parser_context['kwargs']
node = AbstractNode.load(context[view.node_lookup_url_kwarg])
if node.is_retracted:
return request.method in permissions.SAFE_METHODS
return True
class ContributorDetailPermissions(permissions.BasePermission):
"""Permissions for contributor detail page."""
acceptable_models = (AbstractNode, OSFUser, Contributor)
def load_resource(self, context, view):
return AbstractNode.load(context[view.node_lookup_url_kwarg])
def has_permission(self, request, view):
auth = get_user_auth(request)
context = request.parser_context['kwargs']
resource = self.load_resource(context, view)
if request.method == 'POST' and not resource.has_permission(auth.user, osf_permissions.ADMIN):
return False
return super().has_permission(request, view)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
context = request.parser_context['kwargs']
resource = self.load_resource(context, view)
user = OSFUser.load(context['user_id'])
if request.method in permissions.SAFE_METHODS:
return resource.is_public or resource.can_view(auth)
elif request.method == 'DELETE':
return resource.has_permission(auth.user, osf_permissions.ADMIN) or auth.user == user
else:
return resource.has_permission(auth.user, osf_permissions.ADMIN)
class NodeGroupDetailPermissions(permissions.BasePermission):
"""Permissions for node group detail - involving who can update the relationship
between a node and an OSF Group."""
acceptable_models = (OSFGroup, AbstractNode)
def load_resource(self, context, view):
return AbstractNode.load(context[view.node_lookup_url_kwarg])
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
node = self.load_resource(request.parser_context['kwargs'], view)
if request.method in permissions.SAFE_METHODS:
return node.is_public or node.can_view(auth)
elif request.method == 'DELETE':
# If deleting an OSF group from a node, you either need admin perms
# or you need to be an OSF group manager
return node.has_permission(auth.user, osf_permissions.ADMIN) or obj.has_permission(auth.user, 'manage')
else:
return node.has_permission(auth.user, osf_permissions.ADMIN)
class ContributorOrPublicForPointers(permissions.BasePermission):
acceptable_models = (AbstractNode, NodeRelation)
def has_object_permission(self, request, view, obj):
assert_resource_type(obj, self.acceptable_models)
auth = get_user_auth(request)
parent_node = AbstractNode.load(request.parser_context['kwargs']['node_id'])
pointer_node = NodeRelation.load(request.parser_context['kwargs']['node_link_id']).child
if request.method in permissions.SAFE_METHODS:
has_parent_auth = parent_node.can_view(auth)
has_pointer_auth = pointer_node.can_view(auth)
public = pointer_node.is_public
has_auth = public or (has_parent_auth and has_pointer_auth)
return has_auth
else:
has_auth = parent_node.can_edit(auth)
return has_auth
class ContributorOrPublicForRelationshipPointers(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
assert isinstance(obj, dict)
auth = get_user_auth(request)
parent_node = obj['self']
if request.method in permissions.SAFE_METHODS:
return parent_node.can_view(auth)
elif request.method == 'DELETE':
return parent_node.can_edit(auth)
else:
has_parent_auth = parent_node.can_edit(auth)
if not has_parent_auth:
return False
pointer_nodes = []
for pointer in request.data.get('data', []):
node = AbstractNode.load(pointer['id'])
if not node or node.is_collection:
raise exceptions.NotFound(detail='Node with id "{}" was not found'.format(pointer['id']))
pointer_nodes.append(node)
has_pointer_auth = True
for pointer in pointer_nodes:
if not pointer.can_view(auth):
has_pointer_auth = False
break
return has_pointer_auth
class RegistrationAndPermissionCheckForPointers(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
node_link = NodeRelation.load(request.parser_context['kwargs']['node_link_id'])
node = AbstractNode.load(request.parser_context['kwargs'][view.node_lookup_url_kwarg])
auth = get_user_auth(request)
if request.method == 'DELETE' and node.is_registration:
raise exceptions.MethodNotAllowed(method=request.method)
if node.is_collection or node.is_registration:
raise exceptions.NotFound
if node != node_link.parent:
raise exceptions.NotFound
if request.method == 'DELETE' and not node.can_edit(auth):
return False
return True
class ReadOnlyIfRegistration(permissions.BasePermission):
"""Makes PUT and POST forbidden for registrations."""
acceptable_models = (AbstractNode,)
def has_object_permission(self, request, view, obj):
# Preprints cannot be registrations
if isinstance(obj, Preprint):
return True
if not isinstance(obj, AbstractNode):
obj = AbstractNode.load(request.parser_context['kwargs'][view.node_lookup_url_kwarg])
assert_resource_type(obj, self.acceptable_models)
if obj.is_registration:
return request.method in permissions.SAFE_METHODS
return True
class WriteAdmin(permissions.BasePermission):
acceptable_models = (AbstractNode,)
def has_object_permission(self, request, view, obj):
auth = get_user_auth(request)
return obj.can_edit(auth)
class ShowIfVersion(permissions.BasePermission):
def __init__(self, min_version, max_version, deprecated_message):
super().__init__()
self.min_version = min_version
self.max_version = max_version
self.deprecated_message = deprecated_message
def has_object_permission(self, request, view, obj):
if is_deprecated(request.version, self.min_version, self.max_version):
raise exceptions.NotFound(detail=self.deprecated_message)
return True
class NodeLinksShowIfVersion(ShowIfVersion):
def __init__(self):
min_version = '2.0'
max_version = '2.0'
deprecated_message = 'This feature is deprecated as of version 2.1'
super().__init__(min_version, max_version, deprecated_message)