From 0fed5f54f638667b5dd492a5b90a2307f8883a38 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:52:33 -0600 Subject: [PATCH] fix: Prevent Users From Being Created With Missing Group/Household (#4500) --- mealie/db/models/users/users.py | 8 ++++ .../providers/test_openid_provider.py | 37 ++++++++++++++ tests/unit_tests/test_security.py | 48 ++++++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/mealie/db/models/users/users.py b/mealie/db/models/users/users.py index 51dd1cbd1fd..f25bbb9245a 100644 --- a/mealie/db/models/users/users.py +++ b/mealie/db/models/users/users.py @@ -151,6 +151,14 @@ def __init__( else: self.household = None + if self.group is None: + raise ValueError(f"Group {group} does not exist; cannot create user") + if self.household is None: + raise ValueError( + f'Household "{household}" does not exist on group ' + f'"{self.group.name}" ({self.group.id}); cannot create user' + ) + self.rated_recipes = [] self.password = password diff --git a/tests/unit_tests/core/security/providers/test_openid_provider.py b/tests/unit_tests/core/security/providers/test_openid_provider.py index 3699d2ef7d3..94fca7d9b16 100644 --- a/tests/unit_tests/core/security/providers/test_openid_provider.py +++ b/tests/unit_tests/core/security/providers/test_openid_provider.py @@ -1,8 +1,10 @@ +import pytest from pytest import MonkeyPatch, Session from mealie.core.config import get_app_settings from mealie.core.security.providers.openid_provider import OpenIDProvider from mealie.repos.all_repositories import get_repositories +from tests.utils.factories import random_email, random_string from tests.utils.fixture_schemas import TestUser @@ -125,3 +127,38 @@ def test_has_admin_group_new_user(monkeypatch: MonkeyPatch, session: Session): user = db.users.get_one("dude2", "username") assert user is not None assert user.admin + + +@pytest.mark.parametrize("valid_group", [True, False]) +@pytest.mark.parametrize("valid_household", [True, False]) +def test_ldap_user_creation_invalid_group_or_household( + monkeypatch: MonkeyPatch, session: Session, valid_group: bool, valid_household: bool +): + monkeypatch.setenv("OIDC_USER_GROUP", "mealie_user") + monkeypatch.setenv("OIDC_ADMIN_GROUP", "mealie_admin") + if not valid_group: + monkeypatch.setenv("DEFAULT_GROUP", random_string()) + if not valid_household: + monkeypatch.setenv("DEFAULT_HOUSEHOLD", random_string()) + get_app_settings.cache_clear() + + data = { + "preferred_username": random_string(), + "email": random_email(), + "name": random_string(), + "groups": ["mealie_user"], + } + auth_provider = OpenIDProvider(session, data) + + if valid_group and valid_household: + assert auth_provider.authenticate() is not None + else: + assert auth_provider.authenticate() is None + + db = get_repositories(session, group_id=None, household_id=None) + user = db.users.get_one(data["preferred_username"], "username") + + if valid_group and valid_household: + assert user is not None + else: + assert user is None diff --git a/tests/unit_tests/test_security.py b/tests/unit_tests/test_security.py index 6dc311477bb..5aad766165a 100644 --- a/tests/unit_tests/test_security.py +++ b/tests/unit_tests/test_security.py @@ -1,6 +1,7 @@ from pathlib import Path import ldap +import pytest from pytest import MonkeyPatch from mealie.core import security @@ -13,6 +14,7 @@ from mealie.core.security.providers.ldap_provider import LDAPProvider from mealie.db.db_setup import session_context from mealie.db.models.users.users import AuthMethod +from mealie.repos.repository_factory import AllRepositories from mealie.schema.user.auth import CredentialsRequestForm from mealie.schema.user.user import PrivateUser from tests.utils import random_string @@ -92,7 +94,7 @@ def start_tls_s(self): pass -def setup_env(monkeypatch: MonkeyPatch): +def setup_env(monkeypatch: MonkeyPatch, **kwargs): user = random_string(10) mail = random_string(10) name = random_string(10) @@ -140,11 +142,55 @@ def ldap_initialize_mock(url): provider = get_provider(session, user, password) result = provider.get_user() + app_settings = get_app_settings() + assert result assert result.username == user assert result.email == mail assert result.full_name == name assert result.admin is False + assert result.group == app_settings.DEFAULT_GROUP + assert result.household == app_settings.DEFAULT_HOUSEHOLD + assert result.auth_method == AuthMethod.LDAP + + +@pytest.mark.parametrize("valid_group", [True, False]) +@pytest.mark.parametrize("valid_household", [True, False]) +def test_ldap_user_creation_invalid_group_or_household( + unfiltered_database: AllRepositories, monkeypatch: MonkeyPatch, valid_group: bool, valid_household: bool +): + user, mail, name, password, query_bind, query_password = setup_env(monkeypatch) + if not valid_group: + monkeypatch.setenv("DEFAULT_GROUP", random_string()) + if not valid_household: + monkeypatch.setenv("DEFAULT_HOUSEHOLD", random_string()) + + def ldap_initialize_mock(url): + assert url == "" + return LdapConnMock(user, password, False, query_bind, query_password, mail, name) + + monkeypatch.setattr(ldap, "initialize", ldap_initialize_mock) + + get_app_settings.cache_clear() + + with session_context() as session: + provider = get_provider(session, user, password) + try: + result = provider.get_user() + except ValueError: + result = None + + if valid_group and valid_household: + assert result + else: + assert not result + + # check if the user exists in the db + user = unfiltered_database.users.get_by_username(user) + if valid_group and valid_household: + assert user + else: + assert not user def test_ldap_user_creation_fail(monkeypatch: MonkeyPatch):