policies/dummy: separate dummy policy from core into app

This commit is contained in:
Jens Langhammer 2020-05-10 02:14:55 +02:00
parent c0b05a62f4
commit 9bccf9bb0a
16 changed files with 152 additions and 44 deletions

View File

@ -1,4 +1,5 @@
"""api v2 urls"""
from django.conf import settings
from django.conf.urls import url
from django.urls import path
from drf_yasg import openapi
@ -31,7 +32,6 @@ from passbook.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProvider
from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet
from passbook.sources.oauth.api import OAuthSourceViewSet
from passbook.stages.captcha.api import CaptchaStageViewSet
from passbook.stages.dummy.api import DummyStageViewSet
from passbook.stages.email.api import EmailStageViewSet
from passbook.stages.identification.api import IdentificationStageViewSet
from passbook.stages.login.api import LoginStageViewSet
@ -78,7 +78,6 @@ router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
router.register("stages/all", StageViewSet)
router.register("stages/captcha", CaptchaStageViewSet)
router.register("stages/dummy", DummyStageViewSet)
router.register("stages/email", EmailStageViewSet)
router.register("stages/otp", OTPStageViewSet)
router.register("stages/password", PasswordStageViewSet)
@ -88,6 +87,13 @@ router.register("stages/login", LoginStageViewSet)
router.register("flows", FlowViewSet)
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("policies/dummy", DummyPolicyViewSet)
info = openapi.Info(
title="passbook API",
default_version="v2",

View File

@ -0,0 +1,14 @@
# Generated by Django 3.0.5 on 2020-05-10 00:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0012_delete_factor"),
]
operations = [
migrations.DeleteModel(name="DebugPolicy",),
]

View File

@ -1,7 +1,5 @@
"""passbook core models"""
from datetime import timedelta
from random import SystemRandom
from time import sleep
from typing import Any, Optional
from uuid import uuid4
@ -198,29 +196,6 @@ class Policy(ExportModelOperationsMixin("policy"), UUIDModel, CreatedUpdatedMode
raise PolicyException()
class DebugPolicy(Policy):
"""Policy used for debugging the PolicyEngine. Returns a fixed result,
but takes a random time to process."""
result = models.BooleanField(default=False)
wait_min = models.IntegerField(default=5)
wait_max = models.IntegerField(default=30)
form = "passbook.core.forms.policies.DebugPolicyForm"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Wait random time then return result"""
wait = SystemRandom().randrange(self.wait_min, self.wait_max)
LOGGER.debug("Policy waiting", policy=self, delay=wait)
sleep(wait)
return PolicyResult(self.result, "Debugging")
class Meta:
verbose_name = _("Debug Policy")
verbose_name_plural = _("Debug Policies")
class Invitation(ExportModelOperationsMixin("invitation"), UUIDModel):
"""Single-use invitation link"""

View File

View File

@ -0,0 +1,21 @@
"""Dummy Policy API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.dummy.models import DummyPolicy
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
class DummyPolicySerializer(ModelSerializer):
"""Dummy Policy Serializer"""
class Meta:
model = DummyPolicy
fields = GENERAL_SERIALIZER_FIELDS + ["result", "wait_min", "wait_max"]
class DummyPolicyViewSet(ModelViewSet):
"""Dummy Viewset"""
queryset = DummyPolicy.objects.all()
serializer_class = DummyPolicySerializer

View File

@ -0,0 +1,11 @@
"""Passbook policy dummy app config"""
from django.apps import AppConfig
class PassbookPolicyDummyConfig(AppConfig):
"""Passbook policy_dummy app config"""
name = "passbook.policies.dummy"
label = "passbook_policies_dummy"
verbose_name = "passbook Policies.Dummy"

View File

@ -3,16 +3,16 @@
from django import forms
from django.utils.translation import gettext as _
from passbook.core.models import DebugPolicy
from passbook.policies.dummy.models import DummyPolicy
from passbook.policies.forms import GENERAL_FIELDS
class DebugPolicyForm(forms.ModelForm):
"""DebugPolicyForm Form"""
class DummyPolicyForm(forms.ModelForm):
"""DummyPolicyForm Form"""
class Meta:
model = DebugPolicy
model = DummyPolicy
fields = GENERAL_FIELDS + ["result", "wait_min", "wait_max"]
widgets = {
"name": forms.TextInput(),

View File

@ -0,0 +1,40 @@
# Generated by Django 3.0.5 on 2020-05-10 00:08
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_core", "0013_delete_debugpolicy"),
]
operations = [
migrations.CreateModel(
name="DummyPolicy",
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": "Dummy Policy",
"verbose_name_plural": "Dummy Policies",
},
bases=("passbook_core.policy",),
),
]

View File

@ -0,0 +1,35 @@
"""Dummy policy"""
from random import SystemRandom
from time import sleep
from django.db import models
from django.utils.translation import gettext_lazy as _
from structlog import get_logger
from passbook.core.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger()
class DummyPolicy(Policy):
"""Policy used for debugging the PolicyEngine. Returns a fixed result,
but takes a random time to process."""
result = models.BooleanField(default=False)
wait_min = models.IntegerField(default=5)
wait_max = models.IntegerField(default=30)
form = "passbook.policies.dummy.forms.DummyPolicyForm"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Wait random time then return result"""
wait = SystemRandom().randrange(self.wait_min, self.wait_max)
LOGGER.debug("Policy waiting", policy=self, delay=wait)
sleep(wait)
return PolicyResult(self.result, "dummy")
class Meta:
verbose_name = _("Dummy Policy")
verbose_name_plural = _("Dummy Policies")

View File

@ -1,4 +1,4 @@
"""Source API Views"""
"""Password Expiry Policy API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
@ -15,7 +15,7 @@ class PasswordExpiryPolicySerializer(ModelSerializer):
class PasswordExpiryPolicyViewSet(ModelViewSet):
"""Source Viewset"""
"""Password Expiry Viewset"""
queryset = PasswordExpiryPolicy.objects.all()
serializer_class = PasswordExpiryPolicySerializer

View File

@ -4,8 +4,8 @@ from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
from passbook.core.forms.policies import GENERAL_FIELDS
from passbook.policies.expiry.models import PasswordExpiryPolicy
from passbook.policies.forms import GENERAL_FIELDS
class PasswordExpiryPolicyForm(forms.ModelForm):

View File

@ -4,7 +4,7 @@ from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
from passbook.core.forms.policies import GENERAL_FIELDS
from passbook.policies.forms import GENERAL_FIELDS
from passbook.policies.hibp.models import HaveIBeenPwendPolicy

View File

@ -2,7 +2,7 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from passbook.core.forms.policies import GENERAL_FIELDS
from passbook.policies.forms import GENERAL_FIELDS
from passbook.policies.reputation.models import ReputationPolicy

View File

@ -2,7 +2,8 @@
from django.core.cache import cache
from django.test import TestCase
from passbook.core.models import DebugPolicy, Policy, User
from passbook.core.models import Policy, User
from passbook.policies.dummy.models import DummyPolicy
from passbook.policies.engine import PolicyEngine
@ -12,13 +13,13 @@ class PolicyTestEngine(TestCase):
def setUp(self):
cache.clear()
self.user = User.objects.create_user(username="policyuser")
self.policy_false = DebugPolicy.objects.create(
self.policy_false = DummyPolicy.objects.create(
result=False, wait_min=0, wait_max=1
)
self.policy_true = DebugPolicy.objects.create(
self.policy_true = DummyPolicy.objects.create(
result=True, wait_min=0, wait_max=1
)
self.policy_negate = DebugPolicy.objects.create(
self.policy_negate = DummyPolicy.objects.create(
negate=True, result=True, wait_min=0, wait_max=1
)
self.policy_raises = Policy.objects.create(name="raises")
@ -31,13 +32,13 @@ class PolicyTestEngine(TestCase):
def test_engine(self):
"""Ensure all policies passes (Mix of false and true -> false)"""
engine = PolicyEngine(
DebugPolicy.objects.filter(negate__exact=False), self.user
DummyPolicy.objects.filter(negate__exact=False), self.user
)
self.assertEqual(engine.build().passing, False)
def test_engine_negate(self):
"""Test negate flag"""
engine = PolicyEngine(DebugPolicy.objects.filter(negate__exact=True), self.user)
engine = PolicyEngine(DummyPolicy.objects.filter(negate__exact=True), self.user)
self.assertEqual(engine.build().passing, False)
def test_engine_policy_error(self):
@ -48,7 +49,7 @@ class PolicyTestEngine(TestCase):
def test_engine_cache(self):
"""Ensure empty policy list passes"""
engine = PolicyEngine(
DebugPolicy.objects.filter(negate__exact=False), self.user
DummyPolicy.objects.filter(negate__exact=False), self.user
)
self.assertEqual(len(cache.keys("policy_*")), 0)
self.assertEqual(engine.build().passing, False)

View File

@ -101,7 +101,6 @@ INSTALLED_APPS = [
"passbook.stages.otp.apps.PassbookStageOTPConfig",
"passbook.stages.captcha.apps.PassbookStageCaptchaConfig",
"passbook.stages.password.apps.PassbookStagePasswordConfig",
"passbook.stages.dummy.apps.PassbookStageDummyConfig",
"passbook.stages.email.apps.PassbookStageEmailConfig",
"passbook.policies.expiry.apps.PassbookPolicyExpiryConfig",
"passbook.policies.reputation.apps.PassbookPolicyReputationConfig",
@ -391,4 +390,10 @@ if DEBUG:
INSTALLED_APPS.append("debug_toolbar")
MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")
# Load Dummy/Debug objects
INSTALLED_APPS += [
"passbook.stages.dummy.apps.PassbookStageDummyConfig",
"passbook.policies.dummy.apps.PassbookPolicyDummyConfig",
]
INSTALLED_APPS.append("passbook.core.apps.PassbookCoreConfig")