Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/permissions.py: 50%
117 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
2Provides a set of pluggable permission policies.
3"""
4from django.http import Http404
6from rest_framework import exceptions
8SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
11class OperationHolderMixin:
12 def __and__(self, other):
13 return OperandHolder(AND, self, other)
15 def __or__(self, other):
16 return OperandHolder(OR, self, other)
18 def __rand__(self, other):
19 return OperandHolder(AND, other, self)
21 def __ror__(self, other):
22 return OperandHolder(OR, other, self)
24 def __invert__(self):
25 return SingleOperandHolder(NOT, self)
28class SingleOperandHolder(OperationHolderMixin):
29 def __init__(self, operator_class, op1_class):
30 self.operator_class = operator_class
31 self.op1_class = op1_class
33 def __call__(self, *args, **kwargs):
34 op1 = self.op1_class(*args, **kwargs)
35 return self.operator_class(op1)
38class OperandHolder(OperationHolderMixin):
39 def __init__(self, operator_class, op1_class, op2_class):
40 self.operator_class = operator_class
41 self.op1_class = op1_class
42 self.op2_class = op2_class
44 def __call__(self, *args, **kwargs):
45 op1 = self.op1_class(*args, **kwargs)
46 op2 = self.op2_class(*args, **kwargs)
47 return self.operator_class(op1, op2)
50class AND:
51 def __init__(self, op1, op2):
52 self.op1 = op1
53 self.op2 = op2
55 def has_permission(self, request, view):
56 return (
57 self.op1.has_permission(request, view) and
58 self.op2.has_permission(request, view)
59 )
61 def has_object_permission(self, request, view, obj):
62 return (
63 self.op1.has_object_permission(request, view, obj) and
64 self.op2.has_object_permission(request, view, obj)
65 )
68class OR:
69 def __init__(self, op1, op2):
70 self.op1 = op1
71 self.op2 = op2
73 def has_permission(self, request, view):
74 return (
75 self.op1.has_permission(request, view) or
76 self.op2.has_permission(request, view)
77 )
79 def has_object_permission(self, request, view, obj):
80 return (
81 self.op1.has_object_permission(request, view, obj) or
82 self.op2.has_object_permission(request, view, obj)
83 )
86class NOT:
87 def __init__(self, op1):
88 self.op1 = op1
90 def has_permission(self, request, view):
91 return not self.op1.has_permission(request, view)
93 def has_object_permission(self, request, view, obj):
94 return not self.op1.has_object_permission(request, view, obj)
97class BasePermissionMetaclass(OperationHolderMixin, type):
98 pass
101class BasePermission(metaclass=BasePermissionMetaclass):
102 """
103 A base class from which all permission classes should inherit.
104 """
106 def has_permission(self, request, view):
107 """
108 Return `True` if permission is granted, `False` otherwise.
109 """
110 return True
112 def has_object_permission(self, request, view, obj):
113 """
114 Return `True` if permission is granted, `False` otherwise.
115 """
116 return True
119class AllowAny(BasePermission):
120 """
121 Allow any access.
122 This isn't strictly required, since you could use an empty
123 permission_classes list, but it's useful because it makes the intention
124 more explicit.
125 """
127 def has_permission(self, request, view):
128 return True
131class IsAuthenticated(BasePermission):
132 """
133 Allows access only to authenticated users.
134 """
136 def has_permission(self, request, view):
137 return bool(request.user and request.user.is_authenticated)
140class IsAdminUser(BasePermission):
141 """
142 Allows access only to admin users.
143 """
145 def has_permission(self, request, view):
146 return bool(request.user and request.user.is_staff)
149class IsAuthenticatedOrReadOnly(BasePermission):
150 """
151 The request is authenticated as a user, or is a read-only request.
152 """
154 def has_permission(self, request, view):
155 return bool(
156 request.method in SAFE_METHODS or
157 request.user and
158 request.user.is_authenticated
159 )
162class DjangoModelPermissions(BasePermission):
163 """
164 The request is authenticated using `django.contrib.auth` permissions.
165 See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
167 It ensures that the user is authenticated, and has the appropriate
168 `add`/`change`/`delete` permissions on the model.
170 This permission can only be applied against view classes that
171 provide a `.queryset` attribute.
172 """
174 # Map methods into required permission codes.
175 # Override this if you need to also provide 'view' permissions,
176 # or if you want to provide custom permission codes.
177 perms_map = {
178 'GET': [],
179 'OPTIONS': [],
180 'HEAD': [],
181 'POST': ['%(app_label)s.add_%(model_name)s'],
182 'PUT': ['%(app_label)s.change_%(model_name)s'],
183 'PATCH': ['%(app_label)s.change_%(model_name)s'],
184 'DELETE': ['%(app_label)s.delete_%(model_name)s'],
185 }
187 authenticated_users_only = True
189 def get_required_permissions(self, method, model_cls):
190 """
191 Given a model and an HTTP method, return the list of permission
192 codes that the user is required to have.
193 """
194 kwargs = {
195 'app_label': model_cls._meta.app_label,
196 'model_name': model_cls._meta.model_name
197 }
199 if method not in self.perms_map:
200 raise exceptions.MethodNotAllowed(method)
202 return [perm % kwargs for perm in self.perms_map[method]]
204 def _queryset(self, view):
205 assert hasattr(view, 'get_queryset') \
206 or getattr(view, 'queryset', None) is not None, (
207 'Cannot apply {} on a view that does not set '
208 '`.queryset` or have a `.get_queryset()` method.'
209 ).format(self.__class__.__name__)
211 if hasattr(view, 'get_queryset'):
212 queryset = view.get_queryset()
213 assert queryset is not None, (
214 '{}.get_queryset() returned None'.format(view.__class__.__name__)
215 )
216 return queryset
217 return view.queryset
219 def has_permission(self, request, view):
220 # Workaround to ensure DjangoModelPermissions are not applied
221 # to the root view when using DefaultRouter.
222 if getattr(view, '_ignore_model_permissions', False):
223 return True
225 if not request.user or (
226 not request.user.is_authenticated and self.authenticated_users_only):
227 return False
229 queryset = self._queryset(view)
230 perms = self.get_required_permissions(request.method, queryset.model)
232 return request.user.has_perms(perms)
235class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
236 """
237 Similar to DjangoModelPermissions, except that anonymous users are
238 allowed read-only access.
239 """
240 authenticated_users_only = False
243class DjangoObjectPermissions(DjangoModelPermissions):
244 """
245 The request is authenticated using Django's object-level permissions.
246 It requires an object-permissions-enabled backend, such as Django Guardian.
248 It ensures that the user is authenticated, and has the appropriate
249 `add`/`change`/`delete` permissions on the object using .has_perms.
251 This permission can only be applied against view classes that
252 provide a `.queryset` attribute.
253 """
254 perms_map = {
255 'GET': [],
256 'OPTIONS': [],
257 'HEAD': [],
258 'POST': ['%(app_label)s.add_%(model_name)s'],
259 'PUT': ['%(app_label)s.change_%(model_name)s'],
260 'PATCH': ['%(app_label)s.change_%(model_name)s'],
261 'DELETE': ['%(app_label)s.delete_%(model_name)s'],
262 }
264 def get_required_object_permissions(self, method, model_cls):
265 kwargs = {
266 'app_label': model_cls._meta.app_label,
267 'model_name': model_cls._meta.model_name
268 }
270 if method not in self.perms_map:
271 raise exceptions.MethodNotAllowed(method)
273 return [perm % kwargs for perm in self.perms_map[method]]
275 def has_object_permission(self, request, view, obj):
276 # authentication checks have already executed via has_permission
277 queryset = self._queryset(view)
278 model_cls = queryset.model
279 user = request.user
281 perms = self.get_required_object_permissions(request.method, model_cls)
283 if not user.has_perms(perms, obj):
284 # If the user does not have permissions we need to determine if
285 # they have read permissions to see 403, or not, and simply see
286 # a 404 response.
288 if request.method in SAFE_METHODS:
289 # Read permissions already checked and failed, no need
290 # to make another lookup.
291 raise Http404
293 read_perms = self.get_required_object_permissions('GET', model_cls)
294 if not user.has_perms(read_perms, obj):
295 raise Http404
297 # Has read permissions.
298 return False
300 return True