mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
[enh] Adapt yunohost_hook.py and allow bypass of the locking process
This commit is contained in:
parent
18eb0f9406
commit
962b78329f
6 changed files with 98 additions and 56 deletions
|
@ -50,6 +50,7 @@ _global:
|
||||||
uri: ldap://localhost:389
|
uri: ldap://localhost:389
|
||||||
base_dn: dc=yunohost,dc=org
|
base_dn: dc=yunohost,dc=org
|
||||||
argument_auth: true
|
argument_auth: true
|
||||||
|
lock: true
|
||||||
arguments:
|
arguments:
|
||||||
-v:
|
-v:
|
||||||
full: --version
|
full: --version
|
||||||
|
@ -70,6 +71,7 @@ user:
|
||||||
api: GET /users
|
api: GET /users
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
--fields:
|
--fields:
|
||||||
help: fields to fetch
|
help: fields to fetch
|
||||||
|
@ -183,6 +185,7 @@ user:
|
||||||
api: 'GET /users/<username>'
|
api: 'GET /users/<username>'
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
username:
|
username:
|
||||||
help: Username or mail to get informations
|
help: Username or mail to get informations
|
||||||
|
@ -201,6 +204,7 @@ domain:
|
||||||
api: GET /domains
|
api: GET /domains
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
-f:
|
-f:
|
||||||
full: --filter
|
full: --filter
|
||||||
|
@ -361,6 +365,8 @@ app:
|
||||||
api: POST /app
|
api: POST /app
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
|
lock: false
|
||||||
arguments:
|
arguments:
|
||||||
app:
|
app:
|
||||||
help: App to install
|
help: App to install
|
||||||
|
@ -375,6 +381,9 @@ app:
|
||||||
remove:
|
remove:
|
||||||
action_help: Remove app
|
action_help: Remove app
|
||||||
api: DELETE /app
|
api: DELETE /app
|
||||||
|
configuration:
|
||||||
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
app:
|
app:
|
||||||
help: App(s) to delete
|
help: App(s) to delete
|
||||||
|
@ -383,6 +392,9 @@ app:
|
||||||
upgrade:
|
upgrade:
|
||||||
action_help: Upgrade app
|
action_help: Upgrade app
|
||||||
api: PUT /app
|
api: PUT /app
|
||||||
|
configuration:
|
||||||
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
app:
|
app:
|
||||||
help: App(s) to upgrade (default all)
|
help: App(s) to upgrade (default all)
|
||||||
|
@ -450,6 +462,7 @@ app:
|
||||||
api: GET /app/checkurl
|
api: GET /app/checkurl
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
url:
|
url:
|
||||||
help: Url to check
|
help: Url to check
|
||||||
|
@ -480,6 +493,7 @@ app:
|
||||||
api: PUT /ssowatconf
|
api: PUT /ssowatconf
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
|
|
||||||
### app_addaccess() TODO: Write help
|
### app_addaccess() TODO: Write help
|
||||||
addaccess:
|
addaccess:
|
||||||
|
@ -487,6 +501,7 @@ app:
|
||||||
api: PUT /app/access
|
api: PUT /app/access
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
apps:
|
apps:
|
||||||
nargs: "+"
|
nargs: "+"
|
||||||
|
@ -500,6 +515,7 @@ app:
|
||||||
api: DELETE /app/access
|
api: DELETE /app/access
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
apps:
|
apps:
|
||||||
nargs: "+"
|
nargs: "+"
|
||||||
|
@ -513,6 +529,7 @@ app:
|
||||||
api: POST /app/access
|
api: POST /app/access
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
arguments:
|
arguments:
|
||||||
apps:
|
apps:
|
||||||
nargs: "+"
|
nargs: "+"
|
||||||
|
|
|
@ -23,15 +23,13 @@
|
||||||
|
|
||||||
Manage hooks
|
Manage hooks
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
logging.warning('the module yunohost.hook has not been revisited and updated yet')
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import json
|
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/'
|
hook_folder = '/usr/share/yunohost/hooks/'
|
||||||
|
|
||||||
|
@ -56,8 +54,8 @@ def hook_add(app, file):
|
||||||
|
|
||||||
finalpath = hook_folder + action +'/'+ priority +'-'+ app
|
finalpath = hook_folder + action +'/'+ priority +'-'+ app
|
||||||
print app
|
print app
|
||||||
os.system('cp '+ file +' '+ finalpath)
|
os.system('cp %s %s' % (file, finalpath))
|
||||||
os.system('chown -hR admin: '+ hook_folder)
|
os.system('chown -hR admin: %s' % hook_folder)
|
||||||
|
|
||||||
return { 'hook': finalpath }
|
return { 'hook': finalpath }
|
||||||
|
|
||||||
|
@ -87,19 +85,18 @@ def hook_callback(action, args=None):
|
||||||
args -- Ordered list of arguments to pass to the script
|
args -- Ordered list of arguments to pass to the script
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with YunoHostLDAP() as yldap:
|
try: os.listdir(hook_folder + action)
|
||||||
try: os.listdir(hook_folder + action)
|
except OSError: pass
|
||||||
except OSError: pass
|
else:
|
||||||
else:
|
if args is None:
|
||||||
if args is None:
|
args = []
|
||||||
args = []
|
elif not isinstance(args, list):
|
||||||
elif not isinstance(args, list):
|
args = [args]
|
||||||
args = [args]
|
|
||||||
|
|
||||||
for hook in os.listdir(hook_folder + action):
|
for hook in os.listdir(hook_folder + action):
|
||||||
try:
|
try:
|
||||||
hook_exec(file=hook_folder + action +'/'+ hook, args=args)
|
hook_exec(file=hook_folder + action +'/'+ hook, args=args)
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
|
||||||
def hook_check(file):
|
def hook_check(file):
|
||||||
|
@ -114,7 +111,7 @@ def hook_check(file):
|
||||||
with open(file[:file.index('scripts/')] + 'manifest.json') as f:
|
with open(file[:file.index('scripts/')] + 'manifest.json') as f:
|
||||||
manifest = json.loads(str(f.read()))
|
manifest = json.loads(str(f.read()))
|
||||||
except:
|
except:
|
||||||
raise YunoHostError(22, _("Invalid app package"))
|
raise MoulinetteError(22, _("Invalid app package"))
|
||||||
|
|
||||||
action = file[file.index('scripts/') + 8:]
|
action = file[file.index('scripts/') + 8:]
|
||||||
if 'arguments' in manifest and action in manifest['arguments']:
|
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
|
args -- Arguments to pass to the script
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with YunoHostLDAP() as yldap:
|
if isinstance(args, list):
|
||||||
if isinstance(args, list):
|
arg_list = args
|
||||||
arg_list = args
|
else:
|
||||||
else:
|
required_args = hook_check(file)
|
||||||
required_args = hook_check(file)
|
if args is None:
|
||||||
if args is None:
|
args = {}
|
||||||
args = {}
|
|
||||||
|
|
||||||
arg_list = []
|
arg_list = []
|
||||||
for arg in required_args:
|
for arg in required_args:
|
||||||
if arg['name'] in args:
|
if arg['name'] in args:
|
||||||
if 'choices' in arg and args[arg['name']] not in arg['choices']:
|
if 'choices' in arg and args[arg['name']] not in arg['choices']:
|
||||||
raise YunoHostError(22, _("Invalid choice") + ': ' + args[arg['name']])
|
raise MoulinetteError(22, _("Invalid choice") + ': ' + args[arg['name']])
|
||||||
arg_list.append(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:
|
else:
|
||||||
if os.isatty(1) and 'ask' in arg:
|
raise MoulinetteError(22, _("Missing argument : %s") % arg['name'])
|
||||||
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'))
|
file_path = "./"
|
||||||
|
if "/" in file and file[0:2] != file_path:
|
||||||
if input_string == '' and 'default' in arg:
|
file_path = os.path.dirname(file)
|
||||||
input_string = arg['default']
|
file = file.replace(file_path +"/", "")
|
||||||
|
return os.system('su - admin -c "cd \\"%s\\" && bash \\"%s\\" %s"' % (file_path, file, ' '.join(arg_list)))
|
||||||
arg_list.append(input_string)
|
#TODO: Allow python script
|
||||||
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
|
|
||||||
|
|
|
@ -347,6 +347,7 @@ class MoulinetteLock(object):
|
||||||
|
|
||||||
self._lockfile = '/var/run/moulinette_%s.lock' % namespace
|
self._lockfile = '/var/run/moulinette_%s.lock' % namespace
|
||||||
self._locked = False
|
self._locked = False
|
||||||
|
self._bypass = False
|
||||||
|
|
||||||
def acquire(self):
|
def acquire(self):
|
||||||
"""Attempt to acquire the lock for the moulinette instance
|
"""Attempt to acquire the lock for the moulinette instance
|
||||||
|
@ -359,9 +360,16 @@ class MoulinetteLock(object):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
while True:
|
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):
|
if not os.path.isfile(self._lockfile):
|
||||||
# Create the lock file
|
# 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
|
break
|
||||||
|
|
||||||
if (time.time() - start_time) > self.timeout:
|
if (time.time() - start_time) > self.timeout:
|
||||||
|
@ -378,7 +386,8 @@ class MoulinetteLock(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if self._locked:
|
if self._locked:
|
||||||
os.unlink(self._lockfile)
|
if not self._bypass:
|
||||||
|
os.unlink(self._lockfile)
|
||||||
self._locked = False
|
self._locked = False
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
|
|
@ -263,6 +263,17 @@ class BaseActionsMapParser(object):
|
||||||
# TODO: Log error instead and tell valid values
|
# TODO: Log error instead and tell valid values
|
||||||
raise MoulinetteError(errno.EINVAL, "Invalid value '%r' for configuration 'argument_auth'" % arg_auth)
|
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
|
return conf
|
||||||
|
|
||||||
def _format_conf(self, name, value):
|
def _format_conf(self, name, value):
|
||||||
|
|
|
@ -393,6 +393,10 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
raise MoulinetteError(errno.EINVAL, "No parser found for route '%s'" % route)
|
raise MoulinetteError(errno.EINVAL, "No parser found for route '%s'" % route)
|
||||||
ret = argparse.Namespace()
|
ret = argparse.Namespace()
|
||||||
|
|
||||||
|
|
||||||
|
if not self.get_conf(tid, 'lock'):
|
||||||
|
os.environ['BYPASS_LOCK'] = 'yes'
|
||||||
|
|
||||||
# Perform authentication if needed
|
# Perform authentication if needed
|
||||||
if self.get_conf(tid, 'authenticate'):
|
if self.get_conf(tid, 'authenticate'):
|
||||||
auth_conf, klass = self.get_conf(tid, 'authenticator')
|
auth_conf, klass = self.get_conf(tid, 'authenticator')
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
import errno
|
import errno
|
||||||
import getpass
|
import getpass
|
||||||
import argparse
|
import argparse
|
||||||
|
@ -126,6 +127,9 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
def parse_args(self, args, **kwargs):
|
def parse_args(self, args, **kwargs):
|
||||||
ret = self._parser.parse_args(args)
|
ret = self._parser.parse_args(args)
|
||||||
|
|
||||||
|
if not self.get_conf(ret._tid, 'lock'):
|
||||||
|
os.environ['BYPASS_LOCK'] = 'yes'
|
||||||
|
|
||||||
# Perform authentication if needed
|
# Perform authentication if needed
|
||||||
if self.get_conf(ret._tid, 'authenticate'):
|
if self.get_conf(ret._tid, 'authenticate'):
|
||||||
auth_conf, klass = self.get_conf(ret._tid, 'authenticator')
|
auth_conf, klass = self.get_conf(ret._tid, 'authenticator')
|
||||||
|
|
Loading…
Add table
Reference in a new issue