mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
REST API for moulinette :d
This commit is contained in:
parent
aecf43f259
commit
ad7fa4e40b
3 changed files with 257 additions and 112 deletions
111
yunohost
111
yunohost
|
@ -38,121 +38,12 @@ if not __debug__:
|
|||
gettext.install('YunoHost')
|
||||
|
||||
try:
|
||||
from yunohost import YunoHostError, YunoHostLDAP, str_to_func, colorize, pretty_print_dict, display_error, validate, win
|
||||
from yunohost import YunoHostError, YunoHostLDAP, str_to_func, colorize, pretty_print_dict, display_error, validate, win, parse_dict
|
||||
except ImportError:
|
||||
sys.stderr.write('Error: Yunohost CLI Require YunoHost lib\n')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parse_dict(action_map):
|
||||
"""
|
||||
Turn action dictionnary to parser, subparsers and arguments
|
||||
|
||||
Keyword arguments:
|
||||
action_map -- Multi-level dictionnary of categories/actions/arguments list
|
||||
|
||||
Returns:
|
||||
Namespace of args
|
||||
|
||||
"""
|
||||
# Intialize parsers
|
||||
parsers = subparsers_category = subparsers_action = {}
|
||||
parsers['general'] = argparse.ArgumentParser()
|
||||
subparsers = parsers['general'].add_subparsers()
|
||||
new_args = []
|
||||
patterns = {}
|
||||
|
||||
# Add general arguments
|
||||
for arg_name, arg_params in action_map['general_arguments'].items():
|
||||
if 'full' in arg_params:
|
||||
arg_names = [arg_name, arg_params['full']]
|
||||
arg_fullname = arg_params['full']
|
||||
del arg_params['full']
|
||||
else: arg_names = [arg_name]
|
||||
parsers['general'].add_argument(*arg_names, **arg_params)
|
||||
|
||||
del action_map['general_arguments']
|
||||
|
||||
# Split categories into subparsers
|
||||
for category, category_params in action_map.items():
|
||||
if 'category_help' not in category_params: category_params['category_help'] = ''
|
||||
subparsers_category[category] = subparsers.add_parser(category, help=category_params['category_help'])
|
||||
subparsers_action[category] = subparsers_category[category].add_subparsers()
|
||||
# Split actions
|
||||
if 'actions' in category_params:
|
||||
for action, action_params in category_params['actions'].items():
|
||||
if 'action_help' not in action_params: action_params['action_help'] = ''
|
||||
parsers[category + '_' + action] = subparsers_action[category].add_parser(action, help=action_params['action_help'])
|
||||
# Set the action s related function
|
||||
parsers[category + '_' + action].set_defaults(
|
||||
func=str_to_func('yunohost_' + category + '.'
|
||||
+ category + '_' + action))
|
||||
# Add arguments
|
||||
if 'arguments' in action_params:
|
||||
for arg_name, arg_params in action_params['arguments'].items():
|
||||
arg_fullname = False
|
||||
|
||||
if 'password' in arg_params:
|
||||
if arg_params['password']: is_password = True
|
||||
del arg_params['password']
|
||||
else: is_password = False
|
||||
|
||||
if 'full' in arg_params:
|
||||
arg_names = [arg_name, arg_params['full']]
|
||||
arg_fullname = arg_params['full']
|
||||
del arg_params['full']
|
||||
else: arg_names = [arg_name]
|
||||
|
||||
if 'ask' in arg_params:
|
||||
require_input = True
|
||||
if '-h' in sys.argv or '--help' in sys.argv:
|
||||
require_input = False
|
||||
if (category != sys.argv[1]) or (action != sys.argv[2]):
|
||||
require_input = False
|
||||
for name in arg_names:
|
||||
if name in sys.argv[2:]: require_input = False
|
||||
|
||||
if require_input:
|
||||
if is_password:
|
||||
if os.isatty(1):
|
||||
pwd1 = getpass.getpass(colorize(arg_params['ask'] + ': ', 'cyan'))
|
||||
pwd2 = getpass.getpass(colorize('Retype ' + arg_params['ask'][0].lower() + arg_params['ask'][1:] + ': ', 'cyan'))
|
||||
if pwd1 != pwd2:
|
||||
raise YunoHostError(22, _("Passwords don't match"))
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise YunoHostError(22, _("Missing arguments") + ': ' + arg_name)
|
||||
if arg_name[0] == '-': arg_extend = [arg_name, pwd1]
|
||||
else: arg_extend = [pwd1]
|
||||
else:
|
||||
if os.isatty(1):
|
||||
arg_value = raw_input(colorize(arg_params['ask'] + ': ', 'cyan'))
|
||||
else:
|
||||
raise YunoHostError(22, _("Missing arguments") + ': ' + arg_name)
|
||||
if arg_name[0] == '-': arg_extend = [arg_name, arg_value]
|
||||
else: arg_extend = [arg_value]
|
||||
new_args.extend(arg_extend)
|
||||
del arg_params['ask']
|
||||
|
||||
if 'pattern' in arg_params:
|
||||
if (category == sys.argv[1]) and (action == sys.argv[2]):
|
||||
if 'dest' in arg_params: name = arg_params['dest']
|
||||
elif arg_fullname: name = arg_fullname[2:]
|
||||
else: name = arg_name
|
||||
name = name.replace('-', '_')
|
||||
patterns[name] = arg_params['pattern']
|
||||
del arg_params['pattern']
|
||||
|
||||
parsers[category + '_' + action].add_argument(*arg_names, **arg_params)
|
||||
|
||||
args = parsers['general'].parse_args(sys.argv.extend(new_args))
|
||||
args_dict = vars(args)
|
||||
for key, value in patterns.items():
|
||||
validate(value, args_dict[key])
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main instructions
|
||||
|
|
117
yunohost.py
117
yunohost.py
|
@ -9,11 +9,15 @@ except ImportError:
|
|||
sys.stderr.write('apt-get install python-ldap\n')
|
||||
sys.exit(1)
|
||||
import ldap.modlist as modlist
|
||||
import yaml
|
||||
import json
|
||||
import re
|
||||
import getpass
|
||||
import random
|
||||
import string
|
||||
import argparse
|
||||
import gettext
|
||||
import getpass
|
||||
if not __debug__:
|
||||
import traceback
|
||||
|
||||
|
@ -93,8 +97,8 @@ def win_msg(astr):
|
|||
global win
|
||||
if os.isatty(1):
|
||||
print('\n' + colorize(_("Success: "), 'green') + astr + '\n')
|
||||
else:
|
||||
win.append(astr)
|
||||
|
||||
win.append(astr)
|
||||
|
||||
|
||||
|
||||
|
@ -444,3 +448,112 @@ class YunoHostLDAP(Singleton):
|
|||
else:
|
||||
raise YunoHostError(17, _('Attribute already exists') + ' "' + attr + '=' + value + '"')
|
||||
return True
|
||||
|
||||
|
||||
def parse_dict(action_map):
|
||||
"""
|
||||
Turn action dictionnary to parser, subparsers and arguments
|
||||
|
||||
Keyword arguments:
|
||||
action_map -- Multi-level dictionnary of categories/actions/arguments list
|
||||
|
||||
Returns:
|
||||
Namespace of args
|
||||
|
||||
"""
|
||||
# Intialize parsers
|
||||
parsers = subparsers_category = subparsers_action = {}
|
||||
parsers['general'] = argparse.ArgumentParser()
|
||||
subparsers = parsers['general'].add_subparsers()
|
||||
new_args = []
|
||||
patterns = {}
|
||||
|
||||
# Add general arguments
|
||||
for arg_name, arg_params in action_map['general_arguments'].items():
|
||||
if 'full' in arg_params:
|
||||
arg_names = [arg_name, arg_params['full']]
|
||||
arg_fullname = arg_params['full']
|
||||
del arg_params['full']
|
||||
else: arg_names = [arg_name]
|
||||
parsers['general'].add_argument(*arg_names, **arg_params)
|
||||
|
||||
del action_map['general_arguments']
|
||||
|
||||
# Split categories into subparsers
|
||||
for category, category_params in action_map.items():
|
||||
if 'category_help' not in category_params: category_params['category_help'] = ''
|
||||
subparsers_category[category] = subparsers.add_parser(category, help=category_params['category_help'])
|
||||
subparsers_action[category] = subparsers_category[category].add_subparsers()
|
||||
# Split actions
|
||||
if 'actions' in category_params:
|
||||
for action, action_params in category_params['actions'].items():
|
||||
if 'action_help' not in action_params: action_params['action_help'] = ''
|
||||
parsers[category + '_' + action] = subparsers_action[category].add_parser(action, help=action_params['action_help'])
|
||||
# Set the action s related function
|
||||
parsers[category + '_' + action].set_defaults(
|
||||
func=str_to_func('yunohost_' + category + '.'
|
||||
+ category + '_' + action))
|
||||
# Add arguments
|
||||
if 'arguments' in action_params:
|
||||
for arg_name, arg_params in action_params['arguments'].items():
|
||||
arg_fullname = False
|
||||
|
||||
if 'password' in arg_params:
|
||||
if arg_params['password']: is_password = True
|
||||
del arg_params['password']
|
||||
else: is_password = False
|
||||
|
||||
if 'full' in arg_params:
|
||||
arg_names = [arg_name, arg_params['full']]
|
||||
arg_fullname = arg_params['full']
|
||||
del arg_params['full']
|
||||
else: arg_names = [arg_name]
|
||||
|
||||
if 'ask' in arg_params:
|
||||
require_input = True
|
||||
if '-h' in sys.argv or '--help' in sys.argv:
|
||||
require_input = False
|
||||
if (category != sys.argv[1]) or (action != sys.argv[2]):
|
||||
require_input = False
|
||||
for name in arg_names:
|
||||
if name in sys.argv[2:]: require_input = False
|
||||
|
||||
if require_input:
|
||||
if is_password:
|
||||
if os.isatty(1):
|
||||
pwd1 = getpass.getpass(colorize(arg_params['ask'] + ': ', 'cyan'))
|
||||
pwd2 = getpass.getpass(colorize('Retype ' + arg_params['ask'][0].lower() + arg_params['ask'][1:] + ': ', 'cyan'))
|
||||
if pwd1 != pwd2:
|
||||
raise YunoHostError(22, _("Passwords don't match"))
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise YunoHostError(22, _("Missing arguments") + ': ' + arg_name)
|
||||
if arg_name[0] == '-': arg_extend = [arg_name, pwd1]
|
||||
else: arg_extend = [pwd1]
|
||||
else:
|
||||
if os.isatty(1):
|
||||
arg_value = raw_input(colorize(arg_params['ask'] + ': ', 'cyan'))
|
||||
else:
|
||||
raise YunoHostError(22, _("Missing arguments") + ': ' + arg_name)
|
||||
if arg_name[0] == '-': arg_extend = [arg_name, arg_value]
|
||||
else: arg_extend = [arg_value]
|
||||
new_args.extend(arg_extend)
|
||||
del arg_params['ask']
|
||||
|
||||
if 'pattern' in arg_params:
|
||||
if (category == sys.argv[1]) and (action == sys.argv[2]):
|
||||
if 'dest' in arg_params: name = arg_params['dest']
|
||||
elif arg_fullname: name = arg_fullname[2:]
|
||||
else: name = arg_name
|
||||
name = name.replace('-', '_')
|
||||
patterns[name] = arg_params['pattern']
|
||||
del arg_params['pattern']
|
||||
|
||||
parsers[category + '_' + action].add_argument(*arg_names, **arg_params)
|
||||
|
||||
args = parsers['general'].parse_args(sys.argv.extend(new_args))
|
||||
args_dict = vars(args)
|
||||
for key, value in patterns.items():
|
||||
validate(value, args_dict[key])
|
||||
|
||||
return args
|
||||
|
|
141
yunohost.tac
Executable file
141
yunohost.tac
Executable file
|
@ -0,0 +1,141 @@
|
|||
# -*- mode: python -*-
|
||||
import os
|
||||
import sys
|
||||
import gettext
|
||||
import ldap
|
||||
import yaml
|
||||
import json
|
||||
from twisted.python import log
|
||||
from twisted.web.server import Site
|
||||
from twisted.web.resource import IResource
|
||||
from twisted.web.guard import HTTPAuthSessionWrapper, BasicCredentialFactory
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.cred.portal import IRealm, Portal
|
||||
from twisted.cred.checkers import ICredentialsChecker
|
||||
from twisted.cred.credentials import IUsernamePassword
|
||||
from twisted.cred.error import UnauthorizedLogin
|
||||
from zope.interface import implements
|
||||
from txrestapi.resource import APIResource
|
||||
from yunohost import YunoHostError, YunoHostLDAP, str_to_func, colorize, pretty_print_dict, display_error, validate, win, parse_dict
|
||||
|
||||
if not __debug__:
|
||||
import traceback
|
||||
|
||||
gettext.install('YunoHost')
|
||||
|
||||
class LDAPHTTPAuth():
|
||||
implements (ICredentialsChecker)
|
||||
|
||||
credentialInterfaces = IUsernamePassword,
|
||||
|
||||
def requestAvatarId(self, credentials):
|
||||
try:
|
||||
if credentials.username != "admin":
|
||||
raise YunoHostError(22, _("Invalid username") + ': ' + credentials.username)
|
||||
YunoHostLDAP(password=credentials.password)
|
||||
return credentials.username
|
||||
|
||||
except Exception as e:
|
||||
raise defer.fail(UnauthorizedLogin("Unable to verify password for "+ credentials.username)) #TODO: Reask for password
|
||||
|
||||
|
||||
class SimpleRealm(object):
|
||||
implements(IRealm)
|
||||
|
||||
_api = None
|
||||
|
||||
def __init__(self, api):
|
||||
self._api = api
|
||||
|
||||
def requestAvatar(self, avatarId, mind, *interfaces):
|
||||
if IResource in interfaces:
|
||||
return IResource, self._api, lambda: None
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
action_dict = {}
|
||||
|
||||
def http_exec(request):
|
||||
global win
|
||||
dict = action_dict[request.method+' '+request.path]
|
||||
args = dict['arguments']
|
||||
for arg, params in args.items():
|
||||
sanitized_key = arg.replace('-', '_')
|
||||
if sanitized_key is not arg:
|
||||
args[sanitized_key] = args[arg]
|
||||
del args[arg]
|
||||
arg = sanitized_key
|
||||
if arg[0] == '_':
|
||||
if 'nargs' not in params:
|
||||
args[arg]['nargs'] = '*'
|
||||
if 'full' in params:
|
||||
new_key = params['full'][2:]
|
||||
else:
|
||||
new_key = arg[2:]
|
||||
args[new_key] = args[arg]
|
||||
del args[arg]
|
||||
|
||||
try:
|
||||
validated_args = {}
|
||||
for key, value in request.args.items():
|
||||
if key in args:
|
||||
# Validate args
|
||||
if 'pattern' in args[key]: validate(args[key]['pattern'], value)
|
||||
if 'nargs' not in args[key] or ('nargs' != '*' and 'nargs' != '+'): value = value[0]
|
||||
if 'action' in args[key] and args[key]['action'] == 'store_true':
|
||||
yes = ['true', 'True', 'yes', 'Yes']
|
||||
value = value in yes
|
||||
validated_args[key] = value
|
||||
|
||||
func = str_to_func(dict['function'])
|
||||
with YunoHostLDAP(password=request.getPassword()):
|
||||
result = func(**validated_args)
|
||||
if result is None:
|
||||
result = {}
|
||||
if win:
|
||||
result['win'] = win
|
||||
win = []
|
||||
except YunoHostError, error:
|
||||
result = { "error" : str(error.code) +' : '+ error.message }
|
||||
|
||||
return json.dumps(result)
|
||||
|
||||
|
||||
def main():
|
||||
global action_dict
|
||||
log.startLogging(sys.stdout)
|
||||
api = APIResource()
|
||||
|
||||
with open('action_map.yml') as f:
|
||||
action_map = yaml.load(f)
|
||||
|
||||
del action_map['general_arguments']
|
||||
for category, category_params in action_map.items():
|
||||
for action, action_params in category_params['actions'].items():
|
||||
if 'help' not in action_params:
|
||||
action_params['help'] = ''
|
||||
if 'api' not in action_params:
|
||||
action_params['api'] = 'GET /'+ category +'/'+ action
|
||||
method, path = action_params['api'].split(' ')
|
||||
api.register(method, path, http_exec)
|
||||
action_dict[action_params['api']] = {
|
||||
'function': 'yunohost_'+ category +'.'+ category +'_'+ action,
|
||||
'help' : action_params['help']
|
||||
}
|
||||
if 'arguments' in action_params:
|
||||
action_dict[action_params['api']]['arguments'] = action_params['arguments']
|
||||
|
||||
ldap_auth = LDAPHTTPAuth()
|
||||
credentialFactory = BasicCredentialFactory("Restricted Area")
|
||||
resource = HTTPAuthSessionWrapper(Portal(SimpleRealm(api), [ldap_auth]), [credentialFactory])
|
||||
try:
|
||||
with open('/etc/yunohost/installed') as f: pass
|
||||
except IOError:
|
||||
resource = APIResource()
|
||||
resource.register('POST', '/postinstall', http_exec)
|
||||
reactor.listenTCP(6767, Site(resource, timeout=None))
|
||||
reactor.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in a new issue