diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 582f8e065..8fcfa0caa 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -24,13 +24,13 @@ Manage apps """ import os -import sys import json import shutil -import stat import yaml import time import re +import string +import random import socket import urlparse import errno @@ -42,6 +42,10 @@ from moulinette.utils.log import getActionLogger from yunohost.service import service_log from yunohost.utils import packages +from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback +from yunohost.user import user_list, user_info +from yunohost.domain import domain_list + logger = getActionLogger('yunohost.app') @@ -333,8 +337,6 @@ def app_upgrade(auth, app=[], url=None, file=None): url -- Git url to fetch for upgrade """ - from yunohost.hook import hook_add, hook_remove, hook_exec - try: app_list() except MoulinetteError: @@ -445,8 +447,6 @@ def app_install(auth, app, label=None, args=None): args -- Serialize arguments for app installation """ - from yunohost.hook import hook_add, hook_remove, hook_exec - # Fetch or extract sources try: os.listdir(install_tmp) except OSError: os.makedirs(install_tmp) @@ -588,8 +588,6 @@ def app_remove(auth, app): app -- App(s) to delete """ - from yunohost.hook import hook_exec, hook_remove - if not _is_installed(app): raise MoulinetteError(errno.EINVAL, m18n.n('app_not_installed', app=app)) @@ -631,9 +629,6 @@ def app_addaccess(auth, apps, users=[]): apps """ - from yunohost.user import user_list, user_info - from yunohost.hook import hook_callback - result = {} if not users: @@ -686,9 +681,6 @@ def app_removeaccess(auth, apps, users=[]): apps """ - from yunohost.user import user_list - from yunohost.hook import hook_callback - result = {} remove_all = False @@ -736,8 +728,6 @@ def app_clearaccess(auth, apps): apps """ - from yunohost.hook import hook_callback - if not isinstance(apps, list): apps = [apps] for app in apps: @@ -788,8 +778,6 @@ def app_makedefault(auth, app, domain=None): domain """ - from yunohost.domain import domain_list - app_settings = _get_app_settings(app) app_domain = app_settings['domain'] app_path = app_settings['path'] @@ -881,8 +869,6 @@ def app_checkurl(auth, url, app=None): app -- Write domain & path to app settings for further checks """ - from yunohost.domain import domain_list - if "https://" == url[:8]: url = url[8:] elif "http://" == url[:7]: @@ -965,9 +951,6 @@ def app_ssowatconf(auth): """ - from yunohost.domain import domain_list - from yunohost.user import user_list - with open('/etc/yunohost/current_host', 'r') as f: main_domain = f.readline().rstrip() @@ -1404,11 +1387,11 @@ def _value_for_locale(values): if not isinstance(values, dict): return values - for lang in [m18n.locale, m18n.default_locale]: - try: - return _encode_string(values[lang]) - except KeyError: - continue + if m18n.locale in values: + return _encode_string(values[m18n.locale]) + + if m18n.default_locale in values: + return _encode_string(values[m18n.default_locale]) # Fallback to first value return _encode_string(values.values()[0]) @@ -1466,6 +1449,7 @@ def _check_manifest_requirements(manifest): pkgname=pkgname, version=version, spec=spec)) + def _parse_args_from_manifest(manifest, action, args={}, auth=None): """Parse arguments needed for an action from the manifest @@ -1480,9 +1464,6 @@ def _parse_args_from_manifest(manifest, action, args={}, auth=None): args -- A dictionnary of arguments to parse """ - from yunohost.domain import domain_list - from yunohost.user import user_info - args_list = OrderedDict() try: action_args = manifest['arguments'][action] @@ -1577,6 +1558,7 @@ def _parse_args_from_manifest(manifest, action, args={}, auth=None): args_list[arg_name] = arg_value return args_list + def _make_environment_dict(args_dict): """ Convert a dictionnary containing manifest arguments @@ -1591,6 +1573,7 @@ def _make_environment_dict(args_dict): env_dict[ "YNH_APP_ARG_%s" % arg_name.upper() ] = arg_value return env_dict + def _parse_app_instance_name(app_instance_name): """ Parse a Yunohost app instance name and extracts the original appid @@ -1618,6 +1601,7 @@ def _parse_app_instance_name(app_instance_name): app_instance_nb = int(match.groupdict().get('appinstancenb')) if match.groupdict().get('appinstancenb') is not None else 1 return (appid, app_instance_nb) + def is_true(arg): """ Convert a string into a boolean @@ -1650,7 +1634,5 @@ def random_password(length=8): length -- The string length to generate """ - import string, random - char_set = string.ascii_uppercase + string.digits + string.ascii_lowercase return ''.join(random.sample(char_set, length)) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 649f3116e..f037941f6 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -25,7 +25,6 @@ """ import os import re -import sys import json import errno import time diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index b41891fb9..b5aee0150 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -2,7 +2,7 @@ """ License - Copyright (C) 2013 YunoHost + Copyright (C) 2013-2016 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 @@ -24,20 +24,20 @@ Manage domains """ import os -import sys -import datetime import re -import shutil import json import yaml import errno +import shutil import requests -from urllib import urlopen from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from yunohost.service import service_regen_conf +from yunohost.hook import hook_callback +from yunohost.dyndns import dyndns_subscribe +from yunohost.app import app_ssowatconf logger = getActionLogger('yunohost.domain') @@ -80,13 +80,9 @@ def domain_add(auth, domain, dyndns=False): dyndns -- Subscribe to DynDNS """ - from yunohost.hook import hook_callback attr_dict = { 'objectClass' : ['mailDomain', 'top'] } - now = datetime.datetime.now() - timestamp = str(now.year) + str(now.month) + str(now.day) - if domain in domain_list(auth)['domains']: raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists')) @@ -94,7 +90,6 @@ def domain_add(auth, domain, dyndns=False): if dyndns: if len(domain.split('.')) < 3: raise MoulinetteError(errno.EINVAL, m18n.n('domain_dyndns_invalid')) - from yunohost.dyndns import dyndns_subscribe try: r = requests.get('https://dyndns.yunohost.org/domains') @@ -160,12 +155,14 @@ def domain_add(auth, domain, dyndns=False): with open('/etc/yunohost/installed', 'r') as f: service_regen_conf(names=[ 'nginx', 'metronome', 'dnsmasq', 'rmilter']) - os.system('yunohost app ssowatconf > /dev/null 2>&1') + app_ssowatconf(auth) except IOError: pass except: # Force domain removal silently - try: domain_remove(auth, domain, True) - except: pass + try: + domain_remove(auth, domain, True) + except: + pass raise hook_callback('post_domain_add', args=[domain]) @@ -182,8 +179,6 @@ def domain_remove(auth, domain, force=False): force -- Force the domain removal """ - from yunohost.hook import hook_callback - if not force and domain not in domain_list(auth)['domains']: raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) @@ -200,12 +195,12 @@ def domain_remove(auth, domain, force=False): m18n.n('domain_uninstall_app_first')) if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: - os.system('rm -rf /etc/yunohost/certs/%s' % domain) + shutil.rmtree('/etc/yunohost/certs/%s' % domain) else: raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) service_regen_conf(names=['nginx', 'metronome', 'dnsmasq']) - os.system('yunohost app ssowatconf > /dev/null 2>&1') + app_ssowatconf(auth) hook_callback('post_domain_remove', args=[domain]) @@ -296,7 +291,7 @@ def get_public_ip(protocol=4): else: raise ValueError("invalid protocol version") try: - return urlopen(url).read().strip() + return requests.get(url).content.strip() except IOError: logger.debug('cannot retrieve public IPv%d' % protocol, exc_info=1) raise MoulinetteError(errno.ENETUNREACH, diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 878bc577e..55f7fd45b 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -40,6 +40,10 @@ from yunohost.domain import get_public_ip logger = getActionLogger('yunohost.dyndns') +RE_DYNDNS_PRIVATE_KEY = re.compile( + r'.*/K(?P[^\s\+]+)\.\+157.+\.private$' +) + class IPRouteLine(object): """ Utility class to parse an ip route output line @@ -62,10 +66,6 @@ class IPRouteLine(object): for k, v in self.m.groupdict().items(): setattr(self, k, v) -re_dyndns_private_key = re.compile( - r'.*/K(?P[^\s\+]+)\.\+157.+\.private$' -) - def dyndns_subscribe(subscribe_host="dyndns.yunohost.org", domain=None, key=None): """ @@ -171,7 +171,7 @@ def dyndns_update(dyn_host="dyndns.yunohost.org", domain=None, key=None, if domain is None: # Retrieve the first registered domain for path in glob.iglob('/etc/yunohost/dyndns/K*.private'): - match = re_dyndns_private_key.match(path) + match = RE_DYNDNS_PRIVATE_KEY.match(path) if not match: continue _domain = match.group('domain') diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index ef6249434..f304deb00 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -36,7 +36,9 @@ except ImportError: from moulinette.core import MoulinetteError from moulinette.utils import process from moulinette.utils.log import getActionLogger -from moulinette.utils.text import prependlines +from moulinette.utils.text import prependlines, searchf + +from yunohost.hook import hook_callback firewall_file = '/etc/yunohost/firewall.yml' upnp_cron_job = '/etc/cron.d/yunohost-firewall-upnp' @@ -123,7 +125,7 @@ def firewall_disallow(protocol, port, ipv4_only=False, ipv6_only=False, ipvs = ['ipv4', 'ipv6'] upnp = True if ipv4_only and ipv6_only: - upnp = True # automatically disallow UPnP + upnp = True # automatically disallow UPnP elif ipv4_only: ipvs = ['ipv4',] upnp = upnp_only @@ -178,7 +180,7 @@ def firewall_list(raw=False, by_ip_version=False, list_forwarded=False): ports = sorted(set(ports['ipv4']) | set(ports['ipv6'])) # Format returned dict - ret = { "opened_ports": ports } + ret = {"opened_ports": ports} if list_forwarded: # Combine TCP and UDP forwarded ports ret['forwarded_ports'] = sorted( @@ -194,8 +196,6 @@ def firewall_reload(skip_upnp=False): skip_upnp -- Do not refresh port forwarding using UPnP """ - from yunohost.hook import hook_callback - reloaded = False errors = False @@ -224,8 +224,8 @@ def firewall_reload(skip_upnp=False): # Iterate over ports and add rule for protocol in ['TCP', 'UDP']: for port in firewall['ipv4'][protocol]: - rules.append("iptables -A INPUT -p %s --dport %s -j ACCEPT" \ - % (protocol, process.quote(str(port)))) + rules.append("iptables -A INPUT -p %s --dport %s -j ACCEPT" + % (protocol, process.quote(str(port)))) rules += [ "iptables -A INPUT -i lo -j ACCEPT", "iptables -A INPUT -p icmp -j ACCEPT", @@ -253,8 +253,8 @@ def firewall_reload(skip_upnp=False): # Iterate over ports and add rule for protocol in ['TCP', 'UDP']: for port in firewall['ipv6'][protocol]: - rules.append("ip6tables -A INPUT -p %s --dport %s -j ACCEPT" \ - % (protocol, process.quote(str(port)))) + rules.append("ip6tables -A INPUT -p %s --dport %s -j ACCEPT" + % (protocol, process.quote(str(port)))) rules += [ "ip6tables -A INPUT -i lo -j ACCEPT", "ip6tables -A INPUT -p icmpv6 -j ACCEPT", @@ -308,13 +308,14 @@ def firewall_upnp(action='status', no_refresh=False): try: # Remove old cron job os.remove('/etc/cron.d/yunohost-firewall') - except: pass + except: + pass action = 'status' no_refresh = False if action == 'status' and no_refresh: # Only return current state - return { 'enabled': enabled } + return {'enabled': enabled} elif action == 'enable' or (enabled and action == 'status'): # Add cron job with open(upnp_cron_job, 'w+') as f: @@ -330,7 +331,8 @@ def firewall_upnp(action='status', no_refresh=False): try: # Remove cron job os.remove(upnp_cron_job) - except: pass + except: + pass enabled = False if action == 'status': no_refresh = True @@ -364,7 +366,8 @@ def firewall_upnp(action='status', no_refresh=False): if upnpc.getspecificportmapping(port, protocol): try: upnpc.deleteportmapping(port, protocol) - except: pass + except: + pass if not enabled: continue try: @@ -403,7 +406,7 @@ def firewall_upnp(action='status', no_refresh=False): if action == 'enable' and not enabled: raise MoulinetteError(errno.ENXIO, m18n.n('upnp_port_open_failed')) - return { 'enabled': enabled } + return {'enabled': enabled} def firewall_stop(): @@ -434,7 +437,6 @@ def _get_ssh_port(default=22): Retrieve the SSH port from the sshd_config file or used the default one if it's not defined. """ - from moulinette.utils.text import searchf try: m = searchf(r'^Port[ \t]+([0-9]+)$', '/etc/ssh/sshd_config', count=-1) @@ -444,12 +446,14 @@ def _get_ssh_port(default=22): pass return default + def _update_firewall_file(rules): """Make a backup and write new rules to firewall file""" os.system("cp {0} {0}.old".format(firewall_file)) with open(firewall_file, 'w') as f: yaml.safe_dump(rules, f, default_flow_style=False) + def _on_rule_command_error(returncode, cmd, output): """Callback for rules commands error""" # Log error and continue commands execution diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index aafe15459..0bf015320 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -24,15 +24,12 @@ Manage hooks """ import os -import sys -import re -import json import errno -import subprocess from glob import iglob from moulinette.core import MoulinetteError from moulinette.utils import log +from moulinette.utils.process import call_async_output hook_folder = '/usr/share/yunohost/hooks/' custom_hook_folder = '/etc/yunohost/hooks.d/' @@ -52,14 +49,16 @@ def hook_add(app, file): path, filename = os.path.split(file) priority, action = _extract_filename_parts(filename) - try: os.listdir(custom_hook_folder + action) - except OSError: os.makedirs(custom_hook_folder + action) + try: + os.listdir(custom_hook_folder + action) + except OSError: + os.makedirs(custom_hook_folder + action) - finalpath = custom_hook_folder + action +'/'+ priority +'-'+ app + finalpath = custom_hook_folder + action + '/' + priority + '-' + app os.system('cp %s %s' % (file, finalpath)) os.system('chown -hR admin: %s' % hook_folder) - return { 'hook': finalpath } + return {'hook': finalpath} def hook_remove(app): @@ -74,8 +73,9 @@ def hook_remove(app): for action in os.listdir(custom_hook_folder): for script in os.listdir(custom_hook_folder + action): if script.endswith(app): - os.remove(custom_hook_folder + action +'/'+ script) - except OSError: pass + os.remove(custom_hook_folder + action + '/' + script) + except OSError: + pass def hook_info(action, name): @@ -136,11 +136,11 @@ def hook_list(action, list_by='name', show_info=False): def _append_hook(d, priority, name, path): # Use the priority as key and a dict of hooks names # with their info as value - value = { 'path': path } + value = {'path': path} try: d[priority][name] = value except KeyError: - d[priority] = { name: value } + d[priority] = {name: value} else: def _append_hook(d, priority, name, path): # Use the priority as key and the name as value @@ -162,11 +162,12 @@ def hook_list(action, list_by='name', show_info=False): if h['path'] != path: h['path'] = path return - l.append({ 'priority': priority, 'path': path }) + l.append({'priority': priority, 'path': path}) d[name] = l else: if list_by == 'name': result = set() + def _append_hook(d, priority, name, path): # Add only the name d.add(name) @@ -204,7 +205,7 @@ def hook_list(action, list_by='name', show_info=False): logger.debug("custom hook folder not found for action '%s' in %s", action, custom_hook_folder) - return { 'hooks': result } + return {'hooks': result} def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, @@ -226,7 +227,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, (name, priority, path, succeed) as arguments """ - result = { 'succeed': {}, 'failed': {} } + result = {'succeed': {}, 'failed': {}} hooks_dict = {} # Retrieve hooks @@ -258,7 +259,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, for h in hl: # Update hooks dict d = hooks_dict.get(h['priority'], dict()) - d.update({ n: { 'path': h['path'] }}) + d.update({n: {'path': h['path']}}) hooks_dict[h['priority']] = d if not hooks_dict: return result @@ -308,9 +309,6 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, env -- Dictionnary of environment variables to export """ - from moulinette.utils.process import call_async_output - from yunohost.app import _value_for_locale - # Validate hook path if path[0] != '/': path = os.path.realpath(path) diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index 5142c8305..1ff69b9b2 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -35,12 +35,15 @@ import errno import os import dns.resolver import cPickle as pickle -from datetime import datetime, timedelta +from datetime import datetime from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from yunohost.domain import get_public_ip +from yunohost.service import (service_status, service_enable, + service_start, service_disable, service_stop) + logger = getActionLogger('yunohost.monitor') @@ -406,9 +409,6 @@ def monitor_enable(no_stats=False): no_stats -- Disable monitoring statistics """ - from yunohost.service import (service_status, service_enable, - service_start) - glances = service_status('glances') if glances['status'] != 'running': service_start('glances') @@ -433,9 +433,6 @@ def monitor_disable(): Disable server monitoring """ - from yunohost.service import (service_status, service_disable, - service_stop) - glances = service_status('glances') if glances['status'] != 'inactive': service_stop('glances') @@ -467,8 +464,6 @@ def _get_glances_api(): else: return p - from yunohost.service import service_status - if service_status('glances')['status'] != 'running': raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled')) raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed')) @@ -722,22 +717,26 @@ def _append_to_stats(stats, monitor, statics=[]): statics = [statics] # Appending function - def _append(s, m, st): - for k, v in m.items(): - if k in st: - s[k] = v - elif isinstance(v, dict): - if k not in s: - s[k] = {} - s[k] = _append(s[k], v, st) + def _append(_stats, _monitor, _statics): + for key, value in _monitor.items(): + if key in _statics: + _stats[key] = value + + elif isinstance(value, dict): + if key not in _stats: + _stats[key] = {} + _stats[key] = _append(_stats[key], value, _statics) + else: - if k not in s: - s[k] = [] - if isinstance(v, list): - s[k].extend(v) + if key not in _stats: + _stats[key] = [] + + if isinstance(value, list): + _stats[key].extend(value) else: - s[k].append(v) - return s + _stats[key].append(value) + + return _stats stats = _append(stats, monitor, statics) return stats diff --git a/src/yunohost/service.py b/src/yunohost/service.py index ab26dd2bc..b1b776326 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -36,7 +36,7 @@ from difflib import unified_diff from moulinette.core import MoulinetteError from moulinette.utils import log, filesystem -from yunohost.hook import hook_list, hook_callback +from yunohost.hook import hook_callback base_conf_path = '/home/yunohost.conf' diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index f78e32363..e56196760 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -24,10 +24,7 @@ Specific tools """ import os -import sys import yaml -import re -import getpass import requests import json import errno @@ -42,12 +39,12 @@ from moulinette.utils.log import getActionLogger from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, app_list from yunohost.domain import domain_add, domain_list, get_public_ip from yunohost.dyndns import dyndns_subscribe -from yunohost.firewall import firewall_upnp, firewall_reload +from yunohost.firewall import firewall_upnp from yunohost.service import service_status, service_regen_conf, service_log -from yunohost.monitor import monitor_disk, monitor_network, monitor_system +from yunohost.monitor import monitor_disk, monitor_system from yunohost.utils.packages import ynh_packages_version -apps_setting_path= '/etc/yunohost/apps/' +apps_setting_path = '/etc/yunohost/apps/' logger = getActionLogger('yunohost.tools') @@ -62,12 +59,16 @@ def tools_ldapinit(auth): ldap_map = yaml.load(f) for rdn, attr_dict in ldap_map['parents'].items(): - try: auth.add(rdn, attr_dict) - except: pass + try: + auth.add(rdn, attr_dict) + except: + pass for rdn, attr_dict in ldap_map['children'].items(): - try: auth.add(rdn, attr_dict) - except: pass + try: + auth.add(rdn, attr_dict) + except: + pass admin_dict = { 'cn': 'admin', @@ -118,7 +119,7 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False): old_domain = f.readline().rstrip() if not new_domain: - return { 'current_main_domain': old_domain } + return {'current_main_domain': old_domain} if not new_domain: raise MoulinetteError(errno.EINVAL, m18n.n('new_domain_required')) @@ -130,7 +131,7 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False): command_list = [ 'ln -s /etc/yunohost/certs/%s/key.pem /etc/ssl/private/yunohost_key.pem' % new_domain, - 'ln -s /etc/yunohost/certs/%s/crt.pem /etc/ssl/certs/yunohost_crt.pem' % new_domain, + 'ln -s /etc/yunohost/certs/%s/crt.pem /etc/ssl/certs/yunohost_crt.pem' % new_domain, 'echo %s > /etc/yunohost/current_host' % new_domain, ] @@ -146,14 +147,15 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False): pass else: dyndomains = json.loads(r.text) - dyndomain = '.'.join(new_domain.split('.')[1:]) + dyndomain = '.'.join(new_domain.split('.')[1:]) if dyndomain in dyndomains: dyndns_subscribe(domain=new_domain) try: with open('/etc/yunohost/installed', 'r') as f: service_regen_conf() - except IOError: pass + except IOError: + pass logger.success(m18n.n('maindomain_changed')) @@ -181,7 +183,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False): pass else: dyndomains = json.loads(r.text) - dyndomain = '.'.join(domain.split('.')[1:]) + dyndomain = '.'.join(domain.split('.')[1:]) if dyndomain in dyndomains: if requests.get('https://dyndns.yunohost.org/test/%s' % domain).status_code == 200: dyndns = True @@ -195,7 +197,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False): auth = init_authenticator(('ldap', 'default'), {'uri': "ldap://localhost:389", 'base_dn': "dc=yunohost,dc=org", - 'user_rdn': "cn=admin" }) + 'user_rdn': "cn=admin"}) auth.authenticate('yunohost') # Initialize LDAP for YunoHost @@ -212,8 +214,10 @@ def tools_postinstall(domain, password, ignore_dyndns=False): ] for folder in folders_to_create: - try: os.listdir(folder) - except OSError: os.makedirs(folder) + try: + os.listdir(folder) + except OSError: + os.makedirs(folder) # Change folders permissions os.system('chmod 755 /home/yunohost.app') @@ -232,7 +236,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False): if 'redirected_urls' not in ssowat_conf: ssowat_conf['redirected_urls'] = {} - ssowat_conf['redirected_urls']['/'] = domain +'/yunohost/admin' + ssowat_conf['redirected_urls']['/'] = domain + '/yunohost/admin' with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) @@ -244,8 +248,8 @@ def tools_postinstall(domain, password, ignore_dyndns=False): ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA' command_list = [ 'echo "01" > %s/serial' % ssl_dir, - 'rm %s/index.txt' % ssl_dir, - 'touch %s/index.txt' % ssl_dir, + 'rm %s/index.txt' % ssl_dir, + 'touch %s/index.txt' % ssl_dir, 'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir), 'sed -i "s/yunohost.org/%s/g" %s/openssl.ca.cnf ' % (domain, ssl_dir), 'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch' % (ssl_dir, ssl_dir, ssl_dir), @@ -317,35 +321,34 @@ def tools_update(ignore_apps=False, ignore_packages=False): app_fetchlist() except MoulinetteError: pass - app_list = os.listdir(apps_setting_path) - if len(app_list) > 0: - for app_id in app_list: - if '__' in app_id: - original_app_id = app_id[:app_id.index('__')] - else: - original_app_id = app_id - current_app_dict = app_info(app_id, raw=True) - new_app_dict = app_info(original_app_id, raw=True) + for app_id in os.listdir(apps_setting_path): + if '__' in app_id: + original_app_id = app_id[:app_id.index('__')] + else: + original_app_id = app_id - # Custom app - if new_app_dict is None or 'lastUpdate' not in new_app_dict or 'git' not in new_app_dict: - continue + current_app_dict = app_info(app_id, raw=True) + new_app_dict = app_info(original_app_id, raw=True) - if (new_app_dict['lastUpdate'] > current_app_dict['lastUpdate']) \ - or ('update_time' not in current_app_dict['settings'] \ - and (new_app_dict['lastUpdate'] > current_app_dict['settings']['install_time'])) \ - or ('update_time' in current_app_dict['settings'] \ - and (new_app_dict['lastUpdate'] > current_app_dict['settings']['update_time'])): - apps.append({ - 'id': app_id, - 'label': current_app_dict['settings']['label'] - }) + # Custom app + if new_app_dict is None or 'lastUpdate' not in new_app_dict or 'git' not in new_app_dict: + continue + + if (new_app_dict['lastUpdate'] > current_app_dict['lastUpdate']) \ + or ('update_time' not in current_app_dict['settings'] \ + and (new_app_dict['lastUpdate'] > current_app_dict['settings']['install_time'])) \ + or ('update_time' in current_app_dict['settings'] \ + and (new_app_dict['lastUpdate'] > current_app_dict['settings']['update_time'])): + apps.append({ + 'id': app_id, + 'label': current_app_dict['settings']['label'] + }) if len(apps) == 0 and len(packages) == 0: logger.info(m18n.n('packages_no_upgrade')) - return { 'packages': packages, 'apps': apps } + return {'packages': packages, 'apps': apps} def tools_upgrade(auth, ignore_apps=False, ignore_packages=False): @@ -381,7 +384,7 @@ def tools_upgrade(auth, ignore_apps=False, ignore_packages=False): # ... and set a hourly cron up to upgrade critical packages if critical_upgrades: logger.info(m18n.n('packages_upgrade_critical_later', - packages=', '.join(critical_upgrades))) + packages=', '.join(critical_upgrades))) with open('/etc/cron.d/yunohost-upgrade', 'w+') as f: f.write('00 * * * * root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin apt-get install %s -y && rm -f /etc/cron.d/yunohost-upgrade\n' % ' '.join(critical_upgrades)) @@ -414,7 +417,7 @@ def tools_upgrade(auth, ignore_apps=False, ignore_packages=False): # Return API logs if it is an API call if is_api: - return { "log": service_log('yunohost-api', number="100").values()[0] } + return {"log": service_log('yunohost-api', number="100").values()[0]} def tools_diagnosis(auth, private=False): @@ -422,7 +425,7 @@ def tools_diagnosis(auth, private=False): Return global info about current yunohost instance to help debugging """ - diagnosis = OrderedDict(); + diagnosis = OrderedDict() # Debian release try: @@ -466,8 +469,8 @@ def tools_diagnosis(auth, private=False): logger.warning(m18n.n('diagnosis_monitor_system_error', error=format(e)), exc_info=1) else: diagnosis['system']['memory'] = { - 'ram' : '%s (%s free)' % (system['memory']['ram']['total'], system['memory']['ram']['free']), - 'swap' : '%s (%s free)' % (system['memory']['swap']['total'], system['memory']['swap']['free']), + 'ram': '%s (%s free)' % (system['memory']['ram']['total'], system['memory']['ram']['free']), + 'swap': '%s (%s free)' % (system['memory']['swap']['total'], system['memory']['swap']['free']), } # Services status diff --git a/src/yunohost/user.py b/src/yunohost/user.py index ec7dd539c..e6fe112e6 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -30,12 +30,17 @@ import string import json import errno import subprocess -import math import re +import pwd from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger +from yunohost.domain import domain_list +from yunohost.hook import hook_callback +from yunohost.app import app_ssowatconf + + logger = getActionLogger('yunohost.user') @@ -50,12 +55,12 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None): fields -- fields to fetch """ - user_attrs = { 'uid': 'username', - 'cn': 'fullname', - 'mail': 'mail', - 'maildrop': 'mail-forward', - 'mailuserquota': 'mailbox-quota' } - attrs = [ 'uid' ] + user_attrs = {'uid': 'username', + 'cn': 'fullname', + 'mail': 'mail', + 'maildrop': 'mail-forward', + 'mailuserquota': 'mailbox-quota'} + attrs = ['uid'] users = {} # Set default arguments values @@ -74,12 +79,12 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None): raise MoulinetteError(errno.EINVAL, m18n.n('field_invalid', attr)) else: - attrs = [ 'uid', 'cn', 'mail', 'mailuserquota' ] + attrs = ['uid', 'cn', 'mail', 'mailuserquota'] result = auth.search('ou=users,dc=yunohost,dc=org', filter, attrs) if len(result) > offset and limit > 0: - for user in result[offset:offset+limit]: + for user in result[offset:offset + limit]: entry = {} for attr, values in user.items(): try: @@ -88,7 +93,7 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None): pass uid = entry[user_attrs['uid']] users[uid] = entry - return { 'users' : users } + return {'users': users} def user_create(auth, username, firstname, lastname, mail, password, @@ -105,15 +110,10 @@ def user_create(auth, username, firstname, lastname, mail, password, mailbox_quota -- Mailbox size quota """ - import pwd - from yunohost.domain import domain_list - from yunohost.hook import hook_callback - from yunohost.app import app_ssowatconf - # Validate uniqueness of username and mail in LDAP auth.validate_uniqueness({ - 'uid' : username, - 'mail' : mail + 'uid': username, + 'mail': mail }) # Validate uniqueness of username in system users @@ -125,10 +125,10 @@ def user_create(auth, username, firstname, lastname, mail, password, raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists')) # Check that the mail domain exists - if mail[mail.find('@')+1:] not in domain_list(auth)['domains']: + if mail[mail.find('@') + 1:] not in domain_list(auth)['domains']: raise MoulinetteError(errno.EINVAL, m18n.n('mail_domain_unknown', - domain=mail[mail.find('@')+1:])) + domain=mail[mail.find('@') + 1:])) # Get random UID/GID uid_check = gid_check = 0 @@ -141,7 +141,7 @@ def user_create(auth, username, firstname, lastname, mail, password, fullname = '%s %s' % (firstname, lastname) rdn = 'uid=%s,ou=users' % username char_set = string.ascii_uppercase + string.digits - salt = ''.join(random.sample(char_set,8)) + salt = ''.join(random.sample(char_set, 8)) salt = '$1$' + salt + '$' user_pwd = '{CRYPT}' + crypt.crypt(str(password), salt) attr_dict = { @@ -166,12 +166,12 @@ def user_create(auth, username, firstname, lastname, mail, password, with open('/etc/yunohost/current_host') as f: main_domain = f.readline().rstrip() aliases = [ - 'root@'+ main_domain, - 'admin@'+ main_domain, - 'webmaster@'+ main_domain, - 'postmaster@'+ main_domain, + 'root@' + main_domain, + 'admin@' + main_domain, + 'webmaster@' + main_domain, + 'postmaster@' + main_domain, ] - attr_dict['mail'] = [ attr_dict['mail'] ] + aliases + attr_dict['mail'] = [attr_dict['mail']] + aliases # If exists, remove the redirection from the SSO try: @@ -184,8 +184,8 @@ def user_create(auth, username, firstname, lastname, mail, password, with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) - except IOError: pass - + except IOError: + pass if auth.add(rdn, attr_dict): # Invalidate passwd to take user creation into account @@ -194,7 +194,7 @@ def user_create(auth, username, firstname, lastname, mail, password, # Update SFTP user group memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] memberlist.append(username) - if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }): + if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): try: # Attempt to create user home folder subprocess.check_call( @@ -204,12 +204,12 @@ def user_create(auth, username, firstname, lastname, mail, password, logger.warning(m18n.n('user_home_creation_failed'), exc_info=1) app_ssowatconf(auth) - #TODO: Send a welcome mail to user + # TODO: Send a welcome mail to user logger.success(m18n.n('user_created')) hook_callback('post_user_create', args=[username, mail, password, firstname, lastname]) - return { 'fullname' : fullname, 'username' : username, 'mail' : mail } + return {'fullname': fullname, 'username': username, 'mail': mail} raise MoulinetteError(169, m18n.n('user_creation_failed')) @@ -223,18 +223,17 @@ def user_delete(auth, username, purge=False): purge """ - from yunohost.app import app_ssowatconf - from yunohost.hook import hook_callback - if auth.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'] - try: memberlist.remove(username) - except: pass - if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }): + try: + memberlist.remove(username) + except: + pass + if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): if purge: subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) else: @@ -265,9 +264,6 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, remove_mailalias -- Mail aliases to remove """ - from yunohost.domain import domain_list - from yunohost.app import app_ssowatconf - attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop'] new_attr_dict = {} domains = domain_list(auth)['domains'] @@ -280,11 +276,11 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, # Get modifications from arguments if firstname: - new_attr_dict['givenName'] = firstname # TODO: Validate + new_attr_dict['givenName'] = firstname # TODO: Validate new_attr_dict['cn'] = new_attr_dict['displayName'] = firstname + ' ' + user['sn'][0] if lastname: - new_attr_dict['sn'] = lastname # TODO: Validate + new_attr_dict['sn'] = lastname # TODO: Validate new_attr_dict['cn'] = new_attr_dict['displayName'] = user['givenName'][0] + ' ' + lastname if lastname and firstname: @@ -292,34 +288,34 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, if change_password: char_set = string.ascii_uppercase + string.digits - salt = ''.join(random.sample(char_set,8)) + salt = ''.join(random.sample(char_set, 8)) salt = '$1$' + salt + '$' new_attr_dict['userPassword'] = '{CRYPT}' + crypt.crypt(str(change_password), salt) if mail: - auth.validate_uniqueness({ 'mail': mail }) - if mail[mail.find('@')+1:] not in domains: + auth.validate_uniqueness({'mail': mail}) + if mail[mail.find('@') + 1:] not in domains: raise MoulinetteError(errno.EINVAL, m18n.n('mail_domain_unknown', - domain=mail[mail.find('@')+1:])) + domain=mail[mail.find('@') + 1:])) del user['mail'][0] new_attr_dict['mail'] = [mail] + user['mail'] if add_mailalias: if not isinstance(add_mailalias, list): - add_mailalias = [ add_mailalias ] + add_mailalias = [add_mailalias] for mail in add_mailalias: - auth.validate_uniqueness({ 'mail': mail }) - if mail[mail.find('@')+1:] not in domains: + auth.validate_uniqueness({'mail': mail}) + if mail[mail.find('@') + 1:] not in domains: raise MoulinetteError(errno.EINVAL, m18n.n('mail_domain_unknown', - domain=mail[mail.find('@')+1:])) + domain=mail[mail.find('@') + 1:])) user['mail'].append(mail) new_attr_dict['mail'] = user['mail'] if remove_mailalias: if not isinstance(remove_mailalias, list): - remove_mailalias = [ remove_mailalias ] + remove_mailalias = [remove_mailalias] for mail in remove_mailalias: if len(user['mail']) > 1 and mail in user['mail'][1:]: user['mail'].remove(mail) @@ -330,7 +326,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, if add_mailforward: if not isinstance(add_mailforward, list): - add_mailforward = [ add_mailforward ] + add_mailforward = [add_mailforward] for mail in add_mailforward: if mail in user['maildrop'][1:]: continue @@ -339,7 +335,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, if remove_mailforward: if not isinstance(remove_mailforward, list): - remove_mailforward = [ remove_mailforward ] + remove_mailforward = [remove_mailforward] for mail in remove_mailforward: if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]: user['maildrop'].remove(mail) @@ -352,11 +348,11 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, new_attr_dict['mailuserquota'] = mailbox_quota if auth.update('uid=%s,ou=users' % username, new_attr_dict): - logger.success(m18n.n('user_updated')) - app_ssowatconf(auth) - return user_info(auth, username) + logger.success(m18n.n('user_updated')) + app_ssowatconf(auth) + return user_info(auth, username) else: - raise MoulinetteError(169, m18n.n('user_update_failed')) + raise MoulinetteError(169, m18n.n('user_update_failed')) def user_info(auth, username): @@ -372,9 +368,9 @@ def user_info(auth, username): ] if len(username.split('@')) is 2: - filter = 'mail='+ username + filter = 'mail=' + username else: - filter = 'uid='+ username + filter = 'uid=' + username result = auth.search('ou=users,dc=yunohost,dc=org', filter, user_attrs) @@ -398,28 +394,31 @@ def user_info(auth, username): result_dict['mail-forward'] = user['maildrop'][1:] if 'mailuserquota' in user: - if user['mailuserquota'][0] != '0': - cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0] - userquota = subprocess.check_output(cmd,stderr=subprocess.STDOUT, - shell=True) - quotavalue = re.findall(r'\d+', userquota) - result = '%s (%s%s)' % ( _convertSize(eval(quotavalue[0])), - quotavalue[2], '%') - result_dict['mailbox-quota'] = { - 'limit' : user['mailuserquota'][0], - 'use' : result - } + if user['mailuserquota'][0] != '0': + cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0] + userquota = subprocess.check_output(cmd, stderr=subprocess.STDOUT, + shell=True) + quotavalue = re.findall(r'\d+', userquota) + result = '%s (%s%s)' % (_convertSize(eval(quotavalue[0])), + quotavalue[2], '%') + result_dict['mailbox-quota'] = { + 'limit': user['mailuserquota'][0], + 'use': result + } else: - result_dict['mailbox-quota'] = m18n.n('unlimit') - + result_dict['mailbox-quota'] = m18n.n('unlimit') + if result: return result_dict else: raise MoulinetteError(167, m18n.n('user_info_failed')) + def _convertSize(num, suffix=''): - for unit in ['K','M','G','T','P','E','Z']: + for unit in ['K', 'M', 'G', 'T', 'P', 'E', 'Z']: if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Yi', suffix)