# Copyright 2018, National Technology & Engineering Solutions of Sandia, LLC
# (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
# Government retains certain rights in this software.
import functools
import os
import uuid
import arrow
import flask
import flask_socketio
import werkzeug.routing
# Setup the web server.
application = flask.Flask(__name__)
application.secret_key = os.urandom(24)
socketio = flask_socketio.SocketIO(application, json=flask.json)
[docs]class AllowConverter(werkzeug.routing.BaseConverter):
def __init__(self, map, *items):
werkzeug.routing.BaseConverter.__init__(self, map)
self.items = items
[docs] def to_python(self, value):
if value not in self.items:
raise werkzeug.routing.ValidationError()
return value
application.url_map.converters["allow"] = AllowConverter
[docs]class ExcludeConverter(werkzeug.routing.BaseConverter):
def __init__(self, map, *items):
werkzeug.routing.BaseConverter.__init__(self, map)
self.items = items
[docs] def to_python(self, value):
if value in self.items:
raise werkzeug.routing.ValidationError()
return value
application.url_map.converters["exclude"] = ExcludeConverter
##################################################################################
# Authentication and permissions
sessions = {}
[docs]def require_auth(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
# Validate an existing session.
if "sid" in flask.session:
sid = flask.session["sid"]
if sid in sessions:
session = sessions[sid]
if session["remote-ip"] == flask.request.remote_addr:
elapsed = arrow.utcnow() - session["last-request"]
if elapsed.total_seconds() < application.config["session-timeout"]:
session["last-request"] = arrow.utcnow()
# Handle the request
return f(*args, **kwargs)
del sessions[sid]
del flask.session["sid"]
# No existing session, so authenticate the user.
if application.config["credentials"](flask.request.authorization):
# Create a new session.
flask.session["sid"] = uuid.uuid4()
sessions[flask.session["sid"]] = {
"last-request": arrow.utcnow(),
"remote-ip": flask.request.remote_addr,
}
# Handle the request
return f(*args, **kwargs)
# Prompt the user for their credentials.
return application.config["authentication"]()
return decorated
[docs]def require_permissions(permissions):
if not application.config["acl"](flask.request.authorization, permissions):
flask.abort(flask.make_response(flask.jsonify(message="You don't have the necessary permissions."), 403))