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(',')
|
tokens = input_str.split(',')
|
||||||
if len(tokens) != 3:
|
if len(tokens) != 3:
|
||||||
raise InvalidStringFormatException("Expected exactly two ',' characters.")
|
raise InvalidStringFormatException("Expected exactly three ',' characters.")
|
||||||
|
|
||||||
if not tokens[0].startswith("ou="):
|
if not tokens[0].startswith("ou="):
|
||||||
raise InvalidStringFormatException("Expected 'ou='.")
|
raise InvalidStringFormatException("Expected 'ou='.")
|
||||||
@ -142,6 +142,114 @@ def _assert_is_valid_ou_dn(input_str) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
def _assert_is_valid_user_object_class(input_str) -> None:
|
||||||
"""Checks whether the input string is a valid LDAP user object class.
|
"""Checks whether the input string is a valid LDAP user object class.
|
||||||
|
|
||||||
|
@ -48,6 +48,57 @@ def test_assert_is_valid_ou_dn():
|
|||||||
assert ll._assert_is_valid_ou_dn(valid_string) == None
|
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():
|
def test_assert_is_valid_user_object_class():
|
||||||
for invalid_type in [None, 0, True, ("x",), ["x"]]:
|
for invalid_type in [None, 0, True, ("x",), ["x"]]:
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user