Skip to content
This repository has been archived by the owner on Sep 5, 2019. It is now read-only.

Commit

Permalink
Adds basic login/logout views
Browse files Browse the repository at this point in the history
  • Loading branch information
Denis Krienbühl committed Mar 17, 2015
1 parent e0f4c9d commit 8ebf90f
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 2 deletions.
8 changes: 8 additions & 0 deletions onegov/town/templates/form.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div metal:use-macro="layout.base" i18n:domain="onegov.town">
<tal:block metal:fill-slot="title">
${title}
</tal:block>
<tal:block metal:fill-slot="content">
<div metal:use-macro="layout.macros['form']" />
</tal:block>
</div>
16 changes: 16 additions & 0 deletions onegov/town/templates/macros.pt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,20 @@
</ul>
</metal:bottom_links>

<metal:search define-macro="form">
<form tal:attributes="action form.action|'.'" method="POST">
<metal:fields use-macro="layout.macros['fields']" tal:define="fields form._fields.values()" />
<input type="submit" value="Submit" class="button">
</form>
</metal:search>

<metal:fields define-macro="fields">
<tal:block repeat="field fields">
<label tal:attributes="class field.errors and 'error'">
${field.label} ${field()}
</label>
<small class="error" tal:repeat="error field.errors">${error}</small>
</tal:block>
</metal:fields>

</body>
8 changes: 7 additions & 1 deletion onegov/town/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from morepath import setup
from onegov.core.orm import Base, SessionManager
from onegov.town.initial_content import add_initial_content
from onegov.user import UserCollection
from testing.postgresql import Postgresql
from uuid import uuid4

Expand Down Expand Up @@ -45,11 +46,16 @@ def town_app(postgres_server_url):
app.namespace = 'test_' + uuid4().hex
app.configure_application(
dsn=postgres_server_url,
filestorage='fs.memoryfs.MemoryFS'
filestorage='fs.memoryfs.MemoryFS',
identity_secure=False
)
app.set_application_id(app.namespace + '/' + 'test')

add_initial_content(app.session(), 'Govikon')

users = UserCollection(app.session())
users.add('admin@example.org', 'hunter2', 'admin')

transaction.commit()

yield app
40 changes: 40 additions & 0 deletions onegov/town/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from mock import patch
from morepath import setup
from webtest import TestApp as Client


@patch('morepath.directive.register_view')
Expand All @@ -19,3 +20,42 @@ def test_view_permissions(register_view):
if module.startswith('onegov.town') and permission is None:
assert permission is not None, (
'view {}.{} has no permission'.format(module, view.__name__))


def test_view_login(town_app):

client = Client(town_app)

assert client.get('/logout', expect_errors=True).status_code == 403

response = client.get('/login')
assert response.status_code == 200
assert "Email Address" in response.text
assert "Password" in response.text

assert client.cookies == {}
assert client.get('/logout', expect_errors=True).status_code == 403

response.form.set('email', 'admin@example.org')
response = response.form.submit()
assert response.status_code == 200
assert "Email Address" in response.text
assert "Password" in response.text
assert "This field is required." in response.text

assert client.cookies == {}
assert client.get('/logout', expect_errors=True).status_code == 403

response.form.set('email', 'admin@example.org')
response.form.set('password', 'hunter2')
response = response.form.submit()
assert response.status_code == 302

assert 'userid' in client.cookies
assert 'role' in client.cookies
assert 'application_id' in client.cookies

assert client.get('/logout').status_code == 302
assert client.cookies == {}

assert client.get('/logout', expect_errors=True).status_code == 403
Empty file added onegov/town/views/__init__.py
Empty file.
3 changes: 2 additions & 1 deletion onegov/town/view.py → onegov/town/views/homepage.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" Contains the view handling code for onegov.town. """
""" The onegov town homepage. """

from onegov.core.security import Public
from onegov.town import _
Expand All @@ -9,6 +9,7 @@

@TownApp.html(model=Town, template='town.pt', permission=Public)
def view_town(self, request):
""" Renders the town's homepage. """
return {
'layout': DefaultLayout(self, request),
'title': _(u'Welcome to ${town}', mapping={'town': self.name})
Expand Down
84 changes: 84 additions & 0 deletions onegov/town/views/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
""" The login/logout views. """

import morepath

from onegov.core.security import Public, Private
from onegov.form import Form
from wtforms import StringField, PasswordField, validators
from onegov.town import _
from onegov.town.app import TownApp
from onegov.town.layout import DefaultLayout
from onegov.town.model import Town
from onegov.user import UserCollection


class LoginForm(Form):
""" Defines the login form for onegov town. """

email = StringField(_(u'Email Address'), [validators.InputRequired()])
password = PasswordField(_(u'Password'), [validators.InputRequired()])

def get_identity(self, request):
""" Returns the identity if the username and password match. If they
don't match, None is returned.
"""
users = UserCollection(request.app.session())
user = users.by_username_and_password(
self.email.data, self.password.data
)

if user is None:
return None
else:
return morepath.Identity(
userid=user.username,
role=user.role,
application_id=request.app.application_id
)


@TownApp.html(
model=Town, name='login', template='form.pt', permission=Public,
request_method='GET')
def view_login(self, request):
return handle_login(self, request)


@TownApp.html(
model=Town, name='login', template='form.pt', permission=Public,
request_method='POST')
def view_post_login(self, request):
return handle_login(self, request)


def handle_login(self, request):
""" Handles the GET and POST login requests. """

form = LoginForm(request.POST)
form.action = request.link(self, name='login')

if form.submitted(request):
identity = form.get_identity(request)

if identity is not None:
response = morepath.redirect(request.link(self))
morepath.remember_identity(response, request, identity)
return response

return {
'layout': DefaultLayout(self, request),
'title': _(u'Login to ${town}', mapping={'town': self.name}),
'form': form
}


@TownApp.html(
model=Town, name='logout', permission=Private,
request_method='GET')
def view_logout(self, request):
""" Handles the logout requests. """

response = morepath.redirect(request.link(self))
morepath.forget_identity(response, request)
return response
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies = git+git://github.com/seantis/more.itsdangerous.git#egg=more.itsda
git+git://github.com/OneGov/onegov.server.git#egg=onegov.server
git+git://github.com/OneGov/onegov.core.git#egg=onegov.core
git+git://github.com/OneGov/onegov.page.git#egg=onegov.page
git+git://github.com/OneGov/onegov.form.git#egg=onegov.form
git+git://github.com/OneGov/onegov.user.git#egg=onegov.user
git+git://github.com/OneGov/onegov.foundation.git#egg=onegov.foundation
git+git://github.com/morepath/morepath.git#egg=morepath
Expand Down

0 comments on commit 8ebf90f

Please sign in to comment.