Holger Frey
7 years ago
18 changed files with 511 additions and 29 deletions
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
''' Resources (sub) package, used to connect URLs to views ''' |
||||
|
||||
from pyramid.security import Allow, Everyone, DENY_ALL |
||||
|
||||
|
||||
class BaseChildResource: |
||||
|
||||
def __init__(self, request, name, parent): |
||||
''' Create a child resource |
||||
|
||||
:param pyramid.request.Request request: the current request object |
||||
:param str name: the name of the resource |
||||
:param parent: the parent resouce |
||||
''' |
||||
self.request = request |
||||
self.__name__ = name |
||||
self.__parent__ = parent |
||||
|
||||
def __acl__(self): |
||||
''' access controll list for the resource ''' |
||||
raise NotImplementedError() |
||||
|
||||
def _prepare_form(self, schema, prefill=None, **settings): |
||||
''' prepares a deform form for the resource''' |
||||
form = schema.as_form(self.request, **settings) |
||||
if prefill is not None: |
||||
form.set_appstruct(prefill) |
||||
return form |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
''' Schemas (sub) package, for form rendering and validation ''' |
||||
|
||||
import colander |
||||
import deform |
||||
|
||||
from deform.renderer import configure_zpt_renderer |
||||
|
||||
from .helpers import ( |
||||
deferred_csrf_default, |
||||
deferred_csrf_validator |
||||
) |
||||
|
||||
|
||||
# Base Schema |
||||
|
||||
class CSRFSchema(colander.Schema): |
||||
''' base class for schemas with csrf validation ''' |
||||
|
||||
csrf_token = colander.SchemaNode( |
||||
colander.String(), |
||||
default=deferred_csrf_default, |
||||
validator=deferred_csrf_validator, |
||||
widget=deform.widget.HiddenWidget(), |
||||
) |
||||
|
||||
@classmethod |
||||
def as_form(cls, request, url=None, **kwargs): |
||||
''' returns the schema as a form |
||||
|
||||
:param pyramid.request.Request request: the current request |
||||
:param str url: |
||||
form action url, |
||||
url is not set, the current context and view name will be used to |
||||
constuct a url for the form |
||||
:param kwargs: |
||||
additional parameters for the form rendering. |
||||
''' |
||||
if url is None: |
||||
url = request.resource_url(request.context, request.view_name) |
||||
schema = cls().bind(request=request) |
||||
form = deform.Form(schema, action=url, **kwargs) |
||||
return form |
||||
|
||||
|
||||
def includeme(config): |
||||
''' |
||||
Initialize the form schemas |
||||
|
||||
Activate this setup using ``config.include('ordr.schemas')``. |
||||
|
||||
''' |
||||
# Make Deform widgets aware of our widget template paths |
||||
configure_zpt_renderer(['ordr:templates/deform']) |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
import colander |
||||
import deform |
||||
|
||||
|
||||
from . import CSRFSchema |
||||
from .helpers import ( |
||||
deferred_unique_email_validator, |
||||
deferred_unique_username_validator, |
||||
) |
||||
|
||||
|
||||
# schema for user registration |
||||
|
||||
class RegistrationSchema(CSRFSchema): |
||||
''' new user registration ''' |
||||
|
||||
username = colander.SchemaNode( |
||||
colander.String(), |
||||
widget=deform.widget.TextInputWidget(readonly=True), |
||||
description='automagically generated for you', |
||||
validator=deferred_unique_username_validator, |
||||
) |
||||
first_name = colander.SchemaNode( |
||||
colander.String() |
||||
) |
||||
last_name = colander.SchemaNode( |
||||
colander.String() |
||||
) |
||||
email = colander.SchemaNode( |
||||
colander.String(), |
||||
validator=deferred_unique_email_validator |
||||
) |
||||
password = colander.SchemaNode( |
||||
colander.String(), |
||||
widget=deform.widget.CheckedPasswordWidget() |
||||
) |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
''' helper functions for schemas ''' |
||||
|
||||
import colander |
||||
|
||||
from pyramid.csrf import get_csrf_token, check_csrf_token |
||||
|
||||
from ordr.models import User |
||||
|
||||
|
||||
@colander.deferred |
||||
def deferred_csrf_default(node, kw): |
||||
''' sets the current csrf token ''' |
||||
request = kw.get('request') |
||||
return get_csrf_token(request) |
||||
|
||||
|
||||
@colander.deferred |
||||
def deferred_csrf_validator(node, kw): |
||||
''' validates a submitted csrf token ''' |
||||
def validate_csrf(node, value): |
||||
request = kw.get('request') |
||||
if not check_csrf_token(request, raises=False): |
||||
raise colander.Invalid(node, 'Bad CSRF token') |
||||
return validate_csrf |
||||
|
||||
|
||||
@colander.deferred |
||||
def deferred_unique_username_validator(node, kw): |
||||
''' checks if an username is not registered already ''' |
||||
|
||||
def validate_unique_username(node, value): |
||||
request = kw.get('request') |
||||
user = request.dbsession.query(User).filter_by(username=value).first() |
||||
if user is not None: |
||||
raise colander.Invalid(node, 'User name already registered') |
||||
return validate_unique_username |
||||
|
||||
|
||||
@colander.deferred |
||||
def deferred_unique_email_validator(node, kw): |
||||
''' checks if an email is not registered already ''' |
||||
email_validator = colander.Email() |
||||
|
||||
def validate_unique_email(node, value): |
||||
email_validator(node, value) # raises exception on invalid address |
||||
request = kw.get('request') |
||||
user = request.dbsession.query(User).filter_by(email=value).first() |
||||
if user not in (None, request.context.model): |
||||
# allow existing email addresses if |
||||
# it belongs to the user that is currently edited |
||||
raise colander.Invalid(node, 'Email address in use') |
||||
return validate_unique_email |
||||
|
||||
|
||||
@colander.deferred |
||||
def deferred_password_validator(node, kw): |
||||
''' checks password confirmation for settings ''' |
||||
|
||||
def validate_password_confirmation(node, value): |
||||
request = kw.get('request') |
||||
if request.user is None or not request.user.check_password(value): |
||||
raise colander.Invalid(node, 'Wrong password') |
||||
return validate_password_confirmation |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
{% extends "ordr:templates/layout.jinja2" %} |
||||
|
||||
{% block content %} |
||||
{{ context.get_registration_form().render()|safe }} |
||||
{% endblock content %} |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
# from pyramid.httpexceptions import HTTPFound |
||||
from pyramid.view import view_config |
||||
|
||||
# from ordr.models import User |
||||
|
||||
|
||||
@view_config( |
||||
context='ordr.resources.account.RegistrationResource', |
||||
permission='view', |
||||
request_method='GET', |
||||
renderer='ordr:templates/account/registration_form.jinja2' |
||||
) |
||||
def registration_form(context, request): |
||||
return {} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
''' functional tests for ordr2.views.registration ''' |
||||
|
||||
from . import testappsetup, testapp # noqa: F401 |
||||
|
||||
|
||||
def test_registration_form(testapp): # noqa: F811 |
||||
result = testapp.get('/register') |
||||
active = result.html.find('li', class_='active') |
||||
assert active.a['href'] == '/register' |
@ -1,20 +1,22 @@
@@ -1,20 +1,22 @@
|
||||
''' Tests for the account resources ''' |
||||
|
||||
|
||||
def test_registration_init(): |
||||
from ordr.resources.account import RegistrationResource |
||||
resource = RegistrationResource( |
||||
request='some request', |
||||
name='a name', |
||||
parent='the parent' |
||||
) |
||||
assert resource.__name__ == 'a name' |
||||
assert resource.__parent__ == 'the parent' |
||||
assert resource.request == 'some request' |
||||
|
||||
from pyramid.testing import DummyRequest |
||||
|
||||
|
||||
def test_registration_acl(): |
||||
from pyramid.security import Allow, Everyone, DENY_ALL |
||||
from ordr.resources.account import RegistrationResource |
||||
resource = RegistrationResource('some request', 'a name', 'the parent') |
||||
assert resource.__acl__() == [(Allow, Everyone, 'view'), DENY_ALL] |
||||
|
||||
def test_registration_get_registration_form(): |
||||
from pyramid.security import Allow, Everyone, DENY_ALL |
||||
from ordr.resources.account import RegistrationResource |
||||
import deform |
||||
request = DummyRequest() |
||||
resource = RegistrationResource(request, 'a name', 'the parent') |
||||
form = resource.get_registration_form() |
||||
assert isinstance(form, deform.Form) |
||||
assert len(form.buttons) == 2 |
||||
assert form.buttons[0].title == 'Create account' |
||||
assert form.buttons[1].title == 'Cancel' |
||||
|
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
''' Tests for the root resource ''' |
||||
|
||||
import pytest |
||||
|
||||
from pyramid.testing import DummyRequest, DummyResource |
||||
|
||||
|
||||
def test_base_child_init(): |
||||
from ordr.resources.helpers import BaseChildResource |
||||
resource = BaseChildResource( |
||||
request='some request', |
||||
name='a name', |
||||
parent='the parent' |
||||
) |
||||
assert resource.__name__ == 'a name' |
||||
assert resource.__parent__ == 'the parent' |
||||
assert resource.request == 'some request' |
||||
|
||||
|
||||
def test_base_child_acl(): |
||||
from ordr.resources.helpers import BaseChildResource |
||||
resource = BaseChildResource( |
||||
request='some request', |
||||
name='a name', |
||||
parent='the parent' |
||||
) |
||||
with pytest.raises(NotImplementedError): |
||||
resource.__acl__() |
||||
|
||||
|
||||
def test_base_child_prepare_form(): |
||||
from ordr.resources.helpers import BaseChildResource |
||||
from ordr.schemas.account import RegistrationSchema |
||||
import deform |
||||
parent = DummyResource() |
||||
request = DummyRequest() |
||||
resource = BaseChildResource(request, 'a name', parent) |
||||
form = resource._prepare_form(RegistrationSchema) |
||||
assert isinstance(form, deform.Form) |
||||
assert form.action == 'http://example.com//' |
||||
assert len(form.buttons) == 0 |
||||
|
||||
|
||||
def test_base_child_prepare_form_url(): |
||||
from ordr.resources.helpers import BaseChildResource |
||||
from ordr.schemas.account import RegistrationSchema |
||||
parent = DummyResource() |
||||
request = DummyRequest() |
||||
resource = BaseChildResource(request, 'a name', parent) |
||||
form = resource._prepare_form(RegistrationSchema, url='/foo') |
||||
assert form.action == '/foo' |
||||
|
||||
|
||||
def test_base_child_prepare_form_settings(): |
||||
from ordr.resources.helpers import BaseChildResource |
||||
from ordr.schemas.account import RegistrationSchema |
||||
import deform |
||||
parent = DummyResource() |
||||
request = DummyRequest() |
||||
resource = BaseChildResource(request, 'a name', parent) |
||||
settings = {'buttons': ('ok', 'cancel')} |
||||
form = resource._prepare_form(RegistrationSchema, **settings) |
||||
assert len(form.buttons) == 2 |
||||
assert isinstance(form.buttons[0], deform.Button) |
||||
assert isinstance(form.buttons[1], deform.Button) |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
''' Test package for ordr.schemas ''' |
||||
|
||||
from pyramid.testing import DummyRequest, DummyResource |
||||
|
||||
|
||||
def test_csrf_schema_form_with_custom_url(): |
||||
''' test for creation with custom url ''' |
||||
from ordr.schemas import CSRFSchema |
||||
|
||||
request = DummyRequest() |
||||
form = CSRFSchema.as_form(request, url='/Nudge/Nudge') |
||||
|
||||
assert form.action == '/Nudge/Nudge' |
||||
assert form.buttons == [] |
||||
|
||||
|
||||
def test_csrf_schema_form_with_automatic_url(): |
||||
''' test for creation with custom url ''' |
||||
from ordr.schemas import CSRFSchema |
||||
root = DummyResource() |
||||
context = DummyResource('Crunchy', root) |
||||
|
||||
request = DummyRequest(context=context, view_name='Frog') |
||||
form = CSRFSchema.as_form(request, buttons=['submit']) |
||||
|
||||
assert 'http://example.com/Crunchy/Frog' == form.action |
||||
assert len(form.buttons) == 1 |
||||
assert form.buttons[0].type == 'submit' |
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
''' Tests for ordr.schemas.helpers ''' |
||||
|
||||
import pytest |
||||
|
||||
from pyramid.testing import DummyRequest, DummyResource |
||||
|
||||
from .. import app_config, dbsession, get_example_user # noqa: F401 |
||||
|
||||
|
||||
def test_deferred_csrf_default(): |
||||
''' deferred_csrf_default should return a csrf token ''' |
||||
from ordr.schemas.helpers import deferred_csrf_default |
||||
from pyramid.csrf import get_csrf_token |
||||
|
||||
request = DummyRequest() |
||||
token = deferred_csrf_default(None, {'request': request}) |
||||
|
||||
assert token == get_csrf_token(request) |
||||
|
||||
|
||||
def test_deferred_csrf_validator_ok(): |
||||
''' test deferred_csrf_validator with valid csrf token ''' |
||||
from ordr.schemas.helpers import deferred_csrf_validator |
||||
from pyramid.csrf import get_csrf_token |
||||
|
||||
request = DummyRequest() |
||||
token = get_csrf_token(request) |
||||
request.POST = {'csrf_token': token} |
||||
validation_func = deferred_csrf_validator(None, {'request': request}) |
||||
|
||||
assert validation_func(None, None) is None |
||||
|
||||
|
||||
@pytest.mark.parametrize('post', [{}, {'csrf_token': 'Albatross!'}]) |
||||
def test_deferred_csrf_validator_fails_on_no_csrf_token(post): |
||||
''' test deferred_csrf_validator with invalid or missing csrf token ''' |
||||
from ordr.schemas.helpers import deferred_csrf_validator |
||||
from colander import Invalid |
||||
|
||||
request = DummyRequest() |
||||
request.POST = post |
||||
validation_func = deferred_csrf_validator(None, {'request': request}) |
||||
|
||||
with pytest.raises(Invalid): |
||||
assert validation_func(None, None) is None |
||||
|
||||
|
||||
def test_deferred_unique_username_validator_ok(dbsession): # noqa: F811 |
||||
''' unknown usernames should not raise an invalidation error ''' |
||||
from ordr.schemas.helpers import deferred_unique_username_validator |
||||
from ordr.models.account import Role |
||||
|
||||
request = DummyRequest(dbsession=dbsession) |
||||
user = get_example_user(Role.USER) |
||||
dbsession.add(user) |
||||
validation_func = deferred_unique_username_validator( |
||||
None, |
||||
{'request': request} |
||||
) |
||||
|
||||
assert validation_func(None, 'AnneElk') is None |
||||
|
||||
|
||||
def test_deferred_unique_username_validator_fails(dbsession): # noqa: F811 |
||||
''' known username should raise an invalidation error ''' |
||||
from ordr.schemas.helpers import deferred_unique_username_validator |
||||
from ordr.models.account import Role |
||||
from colander import Invalid |
||||
|
||||
request = DummyRequest(dbsession=dbsession) |
||||
user = get_example_user(Role.USER) |
||||
dbsession.add(user) |
||||
validation_func = deferred_unique_username_validator( |
||||
None, |
||||
{'request': request} |
||||
) |
||||
|
||||
with pytest.raises(Invalid): |
||||
assert validation_func(None, 'TerryGilliam') is None |
||||
|
||||
|
||||
def test_deferred_unique_email_validator_ok(dbsession): # noqa: F811 |
||||
''' unknown emails should not raise an invalidation error ''' |
||||
from ordr.schemas.helpers import deferred_unique_email_validator |
||||
from ordr.models.account import Role |
||||
|
||||
context = DummyResource(model=None) |
||||
request = DummyRequest(dbsession=dbsession, context=context) |
||||
user = get_example_user(Role.USER) |
||||
dbsession.add(user) |
||||
validation_func = deferred_unique_email_validator( |
||||
None, |
||||
{'request': request} |
||||
) |
||||
|
||||
assert validation_func(None, 'elk@example.com') is None |
||||
|
||||
|
||||
def test_deferred_unique_email_validator_ok_same_user(dbsession): # noqa: F811 |
||||
''' known emails of a user might not raise an error |
||||
|
||||
if a user is edited and the mail address is not change, no invalidation |
||||
error should be raised |
||||
''' |
||||
from ordr.schemas.helpers import deferred_unique_email_validator |
||||
from ordr.models.account import Role |
||||
|
||||
user = get_example_user(Role.USER) |
||||
context = DummyResource(model=user) |
||||
request = DummyRequest(dbsession=dbsession, context=context) |
||||
dbsession.add(user) |
||||
validation_func = deferred_unique_email_validator( |
||||
None, |
||||
{'request': request} |
||||
) |
||||
|
||||
assert validation_func(None, user.email) is None |
||||
|
||||
|
||||
@pytest.mark.parametrize( # noqa: F811 |
||||
'email', ['', 'gilliam@example.com', 'malformed'] |
||||
) |
||||
def test_deferred_unique_email_validator_fails(dbsession, email): |
||||
''' known, empty or malformed emails should raise an invalidation error ''' |
||||
from ordr.schemas.helpers import deferred_unique_email_validator |
||||
from ordr.models.account import Role |
||||
from colander import Invalid |
||||
|
||||
context = DummyResource(model=None) |
||||
request = DummyRequest(dbsession=dbsession, context=context) |
||||
user = get_example_user(Role.USER) |
||||
dbsession.add(user) |
||||
validation_func = deferred_unique_email_validator( |
||||
None, |
||||
{'request': request} |
||||
) |
||||
|
||||
with pytest.raises(Invalid): |
||||
assert validation_func(None, email) is None |
||||
|
||||
|
||||
def test_deferred_password_validator_ok(): |
||||
''' correct password should not raise invalidation error ''' |
||||
from ordr.schemas.helpers import deferred_password_validator |
||||
from ordr.models.account import Role |
||||
|
||||
user = get_example_user(Role.USER) |
||||
request = DummyRequest(user=user) |
||||
validation_func = deferred_password_validator(None, {'request': request}) |
||||
|
||||
assert validation_func(None, 'Terry') is None |
||||
|
||||
|
||||
def test_deferred_password_validator_fails(): |
||||
''' incorrect password should raise invalidation error ''' |
||||
from ordr.schemas.helpers import deferred_password_validator |
||||
from ordr.models.account import Role |
||||
from colander import Invalid |
||||
|
||||
user = get_example_user(Role.USER) |
||||
request = DummyRequest(user=user) |
||||
validation_func = deferred_password_validator(None, {'request': request}) |
||||
|
||||
with pytest.raises(Invalid): |
||||
assert validation_func(None, 'Wrong Password') is None |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import pytest |
||||
|
||||
from pyramid.httpexceptions import HTTPFound |
||||
from pyramid.testing import DummyRequest |
||||
|
||||
from .. import app_config, dbsession # noqa: F401 |
||||
|
||||
|
||||
def test_faq(): |
||||
from ordr.views.registration import registration_form |
||||
result = registration_form(None, None) |
||||
assert result == {} |
Reference in new issue