diff --git a/ordr2/models/account.py b/ordr2/models/account.py index 86ecdd5..c77554e 100644 --- a/ordr2/models/account.py +++ b/ordr2/models/account.py @@ -187,7 +187,7 @@ class Token(Base): expires = Column(DateTime, nullable=False) #: additional data to attach to a token - payload = Column(JsonEncoder, nullable=False) + payload = Column(JsonEncoder, nullable=True) #: the user_id the token belongs to owner_id = Column(Integer, ForeignKey('users.id')) diff --git a/ordr2/resources/account.py b/ordr2/resources/account.py index c6f96fc..2c58c0e 100644 --- a/ordr2/resources/account.py +++ b/ordr2/resources/account.py @@ -29,7 +29,7 @@ class EmailVerificationToken(BaseResource): ''' # self.model is a :class:`ordr2.models.account.Token` instance return [ - (Allow, self.model.user.principal, 'settings'), + (Allow, self.model.owner.principal, 'settings'), DENY_ALL ] @@ -61,7 +61,7 @@ class AccountResource(BaseResource): def __init__(self, name, parent, model=None): ''' Create a base resource ''' # the current model is the current logged in user or None - super().__init__(name, parent, self.request.user) + super().__init__(name, parent, parent.request.user) def __acl__(self): ''' access controll list for the resource diff --git a/tests/resources/account.py b/tests/resources/account.py index b573b18..a95f9ee 100644 --- a/tests/resources/account.py +++ b/tests/resources/account.py @@ -2,30 +2,79 @@ import pytest +from pyramid.testing import DummyRequest + +from .. import app_config, dbsession, get_user + +# helper function + +def get_root_resource(role_name=None, **kwargs): + ''' return a root resource ''' + from ordr2.resources import RootResource + user = get_user(role_name) if role_name else None + request = DummyRequest(user=user, **kwargs) + return RootResource(request) + + +# tests for token resources + +def test_registration_token_acl(): + ''' test the access controll list of the registration token resource ''' + from pyramid.security import Allow, Authenticated, Deny, Everyone, DENY_ALL + from ordr2.resources.account import RegistrationToken + + root = get_root_resource() + resource = RegistrationToken(None, root, None) + + assert resource.__acl__() == [ + (Deny, Authenticated, 'register'), + (Allow, Everyone, 'register'), + DENY_ALL + ] + +def test_email_verification_token_acl(app_config): + ''' test the access controll list of the email token resource ''' + from pyramid.security import Allow, Authenticated, Deny, Everyone, DENY_ALL + from ordr2.models.account import User, Token + from ordr2.resources.account import EmailVerificationToken + + root = get_root_resource('user') + token = Token(owner=root.request.user) + resource = EmailVerificationToken(None, root, token) + + assert resource.__acl__() == [(Allow, 'user:3', 'settings'), DENY_ALL] + +def test_password_reset_token_acl(): + ''' test the access controll list of the password token resource ''' + from pyramid.security import Allow, Everyone, DENY_ALL + from ordr2.resources.account import ForgottenPasswordToken + + root = get_root_resource() + resource = ForgottenPasswordToken(None, root, None) + + assert resource.__acl__() == [ + (Allow, Everyone, 'reset password'), + DENY_ALL + ] def test_account_resource_init(): ''' test __init__ function of base resource ''' - from pyramid.testing import DummyRequest - from ordr2.resources import AccountResource, RootResource + from ordr2.resources.account import AccountResource - request = DummyRequest(user='Eric Idle') - root = RootResource(request) + root = get_root_resource('user') resource = AccountResource('resource name', root) assert resource.__name__ == 'resource name' assert resource.__parent__ == root - assert resource.request == request - assert resource.model == request.user - + assert resource.request == root.request + assert resource.model == root.request.user def test_account_resource_acl(): - ''' test __acl__ function of base resource ''' + ''' test the access controll list of the account resource ''' from pyramid.security import Allow, Authenticated, Deny, Everyone, DENY_ALL - from pyramid.testing import DummyRequest - from ordr2.resources import AccountResource, RootResource + from ordr2.resources.account import AccountResource - request = DummyRequest(user=None) - root = RootResource(request) + root = get_root_resource() resource = AccountResource('resource name', root) assert resource.__acl__() == [ @@ -33,7 +82,63 @@ def test_account_resource_acl(): (Allow, Everyone, 'logout'), (Deny, Authenticated, 'register'), (Allow, Everyone, 'register'), + (Allow, Everyone, 'reset password'), (Allow, Authenticated, 'settings'), DENY_ALL ] +def test_account_resource_getitem_token_ok(app_config, dbsession): + ''' test __getitem__ method returns correct token ''' + from ordr2.models.account import TokenSubject + from ordr2.resources.account import ( + AccountResource, + EmailVerificationToken + ) + + root = get_root_resource('user', dbsession=dbsession) + user = root.request.user + dbsession.add(user) + hash = user.issue_token(root.request, TokenSubject.CHANGE_EMAIL) + account = AccountResource(None, root) + resource = account[hash] + + assert isinstance(resource, EmailVerificationToken) + assert resource.__name__ == hash + assert resource.__parent__ == account + assert resource.model.hash == hash + assert resource.model.owner == root.request.user + + +def test_account_resource_getitem_token_not_found(dbsession): + ''' test __getitem__ raises KeyError on unknown token hash ''' + from ordr2.resources.account import AccountResource + + root = get_root_resource('user', dbsession=dbsession) + account = AccountResource(None, root) + + with pytest.raises(KeyError): + resource = account['unknown token hash'] + + +def test_account_resource_getitem_token_expired(dbsession): + ''' test __getitem__ raises KeyError on unknown token hash ''' + from datetime import datetime + from ordr2.models.account import Token, TokenSubject + from ordr2.resources.account import ( + AccountResource, + EmailVerificationToken + ) + + root = get_root_resource('user', dbsession=dbsession) + token = Token.issue( + root.request, + root.request.user, + TokenSubject.CHANGE_EMAIL + ) + token.expires = datetime(year=2000, month=1, day=1) + dbsession.add(token) + account = AccountResource(None, root) + + with pytest.raises(KeyError) as excinfo: + resource = account[token.hash] + assert f'Token {token.hash} has expired' in str(excinfo.value)