Browse Source

finished registration workflow

rework
Holger Frey 7 years ago
parent
commit
1c96f9b970
  1. 4
      ordr/events.py
  2. 29
      ordr/models/account.py
  3. 2
      ordr/resources/__init__.py
  4. 22
      ordr/resources/account.py
  5. 7
      ordr/resources/helpers.py
  6. 6
      ordr/schemas/account.py
  7. 2
      ordr/security.py
  8. 36
      ordr/templates/account/registration_completed.jinja2
  9. 4
      ordr/templates/account/registration_form.jinja2
  10. 35
      ordr/templates/account/registration_verify.jinja2
  11. 4
      ordr/templates/layout.jinja2
  12. 2
      ordr/views/pages.py
  13. 28
      ordr/views/registration.py
  14. 5
      tests/__init__.py
  15. 27
      tests/_functional/__init__.py
  16. 4
      tests/_functional/errors.py
  17. 20
      tests/_functional/layout.py
  18. 34
      tests/_functional/login_logout.py
  19. 14
      tests/_functional/pages.py
  20. 44
      tests/_functional/registration.py
  21. 14
      tests/models/account.py
  22. 91
      tests/resources/account.py
  23. 44
      tests/resources/base_child_resource.py
  24. 2
      tests/resources/root.py
  25. 2
      tests/schemas/__init__.py
  26. 16
      tests/security.py
  27. 2
      tests/views/errors.py
  28. 10
      tests/views/pages.py
  29. 52
      tests/views/registration.py

4
ordr/events.py

@ -30,24 +30,28 @@ class UserNotification(object):
class ActivationNotification(UserNotification): class ActivationNotification(UserNotification):
''' user notification for account activation ''' ''' user notification for account activation '''
subject = '[ordr] Your account was activated' subject = '[ordr] Your account was activated'
template = 'ordr:templates/emails/activation.jinja2' template = 'ordr:templates/emails/activation.jinja2'
class OrderStatusNotification(UserNotification): class OrderStatusNotification(UserNotification):
''' user notification for order status change ''' ''' user notification for order status change '''
subject = '[ordr] Order Status Change' subject = '[ordr] Order Status Change'
template = 'ordr:templates/emails/order.jinja2' template = 'ordr:templates/emails/order.jinja2'
class PasswordResetNotification(UserNotification): class PasswordResetNotification(UserNotification):
''' user notification for password reset link ''' ''' user notification for password reset link '''
subject = '[ordr] Password Reset' subject = '[ordr] Password Reset'
template = 'ordr:templates/emails/password_reset.jinja2' template = 'ordr:templates/emails/password_reset.jinja2'
class RegistrationNotification(UserNotification): class RegistrationNotification(UserNotification):
''' user notification for account activation ''' ''' user notification for account activation '''
subject = '[ordr] Please verify your email address' subject = '[ordr] Please verify your email address'
template = 'ordr:templates/emails/registration.jinja2' template = 'ordr:templates/emails/registration.jinja2'

29
ordr/models/account.py

@ -194,3 +194,32 @@ class Token(Base):
owner=owner, owner=owner,
expires=expires expires=expires
) )
@classmethod
def retrieve(cls, request, hash, subject=None):
''' returns a token from the database
The database is queried for a token with the given hash. If an
optional subject is given, the query will search only for tokens of
this kind.
The method will return None if a token could not be found or the
token has already expired. If the token has expired, it will be deleted
from the database
:param pyramid.request.Request request: the current request object
:param str hash: token hash
:param ordr2.models.account.TokenSubject subject: kind of token
:rtype: ordr2.models.account.Token or None
'''
query = request.dbsession.query(cls).filter_by(hash=hash)
if subject:
query = query.filter_by(subject=subject)
token = query.first()
if token is None:
return None
elif token.expires < datetime.utcnow():
request.dbsession.delete(token)
return None
return token

2
ordr/resources/__init__.py

@ -37,7 +37,7 @@ class RootResource:
'register': RegistrationResource 'register': RegistrationResource
} }
child_class = map[key] child_class = map[key]
return child_class(request=self.request, name=key, parent=self) return child_class(name=key, parent=self)
def includeme(config): def includeme(config):

22
ordr/resources/account.py

@ -3,11 +3,26 @@
import deform import deform
from pyramid.security import Allow, Everyone, DENY_ALL from pyramid.security import Allow, Everyone, DENY_ALL
from ordr.models.account import Token, TokenSubject
from ordr.schemas.account import RegistrationSchema from ordr.schemas.account import RegistrationSchema
from .helpers import BaseChildResource from .helpers import BaseChildResource
class RegistrationTokenResource(BaseChildResource):
''' Resource for vaildating a new registered user's email
:param pyramid.request.Request request: the current request object
:param str name: the name of the resource
:param parent: the parent resouce
'''
def __acl__(self):
''' access controll list for the resource '''
return [(Allow, Everyone, 'view'), DENY_ALL]
class RegistrationResource(BaseChildResource): class RegistrationResource(BaseChildResource):
''' The resource for new user registration ''' The resource for new user registration
@ -22,6 +37,13 @@ class RegistrationResource(BaseChildResource):
''' access controll list for the resource ''' ''' access controll list for the resource '''
return [(Allow, Everyone, 'view'), DENY_ALL] return [(Allow, Everyone, 'view'), DENY_ALL]
def __getitem__(self, key):
''' returns a resource for a valid registration token '''
token = Token.retrieve(self.request, key, TokenSubject.REGISTRATION)
if token is None:
raise KeyError(f'Token {key} not found')
return RegistrationTokenResource(name=key, parent=self, model=token)
def get_registration_form(self, **kwargs): def get_registration_form(self, **kwargs):
''' returns the registration form''' ''' returns the registration form'''
settings = { settings = {

7
ordr/resources/helpers.py

@ -3,16 +3,17 @@
class BaseChildResource: class BaseChildResource:
def __init__(self, request, name, parent): def __init__(self, name, parent, model=None):
''' Create a child resource ''' Create a child resource
:param pyramid.request.Request request: the current request object
:param str name: the name of the resource :param str name: the name of the resource
:param parent: the parent resouce :param parent: the parent resouce
:param model: optional data model for the resource
''' '''
self.request = request
self.__name__ = name self.__name__ = name
self.__parent__ = parent self.__parent__ = parent
self.request = parent.request
self.model = model
def __acl__(self): def __acl__(self):
''' access controll list for the resource ''' ''' access controll list for the resource '''

6
ordr/schemas/account.py

@ -1,8 +1,8 @@
import colander import colander
import deform import deform
from . import CSRFSchema from . import CSRFSchema
from .helpers import ( from .helpers import (
deferred_unique_email_validator, deferred_unique_email_validator,
deferred_unique_username_validator, deferred_unique_username_validator,
@ -23,18 +23,22 @@ class RegistrationSchema(CSRFSchema):
validator=deferred_unique_username_validator, validator=deferred_unique_username_validator,
oid='registration_username' oid='registration_username'
) )
first_name = colander.SchemaNode( first_name = colander.SchemaNode(
colander.String(), colander.String(),
oid='registration_first_name' oid='registration_first_name'
) )
last_name = colander.SchemaNode( last_name = colander.SchemaNode(
colander.String(), colander.String(),
oid='registration_last_name' oid='registration_last_name'
) )
email = colander.SchemaNode( email = colander.SchemaNode(
colander.String(), colander.String(),
validator=deferred_unique_email_validator validator=deferred_unique_email_validator
) )
password = colander.SchemaNode( password = colander.SchemaNode(
colander.String(), colander.String(),
widget=deform.widget.CheckedPasswordWidget(), widget=deform.widget.CheckedPasswordWidget(),

2
ordr/security.py

@ -9,6 +9,8 @@ from pyramid.settings import aslist
from ordr.models.account import User from ordr.models.account import User
#: passlib context for hashing passwords #: passlib context for hashing passwords
# at least one scheme must be set in advance, will be overridden by the
# settings in the .ini file.
password_context = CryptContext(schemes=['argon2']) password_context = CryptContext(schemes=['argon2'])

36
ordr/templates/account/registration_completed.jinja2

@ -0,0 +1,36 @@
{% extends "ordr:templates/layout.jinja2" %}
{% block title %} Ordr | Registration {% endblock title %}
{% block content %}
<div class="row justify-content-md-center mt-3">
<div class="col-6">
<h1>Registration</h1>
</div>
</div>
<div class="row justify-content-md-center mt-3">
<div class="col-2">
<p class="text-secondary">
Step 1: Registration
</p>
</div>
<div class="col-2">
<p class="text-secondary">
Step 2: Validate Email
</p>
</div>
<div class="col-2">
<p class="text-primary">
Step 3: Finished
</p>
</div>
</div>
<div class="row justify-content-md-center mt-3">
<div class="col-6">
<h3>Registration Completed</h3>
<p class="mt-3">Thank you for verifying your email address.</p>
<p>Before you can start ordering, an administrator must activate your account</p>
<p>You'll receive an email when your account is activated</p>
</div>
</div>
{% endblock content %}

4
ordr/templates/account/registration_form.jinja2

@ -1,9 +1,11 @@
{% extends "ordr:templates/layout.jinja2" %} {% extends "ordr:templates/layout.jinja2" %}
{% block title %} Ordr | Registration {% endblock title %}
{% block content %} {% block content %}
<div class="row justify-content-md-center mt-3"> <div class="row justify-content-md-center mt-3">
<div class="col-6"> <div class="col-6">
<h2>Registration</h2> <h1>Registration</h1>
</div> </div>
</div> </div>
<div class="row justify-content-md-center mt-3"> <div class="row justify-content-md-center mt-3">

35
ordr/templates/account/registration_verify.jinja2

@ -0,0 +1,35 @@
{% extends "ordr:templates/layout.jinja2" %}
{% block title %} Ordr | Registration {% endblock title %}
{% block content %}
<div class="row justify-content-md-center mt-3">
<div class="col-6">
<h1>Registration</h1>
</div>
</div>
<div class="row justify-content-md-center mt-3">
<div class="col-2">
<p class="text-secondary">
Step 1: Registration
</p>
</div>
<div class="col-2">
<p class="text-primary">
Step 2: Validate Email
</p>
</div>
<div class="col-2">
<p class="text-secondary">
Step 3: Finished
</p>
</div>
</div>
<div class="row justify-content-md-center mt-3">
<div class="col-6">
<h3>Verify Your Email Address</h3>
<p class="mt-3">To complete the registration process an email has been sent to you.</p>
<p>Please follow the link in the email to verify your address and complete the registration process.</p>
</div>
</div>
{% endblock content %}

4
ordr/templates/layout.jinja2

@ -8,9 +8,7 @@
<meta name="author" content="IMTEk / CPI / Holger Frey"> <meta name="author" content="IMTEk / CPI / Holger Frey">
<link rel="shortcut icon" href="{{request.static_url('ordr:static/pyramid-16x16.png')}}"> <link rel="shortcut icon" href="{{request.static_url('ordr:static/pyramid-16x16.png')}}">
{% block title %} <title>{% block title %} Ordr {% endblock title %}</title>
<title>Ordr</title>
{% endblock title %}
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">

2
ordr/views/pages.py

@ -52,9 +52,11 @@ def check_login(context, request):
.filter_by(username=username) .filter_by(username=username)
.first() .first()
) )
if user and user.is_active and user.check_password(password): if user and user.is_active and user.check_password(password):
headers = remember(request, user.id) headers = remember(request, user.id)
return HTTPFound(request.resource_url(request.root), headers=headers) return HTTPFound(request.resource_url(request.root), headers=headers)
return {'loginerror': True} return {'loginerror': True}

28
ordr/views/registration.py

@ -32,6 +32,7 @@ def registration_form_processing(context, request):
''' process registration form ''' ''' process registration form '''
if 'create' not in request.POST: if 'create' not in request.POST:
return HTTPFound(request.resource_url(request.root)) return HTTPFound(request.resource_url(request.root))
form = context.get_registration_form() form = context.get_registration_form()
data = request.POST.items() data = request.POST.items()
try: try:
@ -56,3 +57,30 @@ def registration_form_processing(context, request):
request.registry.notify(notification) request.registry.notify(notification)
return HTTPFound(request.resource_url(context, 'verify')) return HTTPFound(request.resource_url(context, 'verify'))
@view_config(
context='ordr.resources.account.RegistrationResource',
name='verify',
permission='view',
request_method='GET',
renderer='ordr:templates/account/registration_verify.jinja2'
)
def verify(context, request):
''' show email verification text '''
return {}
@view_config(
context='ordr.resources.account.RegistrationTokenResource',
permission='view',
request_method='GET',
renderer='ordr:templates/account/registration_completed.jinja2'
)
def completed(context, request):
''' show email verification text '''
token = context.model
account = token.owner
account.role = Role.NEW
request.dbsession.delete(token)
return {}

5
tests/__init__.py

@ -64,6 +64,7 @@ def dbsession(app_config):
def get_example_user(role): def get_example_user(role):
''' get the user model for one well known user ''' ''' get the user model for one well known user '''
from ordr.models import User from ordr.models import User
id_, first_name, last_name = EXAMPLE_USER_DATA[role.name] id_, first_name, last_name = EXAMPLE_USER_DATA[role.name]
user = User( user = User(
id=id_, id=id_,
@ -74,11 +75,15 @@ def get_example_user(role):
role=role role=role
) )
user.set_password(first_name) user.set_password(first_name)
return user return user
def get_post_request(dbsession, data): def get_post_request(dbsession, data):
''' returns a dummy request with csrf_token for validating deform forms '''
request = testing.DummyRequest() request = testing.DummyRequest()
post_data = {'csrf_token': get_csrf_token(request)} post_data = {'csrf_token': get_csrf_token(request)}
post_data.update(data) post_data.update(data)
return testing.DummyRequest(dbsession=dbsession, POST=post_data) return testing.DummyRequest(dbsession=dbsession, POST=post_data)

27
tests/_functional/__init__.py

@ -1,13 +1,19 @@
''' functional tests for ordr2 ''' ''' functional tests for ordr2 '''
import pytest import pytest
import re
import transaction import transaction
import webtest import webtest
from bs4 import BeautifulSoup
from .. import APP_SETTINGS, get_example_user from .. import APP_SETTINGS, get_example_user
WEBTEST_SETTINGS = APP_SETTINGS.copy() WEBTEST_SETTINGS = APP_SETTINGS.copy()
# WEBTEST_SETTINGS['pyramid.includes'].append('pyramid_mailer.testing') WEBTEST_SETTINGS['pyramid.includes'] = [
'pyramid_mailer.testing'
]
class CustomTestApp(webtest.TestApp): class CustomTestApp(webtest.TestApp):
@ -15,7 +21,7 @@ class CustomTestApp(webtest.TestApp):
pass pass
def login(self, username, password): def login(self, username, password):
''' stub for user login ''' ''' login '''
self.logout() self.logout()
result = self.get('/login') result = self.get('/login')
login_form = result.forms[0] login_form = result.forms[0]
@ -24,7 +30,7 @@ class CustomTestApp(webtest.TestApp):
login_form.submit() login_form.submit()
def logout(self): def logout(self):
''' stub for user logout ''' ''' logout '''
self.get('/logout') self.get('/logout')
def reset(self): def reset(self):
@ -41,9 +47,21 @@ def create_users(dbsession):
dbsession.add(user) dbsession.add(user)
def get_token_url(email, prefix='/'):
''' extracts an account token url from an email '''
soup = BeautifulSoup(email.html, 'html.parser')
for link in soup.find_all('a'):
if re.search(prefix + '[a-f0-9]{32}', link['href']):
return link['href']
@pytest.fixture(scope='module') @pytest.fixture(scope='module')
def testappsetup(): def testappsetup():
''' fixture for using webtest ''' ''' fixture for using webtest
this fixture just sets up the testapp. please use the testapp() fixture
below for real tests.
'''
from ordr.models.meta import Base from ordr.models.meta import Base
from ordr.models import get_tm_session from ordr.models import get_tm_session
from ordr import main from ordr import main
@ -67,5 +85,6 @@ def testappsetup():
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def testapp(testappsetup): def testapp(testappsetup):
''' fixture using webtests, resets the logged every time '''
testappsetup.reset() testappsetup.reset()
yield testappsetup yield testappsetup

4
tests/_functional/errors.py

@ -4,5 +4,5 @@ from . import testappsetup, testapp # noqa: F401
def test_404(testapp): # noqa: F811 def test_404(testapp): # noqa: F811
result = testapp.get('/unknown', status=404) response = testapp.get('/unknown', status=404)
assert '404' in result assert '404' in response

20
tests/_functional/layout.py

@ -10,13 +10,14 @@ from . import testappsetup, testapp # noqa: F401
def test_navbar_no_user(testapp): # noqa: F811 def test_navbar_no_user(testapp): # noqa: F811
result = testapp.get('/faq') response = testapp.get('/faq')
navbar = result.html.find('nav', class_='navbar-dark') navbar = response.html.find('nav', class_='navbar-dark')
expected = ['/', '/', '/faq', '/register'] expected = ['/', '/', '/faq', '/register']
hrefs = [a['href'] for a in navbar.find_all('a')] hrefs = [a['href'] for a in navbar.find_all('a')]
assert expected == hrefs assert expected == hrefs
assert '/orders' not in result assert '/orders' not in response
assert 'nav-item dropdown' not in result assert 'nav-item dropdown' not in response
@pytest.mark.parametrize( # noqa: F811 @pytest.mark.parametrize( # noqa: F811
@ -28,14 +29,13 @@ def test_navbar_no_user(testapp): # noqa: F811
) )
def test_navbar_with_user(testapp, username, password, extras): def test_navbar_with_user(testapp, username, password, extras):
testapp.login(username, password) testapp.login(username, password)
result = testapp.get('/faq') response = testapp.get('/faq')
navbar = result.html.find('nav', class_='navbar-dark') navbar = response.html.find('nav', class_='navbar-dark')
hrefs = [a['href'] for a in navbar.find_all('a')] hrefs = [a['href'] for a in navbar.find_all('a')]
expected = ['/', '/orders', '/faq'] expected = ['/', '/orders', '/faq']
expected.extend(extras) expected.extend(extras)
expected.extend(['#', '/logout', '/account']) expected.extend(['#', '/logout', '/account'])
print('expected', expected)
print('found ', hrefs)
assert expected == hrefs assert expected == hrefs
assert 'nav-item dropdown' in result assert 'nav-item dropdown' in response
assert username in result assert username in response

34
tests/_functional/login_logout.py

@ -6,27 +6,33 @@ from . import testappsetup, testapp # noqa: F401
def test_login_get(testapp): # noqa: F811 def test_login_get(testapp): # noqa: F811
result = testapp.get('/login') response = testapp.get('/login')
active = result.html.find('li', class_='active') active = response.html.find('li', class_='active')
assert active.a['href'] == '/' assert active.a['href'] == '/'
expected = {'/', '/faq', '/register', '/forgot', '/register'} expected = {'/', '/faq', '/register', '/forgot', '/register'}
hrefs = {a['href'] for a in result.html.find_all('a')} hrefs = {a['href'] for a in response.html.find_all('a')}
assert expected == hrefs assert expected == hrefs
forms = result.html.find_all('form')
forms = response.html.find_all('form')
assert len(forms) == 1 assert len(forms) == 1
login_form = forms[0] login_form = forms[0]
assert login_form['action'] == '/login' assert login_form['action'] == '/login'
assert login_form['method'] == 'POST' assert login_form['method'] == 'POST'
assert 'account is not activated' not in result
assert 'account is not activated' not in response
def test_login_ok(testapp): # noqa: F811 def test_login_ok(testapp): # noqa: F811
result = testapp.get('/login') response = testapp.get('/login')
login_form = result.forms[0]
login_form = response.forms[0]
login_form['username'] = 'TerryGilliam' login_form['username'] = 'TerryGilliam'
login_form['password'] = 'Terry' login_form['password'] = 'Terry'
result = login_form.submit() response = login_form.submit()
assert result.location == 'http://localhost/'
assert response.location == 'http://localhost/'
@pytest.mark.parametrize( # noqa: F811 @pytest.mark.parametrize( # noqa: F811
@ -34,9 +40,11 @@ def test_login_ok(testapp): # noqa: F811
[('John', 'Cleese'), ('unknown user', 'wrong password')] [('John', 'Cleese'), ('unknown user', 'wrong password')]
) )
def test_login_denied(testapp, username, password): def test_login_denied(testapp, username, password):
result = testapp.get('/login') response = testapp.get('/login')
login_form = result.forms[0]
login_form = response.forms[0]
login_form['username'] = 'John' login_form['username'] = 'John'
login_form['password'] = 'Cleese' login_form['password'] = 'Cleese'
result = login_form.submit() response = login_form.submit()
assert 'account is not activated' in result
assert 'account is not activated' in response

14
tests/_functional/pages.py

@ -4,14 +4,16 @@ from . import testappsetup, testapp # noqa: F401
def test_welcome(testapp): # noqa: F811 def test_welcome(testapp): # noqa: F811
result = testapp.get('/') response = testapp.get('/')
assert result.location == 'http://localhost/login' assert response.location == 'http://localhost/login'
testapp.login('TerryGilliam', 'Terry') testapp.login('TerryGilliam', 'Terry')
result = testapp.get('/')
assert result.location == 'http://localhost/orders' response = testapp.get('/')
assert response.location == 'http://localhost/orders'
def test_faq(testapp): # noqa: F811 def test_faq(testapp): # noqa: F811
result = testapp.get('/faq') response = testapp.get('/faq')
active = result.html.find('li', class_='active') active = response.html.find('li', class_='active')
assert active.a['href'] == '/faq' assert active.a['href'] == '/faq'

44
tests/_functional/registration.py

@ -1,9 +1,47 @@
''' functional tests for ordr2.views.registration ''' ''' functional tests for ordr2.views.registration '''
from . import testappsetup, testapp # noqa: F401 from pyramid_mailer import get_mailer
from . import testappsetup, testapp, get_token_url # noqa: F401
def test_registration_form(testapp): # noqa: F811 def test_registration_form(testapp): # noqa: F811
result = testapp.get('/register') response = testapp.get('/register')
active = result.html.find('li', class_='active') active = response.html.find('li', class_='active')
assert active.a['href'] == '/register' assert active.a['href'] == '/register'
def test_registration_form_invalid(testapp): # noqa: F811
response = testapp.get('/register')
form = response.form
form['email'] = 'not an email address'
response = form.submit(name='create')
assert 'Invalid email address' in response
def test_registration_process(testapp): # noqa: F811
response = testapp.get('/register')
form = response.form
form['username'] = 'AmyMcDonald',
form['first_name'] = 'Amy',
form['last_name'] = 'Mc Donald',
form['email'] = 'amy.mcdonald@example.com',
form['password'] = 'Make Amy McDonald A Rich Girl Fund',
form['password-confirm'] = 'Make Amy McDonald A Rich Girl Fund',
response = form.submit(name='create')
assert response.location == 'http://localhost/register/verify'
response = response.follow()
assert 'Please follow the link in the email' in response
# click the email verification token
mailer = get_mailer(testapp.app.registry)
email = mailer.outbox[-1]
assert email.subject == '[ordr] Please verify your email address'
token_link = get_token_url(email, prefix='/register/')
response = testapp.get(token_link)
assert 'Registration Completed' in response

14
tests/models/account.py

@ -43,9 +43,11 @@ def test_user_principal(id_):
) )
def test_user_principals(name, principals): def test_user_principals(name, principals):
from ordr.models.account import User, Role from ordr.models.account import User, Role
user = User(id=1, role=Role[name]) user = User(id=1, role=Role[name])
expected = ['user:1'] expected = ['user:1']
expected.extend(principals) expected.extend(principals)
assert expected == user.principals assert expected == user.principals
@ -68,9 +70,11 @@ def test_user_is_active(name, expected):
def test_user_set_password(): def test_user_set_password():
from ordr.models.account import User from ordr.models.account import User
from ordr.security import password_context from ordr.security import password_context
password_context.update(schemes=['argon2']) password_context.update(schemes=['argon2'])
user = User() user = User()
assert user.password_hash is None assert user.password_hash is None
user.set_password('password') user.set_password('password')
assert user.password_hash.startswith('$argon2') assert user.password_hash.startswith('$argon2')
@ -85,17 +89,20 @@ def test_user_set_password():
def test_user_check_password(password, expected): def test_user_check_password(password, expected):
from ordr.models.account import User from ordr.models.account import User
from ordr.security import password_context from ordr.security import password_context
password_context.update(schemes=['argon2']) password_context.update(schemes=['argon2'])
hash = ('$argon2i$v=19$m=512,t=2,p=2$' hash = ('$argon2i$v=19$m=512,t=2,p=2$'
'YcyZMyak9D7nvFfKmVOq1Q$fnzNh58HWfvxHvRDGjhTqA' 'YcyZMyak9D7nvFfKmVOq1Q$fnzNh58HWfvxHvRDGjhTqA'
) )
user = User(password_hash=hash) user = User(password_hash=hash)
assert user.check_password(password) == expected assert user.check_password(password) == expected
def test_user_check_password_updates_old_sheme(): def test_user_check_password_updates_old_sheme():
from ordr.models.account import User from ordr.models.account import User
from ordr.security import password_context from ordr.security import password_context
password_context.update( password_context.update(
schemes=['argon2', 'bcrypt'], schemes=['argon2', 'bcrypt'],
default='argon2', default='argon2',
@ -103,6 +110,7 @@ def test_user_check_password_updates_old_sheme():
) )
old_hash = '$2b$12$6ljSfpLaXBeEVOeaP1scUe6IAa0cztM.UBbjc1PdrI4j0vwgoYgpi' old_hash = '$2b$12$6ljSfpLaXBeEVOeaP1scUe6IAa0cztM.UBbjc1PdrI4j0vwgoYgpi'
user = User(password_hash=old_hash) user = User(password_hash=old_hash)
assert user.check_password('password') assert user.check_password('password')
assert user.password_hash.startswith('$argon2') assert user.password_hash.startswith('$argon2')
assert user.check_password('password') assert user.check_password('password')
@ -116,9 +124,11 @@ def test_user__str__():
def test_user_issue_token(app_config): # noqa: F811 def test_user_issue_token(app_config): # noqa: F811
from ordr.models.account import User, Token, TokenSubject from ordr.models.account import User, Token, TokenSubject
request = DummyRequest() request = DummyRequest()
user = User() user = User()
token = user.issue_token(request, TokenSubject.REGISTRATION, {'foo': 1}) token = user.issue_token(request, TokenSubject.REGISTRATION, {'foo': 1})
assert isinstance(token, Token) assert isinstance(token, Token)
assert token.hash is not None assert token.hash is not None
assert token.subject == TokenSubject.REGISTRATION assert token.subject == TokenSubject.REGISTRATION
@ -128,10 +138,12 @@ def test_user_issue_token(app_config): # noqa: F811
def test_token_issue_token(app_config): # noqa: F811 def test_token_issue_token(app_config): # noqa: F811
from ordr.models.account import User, Token, TokenSubject from ordr.models.account import User, Token, TokenSubject
request = DummyRequest() request = DummyRequest()
user = User() user = User()
token = Token.issue(request, user, TokenSubject.REGISTRATION, {'foo': 1}) token = Token.issue(request, user, TokenSubject.REGISTRATION, {'foo': 1})
expected_expires = datetime.utcnow() + timedelta(minutes=5) expected_expires = datetime.utcnow() + timedelta(minutes=5)
assert isinstance(token, Token) assert isinstance(token, Token)
assert token.hash is not None assert token.hash is not None
assert token.subject == TokenSubject.REGISTRATION assert token.subject == TokenSubject.REGISTRATION
@ -148,12 +160,14 @@ def test_token_issue_token(app_config): # noqa: F811
) )
def test_token_issue_token_time_from_settings(app_config, subject, delta): def test_token_issue_token_time_from_settings(app_config, subject, delta):
from ordr.models.account import User, Token, TokenSubject from ordr.models.account import User, Token, TokenSubject
request = DummyRequest() request = DummyRequest()
request.registry.settings['token_expiry.reset_password'] = 10 request.registry.settings['token_expiry.reset_password'] = 10
user = User() user = User()
token_subject = TokenSubject[subject] token_subject = TokenSubject[subject]
token = Token.issue(request, user, token_subject, None) token = Token.issue(request, user, token_subject, None)
expected_expires = datetime.utcnow() + timedelta(minutes=delta) expected_expires = datetime.utcnow() + timedelta(minutes=delta)
assert token.expires.timestamp() == pytest.approx( assert token.expires.timestamp() == pytest.approx(
expected_expires.timestamp(), expected_expires.timestamp(),
abs=1 abs=1

91
tests/resources/account.py

@ -1,12 +1,30 @@
''' Tests for the account resources ''' ''' Tests for the account resources '''
from pyramid.testing import DummyRequest import pytest
from datetime import datetime, timedelta
from pyramid.testing import DummyRequest, DummyResource
from .. import app_config, dbsession, get_example_user # noqa: F401
def test_registration_token_acl():
from pyramid.security import Allow, Everyone, DENY_ALL
from ordr.resources.account import RegistrationTokenResource
parent = DummyResource(request='request')
resource = RegistrationTokenResource('name', parent)
assert resource.__acl__() == [(Allow, Everyone, 'view'), DENY_ALL]
def test_registration_acl(): def test_registration_acl():
from pyramid.security import Allow, Everyone, DENY_ALL from pyramid.security import Allow, Everyone, DENY_ALL
from ordr.resources.account import RegistrationResource from ordr.resources.account import RegistrationResource
resource = RegistrationResource('some request', 'a name', 'the parent')
parent = DummyResource(request='request')
resource = RegistrationResource('a name', parent)
assert resource.__acl__() == [(Allow, Everyone, 'view'), DENY_ALL] assert resource.__acl__() == [(Allow, Everyone, 'view'), DENY_ALL]
@ -14,10 +32,77 @@ def test_registration_get_registration_form():
from pyramid.security import Allow, Everyone, DENY_ALL from pyramid.security import Allow, Everyone, DENY_ALL
from ordr.resources.account import RegistrationResource from ordr.resources.account import RegistrationResource
import deform import deform
request = DummyRequest() request = DummyRequest()
resource = RegistrationResource(request, 'a name', 'the parent') parent = DummyResource(request=request)
resource = RegistrationResource('a name', parent)
form = resource.get_registration_form() form = resource.get_registration_form()
assert isinstance(form, deform.Form) assert isinstance(form, deform.Form)
assert len(form.buttons) == 2 assert len(form.buttons) == 2
assert form.buttons[0].title == 'Create Account' assert form.buttons[0].title == 'Create Account'
assert form.buttons[1].title == 'Cancel' assert form.buttons[1].title == 'Cancel'
def test_registration_getitem_found(dbsession): # noqa: F811
from ordr.models.account import Role, TokenSubject
from ordr.resources.account import (
RegistrationResource,
RegistrationTokenResource
)
request = DummyRequest(dbsession=dbsession)
user = get_example_user(Role.NEW)
token = user.issue_token(request, TokenSubject.REGISTRATION)
dbsession.add(user)
dbsession.flush()
parent = DummyResource(request=request)
resource = RegistrationResource('a name', parent)
result = resource[token.hash]
assert isinstance(result, RegistrationTokenResource)
assert result.__name__ == token.hash
assert result.__parent__ == resource
assert result.model == token
def test_registration_getitem_not_found(dbsession): # noqa: F811
from ordr.models.account import Role, TokenSubject
from ordr.resources.account import RegistrationResource
request = DummyRequest(dbsession=dbsession)
user = get_example_user(Role.NEW)
user.issue_token(request, TokenSubject.REGISTRATION)
dbsession.add(user)
dbsession.flush()
parent = DummyResource(request=request)
resource = RegistrationResource('a name', parent)
with pytest.raises(KeyError):
resource['unknown hash']
def test_registration_getitem_expired(dbsession): # noqa: F811
from ordr.models.account import Role, Token, TokenSubject
from ordr.resources.account import RegistrationResource
request = DummyRequest(dbsession=dbsession)
user = get_example_user(Role.NEW)
token = user.issue_token(request, TokenSubject.REGISTRATION)
token.expires = datetime.utcnow() - timedelta(weeks=1)
dbsession.add(user)
dbsession.flush()
parent = DummyResource(request=request)
resource = RegistrationResource('a name', parent)
with pytest.raises(KeyError):
resource[token.hash]
dbsession.flush()
assert dbsession.query(Token).count() == 0

44
tests/resources/base_child_resource.py

@ -7,23 +7,21 @@ from pyramid.testing import DummyRequest, DummyResource
def test_base_child_init(): def test_base_child_init():
from ordr.resources.helpers import BaseChildResource from ordr.resources.helpers import BaseChildResource
resource = BaseChildResource(
request='some request', parent = DummyResource(request='some request')
name='a name', resource = BaseChildResource(name='a name', parent=parent)
parent='the parent'
)
assert resource.__name__ == 'a name' assert resource.__name__ == 'a name'
assert resource.__parent__ == 'the parent' assert resource.__parent__ == parent
assert resource.request == 'some request' assert resource.request == 'some request'
def test_base_child_acl(): def test_base_child_acl():
from ordr.resources.helpers import BaseChildResource from ordr.resources.helpers import BaseChildResource
resource = BaseChildResource(
request='some request', parent = DummyResource(request='some request')
name='a name', resource = BaseChildResource(name='a name', parent=parent)
parent='the parent'
)
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
resource.__acl__() resource.__acl__()
@ -32,10 +30,12 @@ def test_base_child_prepare_form():
from ordr.resources.helpers import BaseChildResource from ordr.resources.helpers import BaseChildResource
from ordr.schemas.account import RegistrationSchema from ordr.schemas.account import RegistrationSchema
import deform import deform
parent = DummyResource()
request = DummyRequest() request = DummyRequest()
resource = BaseChildResource(request, 'a name', parent) parent = DummyResource(request=request)
resource = BaseChildResource('a name', parent)
form = resource._prepare_form(RegistrationSchema) form = resource._prepare_form(RegistrationSchema)
assert isinstance(form, deform.Form) assert isinstance(form, deform.Form)
assert form.action == 'http://example.com//' assert form.action == 'http://example.com//'
assert len(form.buttons) == 0 assert len(form.buttons) == 0
@ -44,10 +44,12 @@ def test_base_child_prepare_form():
def test_base_child_prepare_form_url(): def test_base_child_prepare_form_url():
from ordr.resources.helpers import BaseChildResource from ordr.resources.helpers import BaseChildResource
from ordr.schemas.account import RegistrationSchema from ordr.schemas.account import RegistrationSchema
parent = DummyResource()
request = DummyRequest() request = DummyRequest()
resource = BaseChildResource(request, 'a name', parent) parent = DummyResource(request=request)
resource = BaseChildResource('a name', parent)
form = resource._prepare_form(RegistrationSchema, action='/foo') form = resource._prepare_form(RegistrationSchema, action='/foo')
assert form.action == '/foo' assert form.action == '/foo'
@ -55,11 +57,13 @@ def test_base_child_prepare_form_settings():
from ordr.resources.helpers import BaseChildResource from ordr.resources.helpers import BaseChildResource
from ordr.schemas.account import RegistrationSchema from ordr.schemas.account import RegistrationSchema
import deform import deform
parent = DummyResource()
request = DummyRequest() request = DummyRequest()
resource = BaseChildResource(request, 'a name', parent) parent = DummyResource(request=request)
resource = BaseChildResource('a name', parent)
settings = {'buttons': ('ok', 'cancel')} settings = {'buttons': ('ok', 'cancel')}
form = resource._prepare_form(RegistrationSchema, **settings) form = resource._prepare_form(RegistrationSchema, **settings)
assert len(form.buttons) == 2 assert len(form.buttons) == 2
assert isinstance(form.buttons[0], deform.Button) assert isinstance(form.buttons[0], deform.Button)
assert isinstance(form.buttons[1], deform.Button) assert isinstance(form.buttons[1], deform.Button)
@ -68,15 +72,17 @@ def test_base_child_prepare_form_settings():
def test_base_child_prepare_form_prefill(): def test_base_child_prepare_form_prefill():
from ordr.resources.helpers import BaseChildResource from ordr.resources.helpers import BaseChildResource
from ordr.schemas.account import RegistrationSchema from ordr.schemas.account import RegistrationSchema
parent = DummyResource()
request = DummyRequest() request = DummyRequest()
resource = BaseChildResource(request, 'a name', parent) parent = DummyResource(request=request)
resource = BaseChildResource('a name', parent)
prefill = { prefill = {
'first_name': 'John', 'first_name': 'John',
'last_name': 'Doe', 'last_name': 'Doe',
'email': 'johndoe@example.com' 'email': 'johndoe@example.com'
} }
form = resource._prepare_form(RegistrationSchema, prefill=prefill) form = resource._prepare_form(RegistrationSchema, prefill=prefill)
assert form['first_name'].cstruct == 'John' assert form['first_name'].cstruct == 'John'
assert form['last_name'].cstruct == 'Doe' assert form['last_name'].cstruct == 'Doe'
assert form['email'].cstruct == 'johndoe@example.com' assert form['email'].cstruct == 'johndoe@example.com'

2
tests/resources/root.py

@ -21,8 +21,10 @@ def test_root_acl():
def test_root_getitem(): def test_root_getitem():
from ordr.resources import RootResource from ordr.resources import RootResource
from ordr.resources.account import RegistrationResource from ordr.resources.account import RegistrationResource
root = RootResource(None) root = RootResource(None)
child = root['register'] child = root['register']
assert isinstance(child, RegistrationResource) assert isinstance(child, RegistrationResource)
assert child.__name__ == 'register' assert child.__name__ == 'register'
assert child.__parent__ == root assert child.__parent__ == root

2
tests/schemas/__init__.py

@ -17,9 +17,9 @@ def test_csrf_schema_form_with_custom_url():
def test_csrf_schema_form_with_automatic_url(): def test_csrf_schema_form_with_automatic_url():
''' test for creation with custom url ''' ''' test for creation with custom url '''
from ordr.schemas import CSRFSchema from ordr.schemas import CSRFSchema
root = DummyResource() root = DummyResource()
context = DummyResource('Crunchy', root) context = DummyResource('Crunchy', root)
request = DummyRequest(context=context, view_name='Frog') request = DummyRequest(context=context, view_name='Frog')
form = CSRFSchema.as_form(request, buttons=['submit']) form = CSRFSchema.as_form(request, buttons=['submit'])

16
tests/security.py

@ -7,6 +7,7 @@ from . import app_config, dbsession, get_example_user # noqa: F401
def test_crypt_context_to_settings(): def test_crypt_context_to_settings():
from ordr.security import crypt_context_settings_to_string from ordr.security import crypt_context_settings_to_string
settings = { settings = {
'no_prefix': 'should not appear', 'no_prefix': 'should not appear',
'prefix.something': 'left unchanged', 'prefix.something': 'left unchanged',
@ -20,30 +21,37 @@ def test_crypt_context_to_settings():
'schemes = adjust,list', 'schemes = adjust,list',
'depreceated = do, not, adjust, this, list', 'depreceated = do, not, adjust, this, list',
} }
assert set(result.split('\n')) == expected_lines assert set(result.split('\n')) == expected_lines
def test_authentication_policy_authenticated_user_id_no_user(): def test_authentication_policy_authenticated_user_id_no_user():
from ordr.security import AuthenticationPolicy from ordr.security import AuthenticationPolicy
ap = AuthenticationPolicy('') ap = AuthenticationPolicy('')
request = DummyRequest(user=None) request = DummyRequest(user=None)
assert ap.authenticated_userid(request) is None assert ap.authenticated_userid(request) is None
def test_authentication_policy_authenticated_user_id_with_user(): def test_authentication_policy_authenticated_user_id_with_user():
from ordr.security import AuthenticationPolicy from ordr.security import AuthenticationPolicy
from ordr.models import User from ordr.models import User
ap = AuthenticationPolicy('') ap = AuthenticationPolicy('')
request = DummyRequest(user=User(id=123)) request = DummyRequest(user=User(id=123))
assert ap.authenticated_userid(request) == 123 assert ap.authenticated_userid(request) == 123
def test_authentication_policy_effective_principals_no_user(): def test_authentication_policy_effective_principals_no_user():
from ordr.security import AuthenticationPolicy from ordr.security import AuthenticationPolicy
from pyramid.security import Everyone from pyramid.security import Everyone
request = DummyRequest(user=None) request = DummyRequest(user=None)
ap = AuthenticationPolicy('') ap = AuthenticationPolicy('')
result = ap.effective_principals(request) result = ap.effective_principals(request)
assert result == [Everyone] assert result == [Everyone]
@ -51,6 +59,7 @@ def test_authentication_policy_effective_principals_with_user():
from ordr.security import AuthenticationPolicy from ordr.security import AuthenticationPolicy
from ordr.models import User, Role from ordr.models import User, Role
from pyramid.security import Authenticated, Everyone from pyramid.security import Authenticated, Everyone
ap = AuthenticationPolicy('') ap = AuthenticationPolicy('')
user = User(id=123, role=Role.PURCHASER) user = User(id=123, role=Role.PURCHASER)
request = DummyRequest(user=user) request = DummyRequest(user=user)
@ -62,6 +71,7 @@ def test_authentication_policy_effective_principals_with_user():
'role:purchaser', 'role:purchaser',
'role:user' 'role:user'
] ]
assert result == expected assert result == expected
@ -75,14 +85,17 @@ def test_authentication_policy_effective_principals_with_user():
def test_get_user_returns_user(dbsession, uauid, role_name): def test_get_user_returns_user(dbsession, uauid, role_name):
from ordr.security import get_user from ordr.security import get_user
from ordr.models import Role from ordr.models import Role
# this is a dirty hack, but DummyRequest does not accept setting an # this is a dirty hack, but DummyRequest does not accept setting an
# unauthenticated_userid # unauthenticated_userid
from pyramid.testing import DummyResource from pyramid.testing import DummyResource
request = DummyResource(unauthenticated_userid=uauid, dbsession=dbsession) request = DummyResource(unauthenticated_userid=uauid, dbsession=dbsession)
user_role = Role[role_name] user_role = Role[role_name]
user = get_example_user(user_role) user = get_example_user(user_role)
dbsession.add(user) dbsession.add(user)
dbsession.flush() dbsession.flush()
assert get_user(request) == user assert get_user(request) == user
@ -98,12 +111,15 @@ def test_get_user_returns_user(dbsession, uauid, role_name):
def test_get_user_returns_none(dbsession, uauid, role_name): def test_get_user_returns_none(dbsession, uauid, role_name):
from ordr.security import get_user from ordr.security import get_user
from ordr.models import Role from ordr.models import Role
# this is a dirty hack, but DummyRequest does not accept setting an # this is a dirty hack, but DummyRequest does not accept setting an
# unauthenticated_userid # unauthenticated_userid
from pyramid.testing import DummyResource from pyramid.testing import DummyResource
request = DummyResource(unauthenticated_userid=uauid, dbsession=dbsession) request = DummyResource(unauthenticated_userid=uauid, dbsession=dbsession)
user_role = Role[role_name] user_role = Role[role_name]
user = get_example_user(user_role) user = get_example_user(user_role)
dbsession.add(user) dbsession.add(user)
dbsession.flush() dbsession.flush()
assert get_user(request) is None assert get_user(request) is None

2
tests/views/errors.py

@ -3,7 +3,9 @@ from pyramid.testing import DummyRequest
def test_welcome(): def test_welcome():
from ordr.views.errors import notfound_view from ordr.views.errors import notfound_view
request = DummyRequest() request = DummyRequest()
result = notfound_view(None, request) result = notfound_view(None, request)
assert result == {} assert result == {}
assert request.response.status == '404 Not Found' assert request.response.status == '404 Not Found'

10
tests/views/pages.py

@ -14,8 +14,10 @@ from .. import app_config, dbsession, get_example_user # noqa: F401
) )
def test_welcome(user, location): def test_welcome(user, location):
from ordr.views.pages import welcome from ordr.views.pages import welcome
request = DummyRequest(user=user) request = DummyRequest(user=user)
result = welcome(None, request) result = welcome(None, request)
assert isinstance(result, HTTPFound) assert isinstance(result, HTTPFound)
assert result.location == f'http://example.com/{location}' assert result.location == f'http://example.com/{location}'
@ -37,11 +39,13 @@ def test_login():
) )
def test_check_login_ok(dbsession, role): def test_check_login_ok(dbsession, role):
from ordr.views.pages import check_login from ordr.views.pages import check_login
user = get_example_user(role) user = get_example_user(role)
dbsession.add(user) dbsession.add(user)
post_data = {'username': user.username, 'password': user.first_name} post_data = {'username': user.username, 'password': user.first_name}
request = DummyRequest(dbsession=dbsession, POST=post_data) request = DummyRequest(dbsession=dbsession, POST=post_data)
result = check_login(None, request) result = check_login(None, request)
assert isinstance(result, HTTPFound) assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com//' assert result.location == 'http://example.com//'
@ -51,11 +55,13 @@ def test_check_login_ok(dbsession, role):
) )
def test_check_login_not_activated(dbsession, role): def test_check_login_not_activated(dbsession, role):
from ordr.views.pages import check_login from ordr.views.pages import check_login
user = get_example_user(role) user = get_example_user(role)
dbsession.add(user) dbsession.add(user)
post_data = {'username': user.username, 'password': user.first_name} post_data = {'username': user.username, 'password': user.first_name}
request = DummyRequest(dbsession=dbsession, POST=post_data) request = DummyRequest(dbsession=dbsession, POST=post_data)
result = check_login(None, request) result = check_login(None, request)
assert result == {'loginerror': True} assert result == {'loginerror': True}
@ -70,17 +76,21 @@ def test_check_login_not_activated(dbsession, role):
) )
def test_check_login_invalid_credentials(dbsession, username, password): def test_check_login_invalid_credentials(dbsession, username, password):
from ordr.views.pages import check_login from ordr.views.pages import check_login
user = get_example_user(Role.USER) user = get_example_user(Role.USER)
dbsession.add(user) dbsession.add(user)
post_data = {'username': username, 'password': password} post_data = {'username': username, 'password': password}
request = DummyRequest(dbsession=dbsession, POST=post_data) request = DummyRequest(dbsession=dbsession, POST=post_data)
result = check_login(None, request) result = check_login(None, request)
assert result == {'loginerror': True} assert result == {'loginerror': True}
def test_logout(): def test_logout():
from ordr.views.pages import logout from ordr.views.pages import logout
request = DummyRequest() request = DummyRequest()
result = logout(None, request) result = logout(None, request)
assert isinstance(result, HTTPFound) assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com//' assert result.location == 'http://example.com//'

52
tests/views/registration.py

@ -2,9 +2,14 @@ import pytest
import deform import deform
from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import HTTPFound
from pyramid.testing import DummyRequest from pyramid.testing import DummyRequest, DummyResource
from .. import app_config, dbsession, get_post_request # noqa: F401 from .. import ( # noqa: F401
app_config,
dbsession,
get_example_user,
get_post_request
)
REGISTRATION_FORM_DATA = { REGISTRATION_FORM_DATA = {
@ -26,7 +31,8 @@ def test_registration_form():
from ordr.views.registration import registration_form from ordr.views.registration import registration_form
request = DummyRequest() request = DummyRequest()
context = RegistrationResource(request=request, name=None, parent=None) parent = DummyResource(request=request)
context = RegistrationResource(name=None, parent=parent)
result = registration_form(context, None) result = registration_form(context, None)
form = result['form'] form = result['form']
@ -41,12 +47,13 @@ def test_registration_form_valid(dbsession): # noqa: F811
data = REGISTRATION_FORM_DATA.copy() data = REGISTRATION_FORM_DATA.copy()
request = get_post_request(dbsession, data) request = get_post_request(dbsession, data)
context = RegistrationResource(request=request, name=None, parent=None) parent = DummyResource(request=request)
context = RegistrationResource(name=None, parent=parent)
result = registration_form_processing(context, request) result = registration_form_processing(context, request)
# return value of function call # return value of function call
assert isinstance(result, HTTPFound) assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com/verify' assert result.location == 'http://example.com//verify'
# user should be added to database # user should be added to database
user = dbsession.query(User).first() user = dbsession.query(User).first()
@ -69,20 +76,51 @@ def test_registration_form_valid(dbsession): # noqa: F811
def test_registration_form_invalid(dbsession): # noqa: F811 def test_registration_form_invalid(dbsession): # noqa: F811
from ordr.views.registration import registration_form_processing from ordr.views.registration import registration_form_processing
from ordr.resources.account import RegistrationResource from ordr.resources.account import RegistrationResource
data = REGISTRATION_FORM_DATA.copy() data = REGISTRATION_FORM_DATA.copy()
data['email'] = 'not an email address' data['email'] = 'not an email address'
request = get_post_request(dbsession, data) request = get_post_request(dbsession, data)
context = RegistrationResource(request=request, name=None, parent=None) parent = DummyResource(request=request)
context = RegistrationResource(name=None, parent=parent)
result = registration_form_processing(context, request) result = registration_form_processing(context, request)
assert result['form'].error is not None assert result['form'].error is not None
def test_registration_form_no_create_button(dbsession): # noqa: F811 def test_registration_form_no_create_button(dbsession): # noqa: F811
from ordr.views.registration import registration_form_processing from ordr.views.registration import registration_form_processing
from ordr.resources.account import RegistrationResource from ordr.resources.account import RegistrationResource
data = REGISTRATION_FORM_DATA.copy() data = REGISTRATION_FORM_DATA.copy()
data.pop('create') data.pop('create')
request = get_post_request(dbsession, data) request = get_post_request(dbsession, data)
context = RegistrationResource(request=request, name=None, parent=None) parent = DummyResource(request=request)
context = RegistrationResource(name=None, parent=parent)
result = registration_form_processing(context, request) result = registration_form_processing(context, request)
assert result.location == 'http://example.com//' assert result.location == 'http://example.com//'
def test_registration_verify():
from ordr.views.registration import verify
result = verify(None, None)
assert result == {}
def test_registration_completed(dbsession): # noqa: F811
from ordr.models.account import User, Role, Token, TokenSubject
from ordr.views.registration import completed
request = DummyRequest(dbsession=dbsession)
user = get_example_user(Role.UNVALIDATED)
user.issue_token(request, TokenSubject.REGISTRATION)
dbsession.add(user)
dbsession.flush()
token = user.tokens[0]
context = DummyResource(model=token)
result = completed(context, request)
assert result == {}
assert user.role == Role.NEW
assert dbsession.query(Token).count() == 0
assert dbsession.query(User).count() == 1