From 80d6a6f08dbe5ee3338ccf89daf16d84b0602fe9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 31 Jan 2024 00:16:26 +0100 Subject: [PATCH] tests: fix/add tests for SSO --- src/tests/test_sso_and_portalapi.py | 88 +++++++++++++++++++---------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/src/tests/test_sso_and_portalapi.py b/src/tests/test_sso_and_portalapi.py index 3bc79295e..112a6433c 100644 --- a/src/tests/test_sso_and_portalapi.py +++ b/src/tests/test_sso_and_portalapi.py @@ -1,3 +1,4 @@ +import base64 import time import requests from pathlib import Path @@ -8,7 +9,7 @@ from .conftest import message, raiseYunohostError, get_test_apps_dir from yunohost.domain import _get_maindomain, domain_add, domain_remove, domain_list from yunohost.user import user_create, user_list, user_delete from yunohost.authenticators.ldap_ynhuser import Authenticator, SESSION_FOLDER, short_hash -from yunohost.app import app_install, app_remove +from yunohost.app import app_install, app_remove, app_setting, app_ssowatconf from yunohost.permission import user_permission_list, user_permission_update @@ -20,6 +21,8 @@ dummy_password = "test123Ynh" def setup_function(function): Authenticator.invalidate_all_sessions_for_user("alice") assert number_of_active_session_for_user("alice") == 0 + Authenticator.invalidate_all_sessions_for_user("bob") + assert number_of_active_session_for_user("bob") == 0 def teardown_function(function): @@ -30,8 +33,10 @@ def setup_module(module): assert os.system("systemctl is-active yunohost-portal-api >/dev/null") == 0 - if not "alice" in user_list()["users"]: + if "alice" not in user_list()["users"]: user_create("alice", maindomain, dummy_password, fullname="Alice White", admin=True) + if "bob" not in user_list()["users"]: + user_create("bob", maindomain, dummy_password, fullname="Bob Marley") app_install( os.path.join(get_test_apps_dir(), "hellopy_ynh"), @@ -44,6 +49,8 @@ def setup_module(module): def teardown_module(module): if "alice" in user_list()["users"]: user_delete("alice") + if "bob" in user_list()["users"]: + user_delete("bob") app_remove("hellopy") @@ -58,8 +65,10 @@ def login(session, logged_as): }, verify=False, ) + return r + def logout(session): logout_endpoint = f"https://{maindomain}/yunohost/portalapi/logout" r = session.get( @@ -71,26 +80,31 @@ def logout(session): ) return r + def number_of_active_session_for_user(user): - - return len(list(Path(SESSION_FOLDER).glob(f"{short_hash('alice')}*"))) + return len(list(Path(SESSION_FOLDER).glob(f"{short_hash(user)}*"))) -def request(webpath, logged_as=None, session=None): +def request(webpath, logged_as=None, session=None, inject_auth=None): webpath = webpath.rstrip("/") + headers = {} + if inject_auth: + b64loginpassword = base64.b64encode((inject_auth[0] + ":" + inject_auth[1]).encode()).decode() + headers["Authorization"] = f"Basic {b64loginpassword}" + # Anonymous access if session: - r = session.get(webpath, verify=False, allow_redirects=False) + r = session.get(webpath, verify=False, allow_redirects=False, headers=headers) elif not logged_as: - r = requests.get(webpath, verify=False, allow_redirects=False) + r = requests.get(webpath, verify=False, allow_redirects=False, headers=headers) # Login as a user using dummy password else: with requests.Session() as session: r = login(session, logged_as) # We should have some cookies related to authentication now assert session.cookies - r = session.get(webpath, verify=False, allow_redirects=False) + r = session.get(webpath, verify=False, allow_redirects=False, headers=headers) return r @@ -133,7 +147,6 @@ def test_api_login_nonexistinguser(): def test_api_public_and_me_logged_in(): - r = request(f"https://{maindomain}/yunohost/portalapi/public", logged_as="alice") assert r.status_code == 200 and "apps" in r.json() r = request(f"https://{maindomain}/yunohost/portalapi/me", logged_as="alice") @@ -190,38 +203,59 @@ def test_permission_propagation_on_ssowat(): r = request(f"https://{maindomain}/", logged_as="alice") assert r.status_code == 200 and r.content.decode().strip() == "Hello world!" + r = request(f"https://{maindomain}/", logged_as="bob") + assert r.status_code == 200 and r.content.decode().strip() == "Hello world!" + user_permission_update( "hellopy.main", remove=["visitors", "all_users"], add="alice" ) r = request(f"https://{maindomain}/") assert r.status_code == 302 + assert r.headers['Location'].startswith(f"https://{maindomain}/yunohost/sso?r=") r = request(f"https://{maindomain}/", logged_as="alice") assert r.status_code == 200 and r.content.decode().strip() == "Hello world!" - return + # Bob can't even login because doesnt has access to any app on the domain + # (that's debattable tho) + with requests.Session() as session: + r = login(session, "bob") + assert not session.cookies - res = user_permission_list(full=True)["permissions"] - assert not can_access_webpage(app_webroot, logged_as=None) - assert not can_access_webpage(app_webroot, logged_as="alice") - assert can_access_webpage(app_webroot, logged_as="bob") +def test_sso_basic_auth_header(): + + r = request(f"https://{maindomain}/show-auth") + assert r.status_code == 200 and r.content.decode().strip() == "User: None\nPwd: None" + + r = request(f"https://{maindomain}/show-auth", logged_as="alice") + assert r.status_code == 200 and r.content.decode().strip() == "User: alice\nPwd: -" + + app_setting("hellopy", "auth_header", value="basic-with-password") + app_ssowatconf() + + r = request(f"https://{maindomain}/show-auth", logged_as="alice") + assert r.status_code == 200 and r.content.decode().strip() == f"User: alice\nPwd: {dummy_password}" + + +def test_sso_basic_auth_header_spoofing(): + + r = request(f"https://{maindomain}/show-auth") + assert r.status_code == 200 and r.content.decode().strip() == "User: None\nPwd: None" + + r = request(f"https://{maindomain}/show-auth", inject_auth=("foo", "bar")) + assert r.status_code == 200 and r.content.decode().strip() == "User: None\nPwd: None" + + app_setting("hellopy", "protect_against_basic_auth_spoofing", value="false") + app_ssowatconf() + + r = request(f"https://{maindomain}/show-auth", inject_auth=("foo", "bar")) + assert r.status_code == 200 and r.content.decode().strip() == "User: foo\nPwd: bar" - # Test admin access, as configured during install, only alice should be able to access it - # alice gotta be allowed on the main permission to access the admin tho - user_permission_update("hellopy.main", remove="bob", add="all_users") - assert not can_access_webpage(app_webroot + "/admin", logged_as=None) - assert can_access_webpage(app_webroot + "/admin", logged_as="alice") - assert not can_access_webpage(app_webroot + "/admin", logged_as="bob") -# app privée pour alice -# - pas d'accès si pas loggué -# -> redirection ? -# - accès si loggué si alice -# - pas d'accès même si loggué en tant que bob # accès à l'api portal # -> test des routes @@ -230,10 +264,6 @@ def test_permission_propagation_on_ssowat(): # /update -# dummy app qui montre le header remote_user / authentication ? - -# attempt to inject auth header - # accès aux trucs précédent meme avec une app installée sur la racine ? # ou une app par défaut ?