*: migrate from PolicyModel to PolicyBindingModel, move Policy to passbook_policies

This commit is contained in:
Jens Langhammer 2020-05-16 18:07:00 +02:00
parent 227966e727
commit 7bd65120b9
34 changed files with 187 additions and 161 deletions

View file

@ -5,8 +5,9 @@ from django.views.generic import TemplateView
from passbook import __version__ from passbook import __version__
from passbook.admin.mixins import AdminRequiredMixin from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Application, Policy, Provider, Source, User from passbook.core.models import Application, Provider, Source, User
from passbook.flows.models import Flow, Stage from passbook.flows.models import Flow, Stage
from passbook.policies.models import Policy
from passbook.root.celery import CELERY_APP from passbook.root.celery import CELERY_APP
from passbook.stages.invitation.models import Invitation from passbook.stages.invitation.models import Invitation

View file

@ -13,10 +13,10 @@ from django.views.generic.detail import DetailView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.forms.policies import PolicyTestForm from passbook.admin.forms.policies import PolicyTestForm
from passbook.core.models import Policy
from passbook.lib.utils.reflection import all_subclasses, path_to_class from passbook.lib.utils.reflection import all_subclasses, path_to_class
from passbook.lib.views import CreateAssignPermView from passbook.lib.views import CreateAssignPermView
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
from passbook.policies.models import Policy
class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView): class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):

View file

@ -1,8 +1,8 @@
"""permission classes for django restframework""" """permission classes for django restframework"""
from rest_framework.permissions import BasePermission, DjangoObjectPermissions from rest_framework.permissions import BasePermission, DjangoObjectPermissions
from passbook.core.models import PolicyModel
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
from passbook.policies.models import PolicyBindingModel
class CustomObjectPermissions(DjangoObjectPermissions): class CustomObjectPermissions(DjangoObjectPermissions):
@ -24,8 +24,7 @@ class PolicyPermissions(BasePermission):
policy_engine: PolicyEngine policy_engine: PolicyEngine
def has_object_permission(self, request, view, obj: PolicyModel) -> bool: def has_object_permission(self, request, view, obj: PolicyBindingModel) -> bool:
# if not obj.po
self.policy_engine = PolicyEngine(obj.policies, request.user, request) self.policy_engine = PolicyEngine(obj.policies, request.user, request)
self.policy_engine.request.obj = obj self.policy_engine.request.obj = obj
return self.policy_engine.build().passing return self.policy_engine.build().passing

View file

@ -1,5 +1,4 @@
"""api v2 urls""" """api v2 urls"""
from django.conf import settings
from django.conf.urls import url from django.conf.urls import url
from django.urls import path from django.urls import path
from drf_yasg import openapi from drf_yasg import openapi
@ -19,6 +18,7 @@ from passbook.core.api.users import UserViewSet
from passbook.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet from passbook.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet
from passbook.lib.utils.reflection import get_apps from passbook.lib.utils.reflection import get_apps
from passbook.policies.api import PolicyBindingViewSet from passbook.policies.api import PolicyBindingViewSet
from passbook.policies.dummy.api import DummyPolicyViewSet
from passbook.policies.expiry.api import PasswordExpiryPolicyViewSet from passbook.policies.expiry.api import PasswordExpiryPolicyViewSet
from passbook.policies.expression.api import ExpressionPolicyViewSet from passbook.policies.expression.api import ExpressionPolicyViewSet
from passbook.policies.hibp.api import HaveIBeenPwendPolicyViewSet from passbook.policies.hibp.api import HaveIBeenPwendPolicyViewSet
@ -31,6 +31,7 @@ from passbook.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProvider
from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet
from passbook.sources.oauth.api import OAuthSourceViewSet from passbook.sources.oauth.api import OAuthSourceViewSet
from passbook.stages.captcha.api import CaptchaStageViewSet from passbook.stages.captcha.api import CaptchaStageViewSet
from passbook.stages.dummy.api import DummyStageViewSet
from passbook.stages.email.api import EmailStageViewSet from passbook.stages.email.api import EmailStageViewSet
from passbook.stages.identification.api import IdentificationStageViewSet from passbook.stages.identification.api import IdentificationStageViewSet
from passbook.stages.invitation.api import InvitationStageViewSet, InvitationViewSet from passbook.stages.invitation.api import InvitationStageViewSet, InvitationViewSet
@ -97,10 +98,6 @@ router.register("stages/user_write", UserWriteStageViewSet)
router.register("flows/instances", FlowViewSet) router.register("flows/instances", FlowViewSet)
router.register("flows/bindings", FlowStageBindingViewSet) router.register("flows/bindings", FlowStageBindingViewSet)
if settings.DEBUG:
from passbook.stages.dummy.api import DummyStageViewSet
from passbook.policies.dummy.api import DummyPolicyViewSet
router.register("stages/dummy", DummyStageViewSet) router.register("stages/dummy", DummyStageViewSet)
router.register("policies/dummy", DummyPolicyViewSet) router.register("policies/dummy", DummyPolicyViewSet)

View file

@ -5,7 +5,7 @@ from django.test import TestCase
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from passbook.audit.models import Event, EventAction from passbook.audit.models import Event, EventAction
from passbook.core.models import Policy from passbook.policies.dummy.models import DummyPolicy
class TestAuditEvent(TestCase): class TestAuditEvent(TestCase):
@ -23,7 +23,7 @@ class TestAuditEvent(TestCase):
def test_new_with_uuid_model(self): def test_new_with_uuid_model(self):
"""Create a new Event passing a model (with UUID PK) as kwarg""" """Create a new Event passing a model (with UUID PK) as kwarg"""
temp_model = Policy.objects.create() temp_model = DummyPolicy.objects.create(name="test", result=True)
event = Event.new(EventAction.CUSTOM, model=temp_model) event = Event.new(EventAction.CUSTOM, model=temp_model)
event.save() # We save to ensure nothing is un-saveable event.save() # We save to ensure nothing is un-saveable
model_content_type = ContentType.objects.get_for_model(temp_model) model_content_type = ContentType.objects.get_for_model(temp_model)

View file

@ -2,8 +2,8 @@
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Policy
from passbook.policies.forms import GENERAL_FIELDS from passbook.policies.forms import GENERAL_FIELDS
from passbook.policies.models import Policy
class PolicySerializer(ModelSerializer): class PolicySerializer(ModelSerializer):

View file

@ -19,6 +19,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
("auth", "0011_update_proxy_permissions"), ("auth", "0011_update_proxy_permissions"),
("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -158,7 +159,7 @@ class Migration(migrations.Migration):
), ),
( (
"policies", "policies",
models.ManyToManyField(blank=True, to="passbook_core.Policy"), models.ManyToManyField(blank=True, to="passbook_policies.Policy"),
), ),
], ],
options={"abstract": False,}, options={"abstract": False,},
@ -182,30 +183,6 @@ class Migration(migrations.Migration):
"verbose_name_plural": "Property Mappings", "verbose_name_plural": "Property Mappings",
}, },
), ),
migrations.CreateModel(
name="DebugPolicy",
fields=[
(
"policy_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_core.Policy",
),
),
("result", models.BooleanField(default=False)),
("wait_min", models.IntegerField(default=5)),
("wait_max", models.IntegerField(default=30)),
],
options={
"verbose_name": "Debug Policy",
"verbose_name_plural": "Debug Policies",
},
bases=("passbook_core.policy",),
),
migrations.CreateModel( migrations.CreateModel(
name="Factor", name="Factor",
fields=[ fields=[
@ -217,7 +194,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.PolicyModel", to="passbook_policies.PolicyBindingModel",
), ),
), ),
("name", models.TextField()), ("name", models.TextField()),
@ -226,7 +203,7 @@ class Migration(migrations.Migration):
("enabled", models.BooleanField(default=True)), ("enabled", models.BooleanField(default=True)),
], ],
options={"abstract": False,}, options={"abstract": False,},
bases=("passbook_core.policymodel",), bases=("passbook_policies.policybindingmodel",),
), ),
migrations.CreateModel( migrations.CreateModel(
name="Source", name="Source",
@ -239,7 +216,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.PolicyModel", to="passbook_policies.PolicyBindingModel",
), ),
), ),
("name", models.TextField()), ("name", models.TextField()),
@ -247,7 +224,7 @@ class Migration(migrations.Migration):
("enabled", models.BooleanField(default=True)), ("enabled", models.BooleanField(default=True)),
], ],
options={"abstract": False,}, options={"abstract": False,},
bases=("passbook_core.policymodel",), bases=("passbook_policies.policybindingmodel",),
), ),
migrations.CreateModel( migrations.CreateModel(
name="Provider", name="Provider",
@ -418,7 +395,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.PolicyModel", to="passbook_policies.PolicyBindingModel",
), ),
), ),
("name", models.TextField()), ("name", models.TextField()),
@ -438,7 +415,7 @@ class Migration(migrations.Migration):
), ),
], ],
options={"abstract": False,}, options={"abstract": False,},
bases=("passbook_core.policymodel",), bases=("passbook_policies.policybindingmodel",),
), ),
migrations.AddField( migrations.AddField(
model_name="user", model_name="user",

View file

@ -6,11 +6,7 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("passbook_policies", "0003_auto_20200508_1642"),
("passbook_stages_password", "0001_initial"),
("passbook_core", "0012_delete_factor"), ("passbook_core", "0012_delete_factor"),
] ]
operations = [ operations = []
migrations.DeleteModel(name="DebugPolicy",),
]

View file

@ -0,0 +1,48 @@
# Generated by Django 3.0.5 on 2020-05-16 14:46
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_policies", "__first__"),
("passbook_core", "0015_auto_20200516_1407"),
]
operations = [
migrations.RemoveField(model_name="policymodel", name="policies",),
migrations.RemoveField(model_name="application", name="policymodel_ptr",),
migrations.RemoveField(model_name="source", name="policymodel_ptr",),
migrations.AddField(
model_name="application",
name="policybindingmodel_ptr",
field=models.OneToOneField(
auto_created=True,
default=None,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_policies.PolicyBindingModel",
),
preserve_default=False,
),
migrations.AddField(
model_name="source",
name="policybindingmodel_ptr",
field=models.OneToOneField(
auto_created=True,
default=None,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_policies.PolicyBindingModel",
),
preserve_default=False,
),
migrations.DeleteModel(name="Policy",),
migrations.DeleteModel(name="PolicyModel",),
]

View file

@ -22,8 +22,7 @@ from passbook.core.exceptions import PropertyMappingExpressionException
from passbook.core.signals import password_changed from passbook.core.signals import password_changed
from passbook.core.types import UILoginButton, UIUserSettings from passbook.core.types import UILoginButton, UIUserSettings
from passbook.lib.models import CreatedUpdatedModel, UUIDModel from passbook.lib.models import CreatedUpdatedModel, UUIDModel
from passbook.policies.exceptions import PolicyException from passbook.policies.models import PolicyBindingModel
from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()
NATIVE_ENVIRONMENT = NativeEnvironment() NATIVE_ENVIRONMENT = NativeEnvironment()
@ -94,13 +93,7 @@ class Provider(ExportModelOperationsMixin("provider"), models.Model):
return super().__str__() return super().__str__()
class PolicyModel(UUIDModel, CreatedUpdatedModel): class Application(ExportModelOperationsMixin("application"), PolicyBindingModel):
"""Base model which can have policies applied to it"""
policies = models.ManyToManyField("Policy", blank=True)
class Application(ExportModelOperationsMixin("application"), PolicyModel):
"""Every Application which uses passbook for authentication/identification/authorization """Every Application which uses passbook for authentication/identification/authorization
needs an Application record. Other authentication types can subclass this Model to needs an Application record. Other authentication types can subclass this Model to
add custom fields and other properties""" add custom fields and other properties"""
@ -129,7 +122,7 @@ class Application(ExportModelOperationsMixin("application"), PolicyModel):
return self.name return self.name
class Source(ExportModelOperationsMixin("source"), PolicyModel): class Source(ExportModelOperationsMixin("source"), PolicyBindingModel):
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server""" """Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
name = models.TextField(help_text=_("Source's display Name.")) name = models.TextField(help_text=_("Source's display Name."))
@ -176,25 +169,6 @@ class UserSourceConnection(CreatedUpdatedModel):
unique_together = (("user", "source"),) unique_together = (("user", "source"),)
class Policy(ExportModelOperationsMixin("policy"), UUIDModel, 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."""
name = models.TextField(blank=True, null=True)
negate = models.BooleanField(default=False)
order = models.IntegerField(default=0)
timeout = models.IntegerField(default=30)
objects = InheritanceManager()
def __str__(self):
return f"Policy {self.name}"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if user instance passes this policy"""
raise PolicyException()
class Token(ExportModelOperationsMixin("token"), UUIDModel): class Token(ExportModelOperationsMixin("token"), UUIDModel):
"""One-time link for password resets/sign-up-confirmations""" """One-time link for password resets/sign-up-confirmations"""

View file

@ -17,7 +17,7 @@ password_changed = Signal(providing_args=["user", "password"])
# pylint: disable=unused-argument # pylint: disable=unused-argument
def invalidate_policy_cache(sender, instance, **_): def invalidate_policy_cache(sender, instance, **_):
"""Invalidate Policy cache when policy is updated""" """Invalidate Policy cache when policy is updated"""
from passbook.core.models import Policy from passbook.policies.models import Policy
from passbook.policies.process import cache_key from passbook.policies.process import cache_key
if isinstance(instance, Policy): if isinstance(instance, Policy):

View file

@ -11,7 +11,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0003_auto_20200508_1642"), # ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [

View file

@ -9,8 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0003_auto_20200508_1642"), ("passbook_policies", "0001_initial"),
("passbook_core", "0013_delete_debugpolicy"),
] ]
operations = [ operations = [
@ -25,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
("result", models.BooleanField(default=False)), ("result", models.BooleanField(default=False)),
@ -36,6 +35,6 @@ class Migration(migrations.Migration):
"verbose_name": "Dummy Policy", "verbose_name": "Dummy Policy",
"verbose_name_plural": "Dummy Policies", "verbose_name_plural": "Dummy Policies",
}, },
bases=("passbook_core.policy",), bases=("passbook_policies.policy",),
), ),
] ]

View file

@ -6,7 +6,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Policy from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View file

@ -7,7 +7,8 @@ from django.core.cache import cache
from django.http import HttpRequest from django.http import HttpRequest
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Policy, User from passbook.core.models import User
from passbook.policies.models import Policy
from passbook.policies.process import PolicyProcess, cache_key from passbook.policies.process import PolicyProcess, cache_key
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult

View file

@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_core", "0001_initial"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
("deny_only", models.BooleanField(default=False)), ("deny_only", models.BooleanField(default=False)),
@ -34,6 +34,6 @@ class Migration(migrations.Migration):
"verbose_name": "Password Expiry Policy", "verbose_name": "Password Expiry Policy",
"verbose_name_plural": "Password Expiry Policies", "verbose_name_plural": "Password Expiry Policies",
}, },
bases=("passbook_core.policy",), bases=("passbook_policies.policy",),
), ),
] ]

View file

@ -6,7 +6,7 @@ from django.utils.timezone import now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Policy from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View file

@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_core", "0007_auto_20200217_1934"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
("expression", models.TextField()), ("expression", models.TextField()),
@ -33,6 +33,6 @@ class Migration(migrations.Migration):
"verbose_name": "Expression Policy", "verbose_name": "Expression Policy",
"verbose_name_plural": "Expression Policies", "verbose_name_plural": "Expression Policies",
}, },
bases=("passbook_core.policy",), bases=("passbook_policies.policy",),
), ),
] ]

View file

@ -2,8 +2,8 @@
from django.db import models from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from passbook.core.models import Policy
from passbook.policies.expression.evaluator import Evaluator from passbook.policies.expression.evaluator import Evaluator
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult

View file

@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_core", "0001_initial"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
("allowed_count", models.IntegerField(default=0)), ("allowed_count", models.IntegerField(default=0)),
@ -33,6 +33,6 @@ class Migration(migrations.Migration):
"verbose_name": "Have I Been Pwned Policy", "verbose_name": "Have I Been Pwned Policy",
"verbose_name_plural": "Have I Been Pwned Policies", "verbose_name_plural": "Have I Been Pwned Policies",
}, },
bases=("passbook_core.policy",), bases=("passbook_policies.policy",),
), ),
] ]

View file

@ -6,7 +6,8 @@ from django.utils.translation import gettext as _
from requests import get from requests import get
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Policy, PolicyResult, User from passbook.core.models import User
from passbook.policies.models import Policy, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View file

@ -10,11 +10,28 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [
("passbook_core", "0011_auto_20200222_1822"),
]
operations = [ operations = [
migrations.CreateModel(
name="Policy",
fields=[
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("name", models.TextField(blank=True, null=True)),
("negate", models.BooleanField(default=False)),
("order", models.IntegerField(default=0)),
("timeout", models.IntegerField(default=30)),
],
options={"abstract": False,},
),
migrations.CreateModel( migrations.CreateModel(
name="PolicyBinding", name="PolicyBinding",
fields=[ fields=[
@ -34,7 +51,7 @@ class Migration(migrations.Migration):
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
related_name="+", related_name="+",
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
], ],
@ -60,7 +77,7 @@ class Migration(migrations.Migration):
models.ManyToManyField( models.ManyToManyField(
related_name="_policybindingmodel_policies_+", related_name="_policybindingmodel_policies_+",
through="passbook_policies.PolicyBinding", through="passbook_policies.PolicyBinding",
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
], ],

View file

@ -1,4 +1,4 @@
# Generated by Django 3.0.3 on 2020-05-08 16:42 # Generated by Django 3.0.5 on 2020-05-16 15:16
from django.db import migrations, models from django.db import migrations, models
@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("passbook_core", "0011_auto_20200222_1822"),
("passbook_policies", "0002_auto_20200508_1230"), ("passbook_policies", "0002_auto_20200508_1230"),
] ]
@ -18,7 +17,7 @@ class Migration(migrations.Migration):
blank=True, blank=True,
related_name="_policybindingmodel_policies_+", related_name="_policybindingmodel_policies_+",
through="passbook_policies.PolicyBinding", through="passbook_policies.PolicyBinding",
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
] ]

View file

@ -1,16 +1,18 @@
"""Policy base models""" """Policy base models"""
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from passbook.core.models import Policy from passbook.lib.models import CreatedUpdatedModel, UUIDModel
from passbook.lib.models import UUIDModel from passbook.policies.exceptions import PolicyException
from passbook.policies.types import PolicyRequest, PolicyResult
class PolicyBindingModel(models.Model): class PolicyBindingModel(models.Model):
"""Base Model for objects that have policies applied to them.""" """Base Model for objects that have policies applied to them."""
policies = models.ManyToManyField( policies = models.ManyToManyField(
Policy, through="PolicyBinding", related_name="+", blank=True "Policy", through="PolicyBinding", related_name="+", blank=True
) )
class Meta: class Meta:
@ -24,7 +26,7 @@ class PolicyBinding(UUIDModel):
enabled = models.BooleanField(default=True) enabled = models.BooleanField(default=True)
policy = models.ForeignKey(Policy, on_delete=models.CASCADE, related_name="+") policy = models.ForeignKey("Policy", on_delete=models.CASCADE, related_name="+")
target = models.ForeignKey( target = models.ForeignKey(
PolicyBindingModel, on_delete=models.CASCADE, related_name="+" PolicyBindingModel, on_delete=models.CASCADE, related_name="+"
) )
@ -39,3 +41,22 @@ class PolicyBinding(UUIDModel):
verbose_name = _("Policy Binding") verbose_name = _("Policy Binding")
verbose_name_plural = _("Policy Bindings") verbose_name_plural = _("Policy Bindings")
class Policy(UUIDModel, 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."""
name = models.TextField(blank=True, null=True)
negate = models.BooleanField(default=False)
order = models.IntegerField(default=0)
timeout = models.IntegerField(default=30)
objects = InheritanceManager()
def __str__(self):
return f"Policy {self.name}"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if user instance passes this policy"""
raise PolicyException()

View file

@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_core", "0001_initial"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
("amount_uppercase", models.IntegerField(default=0)), ("amount_uppercase", models.IntegerField(default=0)),
@ -41,6 +41,6 @@ class Migration(migrations.Migration):
"verbose_name": "Password Policy", "verbose_name": "Password Policy",
"verbose_name_plural": "Password Policies", "verbose_name_plural": "Password Policies",
}, },
bases=("passbook_core.policy",), bases=("passbook_policies.policy",),
), ),
] ]

View file

@ -5,7 +5,7 @@ from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Policy from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View file

@ -6,8 +6,9 @@ from typing import Optional
from django.core.cache import cache from django.core.cache import cache
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Policy, User from passbook.core.models import User
from passbook.policies.exceptions import PolicyException from passbook.policies.exceptions import PolicyException
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View file

@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_core", "0001_initial"), ("passbook_policies", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
@ -43,7 +43,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Policy", to="passbook_policies.Policy",
), ),
), ),
("check_ip", models.BooleanField(default=True)), ("check_ip", models.BooleanField(default=True)),
@ -54,7 +54,7 @@ class Migration(migrations.Migration):
"verbose_name": "Reputation Policy", "verbose_name": "Reputation Policy",
"verbose_name_plural": "Reputation Policies", "verbose_name_plural": "Reputation Policies",
}, },
bases=("passbook_core.policy",), bases=("passbook_policies.policy",),
), ),
migrations.CreateModel( migrations.CreateModel(
name="UserReputation", name="UserReputation",

View file

@ -2,8 +2,9 @@
from django.db import models from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from passbook.core.models import Policy, User from passbook.core.models import User
from passbook.lib.utils.http import get_client_ip from passbook.lib.utils.http import get_client_ip
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult

View file

@ -2,9 +2,10 @@
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase from django.test import TestCase
from passbook.core.models import Policy, User from passbook.core.models import User
from passbook.policies.dummy.models import DummyPolicy from passbook.policies.dummy.models import DummyPolicy
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
from passbook.policies.models import Policy
class PolicyTestEngine(TestCase): class PolicyTestEngine(TestCase):

View file

@ -11,6 +11,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
("passbook_core", "0001_initial"), ("passbook_core", "0001_initial"),
("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -87,7 +88,7 @@ class Migration(migrations.Migration):
), ),
( (
"conditions", "conditions",
models.ManyToManyField(blank=True, to="passbook_core.Policy"), models.ManyToManyField(blank=True, to="passbook_policies.Policy"),
), ),
], ],
options={ options={

View file

@ -11,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
("passbook_flows", "0001_initial"), ("passbook_flows", "0001_initial"),
("passbook_core", "0012_delete_factor"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -39,7 +39,7 @@ class Migration(migrations.Migration):
), ),
( (
"password_policies", "password_policies",
models.ManyToManyField(blank=True, to="passbook_core.Policy"), models.ManyToManyField(blank=True, to="passbook_policies.Policy"),
), ),
], ],
options={ options={

View file

@ -12,7 +12,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
("passbook_flows", "0005_auto_20200512_1158"), ("passbook_flows", "0005_auto_20200512_1158"),
("passbook_policies", "0003_auto_20200508_1642"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [

View file

@ -154,7 +154,7 @@ paths:
tags: tags:
- core - core
parameters: [] parameters: []
/core/applications/{uuid}/: /core/applications/{id}/:
get: get:
operationId: core_applications_read operationId: core_applications_read
description: Application Viewset description: Application Viewset
@ -208,12 +208,11 @@ paths:
tags: tags:
- core - core
parameters: parameters:
- name: uuid - name: id
in: path in: path
description: A UUID string identifying this application. description: A unique integer value identifying this application.
required: true required: true
type: string type: integer
format: uuid
/core/groups/: /core/groups/:
get: get:
operationId: core_groups_list operationId: core_groups_list
@ -2658,7 +2657,7 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/all/{uuid}/: /sources/all/{id}/:
get: get:
operationId: sources_all_read operationId: sources_all_read
description: Source Viewset description: Source Viewset
@ -2671,12 +2670,11 @@ paths:
tags: tags:
- sources - sources
parameters: parameters:
- name: uuid - name: id
in: path in: path
description: A UUID string identifying this source. description: A unique integer value identifying this source.
required: true required: true
type: string type: integer
format: uuid
/sources/ldap/: /sources/ldap/:
get: get:
operationId: sources_ldap_list operationId: sources_ldap_list
@ -2744,7 +2742,7 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/ldap/{uuid}/: /sources/ldap/{id}/:
get: get:
operationId: sources_ldap_read operationId: sources_ldap_read
description: LDAP Source Viewset description: LDAP Source Viewset
@ -2798,12 +2796,11 @@ paths:
tags: tags:
- sources - sources
parameters: parameters:
- name: uuid - name: id
in: path in: path
description: A UUID string identifying this LDAP Source. description: A unique integer value identifying this LDAP Source.
required: true required: true
type: string type: integer
format: uuid
/sources/oauth/: /sources/oauth/:
get: get:
operationId: sources_oauth_list operationId: sources_oauth_list
@ -2871,7 +2868,7 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/oauth/{uuid}/: /sources/oauth/{id}/:
get: get:
operationId: sources_oauth_read operationId: sources_oauth_read
description: Source Viewset description: Source Viewset
@ -2925,12 +2922,11 @@ paths:
tags: tags:
- sources - sources
parameters: parameters:
- name: uuid - name: id
in: path in: path
description: A UUID string identifying this Generic OAuth Source. description: A unique integer value identifying this Generic OAuth Source.
required: true required: true
type: string type: integer
format: uuid
/stages/all/: /stages/all/:
get: get:
operationId: stages_all_list operationId: stages_all_list
@ -4837,9 +4833,8 @@ definitions:
type: object type: object
properties: properties:
pk: pk:
title: Uuid title: ID
type: string type: integer
format: uuid
readOnly: true readOnly: true
name: name:
title: Name title: Name
@ -4878,8 +4873,8 @@ definitions:
policies: policies:
type: array type: array
items: items:
type: string type: integer
format: uuid readOnly: true
uniqueItems: true uniqueItems: true
Group: Group:
required: required:
@ -5610,9 +5605,8 @@ definitions:
type: object type: object
properties: properties:
pk: pk:
title: Uuid title: ID
type: string type: integer
format: uuid
readOnly: true readOnly: true
name: name:
title: Name title: Name
@ -5647,9 +5641,8 @@ definitions:
type: object type: object
properties: properties:
pk: pk:
title: Uuid title: ID
type: string type: integer
format: uuid
readOnly: true readOnly: true
name: name:
title: Name title: Name
@ -5743,9 +5736,8 @@ definitions:
type: object type: object
properties: properties:
pk: pk:
title: Uuid title: ID
type: string type: integer
format: uuid
readOnly: true readOnly: true
name: name:
title: Name title: Name