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/passbook/policies/hibp/models.py

74 lines
2.6 KiB
Python
Raw Normal View History

"""passbook HIBP Models"""
from hashlib import sha1
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from requests import get
from rest_framework.serializers import BaseSerializer
2019-10-01 08:24:10 +00:00
from structlog import get_logger
from passbook.policies.models import Policy, PolicyResult
from passbook.policies.types import PolicyRequest
LOGGER = get_logger()
2019-12-31 11:51:16 +00:00
class HaveIBeenPwendPolicy(Policy):
2020-07-01 16:40:52 +00:00
"""Check if password is on HaveIBeenPwned's list by uploading the first
5 characters of the SHA1 Hash."""
password_field = models.TextField(
default="password",
help_text=_(
"Field key to check, field keys defined in Prompt stages are available."
),
)
allowed_count = models.IntegerField(default=0)
@property
def serializer(self) -> BaseSerializer:
from passbook.policies.hibp.api import HaveIBeenPwendPolicySerializer
return HaveIBeenPwendPolicySerializer
def form(self) -> Type[ModelForm]:
from passbook.policies.hibp.forms import HaveIBeenPwnedPolicyForm
return HaveIBeenPwnedPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
characters of Password in request and checks if full hash is in response. Returns 0
if Password is not in result otherwise the count of how many times it was used."""
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]
2019-12-31 11:51:16 +00:00
pw_hash = sha1(password.encode("utf-8")).hexdigest() # nosec
url = f"https://api.pwnedpasswords.com/range/{pw_hash[:5]}"
result = get(url).text
final_count = 0
2019-12-31 11:51:16 +00:00
for line in result.split("\r\n"):
full_hash, count = line.split(":")
if pw_hash[5:] == full_hash.lower():
final_count = int(count)
2020-02-18 20:35:58 +00:00
LOGGER.debug("got hibp result", count=final_count, hash=pw_hash[:5])
if final_count > self.allowed_count:
2019-12-31 11:51:16 +00:00
message = _(
"Password exists on %(count)d online lists." % {"count": final_count}
)
return PolicyResult(False, message)
return PolicyResult(True)
class Meta:
2019-12-31 11:51:16 +00:00
verbose_name = _("Have I Been Pwned Policy")
verbose_name_plural = _("Have I Been Pwned Policies")