Add users_add bulk operation

This commit is contained in:
selfhoster1312 2024-05-10 17:37:46 +02:00
parent 3f070edb61
commit 5efd72a9d7
2 changed files with 72 additions and 13 deletions

View file

@ -7,7 +7,7 @@ import os
from .conftest import message, raiseYunohostError, get_test_apps_dir from .conftest import message, raiseYunohostError, get_test_apps_dir
from yunohost.domain import _get_maindomain, domain_add, domain_remove, domain_list from yunohost.domain import _get_maindomain, domain_add, domain_remove, domain_list
from yunohost.user import user_create, user_list, user_delete from yunohost.user import user_create, user_list, user_delete, User, users_add
from yunohost.authenticators.ldap_ynhuser import Authenticator, SESSION_FOLDER, short_hash from yunohost.authenticators.ldap_ynhuser import Authenticator, SESSION_FOLDER, short_hash
from yunohost.app import app_install, app_remove, app_setting, app_ssowatconf, app_change_url from yunohost.app import app_install, app_remove, app_setting, app_ssowatconf, app_change_url
from yunohost.permission import user_permission_list, user_permission_update from yunohost.permission import user_permission_list, user_permission_update
@ -44,10 +44,11 @@ def setup_module(module):
assert os.system("systemctl is-active yunohost-portal-api >/dev/null") == 0 assert os.system("systemctl is-active yunohost-portal-api >/dev/null") == 0
userlist = user_list()["users"] userlist = user_list()["users"]
if "alice" not in userlist: users_to_add = [ user for user in [
user_create("alice", maindomain, dummy_password, fullname="Alice White", admin=True) User("alice", maindomain, dummy_password, fullname="Alice White", admin=True),
if "bob" not in userlist: User("bob", maindomain, dummy_password, fullname="Bob Marley"),
user_create("bob", maindomain, dummy_password, fullname="Bob Marley") ] if user.name not in userlist ]
users_add(users_to_add)
domainlist = domain_list()["domains"] domainlist = domain_list()["domains"]
domains = [ domain for domain in [ subdomain, secondarydomain ] if domain not in domainlist ] domains = [ domain for domain in [ subdomain, secondarydomain ] if domain not in domainlist ]

View file

@ -24,6 +24,7 @@ import random
import subprocess import subprocess
import copy import copy
from logging import getLogger from logging import getLogger
from typing import List, Optional
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
@ -49,6 +50,20 @@ FIELDS_FOR_IMPORT = {
ADMIN_ALIASES = ["root", "admin", "admins", "webmaster", "postmaster", "abuse"] ADMIN_ALIASES = ["root", "admin", "admins", "webmaster", "postmaster", "abuse"]
class User:
def __init__(
self,
name: str,
domain: str,
password: str,
fullname: Optional[str] = None,
admin: bool = False,
):
self.name = name
self.password = password
self.domain = domain
self.fullname = fullname
self.admin = admin
def user_list(fields=None): def user_list(fields=None):
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
@ -131,6 +146,40 @@ def shellexists(shell):
"""Check if the provided shell exists and is executable.""" """Check if the provided shell exists and is executable."""
return os.path.isfile(shell) and os.access(shell, os.X_OK) return os.path.isfile(shell) and os.access(shell, os.X_OK)
# Used in tests to create many users at once.
# The permissions are synchronized at the end of the entire operation.
@is_unit_operation()
def users_add(
operation_logger,
users: List[User],
):
hooks = []
for user in users:
# Only force regen_conf on the last iteration
hooks.append(user_create(user.name, user.domain, user.password, fullname=user.fullname, admin=user.admin, do_regen_conf=False))
# Invalidate passwd and group to take user and group creation into account
subprocess.call(["nscd", "-i", "passwd"])
subprocess.call(["nscd", "-i", "group"])
# Add new users to all_users group
user_group_update(groupname="all_users", add=[ user.name for user in users ], force=True, sync_perm=False)
# Do we have new admins?
admins = [ user.name for user in users if user.admin ]
if len(admins) > 0:
user_group_update(groupname="admins", add=admins, sync_perm=False)
from yunohost.permission import permission_sync_to_user
from yunohost.hook import hook_callback
# Now we can sync the permissions
permission_sync_to_user()
for hook in hooks:
# Trigger post_user_create hooks
hook_callback("post_user_create", args=[hook["YNH_USER_USERNAME"], hook["YNH_USER_PASSWORD"]], env=hook)
logger.success(m18n.n("user_created"))
@is_unit_operation([("username", "user")]) @is_unit_operation([("username", "user")])
def user_create( def user_create(
@ -143,6 +192,7 @@ def user_create(
admin=False, admin=False,
from_import=False, from_import=False,
loginShell=None, loginShell=None,
do_regen_conf=True,
): ):
if not fullname or not fullname.strip(): if not fullname or not fullname.strip():
raise YunohostValidationError( raise YunohostValidationError(
@ -260,9 +310,6 @@ def user_create(
except Exception as e: except Exception as e:
raise YunohostError("user_creation_failed", user=username, error=e) raise YunohostError("user_creation_failed", user=username, error=e)
# Invalidate passwd and group to take user and group creation into account
subprocess.call(["nscd", "-i", "passwd"])
subprocess.call(["nscd", "-i", "group"])
try: try:
# Attempt to create user home folder # Attempt to create user home folder
@ -277,13 +324,8 @@ def user_create(
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
logger.warning(f"Failed to protect /home/{username}", exc_info=1) logger.warning(f"Failed to protect /home/{username}", exc_info=1)
# Create group for user and add to group 'all_users'
user_group_create(groupname=username, gid=uid, primary_group=True, sync_perm=False) user_group_create(groupname=username, gid=uid, primary_group=True, sync_perm=False)
user_group_update(groupname="all_users", add=username, force=True, sync_perm=True)
if admin:
user_group_update(groupname="admins", add=username, sync_perm=True)
# Trigger post_user_create hooks
env_dict = { env_dict = {
"YNH_USER_USERNAME": username, "YNH_USER_USERNAME": username,
"YNH_USER_MAIL": mail, "YNH_USER_MAIL": mail,
@ -292,6 +334,22 @@ def user_create(
"YNH_USER_LASTNAME": lastname, "YNH_USER_LASTNAME": lastname,
} }
# If do_regen_conf is False, it means we are in a higher operation that will
# finish synchronizing everything, then run the hooks... so we return early,
# transmitting the env_dict for further hook run.
if not do_regen_conf:
return env_dict
# Invalidate passwd and group to take user and group creation into account
subprocess.call(["nscd", "-i", "passwd"])
subprocess.call(["nscd", "-i", "group"])
# Create group for user and add to group 'all_users'
user_group_update(groupname="all_users", add=username, force=True, sync_perm=not admin)
if admin:
user_group_update(groupname="admins", add=username, sync_perm=True)
# Trigger post_user_create hooks
hook_callback("post_user_create", args=[username, mail], env=env_dict) hook_callback("post_user_create", args=[username, mail], env=env_dict)
# TODO: Send a welcome mail to user # TODO: Send a welcome mail to user