form:UserOption: merge choices and default into root validator with first admin user as default

This commit is contained in:
axolotle 2023-11-26 18:29:43 +01:00
parent 4a270b88b6
commit 650c0136f2
2 changed files with 25 additions and 56 deletions

View file

@ -1449,6 +1449,7 @@ class TestSelect(BaseTest):
# │ TAGS │ # │ TAGS │
# ╰───────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────╯
# [], ["one"], {} # [], ["one"], {}
class TestTags(BaseTest): class TestTags(BaseTest):
raw_option = {"type": "tags", "id": "tags_id"} raw_option = {"type": "tags", "id": "tags_id"}
@ -1678,47 +1679,29 @@ class TestApp(BaseTest):
admin_username = "admin_user" admin_username = "admin_user"
admin_user = { admin_user = {
"ssh_allowed": False,
"username": admin_username, "username": admin_username,
"mailbox-quota": "0",
"mail": "a@ynh.local", "mail": "a@ynh.local",
"mail-aliases": [f"root@{main_domain}"], # Faking "admin"
"fullname": "john doe", "fullname": "john doe",
"group": [], "groups": ["admins"],
} }
regular_username = "normal_user" regular_username = "normal_user"
regular_user = { regular_user = {
"ssh_allowed": False,
"username": regular_username, "username": regular_username,
"mailbox-quota": "0",
"mail": "z@ynh.local", "mail": "z@ynh.local",
"fullname": "john doe", "fullname": "john doe",
"group": [], "groups": [],
} }
@contextmanager @contextmanager
def patch_users( def patch_users(*, users):
*,
users,
admin_username,
main_domain,
):
""" """
Data mocking for UserOption: Data mocking for UserOption:
- yunohost.user.user_list - yunohost.user.user_list
- yunohost.user.user_info - yunohost.user.user_info
- yunohost.domain._get_maindomain - yunohost.domain._get_maindomain
""" """
admin_info = next( with patch.object(user, "user_list", return_value={"users": users}):
(user for user in users.values() if user["username"] == admin_username),
{"mail-aliases": []},
)
with patch.object(user, "user_list", return_value={"users": users}), patch.object(
user,
"user_info",
return_value=admin_info, # Faking admin user
), patch.object(domain, "_get_maindomain", return_value=main_domain):
yield yield
@ -1729,8 +1712,8 @@ class TestUser(BaseTest):
# No tests for empty users since it should not happens # No tests for empty users since it should not happens
{ {
"data": [ "data": [
{"users": {admin_username: admin_user}, "admin_username": admin_username, "main_domain": main_domain}, {"users": {admin_username: admin_user}},
{"users": {admin_username: admin_user, regular_username: regular_user}, "admin_username": admin_username, "main_domain": main_domain}, {"users": {admin_username: admin_user, regular_username: regular_user}},
], ],
"scenarios": [ "scenarios": [
# FIXME User option is not really nullable, even if optional # FIXME User option is not really nullable, even if optional
@ -1741,7 +1724,7 @@ class TestUser(BaseTest):
}, },
{ {
"data": [ "data": [
{"users": {admin_username: admin_user, regular_username: regular_user}, "admin_username": admin_username, "main_domain": main_domain}, {"users": {admin_username: admin_user, regular_username: regular_user}},
], ],
"scenarios": [ "scenarios": [
*xpass(scenarios=[ *xpass(scenarios=[
@ -1756,18 +1739,12 @@ class TestUser(BaseTest):
@pytest.mark.usefixtures("patch_no_tty") @pytest.mark.usefixtures("patch_no_tty")
def test_basic_attrs(self): def test_basic_attrs(self):
with patch_users( with patch_users(users={admin_username: admin_user}):
users={admin_username: admin_user},
admin_username=admin_username,
main_domain=main_domain,
):
self._test_basic_attrs() self._test_basic_attrs()
def test_options_prompted_with_ask_help(self, prefill_data=None): def test_options_prompted_with_ask_help(self, prefill_data=None):
with patch_users( with patch_users(
users={admin_username: admin_user, regular_username: regular_user}, users={admin_username: admin_user, regular_username: regular_user}
admin_username=admin_username,
main_domain=main_domain,
): ):
super().test_options_prompted_with_ask_help( super().test_options_prompted_with_ask_help(
prefill_data={"raw_option": {}, "prefill": admin_username} prefill_data={"raw_option": {}, "prefill": admin_username}

View file

@ -46,6 +46,7 @@ from pydantic import (
ValidationError, ValidationError,
create_model, create_model,
validator, validator,
root_validator,
) )
from pydantic.color import Color from pydantic.color import Color
from pydantic.fields import Field from pydantic.fields import Field
@ -1705,43 +1706,34 @@ class UserOption(BaseChoicesOption):
filter: Literal[None] = None filter: Literal[None] = None
choices: Union[dict[str, str], None] choices: Union[dict[str, str], None]
@validator("choices", pre=True, always=True) @root_validator(pre=True)
def inject_users_choices( def inject_users_choices_and_default(cls, values: Values) -> Values:
cls, value: Union[dict[str, str], None], values: Values
) -> dict[str, str]:
# TODO remove calls to resources in validators (pydantic V2 should adress this) # TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.user import user_list from yunohost.user import user_list
value = { users = user_list(fields=["username", "fullname", "mail", "groups"])["users"]
values["choices"] = {
username: f"{infos['fullname']} ({infos['mail']})" username: f"{infos['fullname']} ({infos['mail']})"
for username, infos in user_list()["users"].items() for username, infos in users.items()
} }
# FIXME keep this to test if any user, do not raise error if no admin? # FIXME keep this to test if any user, do not raise error if no admin?
if not value: if not values["choices"]:
raise YunohostValidationError( raise YunohostValidationError(
"app_argument_invalid", "app_argument_invalid",
name=values["id"], name=values["id"],
error="You should create a YunoHost user first.", error="You should create a YunoHost user first.",
) )
return value if not values.get("default"):
values["default"] = next(
username
for username, infos in users.items()
if "admins" in infos["groups"]
)
@validator("default", pre=True, always=True) return values
def inject_default(
cls, value: Union[str, None], values: Values
) -> Union[str, None]:
# TODO remove calls to resources in validators (pydantic V2 should adress this)
# from yunohost.domain import _get_maindomain
# from yunohost.user import user_list, user_info
if value is None:
# FIXME : in the past we looked for the user holding the "root@" alias
# but it's now obsolete...
# Should be replaced by something like "any first user we find in the admin group"
pass
return value
class GroupOption(BaseChoicesOption): class GroupOption(BaseChoicesOption):