mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Merge pull request #341 from YunoHost/portal-api
Tweaks and fixes for new portal API / ssowat refactoring
This commit is contained in:
commit
75f522be56
2 changed files with 39 additions and 12 deletions
|
@ -53,7 +53,7 @@ class Moulinette:
|
||||||
|
|
||||||
|
|
||||||
# Easy access to interfaces
|
# Easy access to interfaces
|
||||||
def api(host="localhost", port=80, routes={}, actionsmap=None, locales_dir=None):
|
def api(host="localhost", port=80, routes={}, actionsmap=None, locales_dir=None, allowed_cors_origins=[]):
|
||||||
"""Web server (API) interface
|
"""Web server (API) interface
|
||||||
|
|
||||||
Run a HTTP server with the moulinette for an API usage.
|
Run a HTTP server with the moulinette for an API usage.
|
||||||
|
@ -73,6 +73,7 @@ def api(host="localhost", port=80, routes={}, actionsmap=None, locales_dir=None)
|
||||||
Api(
|
Api(
|
||||||
routes=routes,
|
routes=routes,
|
||||||
actionsmap=actionsmap,
|
actionsmap=actionsmap,
|
||||||
|
allowed_cors_origins=allowed_cors_origins,
|
||||||
).run(host, port)
|
).run(host, port)
|
||||||
except MoulinetteError as e:
|
except MoulinetteError as e:
|
||||||
import logging
|
import logging
|
||||||
|
|
|
@ -312,6 +312,9 @@ class _ActionsMapPlugin:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
if request.get_header("Content-Type") == "application/json":
|
||||||
|
return callback((request.method, context.rule), request.json)
|
||||||
|
|
||||||
params = kwargs
|
params = kwargs
|
||||||
# Format boolean params
|
# Format boolean params
|
||||||
for a in args:
|
for a in args:
|
||||||
|
@ -353,11 +356,18 @@ class _ActionsMapPlugin:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if request.get_header("Content-Type") == "application/json":
|
||||||
|
if "credentials" not in request.json:
|
||||||
|
raise HTTPResponse("Missing credentials parameter", 400)
|
||||||
|
credentials = request.json["credentials"]
|
||||||
|
profile = request.json.get("profile", self.actionsmap.default_authentication)
|
||||||
|
else:
|
||||||
if "credentials" not in request.params:
|
if "credentials" not in request.params:
|
||||||
raise HTTPResponse("Missing credentials parameter", 400)
|
raise HTTPResponse("Missing credentials parameter", 400)
|
||||||
credentials = request.params["credentials"]
|
credentials = request.params["credentials"]
|
||||||
|
|
||||||
profile = request.params.get("profile", self.actionsmap.default_authentication)
|
profile = request.params.get("profile", self.actionsmap.default_authentication)
|
||||||
|
|
||||||
authenticator = self.actionsmap.get_authenticator(profile)
|
authenticator = self.actionsmap.get_authenticator(profile)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -377,6 +387,7 @@ class _ActionsMapPlugin:
|
||||||
try:
|
try:
|
||||||
session_infos = authenticator.get_session_cookie()
|
session_infos = authenticator.get_session_cookie()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
authenticator.delete_session_cookie()
|
||||||
msg = m18n.g("authentication_required")
|
msg = m18n.g("authentication_required")
|
||||||
raise HTTPResponse(msg, 401)
|
raise HTTPResponse(msg, 401)
|
||||||
|
|
||||||
|
@ -707,9 +718,11 @@ class Interface:
|
||||||
|
|
||||||
type = "api"
|
type = "api"
|
||||||
|
|
||||||
def __init__(self, routes={}, actionsmap=None):
|
def __init__(self, routes={}, actionsmap=None, allowed_cors_origins=[]):
|
||||||
actionsmap = ActionsMap(actionsmap, ActionsMapParser())
|
actionsmap = ActionsMap(actionsmap, ActionsMapParser())
|
||||||
|
|
||||||
|
self.allowed_cors_origins = allowed_cors_origins
|
||||||
|
|
||||||
# Attempt to retrieve log queues from an APIQueueHandler
|
# Attempt to retrieve log queues from an APIQueueHandler
|
||||||
handler = log.getHandlersByClass(APIQueueHandler, limit=1)
|
handler = log.getHandlersByClass(APIQueueHandler, limit=1)
|
||||||
if handler:
|
if handler:
|
||||||
|
@ -719,11 +732,18 @@ class Interface:
|
||||||
# TODO: Return OK to 'OPTIONS' xhr requests (l173)
|
# TODO: Return OK to 'OPTIONS' xhr requests (l173)
|
||||||
app = Bottle(autojson=True)
|
app = Bottle(autojson=True)
|
||||||
|
|
||||||
# Wrapper which sets proper header
|
def cors(callback):
|
||||||
def apiheader(callback):
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
response.set_header("Access-Control-Allow-Origin", "*")
|
r = callback(*args, **kwargs)
|
||||||
return callback(*args, **kwargs)
|
origin = request.headers.environ.get("HTTP_ORIGIN", "")
|
||||||
|
if origin and origin in self.allowed_cors_origins:
|
||||||
|
resp = r if isinstance(r, HTTPResponse) else response
|
||||||
|
resp.headers['Access-Control-Allow-Origin'] = origin
|
||||||
|
resp.headers['Access-Control-Allow-Methods'] = 'GET, HEAD, POST, PUT, OPTIONS, DELETE'
|
||||||
|
resp.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
|
||||||
|
resp.headers['Access-Control-Allow-Credentials'] = 'true'
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@ -732,7 +752,7 @@ class Interface:
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
locale = request.params.pop("locale")
|
locale = request.params.pop("locale")
|
||||||
except KeyError:
|
except (KeyError, ValueError):
|
||||||
locale = m18n.default_locale
|
locale = m18n.default_locale
|
||||||
m18n.set_locale(locale)
|
m18n.set_locale(locale)
|
||||||
return callback(*args, **kwargs)
|
return callback(*args, **kwargs)
|
||||||
|
@ -741,7 +761,7 @@ class Interface:
|
||||||
|
|
||||||
# Install plugins
|
# Install plugins
|
||||||
app.install(filter_csrf)
|
app.install(filter_csrf)
|
||||||
app.install(apiheader)
|
app.install(cors)
|
||||||
app.install(api18n)
|
app.install(api18n)
|
||||||
actionsmapplugin = _ActionsMapPlugin(actionsmap, log_queues)
|
actionsmapplugin = _ActionsMapPlugin(actionsmap, log_queues)
|
||||||
app.install(actionsmapplugin)
|
app.install(actionsmapplugin)
|
||||||
|
@ -750,6 +770,12 @@ class Interface:
|
||||||
self.display = actionsmapplugin.display
|
self.display = actionsmapplugin.display
|
||||||
self.prompt = actionsmapplugin.prompt
|
self.prompt = actionsmapplugin.prompt
|
||||||
|
|
||||||
|
def handle_options():
|
||||||
|
return HTTPResponse("", 204)
|
||||||
|
|
||||||
|
app.route('/<:re:.*>', method="OPTIONS",
|
||||||
|
callback=handle_options, skip=["actionsmap"])
|
||||||
|
|
||||||
# Append additional routes
|
# Append additional routes
|
||||||
# TODO: Add optional authentication to those routes?
|
# TODO: Add optional authentication to those routes?
|
||||||
for (m, p), c in routes.items():
|
for (m, p), c in routes.items():
|
||||||
|
|
Loading…
Reference in a new issue