Browse Source

added forgotten password form

reset password form is missing
master
Holger Frey 7 years ago
parent
commit
eb32a8a474
  1. 16
      ordr2/templates/account/forgot_password_email.jinja2
  2. 23
      ordr2/templates/account/forgot_password_form.jinja2
  3. 71
      ordr2/views/account.py
  4. 73
      tests/views/account.py

16
ordr2/templates/account/forgot_password_email.jinja2

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
{% extends "ordr2:templates/layout.jinja2" %}
{% block title %} Ordr | Forgotten Password {% endblock title %}
{% block content %}
<div class="row">
<div class="col-2"></div>
<div class="col-5">
<h1>Password Reset Link</h1>
<p>To reset your password, an email has been sent to you.</p>
<p>Please follow the link in the email to set a new password.</p>
</div>
</div>
{% endblock content %}

23
ordr2/templates/account/forgot_password_form.jinja2

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
{% extends "ordr2:templates/layout.jinja2" %}
{% block title %} Ordr | Forgotten Password {% endblock title %}
{% block content %}
<div class="row justify-content-center">
<div class="col-4">
<h1>Forgotten password?</h1>
<form action="{{ request.resource_url(request.root, 'account', 'forgot-password') }}" method="POST" id="forgotten-password-form" class="form">
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
<div class="form-group">
<label for="username_or_email" class="form-control-label">Username or Email:</label>
<input name="username_or_email" id="username_or_email" type="text" class="form-control">
</div>
<div class="form-group">
<button type="submit" name="submit" class="btn btn-sm btn-primary">Reset Password</button>
<button type="submit" name="cancel" class="btn btn-sm btn-secondary">Cancel</button>
</div>
</form>
</div>
</div>
{% endblock content %}

71
ordr2/views/account.py

@ -5,8 +5,9 @@ import deform @@ -5,8 +5,9 @@ import deform
from pyramid.httpexceptions import HTTPFound
from pyramid.security import remember, forget
from pyramid.view import view_config
from sqlalchemy import or_
from ordr2.events import CompleteRegistration
from ordr2.events import CompleteRegistration, PasswordReset
from ordr2.models.account import User, Role, TokenSubject
from ordr2.schemas.account import RegistrationSchema
@ -14,6 +15,8 @@ from ordr2.schemas.account import RegistrationSchema @@ -14,6 +15,8 @@ from ordr2.schemas.account import RegistrationSchema
PROPOSED_PASSWORD_LENGTH = 12
# login and logout
@view_config(
context='ordr2:resources.account.AccountResource',
name='login',
@ -33,7 +36,7 @@ def login(context, request): @@ -33,7 +36,7 @@ def login(context, request):
if user.is_active and user.check_password(password):
headers = remember(request, user.id)
return HTTPFound(
request.resource_path(request.root, 'orders'),
request.resource_url(request.root, 'orders'),
headers=headers
)
return {}
@ -50,6 +53,8 @@ def logout(context, request): @@ -50,6 +53,8 @@ def logout(context, request):
return HTTPFound(request.resource_url(request.root), headers=headers)
# account registration
@view_config(
context='ordr2:resources.account.AccountResource',
name='register',
@ -116,6 +121,7 @@ def registration_form_processing(context, request): @@ -116,6 +121,7 @@ def registration_form_processing(context, request):
renderer='ordr2:templates/account/registration_confirmation.jinja2'
)
def registration_confirmation(context, request):
''' email to verify registration was sent '''
return {}
@ -125,6 +131,67 @@ def registration_confirmation(context, request): @@ -125,6 +131,67 @@ def registration_confirmation(context, request):
renderer='ordr2:templates/account/registration_completed.jinja2'
)
def registration_completed(context, request):
''' registration was verified by mail link '''
context.model.owner.role = Role.NEW
request.dbsession.delete(context.model)
return {}
# password recovery
@view_config(
context='ordr2:resources.account.AccountResource',
name='forgot-password',
request_method='GET',
permission='reset password',
renderer='ordr2:templates/account/forgot_password_form.jinja2'
)
def forgot_password_form(context, request):
''' forgot password form '''
return {}
@view_config(
context='ordr2:resources.account.AccountResource',
name='forgot-password',
request_method='POST',
permission='reset password',
renderer='ordr2:templates/account/forgot_password_form.jinja2'
)
def forgot_password_form_processing(context, request):
''' forgot password form processing '''
if 'cancel' in request.POST:
return HTTPFound(request.resource_url(request.root))
identifier = request.POST.get('username_or_email')
account = (
request.dbsession
.query(User)
.filter(or_(User.username == identifier, User.email == identifier))
.first()
)
if not account:
request.session.flash(
'warning',
'Username or email address unknown'
)
return {}
# create a forgot-my-password token and send email
token = account.issue_token(request, TokenSubject.RESET_PASSWORD)
notification = PasswordReset(request, account, {'token': token})
request.registry.notify(notification)
return HTTPFound(request.resource_url(context, 'forgot-password-email'))
@view_config(
context='ordr2:resources.account.AccountResource',
name='forgot-password-email',
request_method='GET',
permission='reset password',
renderer='ordr2:templates/account/forgot_password_email.jinja2'
)
def forgot_password_email_sent(context, request):
''' password reset link was sent '''
return {}

73
tests/views/account.py

@ -40,7 +40,7 @@ def test_account_login_active_users(dbsession, rolename): @@ -40,7 +40,7 @@ def test_account_login_active_users(dbsession, rolename):
result = login(None, request)
assert isinstance(result, HTTPFound)
assert result.location == '//orders'
assert result.location == 'http://example.com//orders'
@pytest.mark.parametrize('rolename', ['unvalidated', 'new', 'inactive'])
@ -99,6 +99,8 @@ def test_logout(app_config): @@ -99,6 +99,8 @@ def test_logout(app_config):
assert result.location == 'http://example.com//'
# tests for the registration form
def test_registration_form(app_config):
''' registration form '''
from ordr2.views.account import registration_form
@ -214,3 +216,72 @@ def test_registration_completed(dbsession): @@ -214,3 +216,72 @@ def test_registration_completed(dbsession):
assert user.role == Role.NEW
assert dbsession.query(Token).count() == 0
assert dbsession.query(User).count() == 1
# tests for password reset
def test_forgot_password_form():
''' display the forgot password form '''
from ordr2.views.account import forgot_password_form
result = forgot_password_form(None, None)
assert result == {}
@pytest.mark.parametrize('who', ['TerryGilliam', 'gilliam@example.com'])
def test_forgot_password_form_processing_ok(dbsession, who):
''' process the forgot password form '''
from ordr2.views.account import forgot_password_form_processing
dbsession.add(get_user('user'))
request = DummyRequest(
dbsession=dbsession,
POST={'username_or_email': who}
)
result = forgot_password_form_processing(None, request)
assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com//forgot-password-email'
def test_forgot_password_form_processing_cancel(dbsession):
''' forgot password form was canceled '''
from ordr2.views.account import forgot_password_form_processing
dbsession.add(get_user('user'))
request = DummyRequest(
dbsession=dbsession,
POST={'username_or_email': 'gilliam@example.com', 'cancel': 'Cancel'}
)
result = forgot_password_form_processing(None, request)
assert isinstance(result, HTTPFound)
assert result.location == 'http://example.com//'
@pytest.mark.parametrize(
'who',
['unknown user', 'unknown@example.com', '']
)
def test_forgot_password_form_processing_error(dbsession, who):
''' process the forgot password form '''
from ordr2.views.account import forgot_password_form_processing
dbsession.add(get_user('user'))
request = DummyRequest(
dbsession=dbsession,
POST = {'username_or_email': who}
)
result = forgot_password_form_processing(None, request)
assert result == {}
def test_forgot_password_email_sent():
''' message that a password reset link was sent '''
from ordr2.views.account import forgot_password_email_sent
result = forgot_password_email_sent(None, None)
assert result == {}