diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 6b5ca2287..237a6e404 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -43,13 +43,8 @@ _global: parameters: uri: ldap://localhost:389 base_dn: dc=yunohost,dc=org - user_rdn: cn=admin - ldap-anonymous: - vendor: ldap - parameters: - uri: ldap://localhost:389 - base_dn: dc=yunohost,dc=org - argument_auth: true + user_rdn: cn=admin,dc=yunohost,dc=org + argument_auth: false arguments: -v: full: --version @@ -70,9 +65,6 @@ user: list: action_help: List users api: GET /users - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: --fields: help: fields to fetch @@ -82,8 +74,6 @@ user: create: action_help: Create user api: POST /users - configuration: - authenticate: all arguments: username: help: The unique username to create @@ -140,8 +130,6 @@ user: delete: action_help: Delete user api: DELETE /users/ - configuration: - authenticate: all arguments: username: help: Username to delete @@ -155,8 +143,6 @@ user: update: action_help: Update user informations api: PUT /users/ - configuration: - authenticate: all arguments: username: help: Username to update @@ -209,9 +195,6 @@ user: info: action_help: Get user information api: GET /users/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: username: help: Username or email to get information @@ -225,8 +208,6 @@ user: allow: action_help: Allow the user to uses ssh api: POST /users/ssh/enable - configuration: - authenticate: all arguments: username: help: Username of the user @@ -237,8 +218,6 @@ user: disallow: action_help: Disallow the user to uses ssh api: POST /users/ssh/disable - configuration: - authenticate: all arguments: username: help: Username of the user @@ -249,8 +228,6 @@ user: list-keys: action_help: Show user's authorized ssh keys api: GET /users/ssh/keys - configuration: - authenticate: all arguments: username: help: Username of the user @@ -261,8 +238,6 @@ user: add-key: action_help: Add a new authorized ssh key for this user api: POST /users/ssh/key - configuration: - authenticate: all arguments: username: help: Username of the user @@ -278,8 +253,6 @@ user: remove-key: action_help: Remove an authorized ssh key for this user api: DELETE /users/ssh/key - configuration: - authenticate: all arguments: username: help: Username of the user @@ -300,16 +273,11 @@ domain: list: action_help: List domains api: GET /domains - configuration: - authenticate: all - authenticator: ldap-anonymous ### domain_add() add: action_help: Create a custom domain api: POST /domains - configuration: - authenticate: all arguments: domain: help: Domain name to add @@ -326,8 +294,6 @@ domain: remove: action_help: Delete domains api: DELETE /domains/ - configuration: - authenticate: all arguments: domain: help: Domain to delete @@ -338,9 +304,6 @@ domain: dns-conf: action_help: Generate DNS configuration for a domain api: GET /domains//dns - configuration: - authenticate: - - api arguments: domain: help: Target domain @@ -356,9 +319,6 @@ domain: cert-status: action_help: List status of current certificates (all by default). api: GET /domains/cert-status/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain_list: help: Domains to check @@ -371,9 +331,6 @@ domain: cert-install: action_help: Install Let's Encrypt certificates for given domains (all by default). api: POST /domains/cert-install/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain_list: help: Domains for which to install the certificates @@ -395,9 +352,6 @@ domain: cert-renew: action_help: Renew the Let's Encrypt certificates for given domains (all by default). api: POST /domains/cert-renew/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain_list: help: Domains for which to renew the certificates @@ -419,9 +373,6 @@ domain: url-available: action_help: Check availability of a web path api: GET /domain/urlavailable - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain: help: The domain for the web path (e.g. your.domain.tld) @@ -542,9 +493,6 @@ app: install: action_help: Install apps api: POST /apps - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: Name, local path or git URL of the app to install @@ -567,9 +515,6 @@ app: remove: action_help: Remove app api: DELETE /apps/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App(s) to delete @@ -578,9 +523,6 @@ app: upgrade: action_help: Upgrade app api: PUT /upgrade/apps - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App(s) to upgrade (default all) @@ -596,9 +538,6 @@ app: change-url: action_help: Change app's URL api: PUT /apps//changeurl - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: Target app instance name @@ -651,9 +590,6 @@ app: action_help: Check availability of a web path api: GET /tools/checkurl deprecated: True - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: url: help: Url to check @@ -665,9 +601,6 @@ app: register-url: action_help: Book/register a web path for a given app api: PUT /tools/registerurl - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App which will use the web path @@ -707,9 +640,6 @@ app: makedefault: action_help: Redirect domain root to an app api: PUT /apps//default - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App name to put on domain root @@ -721,17 +651,11 @@ app: ssowatconf: action_help: Regenerate SSOwat configuration file api: PUT /ssowatconf - configuration: - authenticate: all - authenticator: ldap-anonymous ### app_change_label() change-label: action_help: Change app label api: PUT /apps//label - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App ID @@ -742,9 +666,6 @@ app: addaccess: action_help: Grant access right to users (everyone by default) api: PUT /access - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: apps: nargs: "+" @@ -756,9 +677,6 @@ app: removeaccess: action_help: Revoke access right to users (everyone by default) api: DELETE /access - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: apps: nargs: "+" @@ -770,9 +688,6 @@ app: clearaccess: action_help: Reset access rights for the app api: POST /access - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: apps: nargs: "+" @@ -870,9 +785,6 @@ backup: restore: action_help: Restore from a local backup archive. If neither --apps or --system are given, this will restore all apps and all system parts in the archive. If only --apps if given, this will only restore apps and no system parts. Similarly, if only --system is given, this will only restore system parts and no apps. api: POST /backup/restore/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: name: help: Name of the local backup archive @@ -1481,8 +1393,6 @@ tools: adminpw: action_help: Change password of admin and root users api: PUT /adminpw - configuration: - authenticate: all arguments: -n: full: --new-password @@ -1498,8 +1408,6 @@ tools: api: - GET /domains/main - PUT /domains/main - configuration: - authenticate: all arguments: -n: full: --new-domain @@ -1552,9 +1460,6 @@ tools: upgrade: action_help: YunoHost upgrade api: PUT /upgrade - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: --apps: help: List of apps to upgrade (all by default) @@ -1567,9 +1472,6 @@ tools: diagnosis: action_help: YunoHost diagnosis api: GET /diagnosis - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: -p: full: --private @@ -1588,8 +1490,6 @@ tools: ### tools_shell() shell: - configuration: - authenticate: all action_help: Launch a development shell arguments: -c: diff --git a/data/templates/slapd/slapd.conf b/data/templates/slapd/slapd.conf index 9a8800d9d..c8c363795 100644 --- a/data/templates/slapd/slapd.conf +++ b/data/templates/slapd/slapd.conf @@ -81,6 +81,7 @@ checkpoint 512 30 # These access lines apply to database #1 only access to attrs=userPassword,shadowLastChange by dn="cn=admin,dc=yunohost,dc=org" write + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by anonymous auth by self write by * none @@ -90,6 +91,7 @@ access to attrs=userPassword,shadowLastChange # Others should be able to see it. access to attrs=cn,gecos,givenName,mail,maildrop,displayName,sn by dn="cn=admin,dc=yunohost,dc=org" write + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self write by * read @@ -108,5 +110,6 @@ access to dn.base="" by * read # can read everything. access to * by dn="cn=admin,dc=yunohost,dc=org" write + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by group/groupOfNames/Member="cn=admin,ou=groups,dc=yunohost,dc=org" write by * read diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1426898f2..4ccfb6b47 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -38,12 +38,12 @@ from collections import OrderedDict from datetime import datetime from moulinette import msignals, m18n, msettings -from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_json from yunohost.service import service_log, service_status, _run_service_command from yunohost.utils import packages +from yunohost.utils.error import YunohostError from yunohost.log import is_unit_operation, OperationLogger logger = getActionLogger('yunohost.app') @@ -437,7 +437,7 @@ def app_map(app=None, raw=False, user=None): @is_unit_operation() -def app_change_url(operation_logger, auth, app, domain, path): +def app_change_url(operation_logger, app, domain, path): """ Modify the URL at which an application is installed. @@ -468,7 +468,7 @@ def app_change_url(operation_logger, auth, app, domain, path): raise YunohostError("app_change_url_identical_domains", domain=domain, path=path) # Check the url is available - conflicts = _get_conflicting_apps(auth, domain, path, ignore_app=app) + conflicts = _get_conflicting_apps(domain, path, ignore_app=app) if conflicts: apps = [] for path, app_id, app_label in conflicts: @@ -484,7 +484,7 @@ def app_change_url(operation_logger, auth, app, domain, path): # Retrieve arguments list for change_url script # TODO: Allow to specify arguments - args_odict = _parse_args_from_manifest(manifest, 'change_url', auth=auth) + args_odict = _parse_args_from_manifest(manifest, 'change_url') args_list = args_odict.values() args_list.append(app) @@ -538,7 +538,7 @@ def app_change_url(operation_logger, auth, app, domain, path): app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) - app_ssowatconf(auth) + app_ssowatconf() # avoid common mistakes if _run_service_command("reload", "nginx") == False: @@ -557,7 +557,7 @@ def app_change_url(operation_logger, auth, app, domain, path): hook_callback('post_app_change_url', args=args_list, env=env_dict) -def app_upgrade(auth, app=[], url=None, file=None): +def app_upgrade(app=[], url=None, file=None): """ Upgrade app @@ -633,7 +633,7 @@ def app_upgrade(auth, app=[], url=None, file=None): # Retrieve arguments list for upgrade script # TODO: Allow to specify arguments - args_odict = _parse_args_from_manifest(manifest, 'upgrade', auth=auth) + args_odict = _parse_args_from_manifest(manifest, 'upgrade') args_list = args_odict.values() args_list.append(app_instance_name) @@ -693,13 +693,13 @@ def app_upgrade(auth, app=[], url=None, file=None): if not_upgraded_apps: raise YunohostError('app_not_upgraded', apps=', '.join(not_upgraded_apps)) - app_ssowatconf(auth) + app_ssowatconf() logger.success(m18n.n('upgrade_complete')) @is_unit_operation() -def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False, force=False): +def app_install(operation_logger, app, label=None, args=None, no_remove_on_failure=False, force=False): """ Install apps @@ -791,7 +791,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on # Retrieve arguments list for install script args_dict = {} if not args else \ dict(urlparse.parse_qsl(args, keep_blank_values=True)) - args_odict = _parse_args_from_manifest(manifest, 'install', args=args_dict, auth=auth) + args_odict = _parse_args_from_manifest(manifest, 'install', args=args_dict) args_list = args_odict.values() args_list.append(app_instance_name) @@ -883,7 +883,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on shutil.rmtree(app_setting_path) shutil.rmtree(extracted_app_folder) - app_ssowatconf(auth) + app_ssowatconf() if packages.dpkg_is_broken(): logger.error(m18n.n("this_action_broke_dpkg")) @@ -910,7 +910,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on os.system('chown -R root: %s' % app_setting_path) os.system('chown -R admin: %s/scripts' % app_setting_path) - app_ssowatconf(auth) + app_ssowatconf() logger.success(m18n.n('installation_complete')) @@ -918,7 +918,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on @is_unit_operation() -def app_remove(operation_logger, auth, app): +def app_remove(operation_logger, app): """ Remove app @@ -970,13 +970,13 @@ def app_remove(operation_logger, auth, app): shutil.rmtree(app_setting_path) shutil.rmtree('/tmp/yunohost_remove') hook_remove(app) - app_ssowatconf(auth) + app_ssowatconf() if packages.dpkg_is_broken(): raise YunohostError("this_action_broke_dpkg") -def app_addaccess(auth, apps, users=[]): +def app_addaccess(apps, users=[]): """ Grant access right to users (everyone by default) @@ -991,7 +991,7 @@ def app_addaccess(auth, apps, users=[]): result = {} if not users: - users = user_list(auth)['users'].keys() + users = user_list()['users'].keys() elif not isinstance(users, list): users = [users, ] if not isinstance(apps, list): @@ -1021,7 +1021,7 @@ def app_addaccess(auth, apps, users=[]): for allowed_user in users: if allowed_user not in allowed_users: try: - user_info(auth, allowed_user) + user_info(allowed_user) except YunohostError: logger.warning(m18n.n('user_unknown', user=allowed_user)) continue @@ -1037,12 +1037,12 @@ def app_addaccess(auth, apps, users=[]): result[app] = allowed_users - app_ssowatconf(auth) + app_ssowatconf() return {'allowed_users': result} -def app_removeaccess(auth, apps, users=[]): +def app_removeaccess(apps, users=[]): """ Revoke access right to users (everyone by default) @@ -1084,7 +1084,7 @@ def app_removeaccess(auth, apps, users=[]): if allowed_user not in users: allowed_users.add(allowed_user) else: - for allowed_user in user_list(auth)['users'].keys(): + for allowed_user in user_list()['users'].keys(): if allowed_user not in users: allowed_users.add(allowed_user) @@ -1098,12 +1098,12 @@ def app_removeaccess(auth, apps, users=[]): operation_logger.success() - app_ssowatconf(auth) + app_ssowatconf() return {'allowed_users': result} -def app_clearaccess(auth, apps): +def app_clearaccess(apps): """ Reset access rights for the app @@ -1136,7 +1136,7 @@ def app_clearaccess(auth, apps): operation_logger.success() - app_ssowatconf(auth) + app_ssowatconf() def app_debug(app): @@ -1163,7 +1163,7 @@ def app_debug(app): @is_unit_operation() -def app_makedefault(operation_logger, auth, app, domain=None): +def app_makedefault(operation_logger, app, domain=None): """ Redirect domain root to an app @@ -1181,7 +1181,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): if domain is None: domain = app_domain operation_logger.related_to.append(('domain', domain)) - elif domain not in domain_list(auth)['domains']: + elif domain not in domain_list()['domains']: raise YunohostError('domain_unknown') operation_logger.start() @@ -1260,7 +1260,7 @@ def app_checkport(port): raise YunohostError('port_unavailable', port=int(port)) -def app_register_url(auth, app, domain, path): +def app_register_url(app, domain, path): """ Book/register a web path for a given app @@ -1286,7 +1286,7 @@ def app_register_url(auth, app, domain, path): raise YunohostError('app_already_installed_cant_change_url') # Check the url is available - conflicts = _get_conflicting_apps(auth, domain, path) + conflicts = _get_conflicting_apps(domain, path) if conflicts: apps = [] for path, app_id, app_label in conflicts: @@ -1303,7 +1303,7 @@ def app_register_url(auth, app, domain, path): app_setting(app, 'path', value=path) -def app_checkurl(auth, url, app=None): +def app_checkurl(url, app=None): """ Check availability of a web path @@ -1333,7 +1333,7 @@ def app_checkurl(auth, url, app=None): apps_map = app_map(raw=True) - if domain not in domain_list(auth)['domains']: + if domain not in domain_list()['domains']: raise YunohostError('domain_unknown') if domain in apps_map: @@ -1390,7 +1390,7 @@ def app_initdb(user, password=None, db=None, sql=None): logger.success(m18n.n('mysql_db_initialized')) -def app_ssowatconf(auth): +def app_ssowatconf(): """ Regenerate SSOwat configuration file @@ -1400,7 +1400,7 @@ def app_ssowatconf(auth): from yunohost.user import user_list main_domain = _get_maindomain() - domains = domain_list(auth)['domains'] + domains = domain_list()['domains'] skipped_urls = [] skipped_regex = [] @@ -1477,7 +1477,7 @@ def app_ssowatconf(auth): 'redirected_urls': redirected_urls, 'redirected_regex': redirected_regex, 'users': {username: app_map(user=username) - for username in user_list(auth)['users'].keys()}, + for username in user_list()['users'].keys()}, } with open('/etc/ssowat/conf.json', 'w+') as f: @@ -1486,14 +1486,14 @@ def app_ssowatconf(auth): logger.success(m18n.n('ssowat_conf_generated')) -def app_change_label(auth, app, new_label): +def app_change_label(app, new_label): installed = _is_installed(app) if not installed: raise YunohostError('app_not_installed', app=app) app_setting(app, "label", value=new_label) - app_ssowatconf(auth) + app_ssowatconf() # actions todo list: @@ -2139,7 +2139,7 @@ def _check_manifest_requirements(manifest, app_instance_name): spec=spec, app=app_instance_name) -def _parse_args_from_manifest(manifest, action, args={}, auth=None): +def _parse_args_from_manifest(manifest, action, args={}): """Parse arguments needed for an action from the manifest Retrieve specified arguments for the action from the manifest, and parse @@ -2158,10 +2158,10 @@ def _parse_args_from_manifest(manifest, action, args={}, auth=None): return OrderedDict() action_args = manifest['arguments'][action] - return _parse_action_args_in_yunohost_format(args, action_args, auth) + return _parse_action_args_in_yunohost_format(args, action_args) -def _parse_args_for_action(action, args={}, auth=None): +def _parse_args_for_action(action, args={}): """Parse arguments needed for an action from the actions list Retrieve specified arguments for the action from the manifest, and parse @@ -2182,10 +2182,10 @@ def _parse_args_for_action(action, args={}, auth=None): action_args = action['arguments'] - return _parse_action_args_in_yunohost_format(args, action_args, auth) + return _parse_action_args_in_yunohost_format(args, action_args) -def _parse_action_args_in_yunohost_format(args, action_args, auth=None): +def _parse_action_args_in_yunohost_format(args, action_args): """Parse arguments store in either manifest.json or actions.json """ from yunohost.domain import (domain_list, _get_maindomain, @@ -2238,12 +2238,12 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): arg_default = _get_maindomain() ask_string += ' (default: {0})'.format(arg_default) msignals.display(m18n.n('domains_available')) - for domain in domain_list(auth)['domains']: + for domain in domain_list()['domains']: msignals.display("- {}".format(domain)) elif arg_type == 'user': msignals.display(m18n.n('users_available')) - for user in user_list(auth)['users'].keys(): + for user in user_list()['users'].keys(): msignals.display("- {}".format(user)) elif arg_type == 'password': @@ -2279,11 +2279,11 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): # Validate argument type if arg_type == 'domain': - if arg_value not in domain_list(auth)['domains']: + if arg_value not in domain_list()['domains']: raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown')) elif arg_type == 'user': try: - user_info(auth, arg_value) + user_info(arg_value) except YunohostError as e: raise YunohostError('app_argument_invalid', name=arg_name, error=e) elif arg_type == 'app': @@ -2324,7 +2324,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): domain, path = _normalize_domain_path(domain, path) # Check the url is available - conflicts = _get_conflicting_apps(auth, domain, path) + conflicts = _get_conflicting_apps(domain, path) if conflicts: apps = [] for path, app_id, app_label in conflicts: diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 82a778491..9a87015c0 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2090,7 +2090,7 @@ def backup_create(name=None, description=None, methods=[], } -def backup_restore(auth, name, system=[], apps=[], force=False): +def backup_restore(name, system=[], apps=[], force=False): """ Restore from a local backup archive diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index d7e8c0157..4235d522d 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -86,7 +86,7 @@ DNS_RESOLVERS = [ # -def certificate_status(auth, domain_list, full=False): +def certificate_status(domain_list, full=False): """ Print the status of certificate for given domains (all by default) @@ -99,10 +99,10 @@ def certificate_status(auth, domain_list, full=False): # If no domains given, consider all yunohost domains if domain_list == []: - domain_list = yunohost.domain.domain_list(auth)['domains'] + domain_list = yunohost.domain.domain_list()['domains'] # Else, validate that yunohost knows the domains given else: - yunohost_domains_list = yunohost.domain.domain_list(auth)['domains'] + yunohost_domains_list = yunohost.domain.domain_list()['domains'] for domain in domain_list: # Is it in Yunohost domain list? if domain not in yunohost_domains_list: @@ -126,7 +126,7 @@ def certificate_status(auth, domain_list, full=False): return {"certificates": certificates} -def certificate_install(auth, domain_list, force=False, no_checks=False, self_signed=False, staging=False): +def certificate_install(domain_list, force=False, no_checks=False, self_signed=False, staging=False): """ Install a Let's Encrypt certificate for given domains (all by default) @@ -142,7 +142,7 @@ def certificate_install(auth, domain_list, force=False, no_checks=False, self_si _certificate_install_selfsigned(domain_list, force) else: _certificate_install_letsencrypt( - auth, domain_list, force, no_checks, staging) + domain_list, force, no_checks, staging) def _certificate_install_selfsigned(domain_list, force=False): @@ -237,7 +237,7 @@ def _certificate_install_selfsigned(domain_list, force=False): operation_logger.error(msg) -def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=False, staging=False): +def _certificate_install_letsencrypt(domain_list, force=False, no_checks=False, staging=False): import yunohost.domain if not os.path.exists(ACCOUNT_KEY_FILE): @@ -246,7 +246,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F # If no domains given, consider all yunohost domains with self-signed # certificates if domain_list == []: - for domain in yunohost.domain.domain_list(auth)['domains']: + for domain in yunohost.domain.domain_list()['domains']: status = _get_status(domain) if status["CA_type"]["code"] != "self-signed": @@ -257,7 +257,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F # Else, validate that yunohost knows the domains given else: for domain in domain_list: - yunohost_domains_list = yunohost.domain.domain_list(auth)['domains'] + yunohost_domains_list = yunohost.domain.domain_list()['domains'] if domain not in yunohost_domains_list: raise YunohostError('certmanager_domain_unknown', domain=domain) @@ -285,7 +285,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F operation_logger.start() - _configure_for_acme_challenge(auth, domain) + _configure_for_acme_challenge(domain) _fetch_and_enable_new_certificate(domain, staging, no_checks=no_checks) _install_cron(no_checks=no_checks) @@ -300,7 +300,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F operation_logger.error(msg) -def certificate_renew(auth, domain_list, force=False, no_checks=False, email=False, staging=False): +def certificate_renew(domain_list, force=False, no_checks=False, email=False, staging=False): """ Renew Let's Encrypt certificate for given domains (all by default) @@ -317,7 +317,7 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal # If no domains given, consider all yunohost domains with Let's Encrypt # certificates if domain_list == []: - for domain in yunohost.domain.domain_list(auth)['domains']: + for domain in yunohost.domain.domain_list()['domains']: # Does it have a Let's Encrypt cert? status = _get_status(domain) @@ -344,7 +344,7 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal for domain in domain_list: # Is it in Yunohost dmomain list? - if domain not in yunohost.domain.domain_list(auth)['domains']: + if domain not in yunohost.domain.domain_list()['domains']: raise YunohostError('certmanager_domain_unknown', domain=domain) status = _get_status(domain) @@ -468,7 +468,7 @@ Subject: %s smtp.quit() -def _configure_for_acme_challenge(auth, domain): +def _configure_for_acme_challenge(domain): nginx_conf_folder = "/etc/nginx/conf.d/%s.d" % domain nginx_conf_file = "%s/000-acmechallenge.conf" % nginx_conf_folder @@ -511,7 +511,7 @@ location ^~ '/.well-known/acme-challenge/' # any clean function already implemented in yunohost to do this though) _run_service_command("reload", "nginx") - app_ssowatconf(auth) + app_ssowatconf() def _check_acme_challenge_configuration(domain): diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index a39f45a0e..42a4881ba 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -42,7 +42,7 @@ from yunohost.hook import hook_callback logger = getActionLogger('yunohost.domain') -def domain_list(auth): +def domain_list(): """ List domains @@ -52,10 +52,12 @@ def domain_list(auth): limit -- Maximum number of domain fetched """ + from yunohost.utils.ldap import _get_ldap_interface + + ldap = _get_ldap_interface() + result = ldap.search('ou=domains,dc=yunohost,dc=org', 'virtualdomain=*', ['virtualdomain']) + result_list = [] - - result = auth.search('ou=domains,dc=yunohost,dc=org', 'virtualdomain=*', ['virtualdomain']) - for domain in result: result_list.append(domain['virtualdomain'][0]) @@ -63,7 +65,7 @@ def domain_list(auth): @is_unit_operation() -def domain_add(operation_logger, auth, domain, dyndns=False): +def domain_add(operation_logger, domain, dyndns=False): """ Create a custom domain @@ -74,9 +76,12 @@ def domain_add(operation_logger, auth, domain, dyndns=False): """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf + from yunohost.utils.ldap import _get_ldap_interface + + ldap = _get_ldap_interface() try: - auth.validate_uniqueness({'virtualdomain': domain}) + ldap.validate_uniqueness({'virtualdomain': domain}) except MoulinetteError: raise YunohostError('domain_exists') @@ -107,18 +112,18 @@ def domain_add(operation_logger, auth, domain, dyndns=False): 'virtualdomain': domain, } - if not auth.add('virtualdomain=%s,ou=domains' % domain, attr_dict): + if not ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict): raise YunohostError('domain_creation_failed') # Don't regen these conf if we're still in postinstall if os.path.exists('/etc/yunohost/installed'): regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix', 'rspamd']) - app_ssowatconf(auth) + app_ssowatconf() except Exception: # Force domain removal silently try: - domain_remove(auth, domain, True) + domain_remove(domain, True) except: pass raise @@ -129,7 +134,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False): @is_unit_operation() -def domain_remove(operation_logger, auth, domain, force=False): +def domain_remove(operation_logger, domain, force=False): """ Delete domains @@ -140,8 +145,9 @@ def domain_remove(operation_logger, auth, domain, force=False): """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf + from yunohost.utils.ldap import _get_ldap_interface - if not force and domain not in domain_list(auth)['domains']: + if not force and domain not in domain_list()['domains']: raise YunohostError('domain_unknown') # Check domain is not the main domain @@ -160,13 +166,14 @@ def domain_remove(operation_logger, auth, domain, force=False): raise YunohostError('domain_uninstall_app_first') operation_logger.start() - if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: + ldap = _get_ldap_interface() + if ldap.remove('virtualdomain=' + domain + ',ou=domains') or force: os.system('rm -rf /etc/yunohost/certs/%s' % domain) else: raise YunohostError('domain_deletion_failed') regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) - app_ssowatconf(auth) + app_ssowatconf() hook_callback('post_domain_remove', args=[domain]) @@ -222,19 +229,19 @@ def domain_dns_conf(domain, ttl=None): return result -def domain_cert_status(auth, domain_list, full=False): - return yunohost.certificate.certificate_status(auth, domain_list, full) +def domain_cert_status(domain_list, full=False): + return yunohost.certificate.certificate_status(domain_list, full) -def domain_cert_install(auth, domain_list, force=False, no_checks=False, self_signed=False, staging=False): - return yunohost.certificate.certificate_install(auth, domain_list, force, no_checks, self_signed, staging) +def domain_cert_install(domain_list, force=False, no_checks=False, self_signed=False, staging=False): + return yunohost.certificate.certificate_install(domain_list, force, no_checks, self_signed, staging) -def domain_cert_renew(auth, domain_list, force=False, no_checks=False, email=False, staging=False): - return yunohost.certificate.certificate_renew(auth, domain_list, force, no_checks, email, staging) +def domain_cert_renew(domain_list, force=False, no_checks=False, email=False, staging=False): + return yunohost.certificate.certificate_renew(domain_list, force, no_checks, email, staging) -def _get_conflicting_apps(auth, domain, path, ignore_app=None): +def _get_conflicting_apps(domain, path, ignore_app=None): """ Return a list of all conflicting apps with a domain/path (it can be empty) @@ -247,7 +254,7 @@ def _get_conflicting_apps(auth, domain, path, ignore_app=None): domain, path = _normalize_domain_path(domain, path) # Abort if domain is unknown - if domain not in domain_list(auth)['domains']: + if domain not in domain_list()['domains']: raise YunohostError('domain_unknown') # This import cannot be put on top of file because it would create a @@ -274,7 +281,7 @@ def _get_conflicting_apps(auth, domain, path, ignore_app=None): return conflicts -def domain_url_available(auth, domain, path): +def domain_url_available(domain, path): """ Check availability of a web path @@ -283,7 +290,7 @@ def domain_url_available(auth, domain, path): path -- The path to check (e.g. /coffee) """ - return len(_get_conflicting_apps(auth, domain, path)) == 0 + return len(_get_conflicting_apps(domain, path)) == 0 def _get_maindomain(): diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 456fdcfc4..dd3bbd8b3 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -208,7 +208,7 @@ def log_display(path, number=50, share=False): def is_unit_operation(entities=['app', 'domain', 'service', 'user'], - exclude=['auth', 'password'], operation_key=None): + exclude=['password'], operation_key=None): """ Configure quickly a unit operation @@ -222,9 +222,8 @@ def is_unit_operation(entities=['app', 'domain', 'service', 'user'], (argname, entity_type) instead of just put the entity type. exclude Remove some arguments from the context. By default, arguments - called 'password' and 'auth' are removed. If an argument is an object, you - need to exclude it or create manually the unit operation without this - decorator. + called 'password' are removed. If an argument is an object, you need to + exclude it or create manually the unit operation without this decorator. operation_key A key to describe the unit operation log used to create the filename and search a translation. Please ensure that this key prefixed by diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index b4ac31dbb..f0110b34e 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -11,7 +11,7 @@ from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, SSHD_CONFIG_PATH = "/etc/ssh/sshd_config" -def user_ssh_allow(auth, username): +def user_ssh_allow(username): """ Allow YunoHost user connect as ssh. @@ -20,17 +20,19 @@ def user_ssh_allow(auth, username): """ # TODO it would be good to support different kind of shells - if not _get_user_for_ssh(auth, username): + if not _get_user_for_ssh(username): raise YunohostError('user_unknown', user=username) - auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + ldap.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) # Somehow this is needed otherwise the PAM thing doesn't forget about the # old loginShell value ? subprocess.call(['nscd', '-i', 'passwd']) -def user_ssh_disallow(auth, username): +def user_ssh_disallow(username): """ Disallow YunoHost user connect as ssh. @@ -39,18 +41,20 @@ def user_ssh_disallow(auth, username): """ # TODO it would be good to support different kind of shells - if not _get_user_for_ssh(auth, username): + if not _get_user_for_ssh(username): raise YunohostError('user_unknown', user=username) - auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + ldap.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) # Somehow this is needed otherwise the PAM thing doesn't forget about the # old loginShell value ? subprocess.call(['nscd', '-i', 'passwd']) -def user_ssh_list_keys(auth, username): - user = _get_user_for_ssh(auth, username, ["homeDirectory"]) +def user_ssh_list_keys(username): + user = _get_user_for_ssh(username, ["homeDirectory"]) if not user: raise Exception("User with username '%s' doesn't exists" % username) @@ -82,8 +86,8 @@ def user_ssh_list_keys(auth, username): return {"keys": keys} -def user_ssh_add_key(auth, username, key, comment): - user = _get_user_for_ssh(auth, username, ["homeDirectory", "uid"]) +def user_ssh_add_key(username, key, comment): + user = _get_user_for_ssh(username, ["homeDirectory", "uid"]) if not user: raise Exception("User with username '%s' doesn't exists" % username) @@ -116,8 +120,8 @@ def user_ssh_add_key(auth, username, key, comment): write_to_file(authorized_keys_file, authorized_keys_content) -def user_ssh_remove_key(auth, username, key): - user = _get_user_for_ssh(auth, username, ["homeDirectory", "uid"]) +def user_ssh_remove_key(username, key): + user = _get_user_for_ssh(username, ["homeDirectory", "uid"]) if not user: raise Exception("User with username '%s' doesn't exists" % username) @@ -148,8 +152,8 @@ def user_ssh_remove_key(auth, username, key): # -def _get_user_for_ssh(auth, username, attrs=None): - def ssh_root_login_status(auth): +def _get_user_for_ssh(username, attrs=None): + def ssh_root_login_status(): # XXX temporary placed here for when the ssh_root commands are integrated # extracted from https://github.com/YunoHost/yunohost/pull/345 # XXX should we support all the options? @@ -172,7 +176,7 @@ def _get_user_for_ssh(auth, username, attrs=None): 'username': 'root', 'fullname': '', 'mail': '', - 'ssh_allowed': ssh_root_login_status(auth)["PermitRootLogin"], + 'ssh_allowed': ssh_root_login_status()["PermitRootLogin"], 'shell': root_unix.pw_shell, 'home_path': root_unix.pw_dir, } @@ -189,7 +193,9 @@ def _get_user_for_ssh(auth, username, attrs=None): } # TODO escape input using https://www.python-ldap.org/doc/html/ldap-filter.html - user = auth.search('ou=users,dc=yunohost,dc=org', + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + user = ldap.search('ou=users,dc=yunohost,dc=org', '(&(objectclass=person)(uid=%s))' % username, attrs) diff --git a/src/yunohost/tests/test_appurl.py b/src/yunohost/tests/test_appurl.py index 3a3a1db35..25dc68f9f 100644 --- a/src/yunohost/tests/test_appurl.py +++ b/src/yunohost/tests/test_appurl.py @@ -1,15 +1,9 @@ import pytest -from moulinette.core import init_authenticator from yunohost.utils.error import YunohostError from yunohost.app import app_install, app_remove from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path -# Instantiate LDAP Authenticator -auth_identifier = ('ldap', 'ldap-anonymous') -auth_parameters = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} -auth = init_authenticator(auth_identifier, auth_parameters) - # Get main domain maindomain = _get_maindomain() @@ -18,7 +12,7 @@ maindomain = _get_maindomain() def setup_function(function): try: - app_remove(auth, "register_url_app") + app_remove("register_url_app") except: pass @@ -26,7 +20,7 @@ def setup_function(function): def teardown_function(function): try: - app_remove(auth, "register_url_app") + app_remove("register_url_app") except: pass @@ -41,28 +35,28 @@ def test_normalize_domain_path(): def test_urlavailable(): # Except the maindomain/macnuggets to be available - assert domain_url_available(auth, maindomain, "/macnuggets") + assert domain_url_available(maindomain, "/macnuggets") # We don't know the domain yolo.swag with pytest.raises(YunohostError): - assert domain_url_available(auth, "yolo.swag", "/macnuggets") + assert domain_url_available("yolo.swag", "/macnuggets") def test_registerurl(): - app_install(auth, "./tests/apps/register_url_app_ynh", + app_install("./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"), force=True) - assert not domain_url_available(auth, maindomain, "/urlregisterapp") + assert not domain_url_available(maindomain, "/urlregisterapp") # Try installing at same location with pytest.raises(YunohostError): - app_install(auth, "./tests/apps/register_url_app_ynh", + app_install("./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"), force=True) def test_registerurl_baddomain(): with pytest.raises(YunohostError): - app_install(auth, "./tests/apps/register_url_app_ynh", + app_install("./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp"), force=True) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 353b88f27..4ace0c8b8 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -1,13 +1,10 @@ import pytest -import time -import requests import os import shutil import subprocess from mock import ANY from moulinette import m18n -from moulinette.core import init_authenticator from yunohost.app import app_install, app_remove, app_ssowatconf from yunohost.app import _is_installed from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount @@ -17,12 +14,6 @@ from yunohost.utils.error import YunohostError # Get main domain maindomain = "" -# Instantiate LDAP Authenticator -AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') -AUTH_PARAMETERS = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} -auth = None - - def setup_function(function): global maindomain @@ -30,9 +21,6 @@ def setup_function(function): print "" - global auth - auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - assert backup_test_dependencies_are_met() clean_tmp_backup_directory() @@ -72,10 +60,6 @@ def setup_function(function): def teardown_function(function): - print "" - global auth - auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - assert tmp_backup_directory_is_empty() reset_ssowat_conf() @@ -146,7 +130,7 @@ def reset_ssowat_conf(): # Make sure we have a ssowat os.system("mkdir -p /etc/ssowat/") - app_ssowatconf(auth) + app_ssowatconf() def delete_all_backups(): @@ -158,18 +142,18 @@ def delete_all_backups(): def uninstall_test_apps_if_needed(): if _is_installed("backup_legacy_app"): - app_remove(auth, "backup_legacy_app") + app_remove("backup_legacy_app") if _is_installed("backup_recommended_app"): - app_remove(auth, "backup_recommended_app") + app_remove("backup_recommended_app") if _is_installed("wordpress"): - app_remove(auth, "wordpress") + app_remove("wordpress") def install_app(app, path, additionnal_args=""): - app_install(auth, "./tests/apps/%s" % app, + app_install("./tests/apps/%s" % app, args="domain=%s&path=%s%s" % (maindomain, path, additionnal_args), force=True) @@ -249,7 +233,7 @@ def test_backup_and_restore_all_sys(): assert not os.path.exists("/etc/ssowat/conf.json") # Restore the backup - backup_restore(auth, name=archives[0], force=True, + backup_restore(name=archives[0], force=True, system=[], apps=None) # Check ssowat conf is back @@ -270,13 +254,13 @@ def test_restore_system_from_Ynh2p4(monkeypatch, mocker): # Restore system archive from 2.4 try: - backup_restore(auth, name=backup_list()["archives"][1], + backup_restore(name=backup_list()["archives"][1], system=[], apps=None, force=True) finally: # Restore system as it was - backup_restore(auth, name=backup_list()["archives"][0], + backup_restore(name=backup_list()["archives"][0], system=[], apps=None, force=True) @@ -412,7 +396,7 @@ def test_backup_with_no_compress(): @pytest.mark.with_wordpress_archive_from_2p4 def test_restore_app_wordpress_from_Ynh2p4(): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) @@ -430,7 +414,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): assert not _is_installed("wordpress") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_app_failed', app='wordpress') @@ -451,7 +435,7 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker): assert not _is_installed("wordpress") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_not_enough_disk_space', @@ -470,7 +454,7 @@ def test_restore_app_not_in_backup(mocker): mocker.spy(m18n, "n") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["yoloswag"]) m18n.n.assert_any_call('backup_archive_app_not_found', app="yoloswag") @@ -483,14 +467,14 @@ def test_restore_app_already_installed(mocker): assert not _is_installed("wordpress") - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) assert _is_installed("wordpress") mocker.spy(m18n, "n") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_already_installed_app', app="wordpress") @@ -531,11 +515,11 @@ def _test_backup_and_restore_app(app): assert app in archives_info["apps"].keys() # Uninstall the app - app_remove(auth, app) + app_remove(app) assert not app_is_installed(app) # Restore the app - backup_restore(auth, system=None, name=archives[0], + backup_restore(system=None, name=archives[0], apps=[app]) assert app_is_installed(app) @@ -555,7 +539,7 @@ def test_restore_archive_with_no_json(mocker): mocker.spy(m18n, "n") with pytest.raises(YunohostError): - backup_restore(auth, name="badbackup", force=True) + backup_restore(name="badbackup", force=True) m18n.n.assert_any_call('backup_invalid_archive') diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index e11acdb59..cb9b5d290 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -2,18 +2,11 @@ import pytest import time import requests -from moulinette.core import init_authenticator from yunohost.app import app_install, app_change_url, app_remove, app_map from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError -# Instantiate LDAP Authenticator -AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') -AUTH_PARAMETERS = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} - -auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - # Get main domain maindomain = _get_maindomain() @@ -23,11 +16,11 @@ def setup_function(function): def teardown_function(function): - app_remove(auth, "change_url_app") + app_remove("change_url_app") def install_changeurl_app(path): - app_install(auth, "./tests/apps/change_url_app_ynh", + app_install("./tests/apps/change_url_app_ynh", args="domain=%s&path=%s" % (maindomain, path), force=True) @@ -46,7 +39,7 @@ def test_appchangeurl(): install_changeurl_app("/changeurl") check_changeurl_app("/changeurl") - app_change_url(auth, "change_url_app", maindomain, "/newchangeurl") + app_change_url("change_url_app", maindomain, "/newchangeurl") # For some reason the nginx reload can take some time to propagate ...? time.sleep(2) @@ -59,4 +52,4 @@ def test_appchangeurl_sameurl(): check_changeurl_app("/changeurl") with pytest.raises(YunohostError): - app_change_url(auth, "change_url_app", maindomain, "changeurl") + app_change_url("change_url_app", maindomain, "changeurl") diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index ad721bf29..3bb69c961 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -35,8 +35,6 @@ from importlib import import_module from collections import OrderedDict from moulinette import msignals, m18n -from moulinette.core import init_authenticator -from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output, call_async_output from moulinette.utils.filesystem import read_json, write_to_json @@ -49,6 +47,7 @@ from yunohost.regenconf import regen_conf from yunohost.monitor import monitor_disk, monitor_system from yunohost.utils.packages import ynh_packages_version, _dump_sources_list, _list_upgradable_apt_packages from yunohost.utils.network import get_public_ip +from yunohost.utils.error import YunohostError from yunohost.log import is_unit_operation, OperationLogger # FIXME this is a duplicate from apps.py @@ -65,25 +64,21 @@ def tools_ldapinit(): """ - # Instantiate LDAP Authenticator - auth = init_authenticator(('ldap', 'default'), - {'uri': "ldap://localhost:389", - 'base_dn': "dc=yunohost,dc=org", - 'user_rdn': "cn=admin"}) - auth.authenticate('yunohost') - with open('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') as f: ldap_map = yaml.load(f) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + for rdn, attr_dict in ldap_map['parents'].items(): try: - auth.add(rdn, attr_dict) + ldap.add(rdn, attr_dict) except Exception as e: logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e)) for rdn, attr_dict in ldap_map['children'].items(): try: - auth.add(rdn, attr_dict) + ldap.add(rdn, attr_dict) except Exception as e: logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e)) @@ -99,7 +94,7 @@ def tools_ldapinit(): 'userPassword': 'yunohost' } - auth.update('cn=admin', admin_dict) + ldap.update('cn=admin', admin_dict) # Force nscd to refresh cache to take admin creation into account subprocess.call(['nscd', '-i', 'passwd']) @@ -112,10 +107,9 @@ def tools_ldapinit(): raise YunohostError('installation_failed') logger.success(m18n.n('ldap_initialized')) - return auth -def tools_adminpw(auth, new_password, check_strength=True): +def tools_adminpw(new_password, check_strength=True): """ Change admin password @@ -137,8 +131,11 @@ def tools_adminpw(auth, new_password, check_strength=True): new_hash = _hash_user_password(new_password) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + try: - auth.update("cn=admin", {"userPassword": new_hash, }) + ldap.update("cn=admin", {"userPassword": new_hash, }) except: logger.exception('unable to change admin password') raise YunohostError('admin_password_change_failed') @@ -162,7 +159,7 @@ def tools_adminpw(auth, new_password, check_strength=True): @is_unit_operation() -def tools_maindomain(operation_logger, auth, new_domain=None): +def tools_maindomain(operation_logger, new_domain=None): """ Check the current main domain, or change it @@ -176,7 +173,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): return {'current_main_domain': _get_maindomain()} # Check domain exists - if new_domain not in domain_list(auth)['domains']: + if new_domain not in domain_list()['domains']: raise YunohostError('domain_unknown') operation_logger.related_to.append(('domain', new_domain)) @@ -205,7 +202,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): _set_hostname(new_domain) # Generate SSOwat configuration file - app_ssowatconf(auth) + app_ssowatconf() # Regen configurations try: @@ -332,7 +329,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # Initialize LDAP for YunoHost # TODO: Improve this part by integrate ldapinit into conf_regen hook - auth = tools_ldapinit() + tools_ldapinit() # Create required folders folders_to_create = [ @@ -406,11 +403,11 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # New domain config regen_conf(['nsswitch'], force=True) - domain_add(auth, domain, dyndns) - tools_maindomain(auth, domain) + domain_add(domain, dyndns) + tools_maindomain(domain) # Change LDAP admin password - tools_adminpw(auth, password, check_strength=not force_password) + tools_adminpw(password, check_strength=not force_password) # Enable UPnP silently and reload firewall firewall_upnp('enable', no_refresh=True) @@ -544,7 +541,7 @@ def _list_upgradable_apps(): @is_unit_operation() -def tools_upgrade(operation_logger, auth, apps=None, system=False): +def tools_upgrade(operation_logger, apps=None, system=False): """ Update apps & package cache, then display changelog @@ -583,7 +580,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # Actually start the upgrades try: - app_upgrade(auth, app=apps) + app_upgrade(app=apps) except Exception as e: logger.warning('unable to upgrade apps: %s' % str(e)) logger.error(m18n.n('app_upgrade_some_app_failed')) @@ -706,7 +703,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): operation_logger.success() -def tools_diagnosis(auth, private=False): +def tools_diagnosis(private=False): """ Return global info about current yunohost instance to help debugging @@ -801,7 +798,7 @@ def tools_diagnosis(auth, private=False): diagnosis['private']['public_ip']['IPv6'] = get_public_ip(6) # Domains - diagnosis['private']['domains'] = domain_list(auth)['domains'] + diagnosis['private']['domains'] = domain_list()['domains'] diagnosis['private']['regen_conf'] = regen_conf(with_diff=True, dry_run=True) @@ -1125,18 +1122,21 @@ def tools_migrations_state(): return read_json(MIGRATIONS_STATE_PATH) -def tools_shell(auth, command=None): +def tools_shell(command=None): """ Launch an (i)python shell in the YunoHost context. This is entirely aim for development. """ + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + if command: exec(command) return - logger.warn("The \033[1;34mauth\033[0m is available in this context") + logger.warn("The \033[1;34mldap\033[0m interface is available in this context") try: from IPython import embed embed() diff --git a/src/yunohost/user.py b/src/yunohost/user.py index a38f0b4c5..db0d80ce3 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -41,7 +41,7 @@ from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') -def user_list(auth, fields=None): +def user_list(fields=None): """ List users @@ -52,6 +52,8 @@ def user_list(auth, fields=None): fields -- fields to fetch """ + from yunohost.utils.ldap import _get_ldap_interface + user_attrs = { 'uid': 'username', 'cn': 'fullname', @@ -75,7 +77,8 @@ def user_list(auth, fields=None): else: attrs = ['uid', 'cn', 'mail', 'mailuserquota', 'loginShell'] - result = auth.search('ou=users,dc=yunohost,dc=org', + ldap = _get_ldap_interface() + result = ldap.search('ou=users,dc=yunohost,dc=org', '(&(objectclass=person)(!(uid=root))(!(uid=nobody)))', attrs) @@ -98,7 +101,7 @@ def user_list(auth, fields=None): @is_unit_operation([('username', 'user')]) -def user_create(operation_logger, auth, username, firstname, lastname, mail, password, +def user_create(operation_logger, username, firstname, lastname, mail, password, mailbox_quota="0"): """ Create user @@ -116,12 +119,15 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf 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) + ldap = _get_ldap_interface() + # Validate uniqueness of username and mail in LDAP - auth.validate_uniqueness({ + ldap.validate_uniqueness({ 'uid': username, 'mail': mail }) @@ -143,7 +149,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas raise YunohostError('mail_unavailable') # Check that the mail domain exists - if mail.split("@")[1] not in domain_list(auth)['domains']: + if mail.split("@")[1] not in domain_list()['domains']: raise YunohostError('mail_domain_unknown', domain=mail.split("@")[1]) operation_logger.start() @@ -177,7 +183,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas } # If it is the first user, add some aliases - if not auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'): + if not ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'): attr_dict['mail'] = [attr_dict['mail']] + aliases # If exists, remove the redirection from the SSO @@ -197,14 +203,14 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas except IOError as e: raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror) - if auth.add('uid=%s,ou=users' % username, attr_dict): + if ldap.add('uid=%s,ou=users' % username, attr_dict): # Invalidate passwd to take user creation into account subprocess.call(['nscd', '-i', 'passwd']) # Update SFTP user group - memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] + memberlist = ldap.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] memberlist.append(username) - if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): + if ldap.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): try: # Attempt to create user home folder subprocess.check_call( @@ -213,7 +219,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas if not os.path.isdir('/home/{0}'.format(username)): logger.warning(m18n.n('user_home_creation_failed'), exc_info=1) - app_ssowatconf(auth) + app_ssowatconf() # TODO: Send a welcome mail to user logger.success(m18n.n('user_created')) hook_callback('post_user_create', @@ -225,7 +231,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas @is_unit_operation([('username', 'user')]) -def user_delete(operation_logger, auth, username, purge=False): +def user_delete(operation_logger, username, purge=False): """ Delete user @@ -236,34 +242,37 @@ def user_delete(operation_logger, auth, username, purge=False): """ from yunohost.app import app_ssowatconf from yunohost.hook import hook_callback + from yunohost.utils.ldap import _get_ldap_interface operation_logger.start() - if auth.remove('uid=%s,ou=users' % username): + + ldap = _get_ldap_interface() + if ldap.remove('uid=%s,ou=users' % username): # Invalidate passwd to take user deletion into account subprocess.call(['nscd', '-i', 'passwd']) # Update SFTP user group - memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] + memberlist = ldap.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] try: memberlist.remove(username) except: pass - if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): + if ldap.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): if purge: subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)]) else: raise YunohostError('user_deletion_failed') - app_ssowatconf(auth) + app_ssowatconf() hook_callback('post_user_delete', args=[username, purge]) logger.success(m18n.n('user_deleted')) -@is_unit_operation([('username', 'user')], exclude=['auth', 'change_password']) -def user_update(operation_logger, auth, username, firstname=None, lastname=None, mail=None, +@is_unit_operation([('username', 'user')], exclude=['change_password']) +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): """ @@ -284,13 +293,15 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, 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 + ldap = _get_ldap_interface() attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop'] new_attr_dict = {} - domains = domain_list(auth)['domains'] + domains = domain_list()['domains'] # Populate user informations - result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) + 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] @@ -321,7 +332,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, 'webmaster@' + main_domain, 'postmaster@' + main_domain, ] - auth.validate_uniqueness({'mail': mail}) + ldap.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) if mail in aliases: @@ -334,7 +345,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, if not isinstance(add_mailalias, list): add_mailalias = [add_mailalias] for mail in add_mailalias: - auth.validate_uniqueness({'mail': mail}) + ldap.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) user['mail'].append(mail) @@ -374,15 +385,15 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, operation_logger.start() - if auth.update('uid=%s,ou=users' % username, new_attr_dict): + if ldap.update('uid=%s,ou=users' % username, new_attr_dict): logger.success(m18n.n('user_updated')) - app_ssowatconf(auth) - return user_info(auth, username) + app_ssowatconf() + return user_info(username) else: raise YunohostError('user_update_failed') -def user_info(auth, username): +def user_info(username): """ Get user informations @@ -390,6 +401,10 @@ def user_info(auth, username): username -- Username or mail to get informations """ + from yunohost.utils.ldap import _get_ldap_interface + + ldap = _get_ldap_interface() + user_attrs = [ 'cn', 'mail', 'uid', 'maildrop', 'givenName', 'sn', 'mailuserquota' ] @@ -399,7 +414,7 @@ def user_info(auth, username): else: filter = 'uid=' + username - result = auth.search('ou=users,dc=yunohost,dc=org', filter, user_attrs) + result = ldap.search('ou=users,dc=yunohost,dc=org', filter, user_attrs) if result: user = result[0] @@ -469,24 +484,24 @@ def user_info(auth, username): import yunohost.ssh -def user_ssh_allow(auth, username): - return yunohost.ssh.user_ssh_allow(auth, username) +def user_ssh_allow(username): + return yunohost.ssh.user_ssh_allow(username) -def user_ssh_disallow(auth, username): - return yunohost.ssh.user_ssh_disallow(auth, username) +def user_ssh_disallow(username): + return yunohost.ssh.user_ssh_disallow(username) -def user_ssh_list_keys(auth, username): - return yunohost.ssh.user_ssh_list_keys(auth, username) +def user_ssh_list_keys(username): + return yunohost.ssh.user_ssh_list_keys(username) -def user_ssh_add_key(auth, username, key, comment): - return yunohost.ssh.user_ssh_add_key(auth, username, key, comment) +def user_ssh_add_key(username, key, comment): + return yunohost.ssh.user_ssh_add_key(username, key, comment) -def user_ssh_remove_key(auth, username, key): - return yunohost.ssh.user_ssh_remove_key(auth, username, key) +def user_ssh_remove_key(username, key): + return yunohost.ssh.user_ssh_remove_key(username, key) # # End SSH subcategory diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py new file mode 100644 index 000000000..186cdbdec --- /dev/null +++ b/src/yunohost/utils/ldap.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +""" License + + Copyright (C) 2019 YunoHost + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, see http://www.gnu.org/licenses + +""" + +import atexit +from moulinette.core import init_authenticator + +# We use a global variable to do some caching +# to avoid re-authenticating in case we call _get_ldap_authenticator multiple times +_ldap_interface = None + +def _get_ldap_interface(): + + global _ldap_interface + + if _ldap_interface is None: + # Instantiate LDAP Authenticator + AUTH_IDENTIFIER = ('ldap', 'as-root') + AUTH_PARAMETERS = {'uri': 'ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi', + 'base_dn': 'dc=yunohost,dc=org', + 'user_rdn': 'gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth'} + _ldap_interface = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) + + return _ldap_interface + +# Add this to properly close / delete the ldap interface / authenticator +# when Python exits ... +# Otherwise there's a risk that some funky error appears at the very end +# of the command due to Python stuff being unallocated in wrong order. +def _destroy_ldap_interface(): + global _ldap_interface + if _ldap_interface is not None: + del _ldap_interface + +atexit.register(_destroy_ldap_interface)