def run(self): if not self.package_is_installed("postgresql-9.6"): logger.warning( m18n.n("migration_0017_postgresql_96_not_installed")) return if not self.package_is_installed("postgresql-11"): raise YunohostError("migration_0017_postgresql_11_not_installed") # Make sure there's a 9.6 cluster try: self.runcmd("pg_lsclusters | grep -q '^9.6 '") except Exception: logger.warning( "It looks like there's not active 9.6 cluster, so probably don't need to run this migration" ) return if not space_used_by_directory( "/var/lib/postgresql/9.6") > free_space_in_directory( "/var/lib/postgresql"): raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/") self.runcmd("systemctl stop postgresql") self.runcmd( "LC_ALL=C pg_dropcluster --stop 11 main || true" ) # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster) self.runcmd("LC_ALL=C pg_upgradecluster -m upgrade 9.6 main") self.runcmd("LC_ALL=C pg_dropcluster --stop 9.6 main") self.runcmd("systemctl start postgresql")
def _check_domain_is_ready_for_ACME(domain): dnsrecords = Diagnoser.get_cached_report("dnsrecords", item={ "domain": domain, "category": "basic" }, warn_if_no_cache=False) or {} httpreachable = Diagnoser.get_cached_report( "web", item={"domain": domain}, warn_if_no_cache=False) or {} if not dnsrecords or not httpreachable: raise YunohostError('certmanager_domain_not_diagnosed_yet', domain=domain) # Check if IP from DNS matches public IP if not dnsrecords.get("status") in [ "SUCCESS", "WARNING" ]: # Warning is for missing IPv6 record which ain't critical for ACME raise YunohostError('certmanager_domain_dns_ip_differs_from_public_ip', domain=domain) # Check if domain seems to be accessible through HTTP? if not httpreachable.get("status") == "SUCCESS": raise YunohostError('certmanager_domain_http_not_working', domain=domain)
def resolve_domain(domain, rdtype): # FIXME make this work for IPv6-only hosts too.. ok, result = dig(dyn_host, "A") dyn_host_ip = result[0] if ok == "ok" and len(result) else None if not dyn_host_ip: raise YunohostError("Failed to resolve %s" % dyn_host) ok, result = dig(domain, rdtype, resolvers=[dyn_host_ip]) if ok == "ok": return result[0] if len(result) else None elif result[0] == "Timeout": logger.debug( "Timed-out while trying to resolve %s record for %s using %s" % (rdtype, domain, dyn_host)) else: return None logger.debug("Falling back to external resolvers") ok, result = dig(domain, rdtype, resolvers="force_external") if ok == "ok": return result[0] if len(result) else None elif result[0] == "Timeout": logger.debug( "Timed-out while trying to resolve %s record for %s using external resolvers : %s" % (rdtype, domain, result)) else: return None raise YunohostError("Failed to resolve %s for %s" % (rdtype, domain), raw_msg=True)
def tools_adminpw(new_password, check_strength=True): """ Change admin password Keyword argument: new_password """ from yunohost.user import _hash_user_password from yunohost.utils.password import assert_password_is_strong_enough import spwd if check_strength: assert_password_is_strong_enough("admin", new_password) # UNIX seems to not like password longer than 127 chars ... # e.g. SSH login gets broken (or even 'su admin' when entering the password) if len(new_password) >= 127: raise YunohostError("admin_password_too_long") new_hash = _hash_user_password(new_password) from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() try: ldap.update( "cn=admin", { "userPassword": [new_hash], }, ) except Exception: logger.error("unable to change admin password") raise YunohostError("admin_password_change_failed") else: # Write as root password try: hash_root = spwd.getspnam("root").sp_pwd with open("/etc/shadow", "r") as before_file: before = before_file.read() with open("/etc/shadow", "w") as after_file: after_file.write( before.replace("root:" + hash_root, "root:" + new_hash.replace("{CRYPT}", ""))) # An IOError may be thrown if for some reason we can't read/write /etc/passwd # A KeyError could also be thrown if 'root' is not in /etc/passwd in the first place (for example because no password defined ?) # (c.f. the line about getspnam) except (IOError, KeyError): logger.warning(m18n.n("root_password_desynchronized")) return logger.info(m18n.n("root_password_replaced_by_admin_password")) logger.success(m18n.n("admin_password_changed"))
def domain_main_domain(operation_logger, new_main_domain=None): """ Check the current main domain, or change it Keyword argument: new_main_domain -- The new domain to be set as the main domain """ from yunohost.tools import _set_hostname # If no new domain specified, we return the current main domain if not new_main_domain: return {'current_main_domain': _get_maindomain()} # Check domain exists if new_main_domain not in domain_list()['domains']: raise YunohostError('domain_name_unknown', domain=new_main_domain) operation_logger.related_to.append(('domain', new_main_domain)) operation_logger.start() # Apply changes to ssl certs ssl_key = "/etc/ssl/private/yunohost_key.pem" ssl_crt = "/etc/ssl/private/yunohost_crt.pem" new_ssl_key = "/etc/yunohost/certs/%s/key.pem" % new_main_domain new_ssl_crt = "/etc/yunohost/certs/%s/crt.pem" % new_main_domain try: if os.path.exists(ssl_key) or os.path.lexists(ssl_key): os.remove(ssl_key) if os.path.exists(ssl_crt) or os.path.lexists(ssl_crt): os.remove(ssl_crt) os.symlink(new_ssl_key, ssl_key) os.symlink(new_ssl_crt, ssl_crt) _set_maindomain(new_main_domain) except Exception as e: logger.warning("%s" % e, exc_info=1) raise YunohostError('main_domain_change_failed') _set_hostname(new_main_domain) # Generate SSOwat configuration file app_ssowatconf() # Regen configurations try: with open('/etc/yunohost/installed', 'r'): regen_conf() except IOError: pass logger.success(m18n.n('main_domain_changed'))
def user_delete(operation_logger, username, purge=False): """ Delete user Keyword argument: username -- Username to delete purge """ from yunohost.hook import hook_callback from yunohost.utils.ldap import _get_ldap_interface if username not in user_list()["users"]: raise YunohostError('user_unknown', user=username) operation_logger.start() user_group_update("all_users", remove=username, force=True, sync_perm=False) for group, infos in user_group_list()["groups"].items(): if group == "all_users": continue # If the user is in this group (and it's not the primary group), # remove the member from the group if username != group and username in infos["members"]: user_group_update(group, remove=username, sync_perm=False) # Delete primary group if it exists (why wouldnt it exists ? because some # epic bug happened somewhere else and only a partial removal was # performed...) if username in user_group_list()['groups'].keys(): user_group_delete(username, force=True, sync_perm=True) ldap = _get_ldap_interface() try: ldap.remove('uid=%s,ou=users' % username) except Exception as e: raise YunohostError('user_deletion_failed', user=username, error=e) # Invalidate passwd to take user deletion into account subprocess.call(['nscd', '-i', 'passwd']) if purge: subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)]) hook_callback('post_user_delete', args=[username, purge]) logger.success(m18n.n('user_deleted'))
def tools_migrations_list(pending=False, done=False): """ List existing migrations """ # Check for option conflict if pending and done: raise YunohostError("migrations_list_conflict_pending_done") # Get all migrations migrations = _get_migrations_list() # Reduce to dictionnaries migrations = [{ "id": migration.id, "number": migration.number, "name": migration.name, "mode": migration.mode, "state": migration.state, "description": migration.description, "disclaimer": migration.disclaimer } for migration in migrations] # If asked, filter pending or done migrations if pending or done: if done: migrations = [m for m in migrations if m["state"] != "pending"] if pending: migrations = [m for m in migrations if m["state"] == "pending"] return {"migrations": migrations}
def user_group_info(groupname): """ Get user informations Keyword argument: groupname -- Groupname to get informations """ from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() # Fetch info for this group result = ldap.search('ou=groups,dc=yunohost,dc=org', "cn=" + groupname, ["cn", "member", "permission"]) if not result: raise YunohostError('group_unknown', group=groupname) infos = result[0] # Format data return { 'members': [_ldap_path_extract(p, "uid") for p in infos.get("member", [])], 'permissions': [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])] }
def assert_slapd_is_running(): # Assert slapd is running... if not os.system("pgrep slapd >/dev/null") == 0: raise YunohostError( "Service slapd is not running but is required to perform this action ... You can try to investigate what's happening with 'systemctl status slapd'" )
def get_matching_migration(target): for m in all_migrations: if m.id == target or m.name == target or m.id.split( "_")[0] == target: return m raise YunohostError("migrations_no_such_migration", id=target)
def service_log(name, number=50): """ Log every log files of a service Keyword argument: name -- Service name to log number -- Number of lines to display """ services = _get_services() number = int(number) if name not in services.keys(): raise YunohostError("service_unknown", service=name) log_list = services[name].get("log", []) if not isinstance(log_list, list): log_list = [log_list] # Legacy stuff related to --log_type where we'll typically have the service # name in the log list but it's not an actual logfile. Nowadays journalctl # is automatically fetch as well as regular log files. if name in log_list: log_list.remove(name) result = {} # First we always add the logs from journalctl / systemd result["journalctl"] = _get_journalctl_logs(name, number).splitlines() for log_path in log_list: if not os.path.exists(log_path): continue # Make sure to resolve symlinks log_path = os.path.realpath(log_path) # log is a file, read it if os.path.isfile(log_path): result[log_path] = _tail(log_path, number) continue elif not os.path.isdir(log_path): result[log_path] = [] continue for log_file in os.listdir(log_path): log_file_path = os.path.join(log_path, log_file) # not a file : skip if not os.path.isfile(log_file_path): continue if not log_file.endswith(".log"): continue result[log_file_path] = (_tail(log_file_path, number) if os.path.exists(log_file_path) else []) return result
def _dyndns_provides(provider, domain): """ Checks if a provider provide/manage a given domain. Keyword arguments: provider -- The url of the provider, e.g. "dyndns.yunohost.org" domain -- The full domain that you'd like.. e.g. "foo.nohost.me" Returns: True if the provider provide/manages the domain. False otherwise. """ logger.debug("Checking if %s is managed by %s ..." % (domain, provider)) try: # Dyndomains will be a list of domains supported by the provider # e.g. [ "nohost.me", "noho.st" ] dyndomains = download_json("https://%s/domains" % provider, timeout=30) except MoulinetteError as e: logger.error(str(e)) raise YunohostError("dyndns_could_not_check_provide", domain=domain, provider=provider) # Extract 'dyndomain' from 'domain', e.g. 'nohost.me' from 'foo.nohost.me' dyndomain = ".".join(domain.split(".")[1:]) return dyndomain in dyndomains
def _guess_current_dyndns_domain(dyn_host): """ This function tries to guess which domain should be updated by "dyndns_update()" because there's not proper management of the current dyndns domain :/ (and at the moment the code doesn't support having several dyndns domain, which is sort of a feature so that people don't abuse the dynette...) """ # Retrieve the first registered domain paths = list(glob.iglob("/etc/yunohost/dyndns/K*.private")) for path in paths: match = RE_DYNDNS_PRIVATE_KEY_MD5.match(path) if not match: match = RE_DYNDNS_PRIVATE_KEY_SHA512.match(path) if not match: continue _domain = match.group("domain") # Verify if domain is registered (i.e., if it's available, skip # current domain beause that's not the one we want to update..) # If there's only 1 such key found, then avoid doing the request # for nothing (that's very probably the one we want to find ...) if len(paths) > 1 and _dyndns_available(dyn_host, _domain): continue else: return (_domain, path) raise YunohostError("dyndns_no_domain_registered")
def _get_ldap_interface(): global _ldap_interface if _ldap_interface is None: conf = { "vendor": "ldap", "name": "as-root", "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", }, "extra": {}, } try: _ldap_interface = ldap.Authenticator(**conf) except MoulinetteLdapIsDownError: raise YunohostError( "Service slapd is not running but is required to perform this action ... You can try to investigate what's happening with 'systemctl status slapd'" ) assert_slapd_is_running() return _ldap_interface
def _save_settings(settings, location=SETTINGS_PATH): settings_without_description = {} for key, value in settings.items(): settings_without_description[key] = value if "description" in value: del settings_without_description[key]["description"] try: result = json.dumps(settings_without_description, indent=4) except Exception as e: raise YunohostError("global_settings_cant_serialize_settings", reason=e) try: with open(location, "w") as settings_fd: settings_fd.write(result) except Exception as e: raise YunohostError("global_settings_cant_write_settings", reason=e)
def permission_sync_to_user(): """ Sychronise the inheritPermission attribut in the permission object from the user<->group link and the group<->permission link """ import os from yunohost.app import app_ssowatconf from yunohost.user import user_group_list from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() groups = user_group_list(full=True)["groups"] permissions = user_permission_list(full=True)["permissions"] for permission_name, permission_infos in permissions.items(): # These are the users currently allowed because there's an 'inheritPermission' object corresponding to it currently_allowed_users = set(permission_infos["corresponding_users"]) # These are the users that should be allowed because they are member of a group that is allowed for this permission ... should_be_allowed_users = set([ user for group in permission_infos["allowed"] for user in groups[group]["members"] ]) # Note that a LDAP operation with the same value that is in LDAP crash SLAP. # So we need to check before each ldap operation that we really change something in LDAP if currently_allowed_users == should_be_allowed_users: # We're all good, this permission is already correctly synchronized ! continue new_inherited_perms = { "inheritPermission": [ "uid=%s,ou=users,dc=yunohost,dc=org" % u for u in should_be_allowed_users ], "memberUid": should_be_allowed_users, } # Commit the change with the new inherited stuff try: ldap.update("cn=%s,ou=permission" % permission_name, new_inherited_perms) except Exception as e: raise YunohostError("permission_update_failed", permission=permission_name, error=e) logger.debug("The permission database has been resynchronized") app_ssowatconf() # Reload unscd, otherwise the group ain't propagated to the LDAP database os.system("nscd --invalidate=passwd") os.system("nscd --invalidate=group")
def migrate_LDAP_db(): logger.info(m18n.n("migration_0011_update_LDAP_database")) from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() ldap_map = read_yaml( '/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') try: SetupGroupPermissions.remove_if_exists("ou=permission") SetupGroupPermissions.remove_if_exists('ou=groups') attr_dict = ldap_map['parents']['ou=permission'] ldap.add('ou=permission', attr_dict) attr_dict = ldap_map['parents']['ou=groups'] ldap.add('ou=groups', attr_dict) attr_dict = ldap_map['children']['cn=all_users,ou=groups'] ldap.add('cn=all_users,ou=groups', attr_dict) attr_dict = ldap_map['children']['cn=visitors,ou=groups'] ldap.add('cn=visitors,ou=groups', attr_dict) for rdn, attr_dict in ldap_map['depends_children'].items(): ldap.add(rdn, attr_dict) except Exception as e: raise YunohostError("migration_0011_LDAP_update_failed", error=e) logger.info(m18n.n("migration_0011_create_group")) # Create a group for each yunohost user user_list = ldap.search( 'ou=users,dc=yunohost,dc=org', '(&(objectclass=person)(!(uid=root))(!(uid=nobody)))', ['uid', 'uidNumber']) for user_info in user_list: username = user_info['uid'][0] ldap.update( 'uid=%s,ou=users' % username, { 'objectClass': [ 'mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh' ] }) user_group_create(username, gid=user_info['uidNumber'][0], primary_group=True, sync_perm=False) user_group_update(groupname='all_users', add=username, force=True, sync_perm=False)
def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False, email=False): if (email or except_if_never_ran_yet) and not os.path.exists(DIAGNOSIS_CACHE): return # Get all the categories all_categories = _list_diagnosis_categories() all_categories_names = [category for category, _ in all_categories] # Check the requested category makes sense if categories == []: categories = all_categories_names else: unknown_categories = [ c for c in categories if c not in all_categories_names ] if unknown_categories: raise YunohostError("diagnosis_unknown_categories", categories=", ".join(unknown_categories)) issues = [] # Call the hook ... diagnosed_categories = [] for category in categories: logger.debug("Running diagnosis for %s ..." % category) path = [p for n, p in all_categories if n == category][0] try: code, report = hook_exec(path, args={"force": force}, env=None) except Exception: import traceback logger.error( m18n.n( "diagnosis_failed_for_category", category=category, error="\n" + traceback.format_exc(), )) else: diagnosed_categories.append(category) if report != {}: issues.extend([ item for item in report["items"] if item["status"] in ["WARNING", "ERROR"] ]) if email: _email_diagnosis_issues() if issues and msettings.get("interface") == "cli": logger.warning(m18n.n("diagnosis_display_tip"))
def validate_regex(regex): if '%' in regex: logger.warning( "/!\\ Packagers! You are probably using a lua regex. You should use a PCRE regex instead." ) return try: re.compile(regex) except Exception: raise YunohostError('invalid_regex', regex=regex)
def diagnosis_get(category, item): # Get all the categories all_categories = _list_diagnosis_categories() all_categories_names = [c for c, _ in all_categories] if category not in all_categories_names: raise YunohostError('diagnosis_unknown_categories', categories=category) if isinstance(item, list): if any("=" not in criteria for criteria in item): raise YunohostError( "Criterias should be of the form key=value (e.g. domain=yolo.test)" ) # Convert the provided criteria into a nice dict item = {c.split("=")[0]: c.split("=")[1] for c in item} return Diagnoser.get_cached_report(category, item=item)
def permission_delete(operation_logger, permission, force=False, sync_perm=True): """ Delete a permission Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) """ # By default, manipulate main permission if "." not in permission: permission = permission + ".main" if permission.endswith(".main") and not force: raise YunohostError("permission_cannot_remove_main") from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() # Make sure this permission exists _ = user_permission_info(permission) # Actually delete the permission operation_logger.related_to.append(("app", permission.split(".")[0])) operation_logger.start() try: ldap.remove("cn=%s,ou=permission" % permission) except Exception as e: raise YunohostError("permission_deletion_failed", permission=permission, error=e) if sync_perm: permission_sync_to_user() logger.debug(m18n.n("permission_deleted", permission=permission))
def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): """ Delete user Keyword argument: groupname -- Groupname to delete """ from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface existing_groups = list(user_group_list()['groups'].keys()) if groupname not in existing_groups: raise YunohostError('group_unknown', group=groupname) # Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam') # without the force option... # # We also can't delete "all_users" because that's a special group... existing_users = list(user_list()['users'].keys()) undeletable_groups = existing_users + ["all_users", "visitors"] if groupname in undeletable_groups and not force: raise YunohostError('group_cannot_be_deleted', group=groupname) operation_logger.start() ldap = _get_ldap_interface() try: ldap.remove('cn=%s,ou=groups' % groupname) except Exception as e: raise YunohostError('group_deletion_failed', group=groupname, error=e) if sync_perm: permission_sync_to_user() if groupname not in existing_users: logger.success(m18n.n('group_deleted', group=groupname)) else: logger.debug(m18n.n('group_deleted', group=groupname))
def service_remove(name): """ Remove a custom service Keyword argument: name -- Service name to remove """ services = _get_services() if name not in services: raise YunohostError('service_unknown', service=name) del services[name] try: _save_services(services) except Exception: # we'll get a logger.warning with more details in _save_services raise YunohostError('service_remove_failed', service=name) logger.success(m18n.n('service_removed', service=name))
def dyndns_removecron(): """ Remove IP update cron """ try: os.remove("/etc/cron.d/yunohost-dyndns") except Exception as e: raise YunohostError("dyndns_cron_remove_failed", error=e) logger.success(m18n.n("dyndns_cron_removed"))
def run(self): # FIXME : what do we really want to do here ... # Imho we should just force-regen the conf in all case, and maybe # just display a warning if we detect that the conf was manually modified # Backup LDAP and the apps settings before to do the migration logger.info(m18n.n("migration_0019_backup_before_migration")) try: backup_folder = "/home/yunohost.backup/premigration/" + time.strftime( "%Y%m%d-%H%M%S", time.gmtime()) os.makedirs(backup_folder, 0o750) os.system("systemctl stop slapd") os.system("cp -r --preserve /etc/ldap %s/ldap_config" % backup_folder) os.system("cp -r --preserve /var/lib/ldap %s/ldap_db" % backup_folder) os.system("cp -r --preserve /etc/yunohost/apps %s/apps_settings" % backup_folder) except Exception as e: raise YunohostError( "migration_0019_can_not_backup_before_migration", error=e) finally: os.system("systemctl start slapd") try: # Update LDAP database self.add_new_ldap_attributes() # Migrate old settings migrate_legacy_permission_settings() except Exception: logger.warn( m18n.n("migration_0019_migration_failed_trying_to_rollback")) os.system("systemctl stop slapd") os.system( "rm -r /etc/ldap/slapd.d" ) # To be sure that we don't keep some part of the old config os.system("cp -r --preserve %s/ldap_config/. /etc/ldap/" % backup_folder) os.system("cp -r --preserve %s/ldap_db/. /var/lib/ldap/" % backup_folder) os.system( "cp -r --preserve %s/apps_settings/. /etc/yunohost/apps/" % backup_folder) os.system("systemctl start slapd") os.system("rm -r " + backup_folder) logger.info(m18n.n("migration_0019_rollback_success")) raise else: os.system("rm -r " + backup_folder)
def yunopaste(data): paste_server = "https://paste.yunohost.org" try: data = anonymize(data) except Exception as e: logger.warning( "For some reason, YunoHost was not able to anonymize the pasted data. Sorry about that. Be careful about sharing the link, as it may contain somewhat private infos like domain names or IP addresses. Error: %s" % e) data = data.encode() try: r = requests.post("%s/documents" % paste_server, data=data, timeout=30) except Exception as e: raise YunohostError( "Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e), raw_msg=True, ) if r.status_code != 200: raise YunohostError( "Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text), raw_msg=True, ) try: url = json.loads(r.text)["key"] except Exception: raise YunohostError( "Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text, raw_msg=True, ) return "%s/raw/%s" % (paste_server, url)
def domain_main_domain(operation_logger, new_main_domain=None): """ Check the current main domain, or change it Keyword argument: new_main_domain -- The new domain to be set as the main domain """ from yunohost.tools import _set_hostname # If no new domain specified, we return the current main domain if not new_main_domain: return {"current_main_domain": _get_maindomain()} # Check domain exists if new_main_domain not in domain_list()["domains"]: raise YunohostError("domain_name_unknown", domain=new_main_domain) operation_logger.related_to.append(("domain", new_main_domain)) operation_logger.start() # Apply changes to ssl certs try: write_to_file("/etc/yunohost/current_host", new_main_domain) _set_hostname(new_main_domain) except Exception as e: logger.warning("%s" % e, exc_info=1) raise YunohostError("main_domain_change_failed") # Generate SSOwat configuration file app_ssowatconf() # Regen configurations if os.path.exists("/etc/yunohost/installed"): regen_conf() logger.success(m18n.n("main_domain_changed"))
def check_assertions(self): # Be on stretch (9.x) and yunohost 3.x # NB : we do both check to cover situations where the upgrade crashed # in the middle and debian version could be > 9.x but yunohost package # would still be in 3.x... if not self.debian_major_version() == 9 \ and not self.yunohost_major_version() == 3: raise YunohostError("migration_0015_not_stretch") # Have > 1 Go free space on /var/ ? if free_space_in_directory("/var/") / (1024**3) < 1.0: raise YunohostError("migration_0015_not_enough_free_space") # Check system is up to date # (but we don't if 'stretch' is already in the sources.list ... # which means maybe a previous upgrade crashed and we're re-running it) if " buster " not in read_file("/etc/apt/sources.list"): tools_update(system=True) upgradable_system_packages = list(_list_upgradable_apt_packages()) if upgradable_system_packages: raise YunohostError( "migration_0015_system_not_fully_up_to_date")
def validate_filter_criterias(filter_): # Get all the categories all_categories = _list_diagnosis_categories() all_categories_names = [category for category, _ in all_categories] # Sanity checks for the provided arguments if len(filter_) == 0: raise YunohostError( "You should provide at least one criteria being the diagnosis category to ignore" ) category = filter_[0] if category not in all_categories_names: raise YunohostError("%s is not a diagnosis category" % category) if any("=" not in criteria for criteria in filter_[1:]): raise YunohostError( "Criterias should be of the form key=value (e.g. domain=yolo.test)" ) # Convert the provided criteria into a nice dict criterias = {c.split("=")[0]: c.split("=")[1] for c in filter_[1:]} return category, criterias
def user_list(fields=None): from yunohost.utils.ldap import _get_ldap_interface user_attrs = { "uid": "username", "cn": "fullname", "mail": "mail", "maildrop": "mail-forward", "loginShell": "shell", "homeDirectory": "home_path", "mailuserquota": "mailbox-quota", } attrs = ["uid"] users = {} if fields: keys = user_attrs.keys() for attr in fields: if attr in keys: attrs.append(attr) else: raise YunohostError("field_invalid", attr) else: attrs = ["uid", "cn", "mail", "mailuserquota", "loginShell"] ldap = _get_ldap_interface() result = ldap.search( "ou=users,dc=yunohost,dc=org", "(&(objectclass=person)(!(uid=root))(!(uid=nobody)))", attrs, ) for user in result: entry = {} for attr, values in user.items(): if values: if attr == "loginShell": if values[0].strip() == "/bin/false": entry["ssh_allowed"] = False else: entry["ssh_allowed"] = True entry[user_attrs[attr]] = values[0] uid = entry[user_attrs["uid"]] users[uid] = entry return {"users": users}