# HG changeset patch # User Filip de Waard # Date 1261149406 -3600 # Node ID 8c0298e3a235149977ccc439a8cfa81335eacb62 # Parent c6b7b50597dcd858f552a1473f37d057e1c8f268 added validate method to model.User diff -r c6b7b50597dcd858f552a1473f37d057e1c8f268 -r 8c0298e3a235149977ccc439a8cfa81335eacb62 vix/model/__init__.py --- a/vix/model/__init__.py Thu Dec 17 12:04:40 2009 +0100 +++ b/vix/model/__init__.py Fri Dec 18 16:16:46 2009 +0100 @@ -19,9 +19,13 @@ """ +import re from datetime import datetime import couchdb.schema as schema + +_password_re = re.compile('\$2a\$[\d]{2}\$[A-z\d./]{53}') +_username_re = re.compile('^[.\w]{2,30}$') class User(schema.Document): """ @@ -47,7 +51,7 @@ """ - type = schema.TextField(default="user") + type = schema.TextField(default=u"user") created = schema.DateTimeField(default=datetime.utcnow) id = username = schema.TextField() @@ -67,6 +71,13 @@ )) ))) + def store(self, db): + """Validates the document and calls schema.Document.store(db).""" + + self.validate() + + return super(User, self).store(db) + def get_permissions(self, database, feed): """Returns permission values for given feed and database. @@ -155,3 +166,18 @@ p = self.get_permissions(database, feed) del self.permissions[p[0]] + + def validate(self): + """Validates the User object.""" + + if self.password == None or not _password_re.match(self.password): + raise ValueError('Invalid password (needs to be a bcrypt hash)') + + if self.id == None or not _username_re.match(self.id): + raise ValueError('Invalid username (length needs to be between ' + + '2-30 characters and only alphanumeric characters, dots ' + + 'and underscores are allowed.') + + if self.type != u'user': + raise ValueError("The type of a model.User object can't be " + + "modified") diff -r c6b7b50597dcd858f552a1473f37d057e1c8f268 -r 8c0298e3a235149977ccc439a8cfa81335eacb62 vix/tests/test_models.py --- a/vix/tests/test_models.py Thu Dec 17 12:04:40 2009 +0100 +++ b/vix/tests/test_models.py Fri Dec 18 16:16:46 2009 +0100 @@ -23,6 +23,7 @@ from unittest import TestCase import couchdb +import bcryptor import vix.model as model @@ -77,10 +78,78 @@ timedelta = datetime.utcnow() - user.created self.assertTrue(timedelta.seconds < 1) - #validate (password length, username length, required fields) + #add updated field + #add profile + #duplication #encoding + def test_User_validate(self): + """Test if User objects are validated correctly.""" + + valid_usernames = [u'fmw', u'f.m.dewaard', u'fw', u'o'*30, + u'foobar', u'fmw4', u'fmw_8'] + invalid_usernames = [u'f', u'fóò', u'fo-o', u'"foo', u"fo'o", + u'o'*31] + + bcrypt = bcryptor.Bcrypt() + valid_passwords = [] + + #generate some bcrypt hashes: + for i in range(5): + valid_passwords.append(bcrypt.create(str(i))) + + invalid_passwords = [u'password', + '$2a$hi$zovtWSOSm0PTsiuovPOxC.uEJxsEzVf0AlswKvgT/jtxMqf44.Kpi', + '$2a$100$zovtWSOSm0PTsiuovPOxC.uEJxsEzVf0AlswKvgT/jtxMqf44.Kpi', + '$2a$10$zovtWSOSm0PTsiuovPOxC.uEJxsEzVf0AlswKvgT/jtxMqf4pi', + '$2a$10$zovtWSOSm0PTsiuovPOxC.uEJxsEzVf0AlswKvgT/jtxMqf44.Kp@'] + + + #make sure valid names pass the test: + for v_usr in valid_usernames: + user = model.User(username=v_usr, password=valid_passwords[0]) + user.validate() + + #same goes for valid passwords: + for p in valid_passwords: + user = model.User(username=valid_usernames[0], password=p) + user.validate() + + #assert that all invalid usernames raise an exception: + for inv_usr in invalid_usernames: + user = model.User(username=inv_usr, password=valid_passwords[0]) + self.assertRaises(ValueError, user.validate) + + #and same for invalid passwords: + for inv_pass in invalid_passwords: + user = model.User(username=valid_usernames[0], password=inv_pass) + self.assertRaises(ValueError, user.validate) + + #see what happens when no username is supplied + user = model.User(password=bcrypt.create(u'123')) + self.assertRaises(ValueError, user.validate) + + #and without password: + user = model.User(username=u'fmw') + self.assertRaises(ValueError, user.validate) + + #test wrong type + user = model.User(username=u'fmw', password=bcrypt.create(u'123'), + type='not_a_user') + self.assertRaises(ValueError, user.validate) + + #test if validate is called on user.store() + user = model.User(username=u'fmw') + self.assertRaises(ValueError, user.store, self.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 = model.User.load(self.db, u'fmw') + user.validate() + def test_User_permissions(self): """Test permission handling in the User model object."""