From d83312234b402cac561bf9b383a9cdc9595be08a Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Tue, 17 May 2016 14:16:14 -0600 Subject: [PATCH] Add session creation and handling This commit adds setting the content of the current user from the session cookie the user sends back and forth. That means we can actually pull out the session data for use when handling requests. We can also create new sessions and set up the session cookie --- vanth/api/session.py | 36 ++++++++++++++++++++++++++ vanth/auth.py | 57 ++++++++++++++++++++++++++++++++++++++++++ vanth/errors.py | 5 ++++ vanth/platform/user.py | 25 ++++++++++++++---- vanth/server.py | 3 +++ 5 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 vanth/api/session.py create mode 100644 vanth/auth.py create mode 100644 vanth/errors.py diff --git a/vanth/api/session.py b/vanth/api/session.py new file mode 100644 index 0000000..c97ca1a --- /dev/null +++ b/vanth/api/session.py @@ -0,0 +1,36 @@ +import json + +import flask +import sepiida.endpoints +import sepiida.fields + +import vanth.auth +import vanth.errors +import vanth.platform.user +import vanth.user + + +class Session(sepiida.endpoints.APIEndpoint): + ENDPOINT = '/session/' + SIGNATURE = sepiida.fields.JSONObject(s={ + 'username' : sepiida.fields.String(), + 'password' : sepiida.fields.String(methods=['POST']), + }) + @staticmethod + def post(payload): + user = vanth.platform.user.by_credentials(payload['username'], payload['password']) + if not user: + raise vanth.errors.InvalidCredentials() + vanth.auth.set_session(user) + + @staticmethod + def get(uuid): # pylint: disable=unused-argument + user = vanth.auth.current_user() + del user['password'] + if not user: + raise vanth.errors.ResourceDoesNotExist("You are not currently authenticated and therefore do not have a session") + return user + + def list(self): + payload = self.get(None) + return flask.make_response(json.dumps(payload), 200, {'Content-Type': 'application/json'}) diff --git a/vanth/auth.py b/vanth/auth.py new file mode 100644 index 0000000..c43ca3f --- /dev/null +++ b/vanth/auth.py @@ -0,0 +1,57 @@ +import uuid + +import flask +import sepiida.routing + +import vanth.platform.user + +PUBLIC_ENDPOINTS = [ + 'session.post', + 'about.get', +] + +def register_auth_handlers(app): + app.before_request(require_user) + +def endpoint(): + if flask.request.endpoint and flask.request.method: + return "{}.{}".format(flask.request.endpoint.lower(), flask.request.method.lower()) + +def require_user(): + user = None + if flask.request.method == 'OPTIONS' and 'Access-Control-Request-Method' in flask.request.headers: + return + + if not endpoint(): + return flask.make_response('Resource not found', 404) + + if endpoint() in PUBLIC_ENDPOINTS: + return + + if 'user_uri' not in flask.session: + raise vanth.errors.AuthenticationException( + status_code = 403, + error_code = 'unauthorized', + title = 'You must provide a valid session cookie', + ) + + _, params = sepiida.routing.extract_parameters(flask.current_app, 'GET', flask.session['user_uri']) + user = vanth.platform.user.by_filter({'uuid': [str(params['uuid'])]}) + if not user: + raise vanth.errors.AuthenticationException( + status_code = 403, + error_code = 'invalid-user', + title = 'The user tied to your session does not exist. Figure that out', + ) + + flask.g.current_user = user[0] + +def current_user(): + return getattr(flask.g, 'current_user', None) + +def is_authenticated(): + return current_user() is not None + +def set_session(user): + flask.session['user_uri'] = user['uri'] + flask.session['uuid'] = str(uuid.uuid4()) diff --git a/vanth/errors.py b/vanth/errors.py new file mode 100644 index 0000000..e4b2ed2 --- /dev/null +++ b/vanth/errors.py @@ -0,0 +1,5 @@ +import sepiida.errors + +AuthenticationException = sepiida.errors.api_error(status_code=403, error_code='authentication-exception') +InvalidCredentials = sepiida.errors.api_error(status_code=401, error_code='invalid-credentials') +ResourceDoesNotExist = sepiida.errors.api_error(status_code=404, error_code='resource-does-not-exist') diff --git a/vanth/platform/user.py b/vanth/platform/user.py index 3ea3e77..d21f0f4 100644 --- a/vanth/platform/user.py +++ b/vanth/platform/user.py @@ -8,17 +8,32 @@ import sepiida.routing import vanth.tables +def _to_dict(result): + return { + 'username' : result[vanth.tables.User.c.username], + 'password' : result[vanth.tables.User.c.password], + 'name' : result[vanth.tables.User.c.name], + 'uri' : sepiida.routing.uri('user', result[vanth.tables.User.c.uuid]), + } + def by_filter(filters): engine = chryso.connection.get() query = vanth.tables.User.select() query = chryso.queryadapter.map_and_filter(vanth.tables.User, filters, query) results = engine.execute(query).fetchall() - return [{ - 'username' : result[vanth.tables.User.c.username], - 'password' : result[vanth.tables.User.c.password], - 'name' : result[vanth.tables.User.c.name], - } for result in results] + return [_to_dict(result) for result in results] + +def by_credentials(username, password): + engine = chryso.connection.get() + + query = vanth.tables.User.select().where(vanth.tables.User.c.username == username) + result = engine.execute(query).first() + + if not (result and passlib.apps.custom_app_context.verify(password, result[vanth.tables.User.c.password])): + return None + + return _to_dict(result) def create(name, username, password): engine = chryso.connection.get() diff --git a/vanth/server.py b/vanth/server.py index 422d842..7fdf962 100644 --- a/vanth/server.py +++ b/vanth/server.py @@ -9,6 +9,7 @@ import sepiida.endpoints import vanth.api.about import vanth.api.session import vanth.api.user +import vanth.auth import vanth.user EXPOSE_HEADERS = [ @@ -56,6 +57,7 @@ def create_app(config): supports_credentials=True, expose_headers=EXPOSE_HEADERS, ) + vanth.auth.register_auth_handlers(app) app.route('/', methods=['GET'])(index) app.route('/login/', methods=['GET', 'POST', 'DELETE'])(login) @@ -63,5 +65,6 @@ def create_app(config): sepiida.endpoints.add_resource(app, vanth.api.about.About, endpoint='about') sepiida.endpoints.add_resource(app, vanth.api.user.User, endpoint='user') + sepiida.endpoints.add_resource(app, vanth.api.session.Session, endpoint='session') return app