authentik.stages.authenticator.util

Authenticator utils

 1"""Authenticator utils"""
 2
 3import random
 4import string
 5from binascii import Error, unhexlify
 6from os import urandom
 7
 8from django.core.exceptions import ValidationError
 9
10
11def hex_validator(length=0):
12    """
13    Returns a function to be used as a model validator for a hex-encoded
14    CharField. This is useful for secret keys of all kinds::
15
16        def key_validator(value):
17            return hex_validator(20)(value)
18
19        key = models.CharField(max_length=40,
20            validators=[key_validator], help_text='A hex-encoded 20-byte secret key')
21
22    :param int length: If greater than 0, validation will fail unless the
23        decoded value is exactly this number of bytes.
24
25    :rtype: function
26
27    >>> hex_validator()('0123456789abcdef')
28    >>> hex_validator(8)(b'0123456789abcdef')
29    >>> hex_validator()('phlebotinum')          # doctest: +IGNORE_EXCEPTION_DETAIL
30    Traceback (most recent call last):
31        ...
32    ValidationError: ['phlebotinum is not valid hex-encoded data.']
33    >>> hex_validator(9)('0123456789abcdef')    # doctest: +IGNORE_EXCEPTION_DETAIL
34    Traceback (most recent call last):
35        ...
36    ValidationError: ['0123456789abcdef does not represent exactly 9 bytes.']
37    """
38
39    def _validator(value):
40        try:
41            if isinstance(value, str):
42                value = value.encode()
43
44            unhexlify(value)
45        except Error:
46            raise ValidationError(f"{value} is not valid hex-encoded data.") from None
47
48        if (length > 0) and (len(value) != length * 2):
49            raise ValidationError(f"{value} does not represent exactly {length} bytes.")
50
51    return _validator
52
53
54def random_hex(length=20):
55    """
56    Returns a string of random bytes encoded as hex.
57
58    This uses :func:`os.urandom`, so it should be suitable for generating
59    cryptographic keys.
60
61    :param int length: The number of (decoded) bytes to return.
62
63    :returns: A string of hex digits.
64    :rtype: str
65
66    """
67    return urandom(length).hex()
68
69
70def random_number_token(length=6):
71    """
72    Returns a string of random digits encoded as string.
73
74    :param int length: The number of digits to return.
75
76    :returns: A string of decimal digits.
77    :rtype: str
78
79    """
80    rand = random.SystemRandom()
81
82    if hasattr(rand, "choices"):
83        digits = rand.choices(string.digits, k=length)
84    else:
85        digits = (rand.choice(string.digits) for i in range(length))
86
87    return "".join(digits)
def hex_validator(length=0):
12def hex_validator(length=0):
13    """
14    Returns a function to be used as a model validator for a hex-encoded
15    CharField. This is useful for secret keys of all kinds::
16
17        def key_validator(value):
18            return hex_validator(20)(value)
19
20        key = models.CharField(max_length=40,
21            validators=[key_validator], help_text='A hex-encoded 20-byte secret key')
22
23    :param int length: If greater than 0, validation will fail unless the
24        decoded value is exactly this number of bytes.
25
26    :rtype: function
27
28    >>> hex_validator()('0123456789abcdef')
29    >>> hex_validator(8)(b'0123456789abcdef')
30    >>> hex_validator()('phlebotinum')          # doctest: +IGNORE_EXCEPTION_DETAIL
31    Traceback (most recent call last):
32        ...
33    ValidationError: ['phlebotinum is not valid hex-encoded data.']
34    >>> hex_validator(9)('0123456789abcdef')    # doctest: +IGNORE_EXCEPTION_DETAIL
35    Traceback (most recent call last):
36        ...
37    ValidationError: ['0123456789abcdef does not represent exactly 9 bytes.']
38    """
39
40    def _validator(value):
41        try:
42            if isinstance(value, str):
43                value = value.encode()
44
45            unhexlify(value)
46        except Error:
47            raise ValidationError(f"{value} is not valid hex-encoded data.") from None
48
49        if (length > 0) and (len(value) != length * 2):
50            raise ValidationError(f"{value} does not represent exactly {length} bytes.")
51
52    return _validator

Returns a function to be used as a model validator for a hex-encoded CharField. This is useful for secret keys of all kinds::

def key_validator(value):
    return hex_validator(20)(value)

key = models.CharField(max_length=40,
    validators=[key_validator], help_text='A hex-encoded 20-byte secret key')

:param int length: If greater than 0, validation will fail unless the decoded value is exactly this number of bytes.

:rtype: function

>>> hex_validator()('0123456789abcdef')
>>> hex_validator(8)(b'0123456789abcdef')
>>> hex_validator()('phlebotinum')          # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
ValidationError: ['phlebotinum is not valid hex-encoded data.']
>>> hex_validator(9)('0123456789abcdef')    # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
ValidationError: ['0123456789abcdef does not represent exactly 9 bytes.']
def random_hex(length=20):
55def random_hex(length=20):
56    """
57    Returns a string of random bytes encoded as hex.
58
59    This uses :func:`os.urandom`, so it should be suitable for generating
60    cryptographic keys.
61
62    :param int length: The number of (decoded) bytes to return.
63
64    :returns: A string of hex digits.
65    :rtype: str
66
67    """
68    return urandom(length).hex()

Returns a string of random bytes encoded as hex.

This uses :func:os.urandom, so it should be suitable for generating cryptographic keys.

:param int length: The number of (decoded) bytes to return.

:returns: A string of hex digits. :rtype: str

def random_number_token(length=6):
71def random_number_token(length=6):
72    """
73    Returns a string of random digits encoded as string.
74
75    :param int length: The number of digits to return.
76
77    :returns: A string of decimal digits.
78    :rtype: str
79
80    """
81    rand = random.SystemRandom()
82
83    if hasattr(rand, "choices"):
84        digits = rand.choices(string.digits, k=length)
85    else:
86        digits = (rand.choice(string.digits) for i in range(length))
87
88    return "".join(digits)

Returns a string of random digits encoded as string.

:param int length: The number of digits to return.

:returns: A string of decimal digits. :rtype: str