# HG changeset patch # User Filip de Waard # Date 1261501590 -3600 # Node ID 7ef8a53c4109ca815cf4f9ef8a3a59bc772aae56 # Parent 2e9c39c213fd9fc019c2c59ac0808c2ec46c4313 added vix.lib.auth module diff -r 2e9c39c213fd9fc019c2c59ac0808c2ec46c4313 -r 7ef8a53c4109ca815cf4f9ef8a3a59bc772aae56 vix/lib/auth.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vix/lib/auth.py Tue Dec 22 18:06:30 2009 +0100 @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +""" +vix/lib/auth.py: authentication and authorisation handlers + +Copyright 2009, Net Collective. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +""" + +import bcryptor +import vix.model as model + +bcrypt = bcryptor.Bcrypt() + +def authenticate(user, password): + """Authenticates a username and password combination. + + :param user: username to authenticate or model.User object. + :type user: unicode or model.User + :param password: plain-text password that is to be validated. + :type password: unicode + + :rtype: boolean + + """ + + #load model.User object if only a username is supplied + if isinstance(user, basestring): + user = model.User.load(model.db, user) + + if user is not None and bcrypt.valid(password, user.password): + return True + else: + return False + +def authorize(user, database, feed, action, doc_id=None): + """"Requests authorisation to handle a given database/feed/document. + + #TODO: add support for doc_id and admin related functionality. + + :param user: username to authorize or model.User object. + :type username: unicode or model.User + :param database: name of the database authorisation is requested for. + :type database: unicode + :param feed: name of the feed authorisation is requested for. + :type feed: unicode + :param action: HTTP action authorisation is requested for + ('GET', 'POST', 'PUT' or 'DELETE') + :type action: unicode + :param doc_id: Unimplemented (will be used to determine if it concerns + a document that the provided user owns or one he needs admin + rights for because he hasn't created it. + :type doc_id: unicode + + :rtype: boolean + + """ + + #load model.User object if only a username is supplied + if isinstance(user, basestring): + user = model.User.load(model.db, user) + + if user is None: + return False + + permissions = user.get_permissions(database, feed) + + if permissions is None: + return False + + actions = permissions[1]['actions'] + + if actions.has_key(action) and actions[action] is True: + return True + else: + return False + diff -r 2e9c39c213fd9fc019c2c59ac0808c2ec46c4313 -r 7ef8a53c4109ca815cf4f9ef8a3a59bc772aae56 vix/model/__init__.py --- a/vix/model/__init__.py Sat Dec 19 10:28:02 2009 +0100 +++ b/vix/model/__init__.py Tue Dec 22 18:06:30 2009 +0100 @@ -22,11 +22,15 @@ import re from datetime import datetime -import couchdb.schema as schema +from couchdb import client, schema +from pylons import config _password_re = re.compile('\$2a\$[\d]{2}\$[A-z\d./]{53}') _username_re = re.compile('^[.\w]{2,30}$') +server = client.Server(config['couchdb_server']) +db = None + class User(schema.Document): """ The User object is the model for a Vix user and includes both diff -r 2e9c39c213fd9fc019c2c59ac0808c2ec46c4313 -r 7ef8a53c4109ca815cf4f9ef8a3a59bc772aae56 vix/tests/__init__.py --- a/vix/tests/__init__.py Sat Dec 19 10:28:02 2009 +0100 +++ b/vix/tests/__init__.py Tue Dec 22 18:06:30 2009 +0100 @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + """Pylons application test package This package assumes the Pylons environment is already loaded, such as @@ -7,6 +9,9 @@ This module initializes the application via ``websetup`` (`paster setup-app`) and provides the base testing objects. """ + +import couchdb + from unittest import TestCase from paste.deploy import loadapp @@ -17,7 +22,9 @@ import pylons.test -__all__ = ['environ', 'url', 'TestController'] +import vix.model as model + +__all__ = ['environ', 'url', 'TestController', 'DatabasePoweredTestCase'] # Invoke websetup with the current config file SetupCommand('setup-app').run([config['__file__']]) @@ -34,3 +41,18 @@ self.app = TestApp(wsgiapp) url._push_object(URLGenerator(config['routes.map'], environ)) TestCase.__init__(self, *args, **kwargs) + +class DatabasePoweredTestCase(TestCase): + + def setUp(self): + """Create new test database for every test.""" + + model.db = model.server.create('vix_tests') + + def tearDown(self): + """Delete test database.""" + + try: + del model.server['vix_tests'] + except couchdb.client.ResourceNotFound: + pass diff -r 2e9c39c213fd9fc019c2c59ac0808c2ec46c4313 -r 7ef8a53c4109ca815cf4f9ef8a3a59bc772aae56 vix/tests/test_auth.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vix/tests/test_auth.py Tue Dec 22 18:06:30 2009 +0100 @@ -0,0 +1,87 @@ +""" +vix/tests/test_auth.py: Tests for authentication and authorization code. + +Copyright 2009, Net Collective. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +""" + +from unittest import TestCase + +import couchdb + +import vix.tests +import vix.model as model +import vix.lib.auth as auth + +class TestAuth(vix.tests.DatabasePoweredTestCase): + + def test_authenticate(self): + """Test if users are authenticated properly.""" + + p = auth.bcrypt.create(u'123') + user = model.User(username=u"fmw", password=p) + + user.store(model.db) + + self.assertEquals(auth.authenticate(u'fmw', u'123'), True) + self.assertEquals(auth.authenticate(u'fmw', u'321'), False) + self.assertEquals(auth.authenticate(u'fm2', u'123'), False) + + #test with model.User object as 1st argument instead of string + self.assertEquals(auth.authenticate(user, u'123'), True) + self.assertEquals(auth.authenticate(user, u'321'), False) + + def test_authorize(self): + """Test if users are authorized properly.""" + + #TODO: add checks for doc_id and admin related functionality + + p = auth.bcrypt.create(u'123') + perm = {'GET': True, 'POST': True, 'PUT': True, 'DELETE': False} + + user = model.User(username=u"fmw", password=p) + user.set_permissions(database=u'vix_tests', feed=u'blog', admin=True, + actions=perm) + + user.store(model.db) + + self.assertEquals(auth.authorize(user=u'fmw', + database=u'vix_tests', feed=u'blog', action=u'GET'), True) + self.assertEquals(auth.authorize(user=u'fmw', + database=u'vix_tests', feed=u'blog', action=u'DELETE'), False) + + #test with model.User object instead of username string + self.assertEquals(auth.authorize(user=user, database=u'vix_tests', + feed=u'blog', action=u'GET'), True) + self.assertEquals(auth.authorize(user=user, database=u'vix_tests', + feed=u'blog', action=u'DELETE'), False) + + #see what happens when you try to authorize a user that + #doesn't have any permissions saved. + user.delete_permissions(u'vix_tests', u'blog') + user.store(model.db) + + self.assertEquals(auth.authorize(user=user, database=u'vix_tests', + feed=u'blog', action=u'GET'), False) + self.assertEquals(auth.authorize(user=u"fmw", database=u'vix_tests', + feed=u'blog', action=u'GET'), False) + + """ + def test_create_session(self): + pass + + def test_validate_session(self): + pass + """ diff -r 2e9c39c213fd9fc019c2c59ac0808c2ec46c4313 -r 7ef8a53c4109ca815cf4f9ef8a3a59bc772aae56 vix/tests/test_models.py --- a/vix/tests/test_models.py Sat Dec 19 10:28:02 2009 +0100 +++ b/vix/tests/test_models.py Tue Dec 22 18:06:30 2009 +0100 @@ -21,31 +21,15 @@ import time from datetime import datetime -from unittest import TestCase import couchdb import bcryptor +import vix.tests import vix.model as model -class TestModel(TestCase): +class TestModel(vix.tests.DatabasePoweredTestCase): - def setUp(self): - """Create new test database for every test.""" - - from pylons import config - - self.server = couchdb.client.Server(config['couchdb_server']) - self.db = self.server.create('vix_tests') - - def tearDown(self): - """Delete test database.""" - - try: - del self.server['vix_tests'] - except couchdb.client.ResourceNotFound: - pass - def test_User(self): """General test for the User model object.""" @@ -60,11 +44,11 @@ # and http://code.google.com/p/httplib2/issues/detail?id=67 #updating httplib2 to the latest development version or any #version after 11/15/2009 fixes this issue. - user.store(self.db) + user.store(model.db) #make sure the data is reloaded straight out of the database user = model.User() - user = user.load(self.db, u'fmw') + user = user.load(model.db, u'fmw') assert user.id == user.username @@ -89,8 +73,8 @@ time.sleep(1) #reload user - user.store(self.db) - user = model.User.load(self.db, u'fmw') + user.store(model.db) + user = model.User.load(model.db, u'fmw') #test if .updated got refreshed timedelta = datetime.utcnow() - user.updated @@ -100,10 +84,7 @@ timedelta = now - user.created self.assertTrue(timedelta.seconds < 1) - #add profile - - #duplication - #encoding + #TODO: add profile def test_User_validate(self): """Test if User objects are validated correctly.""" @@ -162,13 +143,13 @@ #test if validate is called on user.store() user = model.User(username=u'fmw') - self.assertRaises(ValueError, user.store, self.db) + self.assertRaises(ValueError, user.store, model.db) #test if a model.User object loaded from the database still validates user = model.User(username=u'fmw', password=bcrypt.create(u'123')) - user.store(self.db) + user.store(model.db) - user = model.User.load(self.db, u'fmw') + user = model.User.load(model.db, u'fmw') user.validate() def test_User_permissions(self): @@ -189,10 +170,10 @@ user.set_permissions(database='vix_tests', feed='news', admin=False, actions=perm_news) - user.store(self.db) + user.store(model.db) user = model.User() - user = user.load(self.db, u'fmw') + user = user.load(model.db, u'fmw') self.assertEquals(user.get_permissions( database='vix_tests', feed='blog'), [0, { @@ -208,10 +189,10 @@ user.set_permissions(database='vix_tests', feed='news', admin=True, actions=perm_blog) - user.store(self.db) + user.store(model.db) user = model.User() - user = user.load(self.db, u'fmw') + user = user.load(model.db, u'fmw') self.assertEquals(user.get_permissions( database='vix_tests', feed='news'), [1, { @@ -242,9 +223,9 @@ #test deleting permissions: user.delete_permissions(database='vix_tests', feed='news') - user.store(self.db) + user.store(model.db) user = model.User() - user = user.load(self.db, u'fmw') + user = user.load(model.db, u'fmw') self.assertEquals(user.get_permissions('vix_tests', 'news'), None)