Browse Source

added login, logout

rework
Holger Frey 7 years ago
parent
commit
c8c0c4678a
  1. 4
      ordr/scripts/initializedb.py
  2. 10
      ordr/templates/pages/login.jinja2
  3. 50
      ordr/views/pages.py
  4. 5
      tests/__init__.py
  5. 28
      tests/_functional/__init__.py
  6. 2
      tests/_functional/errors.py
  7. 2
      tests/_functional/layout.py
  8. 39
      tests/_functional/pages.py
  9. 60
      tests/views/pages.py

4
ordr/scripts/initializedb.py

@ -40,5 +40,5 @@ def main(argv=sys.argv): @@ -40,5 +40,5 @@ def main(argv=sys.argv):
with transaction.manager:
dbsession = get_tm_session(session_factory, transaction.manager)
dbsession.add()
# dbsession.add()

10
ordr/templates/pages/welcome.jinja2 → ordr/templates/pages/login.jinja2

@ -14,10 +14,16 @@ @@ -14,10 +14,16 @@
<h4 class="mb-4">Login</h4>
<form action="/login" method="POST">
<div class="form-group">
<input type="text" class="form-control" id="input-username" placeholder="Username" name="username" autofocus="autofocus">
<input type="hidden" name="csrf_token" value="{{get_csrf_token()}}">
<input type="text" class="form-control {% if loginerror %}is-invalid{% endif %}" id="input-username" placeholder="Username" name="username" autofocus="autofocus">
</div>
<div class="form-group">
<input type="text" class="form-control" id="input-password" placeholder="Password" name="password">
<input type="text" class="form-control {% if loginerror %}is-invalid{% endif %}" id="input-password" placeholder="Password" name="password">
{% if loginerror %}
<div class="invalid-feedback">
Username and password do not match, or account is not activated.
</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary">Login</button>
<small class="float-right mt-2"><a href="/forgot">Forgot your password?</a></small>

50
ordr/views/pages.py

@ -1,11 +1,13 @@ @@ -1,11 +1,13 @@
from pyramid.httpexceptions import HTTPFound
from pyramid.security import remember, forget
from pyramid.view import view_config
from ordr.models import User
@view_config(
context='ordr.resources.RootResource',
permission='view',
renderer='ordr:templates/pages/welcome.jinja2',
)
def welcome(context, request):
next = 'orders' if request.user else 'login'
@ -13,21 +15,55 @@ def welcome(context, request): @@ -13,21 +15,55 @@ def welcome(context, request):
return HTTPFound(redirect_to)
@view_config(
context='ordr.resources.RootResource',
name='faq',
permission='view',
renderer='ordr:templates/pages/faq.jinja2'
)
def faq(context, request):
return {}
@view_config(
context='ordr.resources.RootResource',
name='login',
request_method='GET',
permission='view',
renderer='ordr:templates/pages/welcome.jinja2',
renderer='ordr:templates/pages/login.jinja2',
)
def login(context, request):
return {}
return { 'loginerror': False }
@view_config(
context='ordr.resources.RootResource',
name='faq',
name='login',
request_method='POST',
permission='view',
renderer='ordr:templates/pages/faq.jinja2'
renderer='ordr:templates/pages/login.jinja2',
)
def faq(context, request):
return {}
def check_login(context, request):
username = request.POST.get('username')
password = request.POST.get('password')
user = (
request.dbsession
.query(User)
.filter_by(username=username)
.first()
)
if user and user.is_active and user.check_password(password):
headers = remember(request, user.id)
return HTTPFound(request.resource_url(request.root), headers=headers)
return { 'loginerror': True }
@view_config(
context='ordr.resources.RootResource',
name='logout',
permission='view'
)
def logout(context, request):
''' log out of an user '''
headers = forget(request)
return HTTPFound(request.resource_url(request.root), headers=headers)

5
tests/__init__.py

@ -6,6 +6,11 @@ from pyramid import testing @@ -6,6 +6,11 @@ from pyramid import testing
APP_SETTINGS = {
'sqlalchemy.url': 'sqlite:///:memory:',
'session.secret': 'something',
'session.auto_csrf': True,
'passlib.schemes': 'argon2 bcrypt',
'passlib.default': 'argon2',
'passlib.deprecated': 'auto'
}
EXAMPLE_USER_DATA = {

28
tests/_functional/__init__.py

@ -4,7 +4,7 @@ import pytest @@ -4,7 +4,7 @@ import pytest
import transaction
import webtest
from .. import APP_SETTINGS
from .. import APP_SETTINGS, get_example_user
WEBTEST_SETTINGS = APP_SETTINGS.copy()
# WEBTEST_SETTINGS['pyramid.includes'].append('pyramid_mailer.testing')
@ -17,20 +17,32 @@ class CustomTestApp(webtest.TestApp): @@ -17,20 +17,32 @@ class CustomTestApp(webtest.TestApp):
def login(self, username, password):
''' stub for user login '''
self.logout()
pass
result = self.get('/login')
login_form = result.forms[0]
login_form['username'] = username
login_form['password'] = password
login_form.submit()
def logout(self):
''' stub for user logout '''
pass
self.get('/logout')
def reset(self):
''' reset the webapp '''
self.logout()
super().reset()
def create_users(dbsession):
''' create example users '''
pass
from ordr.models.account import Role
for role in Role:
user = get_example_user(role)
dbsession.add(user)
@pytest.fixture(scope='module')
def testapp():
def testappsetup():
''' fixture for using webtest '''
from ordr.models.meta import Base
from ordr.models import get_tm_session
@ -51,3 +63,9 @@ def testapp(): @@ -51,3 +63,9 @@ def testapp():
yield testapp
Base.metadata.drop_all(engine)
@pytest.fixture(scope='function')
def testapp(testappsetup):
testappsetup.reset()
yield testappsetup

2
tests/_functional/errors.py

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
''' functional tests for ordr2.views.errors '''
from . import testapp # noqa: F401
from . import testappsetup, testapp # noqa: F401
def test_404(testapp): # noqa: F811

2
tests/_functional/layout.py

@ -6,7 +6,7 @@ two urls are accessible by either everyone or all active users @@ -6,7 +6,7 @@ two urls are accessible by either everyone or all active users
import pytest
from . import testapp # noqa: F401
from . import testappsetup, testapp # noqa: F401
def test_navbar_no_user(testapp): # noqa: F811

39
tests/_functional/pages.py

@ -2,18 +2,23 @@ @@ -2,18 +2,23 @@
import pytest
from . import testapp # noqa: F401
from . import testappsetup, testapp # noqa: F401
@pytest.mark.xfail
def test_welcome(testapp): # noqa: F811
testapp.logout()
result = testapp.get('/')
assert result.location == 'http://localhost/login'
testapp.login('user', 'password')
testapp.login('TerryGilliam', 'Terry')
result = testapp.get('/')
assert result.location == 'http://localhost/orders'
def test_faq(testapp): # noqa: F811
result = testapp.get('/faq')
active = result.html.find('li', class_='active')
assert active.a['href'] == '/faq'
def test_login_get(testapp): # noqa: F811
result = testapp.get('/login')
active = result.html.find('li', class_='active')
@ -26,10 +31,26 @@ def test_login_get(testapp): # noqa: F811 @@ -26,10 +31,26 @@ def test_login_get(testapp): # noqa: F811
login_form = forms[0]
assert login_form['action'] == '/login'
assert login_form['method'] == 'POST'
assert 'wrong username' not in result
assert 'account is not activated' not in result
def test_faq(testapp): # noqa: F811
result = testapp.get('/faq')
active = result.html.find('li', class_='active')
assert active.a['href'] == '/faq'
def test_login_ok(testapp): # noqa: F811
result = testapp.get('/login')
login_form = result.forms[0]
login_form['username'] = 'TerryGilliam'
login_form['password'] = 'Terry'
result = login_form.submit()
assert result.location == 'http://localhost/'
@pytest.mark.parametrize(
'username,password',
[('John', 'Cleese'), ('unknown user', 'wrong password')]
)
def test_login_denied(testapp, username, password): # noqa: F811
result = testapp.get('/login')
login_form = result.forms[0]
login_form['username'] = 'John'
login_form['password'] = 'Cleese'
result = login_form.submit()
assert 'account is not activated' in result

60
tests/views/pages.py

@ -3,6 +3,10 @@ import pytest @@ -3,6 +3,10 @@ import pytest
from pyramid.httpexceptions import HTTPFound
from pyramid.testing import DummyRequest
from ordr.models.account import Role
from .. import app_config, dbsession, get_example_user # noqa: F401
@pytest.mark.parametrize(
'user,location',
@ -20,3 +24,59 @@ def test_faq(): @@ -20,3 +24,59 @@ def test_faq():
from ordr.views.pages import faq
result = faq(None, None)
assert result == {}
def test_login():
from ordr.views.pages import login
result = login(None, None)
assert result == { 'loginerror': False }
@pytest.mark.parametrize('role', [Role.USER, Role.PURCHASER, Role.ADMIN])
def test_check_login_ok(dbsession, role):
from ordr.views.pages import check_login
user = get_example_user(role)
dbsession.add(user)
post_data = {'username': user.username, 'password': user.first_name}
request = DummyRequest(dbsession=dbsession, POST=post_data)
result = check_login(None, request)
assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com//'
@pytest.mark.parametrize('role', [Role.UNVALIDATED, Role.NEW, Role.INACTIVE])
def test_check_login_not_activated(dbsession, role):
from ordr.views.pages import check_login
user = get_example_user(role)
dbsession.add(user)
post_data = {'username': user.username, 'password': user.first_name}
request = DummyRequest(dbsession=dbsession, POST=post_data)
result = check_login(None, request)
assert result == { 'loginerror': True }
@pytest.mark.parametrize(
'username,password', [
('', ''),
('TerryGilliam', ''),
('', 'Terry'),
('TerryGilliam', 'wrong password'),
('wrong username', 'Terry'),
]
)
def test_check_login_invalid_credentials(dbsession, username, password):
from ordr.views.pages import check_login
user = get_example_user(Role.USER)
dbsession.add(user)
post_data = {'username': username, 'password': password}
request = DummyRequest(dbsession=dbsession, POST=post_data)
result = check_login(None, request)
assert result == { 'loginerror': True }
def test_logout():
from ordr.views.pages import logout
request = DummyRequest()
result = logout(None, request)
assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com//'