From a4f4d314b623e03a85045aa6471b22eb4bc2da71 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Thu, 3 Apr 2014 15:39:04 -0300 Subject: [PATCH] token is not part of the URL anymore --- luncho/blueprints/users.py | 17 +++++++++-------- luncho/helpers.py | 34 ++++++++++++++++++++-------------- tests/base.py | 28 ++++++++++++++++++++++------ tests/users_tests.py | 23 ++++++++++++----------- 4 files changed, 63 insertions(+), 39 deletions(-) diff --git a/luncho/blueprints/users.py b/luncho/blueprints/users.py index 628485e..a15e7d8 100644 --- a/luncho/blueprints/users.py +++ b/luncho/blueprints/users.py @@ -12,7 +12,7 @@ from flask import jsonify from sqlalchemy.exc import IntegrityError from luncho.helpers import ForceJSON -from luncho.helpers import user_from_token +from luncho.helpers import auth from luncho.server import User from luncho.server import db @@ -53,15 +53,16 @@ def create_user(): return jsonify(status='OK') -@users.route('/', methods=['POST']) +@users.route('/', methods=['POST']) @ForceJSON() -def update_user(token): +@auth +def update_user(): """Update user information. Request can have the following fields: { "full_name": "Full name", "password": "hash" } Any other field will be ignored; only fields that need to be changed must be send.""" json = request.get_json(force=True) - user = user_from_token(token) + user = request.user if 'full_name' in json: LOG.debug('Fullname = {fullname}'.format(fullname=json['full_name'])) @@ -75,10 +76,10 @@ def update_user(token): return jsonify(status='OK') -@users.route('/', methods=['DELETE']) -def delete_user(token): +@users.route('/', methods=['DELETE']) +@auth +def delete_user(): """Delete a user. No confirmation is send.""" - user = user_from_token(token) - db.session.delete(user) + db.session.delete(request.user) db.session.commit() return jsonify(status='OK') diff --git a/luncho/helpers.py b/luncho/helpers.py index dcbbcec..41afec6 100644 --- a/luncho/helpers.py +++ b/luncho/helpers.py @@ -3,6 +3,8 @@ """Helper functions.""" +import logging + from functools import wraps from flask import request @@ -15,6 +17,8 @@ from luncho.exceptions import MissingFieldsException from luncho.exceptions import UserNotFoundException from luncho.exceptions import AuthorizationRequiredException +LOG = logging.getLogger('luncho.helpers') + class ForceJSON(object): """Decorator to check if the request is in JSON format.""" @@ -41,24 +45,26 @@ class ForceJSON(object): return check_json -class Auth(object): - """Validate the token in the Basic Auth header.""" +def auth(func): + @wraps(func) + def check_auth(*args, **kwargs): + if not request.authorization: + LOG.debug('There is no basic auth in the headers') + raise AuthorizationRequiredException - def __call__(self, func): - @wraps(func) - def check_auth(*args, **kwargs): - if not request.authorization: - raise AuthorizationRequiredException + token = request.authorization.username + user = User.query.filter_by(token=token).first() + if not user: + LOG.debug('No user with token {token}'.format(token=token)) + raise UserNotFoundException() - token = request.authorization.username - user = User.query.filter_by(token=token).first() - if not user: - raise UserNotFoundException() + if not user.valid_token(token): + raise InvalidTokenException() - if not user.valid_token(token): - raise InvalidTokenException() + request.user = user - return func(*args, **kwargs) + return func(*args, **kwargs) + return check_auth def user_from_token(token): diff --git a/tests/base.py b/tests/base.py index 0fa5e1a..fe7369e 100644 --- a/tests/base.py +++ b/tests/base.py @@ -3,10 +3,22 @@ import unittest import json +import base64 from luncho import server +def _token_header(token=None): + """Generate the headers required for using the token as an auth.""" + if not token: + return None + + message = '{token}:Ignored'.format(token=token) + headers = {'Authorization': 'Basic {code}'.format( + code=base64.b64encode(message))} + return headers + + class LunchoTests(unittest.TestCase): """Base testing for all Lunch-o tests.""" @@ -75,22 +87,26 @@ class LunchoTests(unittest.TestCase): # Easy way to convert the data to JSON and do requests # ------------------------------------------------------------ - def post(self, url, data): + def post(self, url, data, token=None): """Send a POST request to the URL.""" return self.app.post(url, data=json.dumps(data), + headers=_token_header(token), content_type='application/json') - def put(self, url, data): + def put(self, url, data, token=None): """Send a PUT request to the URL.""" return self.app.put(url, data=json.dumps(data), + headers=_token_header(token), content_type='application/json') - def delete(self, url): + def delete(self, url, token=None): """Send a DELETE request to the URL. There is no data to be send.""" - return self.app.delete(url) + return self.app.delete(url, + headers=_token_header(token)) - def get(self, url): + def get(self, url, token=None): """Send a GET request to the URL. There is no data to be send.""" - return self.app.get(url) + return self.app.get(url, + headers=_token_header(token)) diff --git a/tests/users_tests.py b/tests/users_tests.py index 4eed838..7c01842 100644 --- a/tests/users_tests.py +++ b/tests/users_tests.py @@ -2,7 +2,6 @@ # -*- encoding: utf-8 -*- import unittest -import json from luncho import server @@ -67,10 +66,10 @@ class TestExistingUsers(LunchoTests): """Update user details.""" request = {'full_name': 'New User Name', 'password': 'newhash'} - rv = self.post('/user/{token}/'.format(token=self.user.token), - request) + rv = self.post('/user/', + request, + self.user.token) - expected = {'status': 'OK'} self.assertJsonOk(rv) # check in the database @@ -82,8 +81,9 @@ class TestExistingUsers(LunchoTests): """Send a request with an unexisting token.""" request = {'full_name': 'New User Name', 'password': 'newhash'} - rv = self.post('/user/{token}/'.format(token='no-token'), - request) + rv = self.post('/user/', + request, + 'no-token') self.assertJsonError(rv, 404, 'User not found (via token)') @@ -96,14 +96,15 @@ class TestExistingUsers(LunchoTests): request = {'full_name': 'New User Name', 'password': 'newhash'} - rv = self.post('/user/{token}/'.format(token=self.user.token), - request) + rv = self.post('/user/', + request, + self.user.token) self.assertJsonError(rv, 400, 'Invalid token') def test_delete_user(self): """Delete a user.""" - rv = self.delete('/user/{token}/'.format(token=self.user.token)) + rv = self.delete('/user/', token=self.user.token) self.assertJsonOk(rv) # check the database @@ -112,7 +113,7 @@ class TestExistingUsers(LunchoTests): def test_delete_wrong_token(self): """Send a delete to a non-existing token.""" - rv = self.delete('/user/{token}/'.format(token='no-token')) + rv = self.delete('/user/', token='no-token') self.assertJsonError(rv, 404, 'User not found (via token)') @@ -122,7 +123,7 @@ class TestExistingUsers(LunchoTests): self.user.token = 'expired' server.db.session.commit() - rv = self.delete('/user/{token}/'.format(token=self.user.token)) + rv = self.delete('/user/', token=self.user.token) self.assertJsonError(rv, 400, 'Invalid token')