feat(ldap): add validation for user and group DNs
This commit is contained in:
parent
af90bfd3dd
commit
1b9dc66b08
110
lumi2/ldap.py
110
lumi2/ldap.py
@ -124,7 +124,7 @@ def _assert_is_valid_ou_dn(input_str) -> None:
|
||||
|
||||
tokens = input_str.split(',')
|
||||
if len(tokens) != 3:
|
||||
raise InvalidStringFormatException("Expected exactly two ',' characters.")
|
||||
raise InvalidStringFormatException("Expected exactly three ',' characters.")
|
||||
|
||||
if not tokens[0].startswith("ou="):
|
||||
raise InvalidStringFormatException("Expected 'ou='.")
|
||||
@ -140,6 +140,114 @@ def _assert_is_valid_ou_dn(input_str) -> None:
|
||||
f"Expected only alphanumeric characters in organizational unit " \
|
||||
f"but got: '{char}'."
|
||||
)
|
||||
|
||||
|
||||
def _assert_is_valid_user_dn(input_str) -> None:
|
||||
"""Checks whether the input string is a valid user entry DN.
|
||||
|
||||
A valid user DN is a string of the following format:
|
||||
'uid=<USERNAME>,ou=<USERS_OU>,<BASE_DN>', whereby:
|
||||
<USERNAME> (user's uid) is a substring containing only characters from the
|
||||
character class [A-Za-z0-9_-.], it has minimum length 1 and must start with a
|
||||
letter.
|
||||
<USERS_OU> (RDN of ou holding users) must be a valid ou name as expected by
|
||||
_assert_is_valid_ou_dn().
|
||||
<BASE_DN> is a substring representing an LDAP base DN as expected by
|
||||
_assert_is_valid_base_dn().
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_str : string
|
||||
The input string to check.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError
|
||||
If input_str is not of type string.
|
||||
InvalidStringFormatException
|
||||
If input_str is not a valid LDAP user DN string.
|
||||
"""
|
||||
|
||||
if not isinstance(input_str, str):
|
||||
raise TypeError(f"Expected a string but got: {type(input_str)}.")
|
||||
|
||||
tokens = input_str.split(',')
|
||||
if len(tokens) != 4:
|
||||
raise InvalidStringFormatException("Expected exactly four ',' characters.")
|
||||
|
||||
if not tokens[0].startswith("uid="):
|
||||
raise InvalidStringFormatException("Expected 'uid='.")
|
||||
token_uid = tokens[0][4:]
|
||||
if not len(token_uid):
|
||||
raise InvalidStringFormatException("UID cannot be empty.")
|
||||
valid_uid_chars = ascii_lowercase + ascii_uppercase + digits + "_-."
|
||||
for char in token_uid:
|
||||
if not char in valid_uid_chars:
|
||||
raise InvalidStringFormatException(f"Invalid character in uid: '{char}'.")
|
||||
if not token_uid[0] in ascii_lowercase + ascii_uppercase:
|
||||
raise InvalidStringFormatException("UID must start with a letter character.")
|
||||
|
||||
token_base_dn = tokens[2] + "," + tokens[3]
|
||||
_assert_is_valid_base_dn(token_base_dn)
|
||||
|
||||
token_ou_dn = tokens[1] + "," + token_base_dn
|
||||
_assert_is_valid_ou_dn(token_ou_dn)
|
||||
|
||||
|
||||
def _assert_is_valid_group_dn(input_str) -> None:
|
||||
"""Checks whether the input string is a valid group entry DN.
|
||||
|
||||
A valid group DN is a string of the following format:
|
||||
'cn=<GROUPNAME>,ou=<GROUPS_OU>,<BASE_DN>', whereby:
|
||||
<GROUPNAME> (group name) is a substring containing only characters from the
|
||||
character class [A-Za-z], and it has minimum length 1.
|
||||
<GROUPS_OU> (RDN of ou holding groups) must be a valid ou name as expected by
|
||||
_assert_is_valid_ou_dn().
|
||||
<BASE_DN> is a substring representing an LDAP base DN as expected by
|
||||
_assert_is_valid_base_dn().
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_str : string
|
||||
The input string to check.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError
|
||||
If input_str is not of type string.
|
||||
InvalidStringFormatException
|
||||
If input_str is not a valid LDAP group DN string.
|
||||
"""
|
||||
|
||||
if not isinstance(input_str, str):
|
||||
raise TypeError(f"Expected a string but got: {type(input_str)}.")
|
||||
|
||||
tokens = input_str.split(',')
|
||||
if len(tokens) != 4:
|
||||
raise InvalidStringFormatException("Expected exactly four ',' characters.")
|
||||
|
||||
if not tokens[0].startswith("cn="):
|
||||
raise InvalidStringFormatException("Expected 'cn='.")
|
||||
token_cn = tokens[0][3:]
|
||||
if not len(token_cn):
|
||||
raise InvalidStringFormatException("UID cannot be empty.")
|
||||
for char in token_cn:
|
||||
if not char in ascii_lowercase + ascii_uppercase:
|
||||
raise InvalidStringFormatException(f"Invalid character in group cn: '{char}'.")
|
||||
|
||||
token_base_dn = tokens[2] + "," + tokens[3]
|
||||
_assert_is_valid_base_dn(token_base_dn)
|
||||
|
||||
token_ou_dn = tokens[1] + "," + token_base_dn
|
||||
_assert_is_valid_ou_dn(token_ou_dn)
|
||||
|
||||
|
||||
def _assert_is_valid_user_object_class(input_str) -> None:
|
||||
|
@ -48,6 +48,57 @@ def test_assert_is_valid_ou_dn():
|
||||
assert ll._assert_is_valid_ou_dn(valid_string) == None
|
||||
|
||||
|
||||
def test_assert_is_valid_user_dn():
|
||||
for invalid_type in [None, 0, True, ("x",), ["x"]]:
|
||||
with pytest.raises(TypeError):
|
||||
ll._assert_is_valid_user_dn(invalid_type)
|
||||
|
||||
for invalid_string in [
|
||||
"", " ", "uid=foo", "abc", "\u1337",
|
||||
"uid=,ou=foo,dc=bar,dc=baz", "uid=foo,ou=,dc=bar,dc=baz",
|
||||
"uid=foo,ou=bar,dc=,dc=baz", "uid=foo,ou=bar,dc=baz,dc=",
|
||||
"uid=foo,dc=example,dc=com", "uid=1foo,ou=bar,dc=example,dc=com",
|
||||
"uid=.bob,ou=foo,dc=bar,dc=baz", "uid=_foo,ou=foo,dc=bar,dc=baz",
|
||||
"uid=foo,uid=bar,ou=bar,dc=com", "cn=foo,ou=bar,dc=example,dc=com",
|
||||
"uid=abc%,ou=foo,dc=example,dc=com",
|
||||
]:
|
||||
with pytest.raises(ll.InvalidStringFormatException):
|
||||
print(invalid_string)
|
||||
ll._assert_is_valid_user_dn(invalid_string)
|
||||
|
||||
for valid_string in [
|
||||
"uid=foo,ou=users,dc=example,dc=com", "uid=Foo-bar.baz_,ou=users,dc=a,dc=de",
|
||||
"uid=bOb_.-,ou=Users,dc=example,dc=com", "uid=foo,ou=foo,dc=bar,dc=baz",
|
||||
]:
|
||||
assert ll._assert_is_valid_user_dn(valid_string) == None
|
||||
|
||||
|
||||
def test_assert_is_valid_group_dn():
|
||||
for invalid_type in [None, 0, True, ("x",), ["x"]]:
|
||||
with pytest.raises(TypeError):
|
||||
ll._assert_is_valid_group_dn(invalid_type)
|
||||
|
||||
for invalid_string in [
|
||||
"", " ", "cn=foo", "abc", "\u1337",
|
||||
"cn=,ou=foo,dc=bar,dc=baz", "cn=foo,ou=,dc=bar,dc=baz",
|
||||
"cn=foo,ou=bar,dc=,dc=baz", "cn=foo,ou=bar,dc=baz,dc=",
|
||||
"cn=foo,dc=example,dc=com", "cn=1foo,ou=bar,dc=example,dc=com",
|
||||
"cn=.bob,ou=foo,dc=bar,dc=baz", "cn=_foo,ou=foo,dc=bar,dc=baz",
|
||||
"cn=foo,cn=bar,ou=bar,dc=com",
|
||||
"cn=abc%,ou=foo,dc=bar,dc=baz", "cn=abc1,ou=foo,dc=bar,dc=baz",
|
||||
"cn=ab-c,ou=foo,dc=bar,dc=baz",
|
||||
]:
|
||||
with pytest.raises(ll.InvalidStringFormatException):
|
||||
print(invalid_string)
|
||||
ll._assert_is_valid_group_dn(invalid_string)
|
||||
|
||||
for valid_string in [
|
||||
"cn=foo,ou=groups,dc=example,dc=com", "cn=foo,ou=bar,dc=a,dc=de",
|
||||
"cn=Abc,ou=foo,dc=bar,dc=baz",
|
||||
]:
|
||||
assert ll._assert_is_valid_group_dn(valid_string) == None
|
||||
|
||||
|
||||
def test_assert_is_valid_user_object_class():
|
||||
for invalid_type in [None, 0, True, ("x",), ["x"]]:
|
||||
with pytest.raises(TypeError):
|
||||
|
Loading…
Reference in New Issue
Block a user