Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
ericrasmussen committed Apr 26, 2012
2 parents 8b5f6b5 + 79a82f8 commit afc1a7e
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 16 deletions.
2 changes: 1 addition & 1 deletion demos/simple/development.ini
Expand Up @@ -14,7 +14,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000&blobstorage_d
substanced.secret = seekri1
substanced.initial_login = admin
substanced.initial_password = admin
substanced.form.tempdir = %(here)s/tmp
substanced.uploads_tempdir = %(here)s/tmp

mail.default_sender = substanced_demo@pylonsproject.org

Expand Down
6 changes: 3 additions & 3 deletions substanced/form/__init__.py
Expand Up @@ -137,7 +137,7 @@ class FileUploadTempStore(object):
upload data in the Pyramid session and on disk. The request passed to
its constructor must be a fully-initialized Pyramid request (it have a
``registry`` attribute, which must have a ``settings`` attribute, which
must be a dictionary). The ``substanced.form.tempdir`` variable in the
must be a dictionary). The ``substanced.uploads_tempdir`` variable in the
``settings`` dictionary must be set to the path of an existing directory
on disk. This directory will temporarily store file upload data on
behalf of Deform and Substance D when a form containing a file upload
Expand All @@ -148,11 +148,11 @@ class FileUploadTempStore(object):
"""
def __init__(self, request):
try:
self.tempdir = request.registry.settings['substanced.form.tempdir']
self.tempdir=request.registry.settings['substanced.uploads_tempdir']
except KeyError:
raise ConfigurationError(
'To use FileUploadTempStore, you must set a '
'"substanced.form.tempdir" key in your .ini settings. It '
'"substanced.uploads_tempdir" key in your .ini settings. It '
'points to a directory which will temporarily '
'hold uploaded files when form validation fails.')
self.request = request
Expand Down
8 changes: 7 additions & 1 deletion substanced/form/tests.py
Expand Up @@ -101,7 +101,7 @@ def _makeOne(self, request):
def _makeRequest(self):
request = testing.DummyRequest()
request.registry.settings = {}
request.registry.settings['substanced.form.tempdir'] = self.tempdir
request.registry.settings['substanced.uploads_tempdir'] = self.tempdir
request.session = DummySession()
return request

Expand Down Expand Up @@ -166,6 +166,12 @@ def test_get_with_randid(self):
self.assertEqual(inst.get('a')['fp'].read(),
open(fn, 'rb').read())

def test_get_with_randid_file_doesntexist(self):
request = self._makeRequest()
inst = self._makeOne(request)
inst.tempstore['a'] = {'randid':'1234'}
self.assertFalse('fp' in inst.get('a'))

def test___getitem___notfound(self):
request = self._makeRequest()
inst = self._makeOne(request)
Expand Down
15 changes: 7 additions & 8 deletions substanced/principal/__init__.py
Expand Up @@ -47,8 +47,9 @@ class UserToGroup(Interface):
class Principals(Folder):
""" Object representing a collection of principals. Inherits from
:class:`substanced.folder.Folder`. Contains ``users``, an instance of
:class:`substanced.principal.Users`, and ``groups``, an instance of
:class:`substanced.principal.Groups`."""
:class:`substanced.principal.Users`, ``groups``, an instance of
:class:`substanced.principal.Groups`, and ``resets`` an instance of
:class:`substanced.principal.PasswordResets`"""
def __init__(self):
Folder.__init__(self)
self['users'] = Users()
Expand Down Expand Up @@ -358,7 +359,7 @@ def disconnect(self, *groups):
class UserToPasswordReset(object):
pass

@content(IPasswordResets, icon='icon-star')
@content(IPasswordResets, icon='icon-tags')
class PasswordResets(Folder):
""" Object representing the current set of password reset requests """
def _gen_random_token(self):
Expand All @@ -375,19 +376,17 @@ def add_reset(self, user):
break
reset = PasswordReset()
self[token] = reset
reset.__acl__ = [(Allow, Everyone, 'sdi.view')]
reset.__acl__ = [(Allow, Everyone, ('sdi.view',))]
objectmap = self.find_service('objectmap')
objectmap.connect(user, reset, UserToPasswordReset)
return reset

@content(IPasswordReset, icon='icon-question-mark')
@content(IPasswordReset, icon='icon-tag')
class PasswordReset(Persistent):
""" Object representing the a single password reset request """
def reset_password(self, password):
objectmap = find_service(self, 'objectmap')
sources = list(objectmap.sources(self, UserToPasswordReset))
if not sources:
raise ValueError('No user associated with this password reset')
user = sources[0]
user.set_password(password)
self.commit_suicide()
Expand All @@ -399,7 +398,7 @@ def commit_suicide(self):
def user_will_be_removed(user, event):
""" Remove all password reset objects associated with a user when the user
is removed """
objectmap = find_service(event.object, 'objectmap')
objectmap = find_service(user, 'objectmap')
if objectmap is not None:
resets = objectmap.targets(user, UserToPasswordReset)
for reset in resets:
Expand Down
87 changes: 87 additions & 0 deletions substanced/principal/tests/test_principal.py
Expand Up @@ -249,6 +249,12 @@ def test_it(self):
self.assertEqual(result.values, [('1', 'group')])

class TestUser(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()

def tearDown(self):
testing.tearDown()

def _makeOne(self, password, email=''):
from .. import User
return User(password, email)
Expand All @@ -263,6 +269,24 @@ def test_set_password(self):
inst.set_password('abcdef')
self.assertTrue(inst.pwd_manager.check(inst.password, 'abcdef'))

def test_email_password_reset(self):
from ...testing import make_site
from pyramid_mailer import get_mailer
site = make_site()
principals = site['__services__']['principals']
resets = principals['resets'] = testing.DummyResource()
def add_reset(user):
self.assertEqual(user, inst)
resets.add_reset = add_reset
request = testing.DummyRequest()
request.mgmt_path = lambda *arg: '/mgmt'
request.root = site
self.config.include('pyramid_mailer.testing')
inst = self._makeOne('password')
principals['users']['user'] = inst
inst.email_password_reset(request)
self.assertTrue(get_mailer(request).outbox)

def test_get_properties(self):
inst = self._makeOne('abc', 'email')
inst.__name__ = 'fred'
Expand Down Expand Up @@ -450,6 +474,69 @@ def test_garden_path(self):
request.context = context
result = self._callFUT(1, request)
self.assertEqual(result, (1,2))

class TestPasswordResets(unittest.TestCase):
def _makeOne(self):
from .. import PasswordResets
return PasswordResets()

def test_add_reset(self):
from .. import UserToPasswordReset
inst = self._makeOne()
objectmap = DummyObjectMap()
services = testing.DummyResource()
inst.add('__services__', services, allow_services=True)
services['objectmap'] = objectmap
user = testing.DummyResource()
reset = inst.add_reset(user)
self.assertEqual(
objectmap.connections,
[(user, reset, UserToPasswordReset)])
self.assertTrue(reset.__acl__)
self.assertEqual(len(inst), 2)

class TestPasswordReset(unittest.TestCase):
def _makeOne(self):
from .. import PasswordReset
return PasswordReset()

def test_reset_password(self):
from ...interfaces import IFolder
parent = testing.DummyResource(__provides__=IFolder)
user = testing.DummyResource()
def set_password(password):
user.password = password
user.set_password = set_password
objectmap = DummyObjectMap((user,))
inst = self._makeOne()
parent['reset'] = inst
services = testing.DummyResource()
parent['__services__'] = services
services['objectmap'] = objectmap
inst.reset_password('password')
self.assertEqual(user.password, 'password')
self.assertFalse('reset' in parent)

class Test_user_will_be_removed(unittest.TestCase):
def _callFUT(self, user, event):
from .. import user_will_be_removed
return user_will_be_removed(user, event)

def test_it(self):
from ...interfaces import IFolder
parent = testing.DummyResource(__provides__=IFolder)
user = testing.DummyResource()
reset = testing.DummyResource()
def commit_suicide():
reset.committed = True
reset.commit_suicide = commit_suicide
objectmap = DummyObjectMap((reset,))
services = testing.DummyResource()
parent['__services__'] = services
parent['user'] = user
services['objectmap'] = objectmap
self._callFUT(user, None)
self.assertTrue(reset.committed)

from ...interfaces import IFolder

Expand Down
55 changes: 55 additions & 0 deletions substanced/principal/tests/test_views.py
Expand Up @@ -111,6 +111,61 @@ def test_send_success(self):
self.assertEqual(resp.location, 'http://example.com')
self.assertTrue(user.emailed_password_reset)

class TestResetView(unittest.TestCase):
def _makeOne(self, request):
from ..views import ResetView
return ResetView(request)

def _makeRequest(self):
request = testing.DummyRequest()
request.mgmt_path = lambda *arg : 'http://example.com'
return request

def test_reset_success(self):
context = testing.DummyResource()
def reset_password(password):
self.assertEqual(password, 'thepassword')
context.reset_password = reset_password
request = self._makeRequest()
request.context = context
inst = self._makeOne(request)
resp = inst.reset_success({'new_password':'thepassword'})
self.assertEqual(resp.location, 'http://example.com')


class Test_login_validator(unittest.TestCase):
def _makeOne(self, node, kw):
from ..views import login_validator
return login_validator(node, kw)

def _makeSite(self):
from ...interfaces import IFolder
site = testing.DummyResource(__provides__=IFolder)
services = testing.DummyResource()
principals = testing.DummyResource()
users = testing.DummyResource()
site['__services__'] = services
services['principals'] = principals
principals['users'] = users
return site

def test_no_such_user(self):
import colander
request = testing.DummyRequest()
site = self._makeSite()
request.context = site
inst = self._makeOne(None, dict(request=request))
self.assertRaises(colander.Invalid, inst, None, 'fred')

def test_user_exists(self):
request = testing.DummyRequest()
site = self._makeSite()
request.context = site
fred = testing.DummyResource()
site['__services__']['principals']['users']['fred'] = fred
inst = self._makeOne(None, dict(request=request))
self.assertEqual(inst(None, 'fred'), None)

class DummyPrincipal(object):
def connect(self, *args):
self.connected = args
Expand Down
6 changes: 3 additions & 3 deletions substanced/principal/views.py
Expand Up @@ -105,7 +105,7 @@ def login_validator(node, kw):
def _login_validator(node, value):
principals = find_service(context, 'principals')
users = principals['users']
if users.get(value) is None:
if not value in users:
raise colander.Invalid(node, 'No such user %s' % value)
return _login_validator

Expand All @@ -131,7 +131,7 @@ def send_success(self, appstruct):
users = principals['users']
user = users[login]
user.email_password_reset(request)
request.session.flash('Emailed reset instructions', 'success')
request.session.flash('Emailed password reset instructions', 'success')
home = request.mgmt_path(request.root)
return HTTPFound(location=home)

Expand All @@ -154,6 +154,6 @@ def reset_success(self, appstruct):
request = self.request
context = self.request.context
context.reset_password(appstruct['new_password'])
request.session.flash('Password reset, now please log in', 'success')
request.session.flash('Password reset, you may now log in', 'success')
home = request.mgmt_path(request.root)
return HTTPFound(location=home)

0 comments on commit afc1a7e

Please sign in to comment.