moulinette/yunohost.tac

184 lines
6.5 KiB
Python
Raw Normal View History

2013-06-28 19:58:13 +02:00
# -*- mode: python -*-
import os
import sys
import gettext
import ldap
import yaml
import json
from twisted.python.log import ILogObserver, FileLogObserver, startLogging
from twisted.python.logfile import DailyLogFile
2013-06-28 19:58:13 +02:00
from twisted.web.server import Site
from twisted.internet import reactor
from twisted.application import internet,service
2013-06-28 19:58:13 +02:00
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')
action_dict = {}
api = APIResource()
2013-06-28 19:58:13 +02:00
2013-06-30 18:13:26 +02:00
def http_exec(request, **kwargs):
2013-06-28 19:58:13 +02:00
global win
request.setHeader('Access-Control-Allow-Origin', '*') # Allow cross-domain requests
request.setHeader('Content-Type', 'application/json') # Return JSON anyway
# Return OK to 'OPTIONS' xhr requests
if request.method == 'OPTIONS':
request.setResponseCode(200, 'OK')
request.setHeader('Access-Control-Allow-Headers', 'Authorization')
return ''
2013-07-02 22:02:26 +02:00
# Simple HTTP auth
else:
authorized = request.getUser() == 'admin'
2013-06-30 13:57:23 +02:00
if authorized:
try: YunoHostLDAP(password=request.getPassword())
except YunoHostError: authorized = False
if not authorized:
request.setResponseCode(401, 'Unauthorized')
request.setHeader('Access-Control-Allow-Origin', '*')
2013-06-30 13:57:23 +02:00
request.setHeader('www-authenticate', 'Basic realm="Restricted Area"')
return 'Unauthorized'
2013-07-02 22:02:26 +02:00
2013-06-30 18:13:26 +02:00
path = request.path
given_args = request.args
if kwargs:
for k, v in kwargs.iteritems():
dynamic_key = path.split('/')[-1]
path = path.replace(dynamic_key, '(?P<'+ k +'>[^/]+)')
given_args[k] = [v]
2013-07-02 22:02:26 +02:00
2013-06-30 18:13:26 +02:00
print given_args
2013-07-02 22:02:26 +02:00
# Sanitize arguments
2013-06-30 18:13:26 +02:00
dict = action_dict[request.method +' '+ path]
if 'arguments' in dict: possible_args = dict['arguments']
else: possible_args = {}
for arg, params in possible_args.items():
2013-06-28 19:58:13 +02:00
sanitized_key = arg.replace('-', '_')
if sanitized_key is not arg:
2013-06-30 18:13:26 +02:00
possible_args[sanitized_key] = possible_args[arg]
del possible_args[arg]
2013-06-28 19:58:13 +02:00
arg = sanitized_key
if arg[0] == '_':
if 'nargs' not in params:
2013-06-30 18:13:26 +02:00
possible_args[arg]['nargs'] = '*'
2013-06-28 19:58:13 +02:00
if 'full' in params:
2013-07-02 22:02:26 +02:00
new_key = params['full'][2:].replace('-', '_')
2013-06-28 19:58:13 +02:00
else:
2013-07-02 22:02:26 +02:00
new_key = arg[2:].replace('-', '_')
possible_args[new_key] = possible_args[arg]
2013-06-30 18:13:26 +02:00
del possible_args[arg]
2013-06-28 19:58:13 +02:00
try:
# Validate arguments
2013-06-28 19:58:13 +02:00
validated_args = {}
2013-06-30 18:13:26 +02:00
for key, value in given_args.items():
if key in possible_args:
2013-06-28 19:58:13 +02:00
# Validate args
2013-06-30 18:13:26 +02:00
if 'pattern' in possible_args[key]:
validate(possible_args[key]['pattern'], value)
if 'nargs' not in possible_args[key] or ('nargs' != '*' and 'nargs' != '+'):
2013-06-30 14:11:30 +02:00
value = value[0]
2013-06-30 18:13:26 +02:00
if 'choices' in possible_args[key] and value not in possible_args[key]['choices']:
2013-06-30 14:11:30 +02:00
raise YunoHostError(22, _('Invalid argument') + ' ' + value)
2013-06-30 18:13:26 +02:00
if 'action' in possible_args[key] and possible_args[key]['action'] == 'store_true':
2013-06-28 19:58:13 +02:00
yes = ['true', 'True', 'yes', 'Yes']
2013-07-02 22:02:26 +02:00
value = value in yes
2013-06-28 19:58:13 +02:00
validated_args[key] = value
func = str_to_func(dict['function'])
2013-06-30 18:13:26 +02:00
if func is None:
raise YunoHostError(168, _('Function not yet implemented : ') + dict['function'].split('.')[1])
# Execute requested function
2013-06-28 19:58:13 +02:00
with YunoHostLDAP(password=request.getPassword()):
result = func(**validated_args)
if result is None:
result = {}
if win:
result['win'] = win
win = []
# Build response
2013-06-29 01:05:28 +02:00
if request.method == 'POST':
request.setResponseCode(201, 'Created')
elif request.method == 'DELETE':
request.setResponseCode(204, 'No Content')
else:
request.setResponseCode(200, 'OK')
2013-07-02 22:02:26 +02:00
2013-06-28 19:58:13 +02:00
except YunoHostError, error:
# Set response code with function's raised code
2013-06-30 18:13:26 +02:00
server_errors = [1, 111, 168, 169]
client_errors = [13, 17, 22, 87, 122, 125, 167]
2013-06-29 01:05:28 +02:00
if error.code in client_errors:
request.setResponseCode(400, 'Bad Request')
else:
request.setResponseCode(500, 'Internal Server Error')
2013-07-02 22:02:26 +02:00
2013-06-30 18:13:26 +02:00
result = { 'error' : error.message }
2013-06-29 01:05:28 +02:00
2013-06-28 19:58:13 +02:00
return json.dumps(result)
def main():
global action_dict
global api
# Load & parse yaml file
2013-06-28 19:58:13 +02:00
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():
2013-07-02 22:02:26 +02:00
if 'action_help' not in action_params:
action_params['action_help'] = ''
2013-06-28 19:58:13 +02:00
if 'api' not in action_params:
action_params['api'] = 'GET /'+ category +'/'+ action
method, path = action_params['api'].split(' ')
# Register route
2013-06-28 19:58:13 +02:00
api.register(method, path, http_exec)
api.register('OPTIONS', path, http_exec)
2013-06-28 19:58:13 +02:00
action_dict[action_params['api']] = {
'function': 'yunohost_'+ category +'.'+ category +'_'+ action,
2013-07-02 22:02:26 +02:00
'help' : action_params['action_help']
2013-06-28 19:58:13 +02:00
}
2013-07-02 22:02:26 +02:00
if 'arguments' in action_params:
2013-06-28 19:58:13 +02:00
action_dict[action_params['api']]['arguments'] = action_params['arguments']
2013-07-02 22:02:26 +02:00
# Register only postinstall action if YunoHost isn't completely set up
2013-06-28 19:58:13 +02:00
try:
with open('/etc/yunohost/installed') as f: pass
except IOError:
api = APIResource()
api.register('POST', '/postinstall', http_exec)
api.register('OPTIONS', '/postinstall', http_exec)
action_dict['POST /postinstall'] = {
'function' : 'yunohost_tools.tools_postinstall',
'help' : 'Execute post-install',
'arguments' : action_map['tools']['postinstall']['arguments']
}
2013-06-28 19:58:13 +02:00
if __name__ == '__main__':
2013-06-30 13:30:23 +02:00
startLogging(open('/var/log/yunohost.log', 'a+')) # Log actions to API
2013-06-28 19:58:13 +02:00
main()
2013-06-30 13:30:23 +02:00
reactor.listenTCP(6767, Site(api, timeout=None))
reactor.run()
else:
application = service.Application("YunoHost API")
logfile = DailyLogFile("yunohost.log", "/var/log")
application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
2013-06-30 13:30:23 +02:00
main()
internet.TCPServer(6767, Site(api, timeout=None)).setServiceParent(application)