feat(ldap): add validation for user and group DNs

This commit is contained in:
Julian Lobbes 2022-11-09 19:30:10 +01:00
parent af90bfd3dd
commit 1b9dc66b08
2 changed files with 160 additions and 1 deletions

View File

@ -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:

View File

@ -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):