def has_perm(self, user_obj, perm, obj=None): app_label, action, model_name = resolve_permission(perm) # Superusers implicitly have all permissions if user_obj.is_active and user_obj.is_superuser: return True # Permission is exempt from enforcement (i.e. listed in EXEMPT_VIEW_PERMISSIONS) if permission_is_exempt(perm): return True # Handle inactive/anonymous users if not user_obj.is_active or user_obj.is_anonymous: return False # If no applicable ObjectPermissions have been created for this user/permission, deny permission if perm not in self.get_all_permissions(user_obj): return False # If no object has been specified, grant permission. (The presence of a permission in this set tells # us that the user has permission for *some* objects, but not necessarily a specific object.) if obj is None: return True # Sanity check: Ensure that the requested permission applies to the specified object model = obj._meta.model if model._meta.label_lower != '.'.join((app_label, model_name)): raise ValueError(f"Invalid permission {perm} for model {model}") # Compile a query filter that matches all instances of the specified model obj_perm_constraints = self.get_all_permissions(user_obj)[perm] constraints = Q() for perm_constraints in obj_perm_constraints: if perm_constraints: constraints |= Q(**perm_constraints) else: # Found ObjectPermission with null constraints; allow model-level access constraints = Q() break # Permission to perform the requested action on the object depends on whether the specified object matches # the specified constraints. Note that this check is made against the *database* record representing the object, # not the instance itself. return model.objects.filter(constraints, pk=obj.pk).exists()
def restrict(self, user, action='view'): """ Filter the QuerySet to return only objects on which the specified user has been granted the specified permission. :param user: User instance :param action: The action which must be permitted (e.g. "view" for "backup_file.view_site"); default is 'view' """ # Resolve the full name of the required permission app_label = self.model._meta.app_label model_name = self.model._meta.model_name permission_required = f'{app_label}.{action}_{model_name}' # Bypass restriction for superusers and exempt views if user.is_superuser or permission_is_exempt(permission_required): qs = self # User is anonymous or has not been granted the requisite permission elif not user.is_authenticated or permission_required not in user.get_all_permissions( ): qs = self.none() # Filter the queryset to include only objects with allowed attributes else: attrs = Q() for perm_attrs in user._object_perm_cache[permission_required]: if type(perm_attrs) is list: for p in perm_attrs: attrs |= Q(**p) elif perm_attrs: attrs |= Q(**perm_attrs) else: # Any permission with null constraints grants access to _all_ instances attrs = Q() break qs = self.filter(attrs) return qs