Skip to content

Commit

Permalink
Merge pull request cd34#58 from sontek/add-password-salt
Browse files Browse the repository at this point in the history
Add password salt

Thank you for the pull request.
  • Loading branch information
cd34 committed Dec 7, 2011
2 parents b09f920 + 27ac67b commit 539f73b
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 43 deletions.
9 changes: 5 additions & 4 deletions apex/__init__.py
Expand Up @@ -59,6 +59,11 @@ def includeme(config):
UnencryptedCookieSessionFactoryConfig( \
settings.get('apex.session_secret')))

if not config.registry.queryUtility(IAuthorizationPolicy):
authz_policy = ACLAuthorizationPolicy()
config.set_authorization_policy(authz_policy)


if not config.registry.queryUtility(IAuthenticationPolicy):
if not settings.has_key('apex.auth_secret'):
raise ApexAuthSecret()
Expand All @@ -67,10 +72,6 @@ def includeme(config):
callback=groupfinder)
config.set_authentication_policy(authn_policy)

if not config.registry.queryUtility(IAuthorizationPolicy):
authz_policy = ACLAuthorizationPolicy()
config.set_authorization_policy(authz_policy)

cache = RootFactory.__acl__
config.set_root_factory(RootFactory)

Expand Down
21 changes: 20 additions & 1 deletion apex/models.py
Expand Up @@ -25,6 +25,10 @@

from apex.lib.db import get_or_create

import hashlib
import random
import string

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()

Expand Down Expand Up @@ -79,6 +83,7 @@ class AuthUser(Base):
id = Column(types.Integer(), primary_key=True)
login = Column(Unicode(80), default=u'', index=True)
username = Column(Unicode(80), default=u'', index=True)
salt = Column(Unicode(24))
_password = Column('password', Unicode(80), default=u'')
email = Column(Unicode(80), default=u'', index=True)
active = Column(types.Enum(u'Y',u'N',u'D', name=u"active"), default=u'Y')
Expand All @@ -96,6 +101,8 @@ class AuthUser(Base):
"""

def _set_password(self, password):
self.salt = self.get_salt(24)
password = password + self.salt
self._password = BCRYPTPasswordManager().encode(password, rounds=12)

def _get_password(self):
Expand All @@ -104,6 +111,17 @@ def _get_password(self):
password = synonym('_password', descriptor=property(_get_password, \
_set_password))

def get_salt(self, length):
m = hashlib.sha256()
word = ''

for i in xrange(length):
word += random.choice(string.ascii_letters)

m.update(word)

return unicode(m.hexdigest()[:length])

def in_group(self, group):
"""
Returns True or False if the user is or isn't in the group.
Expand Down Expand Up @@ -171,7 +189,8 @@ def check_password(cls, **kwargs):

if not user:
return False
if BCRYPTPasswordManager().check(user.password, kwargs['password']):
if BCRYPTPasswordManager().check(user.password,
kwargs['password'] + user.salt):
return True
else:
return False
Expand Down
3 changes: 3 additions & 0 deletions apex/tests/CONFIG.yaml
@@ -0,0 +1,3 @@
Store:
Type: SQL
DB: sqlite:///%(here)s/apex_test.db
64 changes: 64 additions & 0 deletions apex/tests/__init__.py
@@ -0,0 +1,64 @@
import os
import unittest
from sqlalchemy import engine_from_config
from pyramid import testing
from sqlalchemy.orm import sessionmaker
from apex.models import DBSession
from apex.models import Base
here = os.path.abspath(os.path.dirname(__file__))


""" bare minimum settings required for testing
"""
settings = {
'sqlalchemy.url':'sqlite:///apex.test.db',
'mako.directories':'{0}/../apex/templates'.format(here),
'apex.session_secret':'session_secret',
'apex.auth_secret':'auth_secret',
'apex.came_from_route':'home',
'apex.velruse_config':'{0}/CONFIG.yaml'.format(here),
'apex.use_recaptcha_on_login': 'false',
}


class BaseTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
""" must add default route 'home' and include apex
we also must create a default user/pass/group to test
"""
cls.engine = engine_from_config(settings, prefix='sqlalchemy.')
DBSession.configure(bind=cls.engine)
Base.metadata.create_all(cls.engine)

cls.Session = sessionmaker()

@classmethod
def tearDownClass(cls):
DBSession.close()
from apex.models import Base
Base.metadata.drop_all(cls.engine)


def setUp(self):
self.config = testing.setUp()
self.config.add_route('home', '/')
self.config.add_settings(settings)
self.config.include('apex')

connection = self.engine.connect()

# begin a non-ORM transaction
self.trans = connection.begin()

# bind an individual Session to the connection
DBSession.configure(bind=connection)
self.session = self.Session(bind=connection)

def tearDown(self):
# rollback - everything that happened with the
# Session above (including calls to commit())
# is rolled back.
testing.tearDown()
self.trans.rollback()
self.session.close()
78 changes: 42 additions & 36 deletions apex/tests/test_views.py
@@ -1,10 +1,11 @@
import os
import unittest

from sqlalchemy import engine_from_config

from pyramid import testing

from apex.tests import BaseTestCase
from paste.util.multidict import MultiDict

here = os.path.abspath(os.path.dirname(__file__))

""" added to provide dummy environment to prevent exception when
Expand All @@ -15,51 +16,56 @@
'SERVER_NAME':'test.com',
}

""" bare minimum settings required for testing
"""
settings = {
'sqlalchemy.url':'sqlite:///apex.test.db',
'mako.directories':'{0}/../apex/templates'.format(here),
'apex.session_secret':'session_secret',
'apex.auth_secret':'auth_secret',
'apex.came_from_route':'home',
'apex.velruse_config':'{0}/CONFIG.yaml',
}

class Test_views(unittest.TestCase):
def setUp(self):
""" must add default route 'home' and include apex
we also must create a default user/pass/group to test
"""
import apex
class Test_views(BaseTestCase):
def test_view_login(self):
from apex.lib.libapex import create_user
self.engine = engine_from_config(settings, 'sqlalchemy.')
self.config = testing.setUp()
self.config.add_route('home', '/')
self.config.add_settings(settings)
self.config.include('apex')
create_user(username='test', password='password')

from apex.views import login
request = testing.DummyRequest()

# wtforms requires this
request.POST = MultiDict()

""" creating user for later testing
request.context = testing.DummyResource()
response = login(request)

self.assertEqual(response['title'], 'You need to login')

def test_simple_login(self):
from apex.lib.libapex import create_user
create_user(username='test', password='password')
"""

def tearDown(self):
""" import Base so that we can drop the tables after each test
"""
from apex.models import Base
testing.tearDown()
Base.metadata.drop_all(self.engine)
request = testing.DummyRequest(environ=environ)
request.method = 'POST'
# wtforms requires this
request.POST = MultiDict()
request.POST['username'] = 'test'
request.POST['password'] = 'password'

def test_login(self):
pass
""" requires REMOTE_ADDR
from apex.views import login
request.context = testing.DummyResource()
response = login(request)

self.assertEqual(response.status_int, 302)

def test_fail_login(self):
from apex.lib.libapex import create_user
create_user(username='test', password='password1')

request = testing.DummyRequest(environ=environ)
request.method = 'POST'
# wtforms requires this
request.POST = MultiDict()
request.POST['username'] = 'test'
request.POST['password'] = 'password'

from apex.views import login
request.context = testing.DummyResource()
response = login(request)

self.assertEqual('302 Found', response.status)
"""
self.assertEqual(len(response['form'].errors), 1)

def test_logout(self):
""" need to import environ for SERVER_NAME and HOST_NAME
Expand Down
6 changes: 4 additions & 2 deletions apex/views.py
Expand Up @@ -65,8 +65,10 @@ def login(request):
public_key=apex_settings('recaptcha_public_key'),
private_key=apex_settings('recaptcha_private_key'),
)
form = LoginForm(request.POST,
captcha={'ip_address': request.environ['REMOTE_ADDR']})
form = LoginForm(request.POST,
captcha={'ip_address': request.environ['REMOTE_ADDR']})
else:
form = LoginForm(request.POST)
else:
form = None

Expand Down

0 comments on commit 539f73b

Please sign in to comment.