diff --git a/ordr2/resources/account.py b/ordr2/resources/account.py index 62a09b5..c6f96fc 100644 --- a/ordr2/resources/account.py +++ b/ordr2/resources/account.py @@ -1,22 +1,67 @@ ''' Resources for account registraion and settings ''' - +from datetime import datetime from pyramid.security import Allow, Authenticated, Everyone, Deny, DENY_ALL +from ordr2.models.account import Token, TokenSubject from ordr2.resources.base import BaseResource +class RegistrationToken(BaseResource): + ''' representing :class:`ordr2.models.account.Token` for registration ''' + + def __acl__(self): + ''' access controll list for the resource ''' + return [ + (Deny, Authenticated, 'register'), + (Allow, Everyone, 'register'), + DENY_ALL + ] + + +class EmailVerificationToken(BaseResource): + ''' representing :class:`ordr2.models.account.Token` for email change ''' + + def __acl__(self): + ''' access controll list for the resource + + a logged in user might only access his own tokens + ''' + # self.model is a :class:`ordr2.models.account.Token` instance + return [ + (Allow, self.model.user.principal, 'settings'), + DENY_ALL + ] + + +class ForgottenPasswordToken(BaseResource): + ''' representing :class:`ordr2.models.account.Token` for password reset ''' + + def __acl__(self): + ''' access controll list for the resource ''' + return [ + (Allow, Everyone, 'reset password'), + DENY_ALL + ] + + class AccountResource(BaseResource): ''' Resouce class for account registration and settings ''' #: name of the main navigation section for template highlighting nav_section = 'account' + #: mapping token subjects to token resouce classes + token_resources = { + TokenSubject.USER_REGISTRATION: RegistrationToken, + TokenSubject.CHANGE_EMAIL: EmailVerificationToken, + TokenSubject.RESET_PASSWORD: ForgottenPasswordToken + } + def __init__(self, name, parent, model=None): ''' Create a base resource ''' - super().__init__(name, parent) - # the current model depends is the current logged in user or None - self.model = self.request.user + # the current model is the current logged in user or None + super().__init__(name, parent, self.request.user) def __acl__(self): ''' access controll list for the resource @@ -31,6 +76,28 @@ class AccountResource(BaseResource): (Allow, Everyone, 'logout'), (Deny, Authenticated, 'register'), (Allow, Everyone, 'register'), + (Allow, Everyone, 'reset password'), (Allow, Authenticated, 'settings'), DENY_ALL ] + + def __getitem__(self, key): + ''' provides the dict like interface to access child resources + + :param str key: + token hash or path segment for a child resource + :rtype: + subclass of ordr2.resources.base.BaseResource + :raises: + KeyError if token hash or path segment is not found + ''' + token = self.request.dbsession.query(Token).filter_by(hash=key).first() + if token is None: + # no token found, search for child node + return super().__getitem__(key) + elif token.expires < datetime.utcnow(): + # token has expired, delete it + self.request.dbsession.delete(token) + raise KeyError(f'Token {key} has expired on {token.expires}') + resource_class = self.token_resources[token.subject] + return resource_class(key, self, model=token)