diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 10cd95a2..284f536c 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -50,6 +50,7 @@ _global: uri: ldap://localhost:389 base_dn: dc=yunohost,dc=org argument_auth: true + lock: true arguments: -v: full: --version @@ -70,6 +71,7 @@ user: api: GET /users configuration: authenticate: all + authenticator: ldap-anonymous arguments: --fields: help: fields to fetch @@ -183,6 +185,7 @@ user: api: 'GET /users/' configuration: authenticate: all + authenticator: ldap-anonymous arguments: username: help: Username or mail to get informations @@ -201,6 +204,7 @@ domain: api: GET /domains configuration: authenticate: all + authenticator: ldap-anonymous arguments: -f: full: --filter @@ -361,6 +365,8 @@ app: api: POST /app configuration: authenticate: all + authenticator: ldap-anonymous + lock: false arguments: app: help: App to install @@ -375,6 +381,9 @@ app: remove: action_help: Remove app api: DELETE /app + configuration: + authenticate: all + authenticator: ldap-anonymous arguments: app: help: App(s) to delete @@ -383,6 +392,9 @@ app: upgrade: action_help: Upgrade app api: PUT /app + configuration: + authenticate: all + authenticator: ldap-anonymous arguments: app: help: App(s) to upgrade (default all) @@ -450,6 +462,7 @@ app: api: GET /app/checkurl configuration: authenticate: all + authenticator: ldap-anonymous arguments: url: help: Url to check @@ -480,6 +493,7 @@ app: api: PUT /ssowatconf configuration: authenticate: all + authenticator: ldap-anonymous ### app_addaccess() TODO: Write help addaccess: @@ -487,6 +501,7 @@ app: api: PUT /app/access configuration: authenticate: all + authenticator: ldap-anonymous arguments: apps: nargs: "+" @@ -500,6 +515,7 @@ app: api: DELETE /app/access configuration: authenticate: all + authenticator: ldap-anonymous arguments: apps: nargs: "+" @@ -513,6 +529,7 @@ app: api: POST /app/access configuration: authenticate: all + authenticator: ldap-anonymous arguments: apps: nargs: "+" diff --git a/lib/yunohost/hook.py b/lib/yunohost/hook.py index 7d57f733..b47dba0d 100644 --- a/lib/yunohost/hook.py +++ b/lib/yunohost/hook.py @@ -23,15 +23,13 @@ Manage hooks """ -import logging -logging.warning('the module yunohost.hook has not been revisited and updated yet') - import os import sys import re import json -from moulinette.helpers import YunoHostError, YunoHostLDAP, win_msg, colorize +from moulinette.helpers import colorize +from moulinette.core import MoulinetteError hook_folder = '/usr/share/yunohost/hooks/' @@ -56,8 +54,8 @@ def hook_add(app, file): finalpath = hook_folder + action +'/'+ priority +'-'+ app print app - os.system('cp '+ file +' '+ finalpath) - os.system('chown -hR admin: '+ hook_folder) + os.system('cp %s %s' % (file, finalpath)) + os.system('chown -hR admin: %s' % hook_folder) return { 'hook': finalpath } @@ -87,19 +85,18 @@ def hook_callback(action, args=None): args -- Ordered list of arguments to pass to the script """ - with YunoHostLDAP() as yldap: - try: os.listdir(hook_folder + action) - except OSError: pass - else: - if args is None: - args = [] - elif not isinstance(args, list): - args = [args] + try: os.listdir(hook_folder + action) + except OSError: pass + else: + if args is None: + args = [] + elif not isinstance(args, list): + args = [args] - for hook in os.listdir(hook_folder + action): - try: - hook_exec(file=hook_folder + action +'/'+ hook, args=args) - except: pass + for hook in os.listdir(hook_folder + action): + try: + hook_exec(file=hook_folder + action +'/'+ hook, args=args) + except: pass def hook_check(file): @@ -114,7 +111,7 @@ def hook_check(file): with open(file[:file.index('scripts/')] + 'manifest.json') as f: manifest = json.loads(str(f.read())) except: - raise YunoHostError(22, _("Invalid app package")) + raise MoulinetteError(22, _("Invalid app package")) action = file[file.index('scripts/') + 8:] if 'arguments' in manifest and action in manifest['arguments']: @@ -132,41 +129,41 @@ def hook_exec(file, args=None): args -- Arguments to pass to the script """ - with YunoHostLDAP() as yldap: - if isinstance(args, list): - arg_list = args - else: - required_args = hook_check(file) - if args is None: - args = {} + if isinstance(args, list): + arg_list = args + else: + required_args = hook_check(file) + if args is None: + args = {} - arg_list = [] - for arg in required_args: - if arg['name'] in args: - if 'choices' in arg and args[arg['name']] not in arg['choices']: - raise YunoHostError(22, _("Invalid choice") + ': ' + args[arg['name']]) - arg_list.append(args[arg['name']]) + arg_list = [] + for arg in required_args: + if arg['name'] in args: + if 'choices' in arg and args[arg['name']] not in arg['choices']: + raise MoulinetteError(22, _("Invalid choice") + ': ' + args[arg['name']]) + arg_list.append(args[arg['name']]) + else: + if os.isatty(1) and 'ask' in arg: + ask_string = arg['ask']['en'] #TODO: I18n + if 'choices' in arg: + ask_string = ask_string +' ('+ '|'.join(arg['choices']) +')' + if 'default' in arg: + ask_string = ask_string +' (default: '+ arg['default'] +')' + + input_string = raw_input(colorize(ask_string + ': ', 'cyan')) + + if input_string == '' and 'default' in arg: + input_string = arg['default'] + + arg_list.append(input_string) + elif 'default' in arg: + arg_list.append(arg['default']) else: - if os.isatty(1) and 'ask' in arg: - ask_string = arg['ask']['en'] #TODO: I18n - if 'choices' in arg: - ask_string = ask_string +' ('+ '|'.join(arg['choices']) +')' - if 'default' in arg: - ask_string = ask_string +' (default: '+ arg['default'] +')' + raise MoulinetteError(22, _("Missing argument : %s") % arg['name']) - input_string = raw_input(colorize(ask_string + ': ', 'cyan')) - - if input_string == '' and 'default' in arg: - input_string = arg['default'] - - arg_list.append(input_string) - elif 'default' in arg: - arg_list.append(arg['default']) - else: - raise YunoHostError(22, _("Missing arguments") + ': ' + arg['name']) - - file_path = "./" - if "/" in file and file[0:2] != file_path: - file_path = os.path.dirname(file) - file = file.replace(file_path +"/", "") - return os.system('su - admin -c "cd \\"'+ file_path +'\\" && bash \\"'+ file +'\\" '+ ' '.join(arg_list) +'"') #TODO: Allow python script + file_path = "./" + if "/" in file and file[0:2] != file_path: + file_path = os.path.dirname(file) + file = file.replace(file_path +"/", "") + return os.system('su - admin -c "cd \\"%s\\" && bash \\"%s\\" %s"' % (file_path, file, ' '.join(arg_list))) + #TODO: Allow python script diff --git a/moulinette/core.py b/moulinette/core.py index 21ca7916..573e806f 100644 --- a/moulinette/core.py +++ b/moulinette/core.py @@ -347,6 +347,7 @@ class MoulinetteLock(object): self._lockfile = '/var/run/moulinette_%s.lock' % namespace self._locked = False + self._bypass = False def acquire(self): """Attempt to acquire the lock for the moulinette instance @@ -359,9 +360,16 @@ class MoulinetteLock(object): start_time = time.time() while True: + if 'BYPASS_LOCK' in os.environ and os.environ['BYPASS_LOCK'] == 'yes': + self._bypass = True + break + if not os.path.isfile(self._lockfile): # Create the lock file - (open(self._lockfile, 'w')).close() + try: + (open(self._lockfile, 'w')).close() + except IOError: + raise MoulinetteError(errno.EPERM, _("Permission denied, did you forget 'sudo' ?")) break if (time.time() - start_time) > self.timeout: @@ -378,7 +386,8 @@ class MoulinetteLock(object): """ if self._locked: - os.unlink(self._lockfile) + if not self._bypass: + os.unlink(self._lockfile) self._locked = False def __enter__(self): diff --git a/moulinette/interfaces/__init__.py b/moulinette/interfaces/__init__.py index 8a916d40..86b98860 100644 --- a/moulinette/interfaces/__init__.py +++ b/moulinette/interfaces/__init__.py @@ -263,6 +263,17 @@ class BaseActionsMapParser(object): # TODO: Log error instead and tell valid values raise MoulinetteError(errno.EINVAL, "Invalid value '%r' for configuration 'argument_auth'" % arg_auth) + # -- 'lock' + try: + lock = configuration['lock'] + except KeyError: + pass + else: + if isinstance(lock, bool): + conf['lock'] = lock + else: + raise MoulinetteError(errno.EINVAL, "Invalid value '%r' for configuration 'lock'" % lock) + return conf def _format_conf(self, name, value): diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 723163d4..22dbca93 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -393,6 +393,10 @@ class ActionsMapParser(BaseActionsMapParser): raise MoulinetteError(errno.EINVAL, "No parser found for route '%s'" % route) ret = argparse.Namespace() + + if not self.get_conf(tid, 'lock'): + os.environ['BYPASS_LOCK'] = 'yes' + # Perform authentication if needed if self.get_conf(tid, 'authenticate'): auth_conf, klass = self.get_conf(tid, 'authenticator') diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index a0e6b515..dc273357 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import os import errno import getpass import argparse @@ -126,6 +127,9 @@ class ActionsMapParser(BaseActionsMapParser): def parse_args(self, args, **kwargs): ret = self._parser.parse_args(args) + if not self.get_conf(ret._tid, 'lock'): + os.environ['BYPASS_LOCK'] = 'yes' + # Perform authentication if needed if self.get_conf(ret._tid, 'authenticate'): auth_conf, klass = self.get_conf(ret._tid, 'authenticator')