0e0898c3cf
* stages/*: Add SerializerModel as base model, implement serializer property * flows: add initial flow exporter and importer * policies/*: implement .serializer for all policies * root: fix missing dacite requirement
77 lines
2.5 KiB
Python
77 lines
2.5 KiB
Python
"""user field matcher models"""
|
|
import re
|
|
from typing import Type
|
|
|
|
from django.db import models
|
|
from django.forms import ModelForm
|
|
from django.utils.translation import gettext as _
|
|
from rest_framework.serializers import BaseSerializer
|
|
from structlog import get_logger
|
|
|
|
from passbook.policies.models import Policy
|
|
from passbook.policies.types import PolicyRequest, PolicyResult
|
|
|
|
LOGGER = get_logger()
|
|
|
|
|
|
class PasswordPolicy(Policy):
|
|
"""Policy to make sure passwords have certain properties"""
|
|
|
|
password_field = models.TextField(
|
|
default="password",
|
|
help_text=_(
|
|
"Field key to check, field keys defined in Prompt stages are available."
|
|
),
|
|
)
|
|
|
|
amount_uppercase = models.IntegerField(default=0)
|
|
amount_lowercase = models.IntegerField(default=0)
|
|
amount_symbols = models.IntegerField(default=0)
|
|
length_min = models.IntegerField(default=0)
|
|
symbol_charset = models.TextField(default=r"!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ")
|
|
error_message = models.TextField()
|
|
|
|
@property
|
|
def serializer(self) -> BaseSerializer:
|
|
from passbook.policies.password.api import PasswordPolicySerializer
|
|
|
|
return PasswordPolicySerializer
|
|
|
|
def form(self) -> Type[ModelForm]:
|
|
from passbook.policies.password.forms import PasswordPolicyForm
|
|
|
|
return PasswordPolicyForm
|
|
|
|
def passes(self, request: PolicyRequest) -> PolicyResult:
|
|
if self.password_field not in request.context:
|
|
LOGGER.warning(
|
|
"Password field not set in Policy Request",
|
|
field=self.password_field,
|
|
fields=request.context.keys(),
|
|
)
|
|
password = request.context[self.password_field]
|
|
|
|
filter_regex = []
|
|
if self.amount_lowercase > 0:
|
|
filter_regex.append(r"[a-z]{%d,}" % self.amount_lowercase)
|
|
if self.amount_uppercase > 0:
|
|
filter_regex.append(r"[A-Z]{%d,}" % self.amount_uppercase)
|
|
if self.amount_symbols > 0:
|
|
filter_regex.append(
|
|
r"[%s]{%d,}" % (self.symbol_charset, self.amount_symbols)
|
|
)
|
|
full_regex = "|".join(filter_regex)
|
|
LOGGER.debug("Built regex", regexp=full_regex)
|
|
result = bool(re.compile(full_regex).match(password))
|
|
|
|
result = result and len(password) >= self.length_min
|
|
|
|
if not result:
|
|
return PolicyResult(result, self.error_message)
|
|
return PolicyResult(result)
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _("Password Policy")
|
|
verbose_name_plural = _("Password Policies")
|