feat(usermodel): implement User parameter validation methods
This commit is contained in:
parent
16fb570cfd
commit
876dc2ef5c
@ -11,6 +11,7 @@ services:
|
|||||||
- ./lumi2/__init__.py:/app/lumi2/__init__.py:ro
|
- ./lumi2/__init__.py:/app/lumi2/__init__.py:ro
|
||||||
- ./lumi2/exceptions.py:/app/lumi2/exceptions.py:ro
|
- ./lumi2/exceptions.py:/app/lumi2/exceptions.py:ro
|
||||||
- ./lumi2/ldap.py:/app/lumi2/ldap.py:ro
|
- ./lumi2/ldap.py:/app/lumi2/ldap.py:ro
|
||||||
|
- ./lumi2/usermodel.py:/app/lumi2/usermodel.py:ro
|
||||||
- ./lumi2/usermanager.py:/app/lumi2/usermanager.py:ro
|
- ./lumi2/usermanager.py:/app/lumi2/usermanager.py:ro
|
||||||
- ./lumi2/static/:/app/lumi2/static/:ro
|
- ./lumi2/static/:/app/lumi2/static/:ro
|
||||||
- ./lumi2/templates/:/app/lumi2/templates/:ro
|
- ./lumi2/templates/:/app/lumi2/templates/:ro
|
||||||
|
@ -596,7 +596,7 @@ def get_user(connection: Connection, uid: str) -> User:
|
|||||||
'sn',
|
'sn',
|
||||||
'displayName',
|
'displayName',
|
||||||
'uid',
|
'uid',
|
||||||
'password',
|
'userPassword',
|
||||||
'jpegPhoto',
|
'jpegPhoto',
|
||||||
'mail',
|
'mail',
|
||||||
]
|
]
|
||||||
|
@ -4,6 +4,10 @@ Also provides methods to convert LDAP user/group entries into user/group objects
|
|||||||
and vice versa.
|
and vice versa.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from string import ascii_lowercase, ascii_uppercase, digits, whitespace
|
||||||
|
from base64 import b64decode
|
||||||
|
from binascii import Error as Base64DecodeError
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
class User:
|
class User:
|
||||||
@ -12,23 +16,17 @@ class User:
|
|||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
username : str
|
username : str
|
||||||
The user's username. Can contain only uppercase/lowercase latin characters,
|
The user's username.
|
||||||
numbers, hyphens, underscores and periods. Must start with a latin character.
|
|
||||||
Minimum length is 1, maximum length is 64 characters.
|
|
||||||
password_hash : str
|
password_hash : str
|
||||||
Base64-encoded SHA512 hash of the user's password.
|
Base64-encoded SHA512 hash of the user's password.
|
||||||
email : str
|
email : str
|
||||||
The user's email address.
|
The user's email address.
|
||||||
Must contain an '@'-character, may not contain any whitespace.
|
|
||||||
first_name : str
|
first_name : str
|
||||||
The user's first name.
|
The user's first name.
|
||||||
May not contain any whitespace.
|
|
||||||
last_name : str
|
last_name : str
|
||||||
The user's last name.
|
The user's last name.
|
||||||
May not contain any whitespace.
|
|
||||||
display_name : str
|
display_name : str
|
||||||
The user's display name (as required by some LDAP-enabled applications).
|
The user's display name (as required by some LDAP-enabled applications).
|
||||||
May not contain any whitespace.
|
|
||||||
picture : PIL.Image
|
picture : PIL.Image
|
||||||
The user's profile picture as a PIL Image object.
|
The user's profile picture as a PIL Image object.
|
||||||
"""
|
"""
|
||||||
@ -62,17 +60,28 @@ class User:
|
|||||||
if not isinstance(input_str, str):
|
if not isinstance(input_str, str):
|
||||||
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
||||||
|
|
||||||
# TODO implement
|
if not len(input_str):
|
||||||
return False
|
return False
|
||||||
|
if len(input_str) > 64:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if input_str[0] not in ascii_lowercase + ascii_uppercase:
|
||||||
|
return False
|
||||||
|
|
||||||
|
valid_chars = ascii_lowercase + ascii_uppercase + digits + "-_."
|
||||||
|
for char in input_str:
|
||||||
|
if char not in valid_chars:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_valid_password_hash(input_str: str) -> bool:
|
def is_valid_password_hash(input_str: str) -> bool:
|
||||||
"""Checks whether the input string is a valid password hash.
|
"""Checks whether the input string is a valid password hash.
|
||||||
|
|
||||||
Password hashes are base64-encoded bytes, and the hashing algorithm used
|
A valid password hash is a non-empty string containing base64-decodeable
|
||||||
to create the hash is hard to determine, but we can verify that the
|
text.
|
||||||
provided hash is a non-empty string containing base64-decodeable text.
|
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -93,15 +102,24 @@ class User:
|
|||||||
if not isinstance(input_str, str):
|
if not isinstance(input_str, str):
|
||||||
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
||||||
|
|
||||||
# TODO implement
|
if not len(input_str):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
b64decode(input_str, validate=True)
|
||||||
|
return True
|
||||||
|
except Base64DecodeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_valid_email(input_str: str) -> bool:
|
def is_valid_email(input_str: str) -> bool:
|
||||||
"""Checks whether the input string is a valid email address.
|
"""Checks whether the input string is a valid email address.
|
||||||
|
|
||||||
A valid email address contains no whitespace and at least one '@'-char.
|
WARNING: this validation is very rudimentary. Proper validation requires
|
||||||
|
a validation email to be sent and confirmed by the user.
|
||||||
|
A valid email address contains no whitespace, at least one '@' character,
|
||||||
|
and a '.' character somewhere after the '@'.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -122,8 +140,18 @@ class User:
|
|||||||
if not isinstance(input_str, str):
|
if not isinstance(input_str, str):
|
||||||
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
||||||
|
|
||||||
# TODO implement
|
if '@' not in input_str:
|
||||||
return False
|
return False
|
||||||
|
if '.' not in input_str:
|
||||||
|
return False
|
||||||
|
if '.' not in input_str.split('@')[1]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for char in input_str:
|
||||||
|
if char in whitespace:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -152,8 +180,14 @@ class User:
|
|||||||
if not isinstance(input_str, str):
|
if not isinstance(input_str, str):
|
||||||
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
raise TypeError(f"Expected a string but got: '{type(input_str)}'.")
|
||||||
|
|
||||||
# TODO implement
|
if not len(input_str):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
for char in input_str:
|
||||||
|
if char in whitespace:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -181,6 +215,10 @@ class User:
|
|||||||
if not isinstance(input_image, Image):
|
if not isinstance(input_image, Image):
|
||||||
raise TypeError(f"Expected a PIL Image but got: '{type(input_image)}'.")
|
raise TypeError(f"Expected a PIL Image but got: '{type(input_image)}'.")
|
||||||
|
|
||||||
|
# TODO implement
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
username: str, password_hash: str, email: str,
|
username: str, password_hash: str, email: str,
|
||||||
|
@ -41,8 +41,7 @@ def test_is_valid_password_hash():
|
|||||||
|
|
||||||
valid_hashes = [
|
valid_hashes = [
|
||||||
# can contain [A-Za-z0-9+/] and up to two '=' at the end
|
# can contain [A-Za-z0-9+/] and up to two '=' at the end
|
||||||
"foobar", "EzM3",
|
"EzM3", "abcABC123+/=",
|
||||||
"abcABC123+/=", "a",
|
|
||||||
]
|
]
|
||||||
for valid_hash in valid_hashes:
|
for valid_hash in valid_hashes:
|
||||||
assert User.is_valid_password_hash(valid_hash)
|
assert User.is_valid_password_hash(valid_hash)
|
||||||
@ -55,7 +54,7 @@ def test_is_valid_email():
|
|||||||
|
|
||||||
invalid_emails = [
|
invalid_emails = [
|
||||||
"", " ", "\t", "\n",
|
"", " ", "\t", "\n",
|
||||||
" alice@example.com", "alice", "@", "@.",
|
" alice@example.com", "alice", "@", "alice@com"
|
||||||
"alice@example.com ", "alice@ex ample.com"
|
"alice@example.com ", "alice@ex ample.com"
|
||||||
]
|
]
|
||||||
for invalid_email in invalid_emails:
|
for invalid_email in invalid_emails:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user