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

1""" 

2Provides a set of pluggable permission policies. 

3""" 

4from django.http import Http404 

5 

6from rest_framework import exceptions 

7 

8SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') 

9 

10 

11class OperationHolderMixin: 

12 def __and__(self, other): 

13 return OperandHolder(AND, self, other) 

14 

15 def __or__(self, other): 

16 return OperandHolder(OR, self, other) 

17 

18 def __rand__(self, other): 

19 return OperandHolder(AND, other, self) 

20 

21 def __ror__(self, other): 

22 return OperandHolder(OR, other, self) 

23 

24 def __invert__(self): 

25 return SingleOperandHolder(NOT, self) 

26 

27 

28class SingleOperandHolder(OperationHolderMixin): 

29 def __init__(self, operator_class, op1_class): 

30 self.operator_class = operator_class 

31 self.op1_class = op1_class 

32 

33 def __call__(self, *args, **kwargs): 

34 op1 = self.op1_class(*args, **kwargs) 

35 return self.operator_class(op1) 

36 

37 

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 

43 

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) 

48 

49 

50class AND: 

51 def __init__(self, op1, op2): 

52 self.op1 = op1 

53 self.op2 = op2 

54 

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 ) 

60 

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 ) 

66 

67 

68class OR: 

69 def __init__(self, op1, op2): 

70 self.op1 = op1 

71 self.op2 = op2 

72 

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 ) 

78 

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 ) 

84 

85 

86class NOT: 

87 def __init__(self, op1): 

88 self.op1 = op1 

89 

90 def has_permission(self, request, view): 

91 return not self.op1.has_permission(request, view) 

92 

93 def has_object_permission(self, request, view, obj): 

94 return not self.op1.has_object_permission(request, view, obj) 

95 

96 

97class BasePermissionMetaclass(OperationHolderMixin, type): 

98 pass 

99 

100 

101class BasePermission(metaclass=BasePermissionMetaclass): 

102 """ 

103 A base class from which all permission classes should inherit. 

104 """ 

105 

106 def has_permission(self, request, view): 

107 """ 

108 Return `True` if permission is granted, `False` otherwise. 

109 """ 

110 return True 

111 

112 def has_object_permission(self, request, view, obj): 

113 """ 

114 Return `True` if permission is granted, `False` otherwise. 

115 """ 

116 return True 

117 

118 

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 """ 

126 

127 def has_permission(self, request, view): 

128 return True 

129 

130 

131class IsAuthenticated(BasePermission): 

132 """ 

133 Allows access only to authenticated users. 

134 """ 

135 

136 def has_permission(self, request, view): 

137 return bool(request.user and request.user.is_authenticated) 

138 

139 

140class IsAdminUser(BasePermission): 

141 """ 

142 Allows access only to admin users. 

143 """ 

144 

145 def has_permission(self, request, view): 

146 return bool(request.user and request.user.is_staff) 

147 

148 

149class IsAuthenticatedOrReadOnly(BasePermission): 

150 """ 

151 The request is authenticated as a user, or is a read-only request. 

152 """ 

153 

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 ) 

160 

161 

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 

166 

167 It ensures that the user is authenticated, and has the appropriate 

168 `add`/`change`/`delete` permissions on the model. 

169 

170 This permission can only be applied against view classes that 

171 provide a `.queryset` attribute. 

172 """ 

173 

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 } 

186 

187 authenticated_users_only = True 

188 

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 } 

198 

199 if method not in self.perms_map: 

200 raise exceptions.MethodNotAllowed(method) 

201 

202 return [perm % kwargs for perm in self.perms_map[method]] 

203 

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__) 

210 

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 

218 

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 

224 

225 if not request.user or ( 

226 not request.user.is_authenticated and self.authenticated_users_only): 

227 return False 

228 

229 queryset = self._queryset(view) 

230 perms = self.get_required_permissions(request.method, queryset.model) 

231 

232 return request.user.has_perms(perms) 

233 

234 

235class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions): 

236 """ 

237 Similar to DjangoModelPermissions, except that anonymous users are 

238 allowed read-only access. 

239 """ 

240 authenticated_users_only = False 

241 

242 

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. 

247 

248 It ensures that the user is authenticated, and has the appropriate 

249 `add`/`change`/`delete` permissions on the object using .has_perms. 

250 

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 } 

263 

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 } 

269 

270 if method not in self.perms_map: 

271 raise exceptions.MethodNotAllowed(method) 

272 

273 return [perm % kwargs for perm in self.perms_map[method]] 

274 

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 

280 

281 perms = self.get_required_object_permissions(request.method, model_cls) 

282 

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. 

287 

288 if request.method in SAFE_METHODS: 

289 # Read permissions already checked and failed, no need 

290 # to make another lookup. 

291 raise Http404 

292 

293 read_perms = self.get_required_object_permissions('GET', model_cls) 

294 if not user.has_perms(read_perms, obj): 

295 raise Http404 

296 

297 # Has read permissions. 

298 return False 

299 

300 return True