This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/authentik/policies/password/models.py
Jens Langhammer 37c29a073e policies/password: fix symbols not being checked correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-04 15:21:48 +02:00

85 lines
3.2 KiB
Python

"""user field matcher models"""
import re
from django.db import models
from django.utils.translation import gettext as _
from rest_framework.serializers import BaseSerializer
from structlog.stdlib import get_logger
from authentik.policies.models import Policy
from authentik.policies.types import PolicyRequest, PolicyResult
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
LOGGER = get_logger()
RE_LOWER = re.compile("[a-z]")
RE_UPPER = re.compile("[A-Z]")
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 authentik.policies.password.api import PasswordPolicySerializer
return PasswordPolicySerializer
@property
def component(self) -> str:
return "ak-policy-password-form"
def passes(self, request: PolicyRequest) -> PolicyResult:
if (
self.password_field not in request.context
and self.password_field not in request.context.get(PLAN_CONTEXT_PROMPT, {})
):
LOGGER.warning(
"Password field not set in Policy Request",
field=self.password_field,
fields=request.context.keys(),
prompt_fields=request.context.get(PLAN_CONTEXT_PROMPT, {}).keys(),
)
return PolicyResult(False, _("Password not set in context"))
if self.password_field in request.context:
password = request.context[self.password_field]
else:
password = request.context[PLAN_CONTEXT_PROMPT][self.password_field]
if len(password) < self.length_min:
LOGGER.debug("password failed", reason="length")
return PolicyResult(False, self.error_message)
if self.amount_lowercase > 0 and len(RE_LOWER.findall(password)) < self.amount_lowercase:
LOGGER.debug("password failed", reason="amount_lowercase")
return PolicyResult(False, self.error_message)
if self.amount_uppercase > 0 and len(RE_UPPER.findall(password)) < self.amount_lowercase:
LOGGER.debug("password failed", reason="amount_uppercase")
return PolicyResult(False, self.error_message)
if self.amount_symbols > 0:
count = 0
for symbol in self.symbol_charset:
count += password.count(symbol)
if count < self.amount_symbols:
LOGGER.debug("password failed", reason="amount_symbols")
return PolicyResult(False, self.error_message)
return PolicyResult(True)
class Meta:
verbose_name = _("Password Policy")
verbose_name_plural = _("Password Policies")