This commit is contained in:
Laurent Peuch 2016-05-13 11:55:58 +00:00
commit 1c41c5e442
10 changed files with 214 additions and 235 deletions

View file

@ -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))

View file

@ -25,7 +25,6 @@
"""
import os
import re
import sys
import json
import errno
import time

View file

@ -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,

View file

@ -40,6 +40,10 @@ from yunohost.domain import get_public_ip
logger = getActionLogger('yunohost.dyndns')
RE_DYNDNS_PRIVATE_KEY = re.compile(
r'.*/K(?P<domain>[^\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<domain>[^\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')

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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)