Browse Source

added sessions to app

this includes some deferred helper functions and the CSRFSchema
master
Holger Frey 7 years ago
parent
commit
ff7aca94ce
  1. 1
      ordr2/__init__.py
  2. 54
      ordr2/schemas/__init__.py
  3. 64
      ordr2/schemas/helpers.py
  4. 1
      ordr2/session.py
  5. 31
      tests/schemas/__init__.py
  6. 158
      tests/schemas/helpers.py

1
ordr2/__init__.py

@ -16,6 +16,7 @@ def main(global_config, **settings): @@ -16,6 +16,7 @@ def main(global_config, **settings):
config.include('.resources')
config.include('.security')
config.include('.session')
config.include('.schemas')
config.include('.views')
config.scan()

54
ordr2/schemas/__init__.py

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
''' 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, **kwargs):
''' returns the schema as a form
:param request:
the current request
:type request:
pyramid.request.Request
:param kwargs:
additional parameters for the form rendering.
url is not set, the current context and view name will be used to
constuct a url for the form
'''
url = kwargs.pop('url', None)
if not url:
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('ordr2.schemas')``.
'''
# Make Deform widgets aware of our widget template paths
configure_zpt_renderer(['ordr2:templates/deform'])

64
ordr2/schemas/helpers.py

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
''' helper functions for schemas '''
import colander
import deform
from pyramid.csrf import get_csrf_token, check_csrf_token
from ordr2.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

1
ordr2/session.py

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
''' Session configuration '''
import binascii
from pyramid_nacl_session import EncryptedCookieSessionFactory

31
tests/schemas/__init__.py

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
''' Test package for ordr2.schemas '''
from pyramid.testing import DummyRequest, DummyResource
from .. import app_config
def test_csrf_schema_form_with_custom_url(app_config):
''' test for creation with custom url '''
from ordr2.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(app_config):
''' test for creation with custom url '''
from deform.form import Button
from ordr2.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'

158
tests/schemas/helpers.py

@ -0,0 +1,158 @@ @@ -0,0 +1,158 @@
''' Tests for ordr2.schemas.helpers '''
import pytest
from pyramid.testing import DummyRequest, DummyResource
from .. import app_config, dbsession, get_user
def test_deferred_csrf_default(app_config):
''' deferred_csrf_default should return a csrf token '''
from ordr2.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(app_config):
''' test deferred_csrf_validator with valid csrf token '''
from ordr2.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(app_config, post):
''' test deferred_csrf_validator with invalid or missing csrf token '''
from ordr2.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):
''' unknown usernames should not raise an invalidation error '''
from ordr2.schemas.helpers import deferred_unique_username_validator
request = DummyRequest(dbsession=dbsession)
user = get_user('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):
''' known username should raise an invalidation error '''
from ordr2.schemas.helpers import deferred_unique_username_validator
from colander import Invalid
request = DummyRequest(dbsession=dbsession)
user = get_user('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):
''' unknown emails should not raise an invalidation error '''
from ordr2.schemas.helpers import deferred_unique_email_validator
context = DummyResource(model=None)
request = DummyRequest(dbsession=dbsession, context=context)
user = get_user('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_belongs_to_same_user(dbsession):
''' 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 ordr2.schemas.helpers import deferred_unique_email_validator
user = get_user('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('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 ordr2.schemas.helpers import deferred_unique_email_validator
from colander import Invalid
context = DummyResource(model=None)
request = DummyRequest(dbsession=dbsession, context=context)
user = get_user('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(app_config):
''' correct password should not raise invalidation error '''
from ordr2.schemas.helpers import deferred_password_validator
from pyramid.csrf import get_csrf_token
user = get_user('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(app_config):
''' incorrect password should raise invalidation error '''
from ordr2.schemas.helpers import deferred_password_validator
from colander import Invalid
user = get_user('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