def tools_reboot(operation_logger, force=False): reboot = force if not reboot: try: # Ask confirmation for restoring i = msignals.prompt(m18n.n("server_reboot_confirm", answers="y/N")) except NotImplemented: pass else: if i.lower() == "y" or i.lower() == "yes": reboot = True if reboot: operation_logger.start() logger.warn(m18n.n("server_reboot")) subprocess.check_call(["systemctl", "reboot"])
def tools_reboot(operation_logger, force=False): reboot = force if not reboot: try: # Ask confirmation for restoring i = msignals.prompt(m18n.n('server_reboot_confirm', answers='y/N')) except NotImplemented: pass else: if i.lower() == 'y' or i.lower() == 'yes': reboot = True if reboot: operation_logger.start() logger.warn(m18n.n('server_reboot')) subprocess.check_call(['systemctl', 'reboot'])
def tools_shutdown(operation_logger, force=False): shutdown = force if not shutdown: try: # Ask confirmation for server shutdown i = msignals.prompt( m18n.n("server_shutdown_confirm", answers="y/N")) except NotImplemented: pass else: if i.lower() == "y" or i.lower() == "yes": shutdown = True if shutdown: operation_logger.start() logger.warn(m18n.n("server_shutdown")) subprocess.check_call(["systemctl", "poweroff"])
def tools_shutdown(operation_logger, force=False): shutdown = force if not shutdown: try: # Ask confirmation for server shutdown i = msignals.prompt( m18n.n('server_shutdown_confirm', answers='y/N')) except NotImplemented: pass else: if i.lower() == 'y' or i.lower() == 'yes': shutdown = True if shutdown: operation_logger.start() logger.warn(m18n.n('server_shutdown')) subprocess.check_call(['systemctl', 'poweroff'])
def user_create(operation_logger, username, firstname, lastname, domain, password, mailbox_quota="0", mail=None): from yunohost.domain import domain_list, _get_maindomain from yunohost.hook import hook_callback from yunohost.utils.password import assert_password_is_strong_enough from yunohost.utils.ldap import _get_ldap_interface # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) if mail is not None: logger.warning( "Packagers ! Using --mail in 'yunohost user create' is deprecated ... please use --domain instead." ) domain = mail.split("@")[-1] # Validate domain used for email address/xmpp account if domain is None: if msettings.get('interface') == 'api': raise YunohostError('Invalide usage, specify domain argument') else: # On affiche les differents domaines possibles msignals.display(m18n.n('domains_available')) for domain in domain_list()['domains']: msignals.display("- {}".format(domain)) maindomain = _get_maindomain() domain = msignals.prompt( m18n.n('ask_user_domain') + ' (default: %s)' % maindomain) if not domain: domain = maindomain # Check that the domain exists if domain not in domain_list()['domains']: raise YunohostError('domain_name_unknown', domain=domain) mail = username + '@' + domain ldap = _get_ldap_interface() if username in user_list()["users"]: raise YunohostError("user_already_exists", user=username) # Validate uniqueness of username and mail in LDAP try: ldap.validate_uniqueness({ 'uid': username, 'mail': mail, 'cn': username }) except Exception as e: raise YunohostError('user_creation_failed', user=username, error=e) # Validate uniqueness of username in system users all_existing_usernames = {x.pw_name for x in pwd.getpwall()} if username in all_existing_usernames: raise YunohostError('system_username_exists') main_domain = _get_maindomain() aliases = [ 'root@' + main_domain, 'admin@' + main_domain, 'webmaster@' + main_domain, 'postmaster@' + main_domain, 'abuse@' + main_domain, ] if mail in aliases: raise YunohostError('mail_unavailable') operation_logger.start() # Get random UID/GID all_uid = {str(x.pw_uid) for x in pwd.getpwall()} all_gid = {str(x.gr_gid) for x in grp.getgrall()} uid_guid_found = False while not uid_guid_found: # LXC uid number is limited to 65536 by default uid = str(random.randint(200, 65000)) uid_guid_found = uid not in all_uid and uid not in all_gid # Adapt values for LDAP fullname = '%s %s' % (firstname, lastname) attr_dict = { 'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh'], 'givenName': [firstname], 'sn': [lastname], 'displayName': [fullname], 'cn': [fullname], 'uid': [username], 'mail': mail, # NOTE: this one seems to be already a list 'maildrop': [username], 'mailuserquota': [mailbox_quota], 'userPassword': [_hash_user_password(password)], 'gidNumber': [uid], 'uidNumber': [uid], 'homeDirectory': ['/home/' + username], 'loginShell': ['/bin/false'] } # If it is the first user, add some aliases if not ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'): attr_dict['mail'] = [attr_dict['mail']] + aliases try: ldap.add('uid=%s,ou=users' % username, attr_dict) except Exception as 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: # Attempt to create user home folder subprocess.check_call(["mkhomedir_helper", username]) except subprocess.CalledProcessError: if not os.path.isdir('/home/{0}'.format(username)): logger.warning(m18n.n('user_home_creation_failed'), 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_update(groupname='all_users', add=username, force=True, sync_perm=True) # Trigger post_user_create hooks env_dict = { "YNH_USER_USERNAME": username, "YNH_USER_MAIL": mail, "YNH_USER_PASSWORD": password, "YNH_USER_FIRSTNAME": firstname, "YNH_USER_LASTNAME": lastname } hook_callback('post_user_create', args=[username, mail], env=env_dict) # TODO: Send a welcome mail to user logger.success(m18n.n('user_created')) return {'fullname': fullname, 'username': username, 'mail': mail}
def user_update(operation_logger, username, firstname=None, lastname=None, mail=None, change_password=None, add_mailforward=None, remove_mailforward=None, add_mailalias=None, remove_mailalias=None, mailbox_quota=None): """ Update user informations Keyword argument: lastname mail firstname add_mailalias -- Mail aliases to add remove_mailforward -- Mailforward addresses to remove username -- Username of user to update add_mailforward -- Mailforward addresses to add change_password -- New password to set remove_mailalias -- Mail aliases to remove """ from yunohost.domain import domain_list, _get_maindomain from yunohost.app import app_ssowatconf from yunohost.utils.password import assert_password_is_strong_enough from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_callback domains = domain_list()['domains'] # Populate user informations ldap = _get_ldap_interface() attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop'] result = ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) if not result: raise YunohostError('user_unknown', user=username) user = result[0] env_dict = {"YNH_USER_USERNAME": username} # Get modifications from arguments new_attr_dict = {} if firstname: new_attr_dict['givenName'] = [firstname] # TODO: Validate new_attr_dict['cn'] = new_attr_dict['displayName'] = [ firstname + ' ' + user['sn'][0] ] env_dict["YNH_USER_FIRSTNAME"] = firstname if lastname: new_attr_dict['sn'] = [lastname] # TODO: Validate new_attr_dict['cn'] = new_attr_dict['displayName'] = [ user['givenName'][0] + ' ' + lastname ] env_dict["YNH_USER_LASTNAME"] = lastname if lastname and firstname: new_attr_dict['cn'] = new_attr_dict['displayName'] = [ firstname + ' ' + lastname ] # change_password is None if user_update is not called to change the password if change_password is not None: # when in the cli interface if the option to change the password is called # without a specified value, change_password will be set to the const 0. # In this case we prompt for the new password. if msettings.get('interface') == 'cli' and not change_password: change_password = msignals.prompt(m18n.n("ask_password"), True, True) # Ensure sufficiently complex password assert_password_is_strong_enough("user", change_password) new_attr_dict['userPassword'] = [_hash_user_password(change_password)] env_dict["YNH_USER_PASSWORD"] = change_password if mail: main_domain = _get_maindomain() aliases = [ 'root@' + main_domain, 'admin@' + main_domain, 'webmaster@' + main_domain, 'postmaster@' + main_domain, ] try: ldap.validate_uniqueness({'mail': mail}) except Exception as e: raise YunohostError('user_update_failed', user=username, error=e) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) if mail in aliases: raise YunohostError('mail_unavailable') del user['mail'][0] new_attr_dict['mail'] = [mail] + user['mail'] if add_mailalias: if not isinstance(add_mailalias, list): add_mailalias = [add_mailalias] for mail in add_mailalias: try: ldap.validate_uniqueness({'mail': mail}) except Exception as e: raise YunohostError('user_update_failed', user=username, error=e) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) user['mail'].append(mail) new_attr_dict['mail'] = user['mail'] if remove_mailalias: if not isinstance(remove_mailalias, list): remove_mailalias = [remove_mailalias] for mail in remove_mailalias: if len(user['mail']) > 1 and mail in user['mail'][1:]: user['mail'].remove(mail) else: raise YunohostError('mail_alias_remove_failed', mail=mail) new_attr_dict['mail'] = user['mail'] if 'mail' in new_attr_dict: env_dict["YNH_USER_MAILS"] = ','.join(new_attr_dict['mail']) if add_mailforward: if not isinstance(add_mailforward, list): add_mailforward = [add_mailforward] for mail in add_mailforward: if mail in user['maildrop'][1:]: continue user['maildrop'].append(mail) new_attr_dict['maildrop'] = user['maildrop'] if remove_mailforward: if not isinstance(remove_mailforward, list): remove_mailforward = [remove_mailforward] for mail in remove_mailforward: if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]: user['maildrop'].remove(mail) else: raise YunohostError('mail_forward_remove_failed', mail=mail) new_attr_dict['maildrop'] = user['maildrop'] if 'maildrop' in new_attr_dict: env_dict["YNH_USER_MAILFORWARDS"] = ','.join(new_attr_dict['maildrop']) if mailbox_quota is not None: new_attr_dict['mailuserquota'] = [mailbox_quota] env_dict["YNH_USER_MAILQUOTA"] = mailbox_quota operation_logger.start() try: ldap.update('uid=%s,ou=users' % username, new_attr_dict) except Exception as e: raise YunohostError('user_update_failed', user=username, error=e) # Trigger post_user_update hooks hook_callback('post_user_update', env=env_dict) logger.success(m18n.n('user_updated')) app_ssowatconf() return user_info(username)
def domain_remove(operation_logger, domain, remove_apps=False, force=False): """ Delete domains Keyword argument: domain -- Domain to delete remove_apps -- Remove applications installed on the domain force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface # the 'force' here is related to the exception happening in domain_add ... # we don't want to check the domain exists because the ldap add may have # failed if not force and domain not in domain_list()['domains']: raise YunohostError('domain_name_unknown', domain=domain) # Check domain is not the main domain if domain == _get_maindomain(): other_domains = domain_list()["domains"] other_domains.remove(domain) if other_domains: raise YunohostError( "domain_cannot_remove_main", domain=domain, other_domains="\n * " + ("\n * ".join(other_domains)), ) else: raise YunohostError("domain_cannot_remove_main_add_new_one", domain=domain) # Check if apps are installed on the domain apps_on_that_domain = [] for app in _installed_apps(): settings = _get_app_settings(app) label = app_info(app)["name"] if settings.get("domain") == domain: apps_on_that_domain.append( (app, " - %s \"%s\" on https://%s%s" % (app, label, domain, settings["path"]) if "path" in settings else app)) if apps_on_that_domain: if remove_apps: if msettings.get('interface') == "cli" and not force: answer = msignals.prompt(m18n.n( 'domain_remove_confirm_apps_removal', apps="\n".join([x[1] for x in apps_on_that_domain]), answers='y/N'), color="yellow") if answer.upper() != "Y": raise YunohostError("aborting") for app, _ in apps_on_that_domain: app_remove(app) else: raise YunohostError('domain_uninstall_app_first', apps="\n".join( [x[1] for x in apps_on_that_domain])) operation_logger.start() ldap = _get_ldap_interface() try: ldap.remove("virtualdomain=" + domain + ",ou=domains") except Exception as e: raise YunohostError("domain_deletion_failed", domain=domain, error=e) os.system("rm -rf /etc/yunohost/certs/%s" % domain) # Sometime we have weird issues with the regenconf where some files # appears as manually modified even though they weren't touched ... # There are a few ideas why this happens (like backup/restore nginx # conf ... which we shouldnt do ...). This in turns creates funky # situation where the regenconf may refuse to re-create the conf # (when re-creating a domain..) # # So here we force-clear the has out of the regenconf if it exists. # This is a pretty ad hoc solution and only applied to nginx # because it's one of the major service, but in the long term we # should identify the root of this bug... _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain]) # And in addition we even force-delete the file Otherwise, if the file was # manually modified, it may not get removed by the regenconf which leads to # catastrophic consequences of nginx breaking because it can't load the # cert file which disappeared etc.. if os.path.exists("/etc/nginx/conf.d/%s.conf" % domain): _process_regen_conf("/etc/nginx/conf.d/%s.conf" % domain, new_conf=None, save=True) regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix"]) app_ssowatconf() hook_callback("post_domain_remove", args=[domain]) logger.success(m18n.n("domain_deleted"))
def user_create( operation_logger, username, firstname, lastname, domain, password, mailbox_quota="0", mail=None, ): from yunohost.domain import domain_list, _get_maindomain from yunohost.hook import hook_callback from yunohost.utils.password import assert_password_is_strong_enough from yunohost.utils.ldap import _get_ldap_interface # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) if mail is not None: logger.warning( "Packagers ! Using --mail in 'yunohost user create' is deprecated ... please use --domain instead." ) domain = mail.split("@")[-1] # Validate domain used for email address/xmpp account if domain is None: if msettings.get("interface") == "api": raise YunohostValidationError( "Invalid usage, you should specify a domain argument") else: # On affiche les differents domaines possibles msignals.display(m18n.n("domains_available")) for domain in domain_list()["domains"]: msignals.display("- {}".format(domain)) maindomain = _get_maindomain() domain = msignals.prompt( m18n.n("ask_user_domain") + " (default: %s)" % maindomain) if not domain: domain = maindomain # Check that the domain exists if domain not in domain_list()["domains"]: raise YunohostValidationError("domain_name_unknown", domain=domain) mail = username + "@" + domain ldap = _get_ldap_interface() if username in user_list()["users"]: raise YunohostValidationError("user_already_exists", user=username) # Validate uniqueness of username and mail in LDAP try: ldap.validate_uniqueness({ "uid": username, "mail": mail, "cn": username }) except Exception as e: raise YunohostValidationError("user_creation_failed", user=username, error=e) # Validate uniqueness of username in system users all_existing_usernames = {x.pw_name for x in pwd.getpwall()} if username in all_existing_usernames: raise YunohostValidationError("system_username_exists") main_domain = _get_maindomain() aliases = [ "root@" + main_domain, "admin@" + main_domain, "webmaster@" + main_domain, "postmaster@" + main_domain, "abuse@" + main_domain, ] if mail in aliases: raise YunohostValidationError("mail_unavailable") operation_logger.start() # Get random UID/GID all_uid = {str(x.pw_uid) for x in pwd.getpwall()} all_gid = {str(x.gr_gid) for x in grp.getgrall()} uid_guid_found = False while not uid_guid_found: # LXC uid number is limited to 65536 by default uid = str(random.randint(1001, 65000)) uid_guid_found = uid not in all_uid and uid not in all_gid # Adapt values for LDAP fullname = "%s %s" % (firstname, lastname) attr_dict = { "objectClass": [ "mailAccount", "inetOrgPerson", "posixAccount", "userPermissionYnh", ], "givenName": [firstname], "sn": [lastname], "displayName": [fullname], "cn": [fullname], "uid": [username], "mail": mail, # NOTE: this one seems to be already a list "maildrop": [username], "mailuserquota": [mailbox_quota], "userPassword": [_hash_user_password(password)], "gidNumber": [uid], "uidNumber": [uid], "homeDirectory": ["/home/" + username], "loginShell": ["/bin/false"], } # If it is the first user, add some aliases if not ldap.search(base="ou=users,dc=yunohost,dc=org", filter="uid=*"): attr_dict["mail"] = [attr_dict["mail"]] + aliases try: ldap.add("uid=%s,ou=users" % username, attr_dict) except Exception as 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: # Attempt to create user home folder subprocess.check_call(["mkhomedir_helper", username]) except subprocess.CalledProcessError: if not os.path.isdir("/home/{0}".format(username)): logger.warning(m18n.n("user_home_creation_failed"), 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_update(groupname="all_users", add=username, force=True, sync_perm=True) # Trigger post_user_create hooks env_dict = { "YNH_USER_USERNAME": username, "YNH_USER_MAIL": mail, "YNH_USER_PASSWORD": password, "YNH_USER_FIRSTNAME": firstname, "YNH_USER_LASTNAME": lastname, } hook_callback("post_user_create", args=[username, mail], env=env_dict) # TODO: Send a welcome mail to user logger.success(m18n.n("user_created")) return {"fullname": fullname, "username": username, "mail": mail}