Misc tweaks and fixes, port ldap auth test from moulinette

This commit is contained in:
Alexandre Aubin 2021-06-14 17:00:41 +02:00
parent 727e135c72
commit 075526303e
6 changed files with 67 additions and 18 deletions

1
debian/control vendored
View file

@ -14,6 +14,7 @@ Depends: ${python3:Depends}, ${misc:Depends}
, python3-psutil, python3-requests, python3-dnspython, python3-openssl , python3-psutil, python3-requests, python3-dnspython, python3-openssl
, python3-miniupnpc, python3-dbus, python3-jinja2 , python3-miniupnpc, python3-dbus, python3-jinja2
, python3-toml, python3-packaging, python3-publicsuffix , python3-toml, python3-packaging, python3-publicsuffix
, python3-ldap
, apt, apt-transport-https, apt-utils, dirmngr , apt, apt-transport-https, apt-utils, dirmngr
, php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl , php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl
, mariadb-server, php7.3-mysql , mariadb-server, php7.3-mysql

View file

@ -358,6 +358,8 @@
"invalid_regex": "Invalid regex:'{regex:s}'", "invalid_regex": "Invalid regex:'{regex:s}'",
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
"ldap_server_down": "Unable to reach LDAP server",
"ldap_server_is_down_restart_it": "The LDAP service is down, attempt to restart it...",
"log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'",
"log_link_to_log": "Full log of this operation: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'", "log_link_to_log": "Full log of this operation: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'",
"log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log show {name}{name}'", "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log show {name}{name}'",
@ -470,6 +472,7 @@
"migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations run`.", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations run`.",
"not_enough_disk_space": "Not enough free space on '{path:s}'", "not_enough_disk_space": "Not enough free space on '{path:s}'",
"invalid_number": "Must be a number", "invalid_number": "Must be a number",
"invalid_password": "Invalid password",
"operation_interrupted": "The operation was manually interrupted?", "operation_interrupted": "The operation was manually interrupted?",
"packages_upgrade_failed": "Could not upgrade all the packages", "packages_upgrade_failed": "Could not upgrade all the packages",
"password_listed": "This password is among the most used passwords in the world. Please choose something more unique.", "password_listed": "This password is among the most used passwords in the world. Please choose something more unique.",

View file

@ -7,7 +7,6 @@ import ldap.sasl
import time import time
from moulinette import m18n from moulinette import m18n
from moulinette.core import MoulinetteError
from moulinette.authentication import BaseAuthenticator from moulinette.authentication import BaseAuthenticator
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
@ -15,19 +14,6 @@ logger = logging.getLogger("yunohost.authenticators.ldap_admin")
class Authenticator(BaseAuthenticator): class Authenticator(BaseAuthenticator):
"""LDAP Authenticator
Initialize a LDAP connexion for the given arguments. It attempts to
authenticate a user if 'user_rdn' is given - by associating user_rdn
and base_dn - and provides extra methods to manage opened connexion.
Keyword arguments:
- uri -- The LDAP server URI
- base_dn -- The base dn
- user_rdn -- The user rdn to authenticate
"""
name = "ldap_admin" name = "ldap_admin"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -46,10 +32,10 @@ class Authenticator(BaseAuthenticator):
try: try:
con = _reconnect() con = _reconnect()
except ldap.INVALID_CREDENTIALS: except ldap.INVALID_CREDENTIALS:
raise MoulinetteError("invalid_password") raise YunohostError("invalid_password")
except ldap.SERVER_DOWN: except ldap.SERVER_DOWN:
# ldap is down, attempt to restart it before really failing # ldap is down, attempt to restart it before really failing
logger.warning(m18n.g("ldap_server_is_down_restart_it")) logger.warning(m18n.n("ldap_server_is_down_restart_it"))
os.system("systemctl restart slapd") os.system("systemctl restart slapd")
time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted
@ -67,7 +53,7 @@ class Authenticator(BaseAuthenticator):
raise raise
else: else:
if who != self.admindn: if who != self.admindn:
raise MoulinetteError(f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?") raise YunohostError(f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?", raw_msg=True)
finally: finally:
# Free the connection, we don't really need it to keep it open as the point is only to check authentication... # Free the connection, we don't really need it to keep it open as the point is only to check authentication...
if con: if con:

View file

@ -0,0 +1,58 @@
import pytest
import os
from yunohost.authenticators.ldap_admin import Authenticator as LDAPAuth
from yunohost.tools import tools_adminpw
from moulinette import m18n
from moulinette.core import MoulinetteError
def setup_function(function):
if os.system("systemctl is-active slapd") != 0:
os.system("systemctl start slapd && sleep 3")
tools_adminpw("yunohost", check_strength=False)
def test_authenticate():
LDAPAuth().authenticate(password="yunohost")
def test_authenticate_with_wrong_password():
with pytest.raises(MoulinetteError) as exception:
LDAPAuth().authenticate(password="bad_password_lul")
translation = m18n.g("invalid_password")
expected_msg = translation.format()
assert expected_msg in str(exception)
def test_authenticate_server_down(mocker):
os.system("systemctl stop slapd && sleep 3")
# Now if slapd is down, moulinette tries to restart it
mocker.patch("os.system")
mocker.patch("time.sleep")
with pytest.raises(MoulinetteError) as exception:
LDAPAuth().authenticate(password="yunohost")
translation = m18n.n("ldap_server_down")
expected_msg = translation.format()
assert expected_msg in str(exception)
def test_authenticate_change_password():
LDAPAuth().authenticate(password="yunohost")
tools_adminpw("plopette", check_strength=False)
with pytest.raises(MoulinetteError) as exception:
LDAPAuth().authenticate(password="yunohost")
translation = m18n.g("invalid_password")
expected_msg = translation.format()
assert expected_msg in str(exception)
LDAPAuth().authenticate(password="plopette")

View file

@ -94,7 +94,7 @@ class LDAPInterface():
con = _reconnect() con = _reconnect()
except ldap.SERVER_DOWN: except ldap.SERVER_DOWN:
# ldap is down, attempt to restart it before really failing # ldap is down, attempt to restart it before really failing
logger.warning(m18n.g("ldap_server_is_down_restart_it")) logger.warning(m18n.n("ldap_server_is_down_restart_it"))
os.system("systemctl restart slapd") os.system("systemctl restart slapd")
time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted
try: try:

View file

@ -33,6 +33,7 @@ def find_expected_string_keys():
python_files = glob.glob("src/yunohost/*.py") python_files = glob.glob("src/yunohost/*.py")
python_files.extend(glob.glob("src/yunohost/utils/*.py")) python_files.extend(glob.glob("src/yunohost/utils/*.py"))
python_files.extend(glob.glob("src/yunohost/data_migrations/*.py")) python_files.extend(glob.glob("src/yunohost/data_migrations/*.py"))
python_files.extend(glob.glob("src/yunohost/authenticators/*.py"))
python_files.extend(glob.glob("data/hooks/diagnosis/*.py")) python_files.extend(glob.glob("data/hooks/diagnosis/*.py"))
python_files.append("bin/yunohost") python_files.append("bin/yunohost")