Repository URL to install this package:
Version:
0.4.0a4 ▾
|
django-iam
/
mixins.py
|
---|
from typing import Any
import rules
from django.db import models
from .registry import get_registered_roles
class RolePredicateMixin(models.Model):
"""
Mixin used on profile classes. Provides class method `get_predicate` to create rules predicates to check if a user
has a profile or not.
Provides `parent` attribute. If a role has a parent, and a user has the parent profile, the user will have all
permissions associated with the child role too.
"""
parent: 'RolePredicateMixin' = None
class Meta:
abstract = True
@classmethod
def get_predicate(cls, extra_check=None):
"""
Return a rules predicate that checks if a user has a corresponding profile thus having that role.
:param extra_check: Optionally check extra parameters on the user's profile instance.
:return: Predicate
"""
def has_role(user):
if user.is_superuser:
return True
profile_instance = user.get_or_set_role(cls._meta.model)
return bool(profile_instance) and (not extra_check or extra_check(profile_instance))
role_name = cls._meta.verbose_name.rstrip(' profile').replace(' ', '_')
predicate = rules.predicate(has_role, name=f'is_{role_name}')
if cls.parent:
return predicate | cls.parent.get_predicate(extra_check)
return predicate
class IAMUserMixin:
"""
Mixin used on custom user models that use IAM for their permission management. Provides interfaces to the user model
to interact with the IAM, and cache permissions.
"""
def __init__(self, *args, **kwargs):
self._roles = {} # { ProfileModel: instance | False }
super(IAMUserMixin, self).__init__(*args, **kwargs)
@property
def roles(self):
return self._roles
def _set_role(self, model_cls) -> Any | bool:
"""
Check if this user has a particular profile denoted by `model_cls`. If they do cache the profile instance. If
not cache a False value, so we don't need to hit the database again for this check.
:param model_cls: The model class of the profile to be checked.
:return: The profile instance belonging to the user or False
"""
try:
profile_instance = model_cls.objects.available().get(user=self)
except model_cls.DoesNotExist:
profile_instance = False
self._roles[model_cls] = profile_instance
return profile_instance
def get_or_set_role(self, model_cls):
"""
Check if this user has a particular profile denoted by `model_cls`. First hit the cache. If there is no value
in the cache, hit the database. If the user has the profile cache the profile instance. If
not cache a False value, so we don't need to hit the database again for this check.
:param model_cls: The model class of the profile to be checked.
:return: The profile instance belonging to the user or False
"""
profile_instance = self.roles.get(model_cls) # None | False | instance
if profile_instance is None:
profile_instance = self._set_role(model_cls)
return profile_instance
def load_roles(self):
"""
Load all the roles (and profiles) this user has in their cache.
"""
self._roles = {}
for model_cls in get_registered_roles():
self._set_role(model_cls)