diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index f18fdeb3..8e8f4ab2 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -199,6 +199,8 @@ domain: list: action_help: List domains api: GET /domains + configuration: + authenticate: all arguments: -f: full: --filter @@ -206,14 +208,18 @@ domain: -l: full: --limit help: Maximum number of domain fetched + type: int -o: full: --offset help: Starting number for domain fetching + type: int ### domain_add() add: action_help: Create a custom domain api: POST /domains + configuration: + authenticate: all arguments: domains: help: Domain name to add @@ -235,6 +241,8 @@ domain: remove: action_help: Delete domains api: 'DELETE /domains/{domains}' + configuration: + authenticate: all arguments: domains: help: Domain(s) to delete @@ -245,16 +253,16 @@ domain: - "Must be a valid domain name (e.g. my-domain.org)" ### domain_info() - info: - action_help: Get domain informations - api: 'GET /domains/' - arguments: - domain: - help: "" - extra: - pattern: - - '^([a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)(\.[a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)*(\.[a-zA-Z]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)$' - - "Must be a valid domain name (e.g. my-domain.org)" +# info: +# action_help: Get domain informations +# api: 'GET /domains/' +# arguments: +# domain: +# help: "" +# extra: +# pattern: +# - '^([a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)(\.[a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)*(\.[a-zA-Z]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)$' +# - "Must be a valid domain name (e.g. my-domain.org)" ############################# diff --git a/lib/yunohost/app.py b/lib/yunohost/app.py index 9436c707..4a4b3530 100644 --- a/lib/yunohost/app.py +++ b/lib/yunohost/app.py @@ -534,8 +534,8 @@ def app_addaccess(apps, users): """ if not users: users = [] - for user in user_list()['Users']: - users.append(user['Username']) + for user in user_list()['users']: + users.append(user['username']) if not isinstance(users, list): users = [users] if not isinstance(apps, list): apps = [apps] @@ -610,11 +610,11 @@ def app_removeaccess(apps, users): new_users = new_users +','+ allowed_user else: new_users='' - for user in user_list()['Users']: - if user['Username'] not in users: + for user in user_list()['users']: + if user['username'] not in users: if new_users == '': - new_users = user['Username'] - new_users=new_users+','+user['Username'] + new_users = user['username'] + new_users=new_users+','+user['username'] app_setting(app, 'allowed_users', new_users.strip()) @@ -771,7 +771,7 @@ def app_checkurl(url, app=None): apps_map = app_map(raw=True) validate(r'^([a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)(\.[a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)*(\.[a-zA-Z]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)$', domain) - if domain not in domain_list()['Domains']: + if domain not in domain_list(YunoHostLDAP())['domains']: raise YunoHostError(22, _("Domain doesn't exists")) if domain in apps_map: @@ -828,11 +828,11 @@ def app_ssowatconf(): with open('/etc/yunohost/current_host', 'r') as f: main_domain = f.readline().rstrip() - domains = domain_list()['Domains'] + domains = domain_list(YunoHostLDAP())['domains'] users = {} - for user in user_list()['Users']: - users[user['Username']] = app_map(user=user['Username']) + for user in user_list()['users']: + users[user['username']] = app_map(user=user['username']) skipped_urls = [] skipped_regex = [] diff --git a/lib/yunohost/domain.py b/lib/yunohost/domain.py index ff0baaed..79b333ac 100644 --- a/lib/yunohost/domain.py +++ b/lib/yunohost/domain.py @@ -23,9 +23,6 @@ Manage domains """ -import logging -logging.warning('the module yunohost.backup has not been revisited and updated yet') - import os import sys import datetime @@ -33,14 +30,12 @@ import re import shutil import json import yaml -import requests from urllib import urlopen -from dyndns import dyndns_subscribe -from moulinette.helpers import YunoHostError, YunoHostLDAP, win_msg, colorize, validate, get_required_args +from moulinette.core import MoulinetteError -def domain_list(filter=None, limit=None, offset=None): +def domain_list(auth, filter=None, limit=None, offset=None): """ List domains @@ -50,29 +45,25 @@ def domain_list(filter=None, limit=None, offset=None): limit -- Maximum number of domain fetched """ - with YunoHostLDAP() as yldap: - result_list = [] - if offset: offset = int(offset) - else: offset = 0 - if limit: limit = int(limit) - else: limit = 1000 - if not filter: filter = 'virtualdomain=*' + result_list = [] - result = yldap.search('ou=domains,dc=yunohost,dc=org', filter, attrs=['virtualdomain']) + # Set default arguments values + if offset is None: + offset = 0 + if limit is None: + limit = 1000 + if filter is None: + filter = 'virtualdomain=*' - if result and len(result) > (0 + offset) and limit > 0: - i = 0 + offset - for domain in result[i:]: - if i <= limit: - result_list.append(domain['virtualdomain'][0]) - i += 1 - else: - raise YunoHostError(167, _("No domain found")) + result = auth.search('ou=domains,dc=yunohost,dc=org', filter, ['virtualdomain']) - return { 'Domains': result_list } + if len(result) > offset and limit > 0: + for domain in result[offset:offset+limit]: + result_list.append(domain['virtualdomain'][0]) + return { 'domains': result_list } -def domain_add(domains, main=False, dyndns=False): +def domain_add(auth, domains, main=False, dyndns=False): """ Create a custom domain @@ -82,166 +73,169 @@ def domain_add(domains, main=False, dyndns=False): dyndns -- Subscribe to DynDNS """ - with YunoHostLDAP() as yldap: - attr_dict = { 'objectClass' : ['mailDomain', 'top'] } - ip = str(urlopen('http://ip.yunohost.org').read()) - now = datetime.datetime.now() - timestamp = str(now.year) + str(now.month) + str(now.day) - result = [] + attr_dict = { 'objectClass' : ['mailDomain', 'top'] } + ip = str(urlopen('http://ip.yunohost.org').read()) + now = datetime.datetime.now() + timestamp = str(now.year) + str(now.month) + str(now.day) + result = [] - if not isinstance(domains, list): - domains = [ domains ] + if not isinstance(domains, list): + domains = [ domains ] - for domain in domains: - try: - if domain in domain_list()['Domains']: continue - except YunoHostError: pass + for domain in domains: + if domain in domain_list(auth)['domains']: + continue - # DynDNS domain - if dyndns and len(domain.split('.')) >= 3: - r = requests.get('http://dyndns.yunohost.org/domains') - dyndomains = json.loads(r.text) - dyndomain = '.'.join(domain.split('.')[1:]) - if dyndomain in dyndomains: - if os.path.exists('/etc/cron.d/yunohost-dyndns'): - raise YunoHostError(22, _("You already have a DynDNS domain")) - else: - dyndns_subscribe(domain=domain) - - # Commands - ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA' - ssl_domain_path = '/etc/yunohost/certs/'+ domain - with open(ssl_dir +'/serial', 'r') as f: - serial = f.readline().rstrip() - try: os.listdir(ssl_domain_path) - except OSError: os.makedirs(ssl_domain_path) - - command_list = [ - 'cp '+ ssl_dir +'/openssl.cnf '+ ssl_domain_path, - 'sed -i "s/yunohost.org/' + domain + '/g" '+ ssl_domain_path +'/openssl.cnf', - 'openssl req -new -config '+ ssl_domain_path +'/openssl.cnf -days 3650 -out '+ ssl_dir +'/certs/yunohost_csr.pem -keyout '+ ssl_dir +'/certs/yunohost_key.pem -nodes -batch', - 'openssl ca -config '+ ssl_domain_path +'/openssl.cnf -days 3650 -in '+ ssl_dir +'/certs/yunohost_csr.pem -out '+ ssl_dir +'/certs/yunohost_crt.pem -batch', - 'ln -s /etc/ssl/certs/ca-yunohost_crt.pem '+ ssl_domain_path +'/ca.pem', - 'cp '+ ssl_dir +'/certs/yunohost_key.pem '+ ssl_domain_path +'/key.pem', - 'cp '+ ssl_dir +'/newcerts/'+ serial +'.pem '+ ssl_domain_path +'/crt.pem', - 'chmod 755 '+ ssl_domain_path, - 'chmod 640 '+ ssl_domain_path +'/key.pem', - 'chmod 640 '+ ssl_domain_path +'/crt.pem', - 'chmod 600 '+ ssl_domain_path +'/openssl.cnf', - 'chown root:metronome '+ ssl_domain_path +'/key.pem', - 'chown root:metronome '+ ssl_domain_path +'/crt.pem' - ] - - for command in command_list: - if os.system(command) != 0: - raise YunoHostError(17, _("An error occurred during certificate generation")) - - try: - yldap.validate_uniqueness({ 'virtualdomain' : domain }) - except YunoHostError: - raise YunoHostError(17, _("Domain already created")) - - - attr_dict['virtualdomain'] = domain - - try: - with open('/var/lib/bind/'+ domain +'.zone') as f: pass - except IOError as e: - zone_lines = [ - '$TTL 38400', - domain +'. IN SOA ns.'+ domain +'. root.'+ domain +'. '+ timestamp +' 10800 3600 604800 38400', - domain +'. IN NS ns.'+ domain +'.', - domain +'. IN A '+ ip, - domain +'. IN MX 5 '+ domain +'.', - domain +'. IN TXT "v=spf1 mx a -all"', - 'ns.'+ domain +'. IN A '+ ip, - '_xmpp-client._tcp.'+ domain +'. IN SRV 0 5 5222 '+ domain +'.', - '_xmpp-server._tcp.'+ domain +'. IN SRV 0 5 5269 '+ domain +'.', - '_jabber._tcp.'+ domain +'. IN SRV 0 5 5269 '+ domain +'.', - ] - if main: - zone_lines.extend([ - 'pubsub.'+ domain +'. IN A '+ ip, - 'muc.'+ domain +'. IN A '+ ip, - 'vjud.'+ domain +'. IN A '+ ip - ]) - with open('/var/lib/bind/' + domain + '.zone', 'w') as zone: - for line in zone_lines: - zone.write(line + '\n') - - os.system('chown bind /var/lib/bind/' + domain + '.zone') + # DynDNS domain + if dyndns: + if len(domain.split('.')) < 3: + raise MoulinetteError(22, _("Invalid domain '%s' for DynDNS" % domain)) + import requests + from yunohost.dyndns import dyndns_subscribe + r = requests.get('http://dyndns.yunohost.org/domains') + dyndomains = json.loads(r.text) + dyndomain = '.'.join(domain.split('.')[1:]) + if dyndomain in dyndomains: + if os.path.exists('/etc/cron.d/yunohost-dyndns'): + raise MoulinetteError(22, _("You already have a DynDNS domain")) + dyndns_subscribe(domain=domain) else: - raise YunoHostError(17, _("Zone file already exists for ") + domain) + raise MoulinetteError(22, _("Unknown DynDNS domain '%s'" % dyndomain)) + # Commands + ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA' + ssl_domain_path = '/etc/yunohost/certs/'+ domain + with open(ssl_dir +'/serial', 'r') as f: + serial = f.readline().rstrip() + try: os.listdir(ssl_domain_path) + except OSError: os.makedirs(ssl_domain_path) + + command_list = [ + 'cp '+ ssl_dir +'/openssl.cnf '+ ssl_domain_path, + 'sed -i "s/yunohost.org/' + domain + '/g" '+ ssl_domain_path +'/openssl.cnf', + 'openssl req -new -config '+ ssl_domain_path +'/openssl.cnf -days 3650 -out '+ ssl_dir +'/certs/yunohost_csr.pem -keyout '+ ssl_dir +'/certs/yunohost_key.pem -nodes -batch', + 'openssl ca -config '+ ssl_domain_path +'/openssl.cnf -days 3650 -in '+ ssl_dir +'/certs/yunohost_csr.pem -out '+ ssl_dir +'/certs/yunohost_crt.pem -batch', + 'ln -s /etc/ssl/certs/ca-yunohost_crt.pem '+ ssl_domain_path +'/ca.pem', + 'cp '+ ssl_dir +'/certs/yunohost_key.pem '+ ssl_domain_path +'/key.pem', + 'cp '+ ssl_dir +'/newcerts/'+ serial +'.pem '+ ssl_domain_path +'/crt.pem', + 'chmod 755 '+ ssl_domain_path, + 'chmod 640 '+ ssl_domain_path +'/key.pem', + 'chmod 640 '+ ssl_domain_path +'/crt.pem', + 'chmod 600 '+ ssl_domain_path +'/openssl.cnf', + 'chown root:metronome '+ ssl_domain_path +'/key.pem', + 'chown root:metronome '+ ssl_domain_path +'/crt.pem' + ] + + for command in command_list: + if os.system(command) != 0: + raise MoulinetteError(17, _("An error occurred during certificate generation")) + + try: + auth.validate_uniqueness({ 'virtualdomain': domain }) + except MoulinetteError: + raise MoulinetteError(17, _("Domain already created")) + + + attr_dict['virtualdomain'] = domain + + try: + with open('/var/lib/bind/'+ domain +'.zone') as f: pass + except IOError as e: + zone_lines = [ + '$TTL 38400', + domain +'. IN SOA ns.'+ domain +'. root.'+ domain +'. '+ timestamp +' 10800 3600 604800 38400', + domain +'. IN NS ns.'+ domain +'.', + domain +'. IN A '+ ip, + domain +'. IN MX 5 '+ domain +'.', + domain +'. IN TXT "v=spf1 mx a -all"', + 'ns.'+ domain +'. IN A '+ ip, + '_xmpp-client._tcp.'+ domain +'. IN SRV 0 5 5222 '+ domain +'.', + '_xmpp-server._tcp.'+ domain +'. IN SRV 0 5 5269 '+ domain +'.', + '_jabber._tcp.'+ domain +'. IN SRV 0 5 5269 '+ domain +'.', + ] + if main: + zone_lines.extend([ + 'pubsub.'+ domain +'. IN A '+ ip, + 'muc.'+ domain +'. IN A '+ ip, + 'vjud.'+ domain +'. IN A '+ ip + ]) + with open('/var/lib/bind/' + domain + '.zone', 'w') as zone: + for line in zone_lines: + zone.write(line + '\n') + + os.system('chown bind /var/lib/bind/' + domain + '.zone') + + else: + raise MoulinetteError(17, _("Zone file already exists for ") + domain) + + conf_lines = [ + 'zone "'+ domain +'" {', + ' type master;', + ' file "/var/lib/bind/'+ domain +'.zone";', + ' allow-transfer {', + ' 127.0.0.1;', + ' localnets;', + ' };', + '};' + ] + with open('/etc/bind/named.conf.local', 'a') as conf: + for line in conf_lines: + conf.write(line + '\n') + + os.system('service bind9 reload') + + # XMPP + try: + with open('/etc/metronome/conf.d/'+ domain +'.cfg.lua') as f: pass + except IOError as e: conf_lines = [ - 'zone "'+ domain +'" {', - ' type master;', - ' file "/var/lib/bind/'+ domain +'.zone";', - ' allow-transfer {', - ' 127.0.0.1;', - ' localnets;', - ' };', - '};' + 'VirtualHost "'+ domain +'"', + ' ssl = {', + ' key = "'+ ssl_domain_path +'/key.pem";', + ' certificate = "'+ ssl_domain_path +'/crt.pem";', + ' }', + ' authentication = "ldap2"', + ' ldap = {', + ' hostname = "localhost",', + ' user = {', + ' basedn = "ou=users,dc=yunohost,dc=org",', + ' filter = "(&(objectClass=posixAccount)(mail=*@'+ domain +'))",', + ' usernamefield = "mail",', + ' namefield = "cn",', + ' },', + ' }', ] - with open('/etc/bind/named.conf.local', 'a') as conf: + with open('/etc/metronome/conf.d/' + domain + '.cfg.lua', 'w') as conf: for line in conf_lines: - conf.write(line + '\n') + conf.write(line + '\n') - os.system('service bind9 reload') - - # XMPP - try: - with open('/etc/metronome/conf.d/'+ domain +'.cfg.lua') as f: pass - except IOError as e: - conf_lines = [ - 'VirtualHost "'+ domain +'"', - ' ssl = {', - ' key = "'+ ssl_domain_path +'/key.pem";', - ' certificate = "'+ ssl_domain_path +'/crt.pem";', - ' }', - ' authentication = "ldap2"', - ' ldap = {', - ' hostname = "localhost",', - ' user = {', - ' basedn = "ou=users,dc=yunohost,dc=org",', - ' filter = "(&(objectClass=posixAccount)(mail=*@'+ domain +'))",', - ' usernamefield = "mail",', - ' namefield = "cn",', - ' },', - ' }', - ] - with open('/etc/metronome/conf.d/' + domain + '.cfg.lua', 'w') as conf: - for line in conf_lines: - conf.write(line + '\n') - - os.system('mkdir -p /var/lib/metronome/'+ domain.replace('.', '%2e') +'/pep') - os.system('chown -R metronome: /var/lib/metronome/') - os.system('chown -R metronome: /etc/metronome/conf.d/') - os.system('service metronome restart') + os.system('mkdir -p /var/lib/metronome/'+ domain.replace('.', '%2e') +'/pep') + os.system('chown -R metronome: /var/lib/metronome/') + os.system('chown -R metronome: /etc/metronome/conf.d/') + os.system('service metronome restart') - # Nginx - os.system('cp /usr/share/yunohost/yunohost-config/nginx/template.conf /etc/nginx/conf.d/'+ domain +'.conf') - os.system('mkdir /etc/nginx/conf.d/'+ domain +'.d/') - os.system('sed -i s/yunohost.org/'+ domain +'/g /etc/nginx/conf.d/'+ domain +'.conf') - os.system('service nginx reload') + # Nginx + os.system('cp /usr/share/yunohost/yunohost-config/nginx/template.conf /etc/nginx/conf.d/'+ domain +'.conf') + os.system('mkdir /etc/nginx/conf.d/'+ domain +'.d/') + os.system('sed -i s/yunohost.org/'+ domain +'/g /etc/nginx/conf.d/'+ domain +'.conf') + os.system('service nginx reload') - if yldap.add('virtualdomain=' + domain + ',ou=domains', attr_dict): - result.append(domain) - continue - else: - raise YunoHostError(169, _("An error occured during domain creation")) + if auth.add('virtualdomain=' + domain + ',ou=domains', attr_dict): + result.append(domain) + continue + else: + raise MoulinetteError(169, _("An error occurred during domain creation")) - os.system('yunohost app ssowatconf > /dev/null 2>&1') + os.system('yunohost app ssowatconf > /dev/null 2>&1') - win_msg(_("Domain(s) successfully created")) - - return { 'Domains' : result } + msignals.display(_("Domain(s) successfully created."), 'success') + return { 'domains': result } -def domain_remove(domains): +def domain_remove(auth, domains): """ Delete domains @@ -249,52 +243,54 @@ def domain_remove(domains): domains -- Domain(s) to delete """ - with YunoHostLDAP() as yldap: - result = [] + result = [] + domains_list = domain_list(auth)['domains'] - if not isinstance(domains, list): - domains = [ domains ] + if not isinstance(domains, list): + domains = [ domains ] - for domain in domains: - # Check if apps are installed on the domain - for app in os.listdir('/etc/yunohost/apps/'): - with open('/etc/yunohost/apps/' + app +'/settings.yml') as f: - if yaml.load(f)['domain'] == domain: - raise YunoHostError(1, _("One or more apps are installed on this domain, please uninstall them before proceed to domain removal")) + for domain in domains: + if domain not in domains_list: + raise MoulinetteError(22, _("Unknown domain '%s'") % domain) - if yldap.remove('virtualdomain=' + domain + ',ou=domains'): - try: - shutil.rmtree('/etc/yunohost/certs/'+ domain) - os.remove('/var/lib/bind/'+ domain +'.zone') - shutil.rmtree('/var/lib/metronome/'+ domain.replace('.', '%2e')) - os.remove('/etc/metronome/conf.d/'+ domain +'.cfg.lua') - shutil.rmtree('/etc/nginx/conf.d/'+ domain +'.d') - os.remove('/etc/nginx/conf.d/'+ domain +'.conf') - except: - pass - with open('/etc/bind/named.conf.local', 'r') as conf: - conf_lines = conf.readlines() - with open('/etc/bind/named.conf.local', 'w') as conf: - in_block = False - for line in conf_lines: - if re.search(r'^zone "'+ domain, line): - in_block = True - if in_block: - if re.search(r'^};$', line): - in_block = False - else: - conf.write(line) - result.append(domain) - continue - else: - raise YunoHostError(169, _("An error occured during domain deletion")) + # Check if apps are installed on the domain + for app in os.listdir('/etc/yunohost/apps/'): + with open('/etc/yunohost/apps/' + app +'/settings.yml') as f: + if yaml.load(f)['domain'] == domain: + raise MoulinetteError(1, _("One or more apps are installed on this domain, please uninstall them before proceed to domain removal")) - os.system('yunohost app ssowatconf > /dev/null 2>&1') - os.system('service nginx reload') - os.system('service bind9 reload') - os.system('service metronome restart') + if auth.remove('virtualdomain=' + domain + ',ou=domains'): + try: + shutil.rmtree('/etc/yunohost/certs/'+ domain) + os.remove('/var/lib/bind/'+ domain +'.zone') + shutil.rmtree('/var/lib/metronome/'+ domain.replace('.', '%2e')) + os.remove('/etc/metronome/conf.d/'+ domain +'.cfg.lua') + shutil.rmtree('/etc/nginx/conf.d/'+ domain +'.d') + os.remove('/etc/nginx/conf.d/'+ domain +'.conf') + except: + pass + with open('/etc/bind/named.conf.local', 'r') as conf: + conf_lines = conf.readlines() + with open('/etc/bind/named.conf.local', 'w') as conf: + in_block = False + for line in conf_lines: + if re.search(r'^zone "'+ domain, line): + in_block = True + if in_block: + if re.search(r'^};$', line): + in_block = False + else: + conf.write(line) + result.append(domain) + continue + else: + raise MoulinetteError(169, _("An error occurred during domain deletion")) - win_msg(_("Domain(s) successfully deleted")) + os.system('yunohost app ssowatconf > /dev/null 2>&1') + os.system('service nginx reload') + os.system('service bind9 reload') + os.system('service metronome restart') - return { 'Domains' : result } + msignals.display(_("Domain(s) successfully deleted."), 'success') + return { 'domains': result } diff --git a/lib/yunohost/user.py b/lib/yunohost/user.py index d63dbbfe..53a71e26 100644 --- a/lib/yunohost/user.py +++ b/lib/yunohost/user.py @@ -105,7 +105,7 @@ def user_create(auth, username, firstname, lastname, mail, password): 'mail' : mail }) - if mail[mail.find('@')+1:] not in domain_list()['Domains']: + if mail[mail.find('@')+1:] not in domain_list(auth)['domains']: raise MoulinetteError(22, _("Unknown domain '%s'") % mail[mail.find('@')+1:]) # Get random UID/GID @@ -199,7 +199,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop'] new_attr_dict = {} - domains = domain_list()['Domains'] + domains = domain_list(auth)['domains'] # Populate user informations result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch)