Cross-domain bugfix + Simplify and document

This commit is contained in:
Kload 2013-06-30 11:25:17 +00:00
parent 588ee4b13f
commit 5c6e68dcfb

View file

@ -5,16 +5,11 @@ import gettext
import ldap import ldap
import yaml import yaml
import json import json
from twisted.python import log from twisted.python.log import ILogObserver, FileLogObserver, startLogging
from twisted.python.logfile import DailyLogFile
from twisted.web.server import Site from twisted.web.server import Site
from twisted.web.resource import IResource from twisted.internet import reactor
from twisted.web.guard import HTTPAuthSessionWrapper, BasicCredentialFactory from twisted.application import internet,service
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 txrestapi.resource import APIResource
from yunohost import YunoHostError, YunoHostLDAP, str_to_func, colorize, pretty_print_dict, display_error, validate, win, parse_dict from yunohost import YunoHostError, YunoHostLDAP, str_to_func, colorize, pretty_print_dict, display_error, validate, win, parse_dict
@ -23,39 +18,33 @@ if not __debug__:
gettext.install('YunoHost') 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:
return defer.fail(UnauthorizedLogin())
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 = {} action_dict = {}
api = APIResource()
def http_exec(request): def http_exec(request):
global win 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 ''
# Simple HTTP auth
else:
authorized = request.getUser() == 'admin'
try: YunoHostLDAP(password=request.getPassword())
except YunoHostError: authorized = False
if not authorized:
request.setResponseCode(401, 'Unauthorized')
request.setHeader('Access-Control-Allow-Origin', '*')
return 'Unauthorized'
# Sanitize arguments
dict = action_dict[request.method+' '+request.path] dict = action_dict[request.method+' '+request.path]
if 'arguments' in dict: args = dict['arguments'] if 'arguments' in dict: args = dict['arguments']
else: args = {} else: args = {}
@ -76,6 +65,8 @@ def http_exec(request):
del args[arg] del args[arg]
try: try:
# Validate arguments
validated_args = {} validated_args = {}
for key, value in request.args.items(): for key, value in request.args.items():
if key in args: if key in args:
@ -88,6 +79,8 @@ def http_exec(request):
validated_args[key] = value validated_args[key] = value
func = str_to_func(dict['function']) func = str_to_func(dict['function'])
# Execute requested function
with YunoHostLDAP(password=request.getPassword()): with YunoHostLDAP(password=request.getPassword()):
result = func(**validated_args) result = func(**validated_args)
if result is None: if result is None:
@ -95,6 +88,8 @@ def http_exec(request):
if win: if win:
result['win'] = win result['win'] = win
win = [] win = []
# Build response
if request.method == 'POST': if request.method == 'POST':
request.setResponseCode(201, 'Created') request.setResponseCode(201, 'Created')
elif request.method == 'DELETE': elif request.method == 'DELETE':
@ -103,6 +98,8 @@ def http_exec(request):
request.setResponseCode(200, 'OK') request.setResponseCode(200, 'OK')
except YunoHostError, error: except YunoHostError, error:
# Set response code with function's raised code
server_errors = [1, 111, 169] server_errors = [1, 111, 169]
client_errors = [13, 17, 22, 87, 122, 125, 167, 168] client_errors = [13, 17, 22, 87, 122, 125, 167, 168]
if error.code in client_errors: if error.code in client_errors:
@ -111,15 +108,16 @@ def http_exec(request):
request.setResponseCode(500, 'Internal Server Error') request.setResponseCode(500, 'Internal Server Error')
result = { 'error' : error.message } result = { 'error' : error.message }
request.setHeader('Content-Type', 'application/json')
return json.dumps(result) return json.dumps(result)
def main(): def main():
global action_dict global action_dict
log.startLogging(sys.stdout) global api
api = APIResource()
startLogging(open('/var/log/yunohost.log', 'a+')) # Log actions to API
# Load & parse yaml file
with open('action_map.yml') as f: with open('action_map.yml') as f:
action_map = yaml.load(f) action_map = yaml.load(f)
@ -131,7 +129,9 @@ def main():
if 'api' not in action_params: if 'api' not in action_params:
action_params['api'] = 'GET /'+ category +'/'+ action action_params['api'] = 'GET /'+ category +'/'+ action
method, path = action_params['api'].split(' ') method, path = action_params['api'].split(' ')
# Register route
api.register(method, path, http_exec) api.register(method, path, http_exec)
api.register('OPTIONS', path, http_exec)
action_dict[action_params['api']] = { action_dict[action_params['api']] = {
'function': 'yunohost_'+ category +'.'+ category +'_'+ action, 'function': 'yunohost_'+ category +'.'+ category +'_'+ action,
'help' : action_params['help'] 'help' : action_params['help']
@ -139,17 +139,28 @@ def main():
if 'arguments' in action_params: if 'arguments' in action_params:
action_dict[action_params['api']]['arguments'] = action_params['arguments'] action_dict[action_params['api']]['arguments'] = action_params['arguments']
ldap_auth = LDAPHTTPAuth()
credentialFactory = BasicCredentialFactory("Restricted Area") # Register only postinstall action if YunoHost isn't completely set up
resource = HTTPAuthSessionWrapper(Portal(SimpleRealm(api), [ldap_auth]), [credentialFactory])
try: try:
with open('/etc/yunohost/installed') as f: pass with open('/etc/yunohost/installed') as f: pass
except IOError: except IOError:
resource = APIResource() api = APIResource()
resource.register('POST', '/postinstall', http_exec) api.register('POST', '/postinstall', http_exec)
reactor.listenTCP(6767, Site(resource, timeout=None)) api.register('OPTIONS', '/postinstall', http_exec)
reactor.run() action_dict['POST /postinstall'] = {
'function' : 'yunohost_tools.tools_postinstall',
'help' : 'Execute post-install',
'arguments' : action_map['tools']['postinstall']['arguments']
}
if __name__ == '__main__': if __name__ == '__main__':
main() main()
reactor.listenTCP(80, Site(api, timeout=None))
reactor.run()
else:
main()
application = service.Application("YunoHost API")
logfile = DailyLogFile("yunohost.log", "/var/log")
application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
internet.TCPServer(6767, Site(api, timeout=None)).setServiceParent(application)