"""Policy base models""" from typing import Type from uuid import uuid4 from django.db import models from django.forms import ModelForm from django.utils.translation import gettext_lazy as _ from model_utils.managers import InheritanceManager from rest_framework.serializers import BaseSerializer from passbook.lib.models import ( CreatedUpdatedModel, InheritanceAutoManager, InheritanceForeignKey, SerializerModel, ) from passbook.policies.exceptions import PolicyException from passbook.policies.types import PolicyRequest, PolicyResult class PolicyBindingModel(models.Model): """Base Model for objects that have policies applied to them.""" pbm_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) policies = models.ManyToManyField( "Policy", through="PolicyBinding", related_name="bindings", blank=True ) objects = InheritanceManager() class Meta: verbose_name = _("Policy Binding Model") verbose_name_plural = _("Policy Binding Models") class PolicyBinding(SerializerModel): """Relationship between a Policy and a PolicyBindingModel.""" policy_binding_uuid = models.UUIDField( primary_key=True, editable=False, default=uuid4 ) enabled = models.BooleanField(default=True) policy = InheritanceForeignKey("Policy", on_delete=models.CASCADE, related_name="+") target = InheritanceForeignKey( PolicyBindingModel, on_delete=models.CASCADE, related_name="+" ) negate = models.BooleanField( default=False, help_text=_("Negates the outcome of the policy. Messages are unaffected."), ) timeout = models.IntegerField( default=30, help_text=_("Timeout after which Policy execution is terminated.") ) order = models.IntegerField() @property def serializer(self) -> BaseSerializer: from passbook.policies.api import PolicyBindingSerializer return PolicyBindingSerializer def __str__(self) -> str: return f"Policy Binding {self.target} #{self.order} {self.policy}" class Meta: verbose_name = _("Policy Binding") verbose_name_plural = _("Policy Bindings") unique_together = ("policy", "target", "order") class Policy(SerializerModel, CreatedUpdatedModel): """Policies which specify if a user is authorized to use an Application. Can be overridden by other types to add other fields, more logic, etc.""" policy_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) name = models.TextField(blank=True, null=True) objects = InheritanceAutoManager() @property def form(self) -> Type[ModelForm]: """Return Form class used to edit this object""" raise NotImplementedError def __str__(self): return f"{self.__class__.__name__} {self.name}" def passes(self, request: PolicyRequest) -> PolicyResult: # pragma: no cover """Check if user instance passes this policy""" raise PolicyException() class Meta: base_manager_name = "objects" verbose_name = _("Policy") verbose_name_plural = _("Policies")