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

62 lines
2.2 KiB
Python
Raw Normal View History

"""passbook HIBP Models"""
from hashlib import sha1
from django.db import models
from django.utils.translation import gettext as _
from requests import get
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)
2019-12-31 11:51:16 +00:00
form = "passbook.policies.hibp.forms.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")