|
|
|
''' views for user accounts
|
|
|
|
|
|
|
|
This includes login, logout, registration, forgotten passwords, changing
|
|
|
|
settings and passwords
|
|
|
|
'''
|
|
|
|
|
|
|
|
import deform
|
|
|
|
|
|
|
|
from pyramid.httpexceptions import HTTPFound
|
|
|
|
from pyramid.security import remember, forget
|
|
|
|
from pyramid.view import view_config
|
|
|
|
from sqlalchemy import func, or_
|
|
|
|
|
|
|
|
|
|
|
|
from ordr.events import (
|
|
|
|
ChangeEmailNotification,
|
|
|
|
PasswordResetNotification,
|
|
|
|
RegistrationNotification
|
|
|
|
)
|
|
|
|
from ordr.models.account import Role, TokenSubject, User
|
|
|
|
|
|
|
|
|
|
|
|
# account resource root
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
permission='view'
|
|
|
|
)
|
|
|
|
def account(context, request):
|
|
|
|
''' redirect if '/account' was requested directly '''
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
|
|
|
|
|
|
|
|
# login and logout
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
name='login',
|
|
|
|
request_method='GET',
|
|
|
|
permission='login',
|
|
|
|
renderer='ordr:templates/account/login.jinja2',
|
|
|
|
)
|
|
|
|
def login(context, request):
|
|
|
|
''' shows the login page '''
|
|
|
|
context.nav_active = 'welcome'
|
|
|
|
return {'loginerror': False}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
name='login',
|
|
|
|
request_method='POST',
|
|
|
|
permission='login',
|
|
|
|
renderer='ordr:templates/account/login.jinja2',
|
|
|
|
)
|
|
|
|
def check_login(context, request):
|
|
|
|
''' check user credentials '''
|
|
|
|
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)
|
|
|
|
|
|
|
|
context.nav_active = 'welcome'
|
|
|
|
return {'loginerror': True}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
name='logout',
|
|
|
|
permission='logout'
|
|
|
|
)
|
|
|
|
def logout(context, request):
|
|
|
|
''' log out of an user '''
|
|
|
|
headers = forget(request)
|
|
|
|
return HTTPFound(request.resource_url(request.root), headers=headers)
|
|
|
|
|
|
|
|
|
|
|
|
# registration process
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.RegistrationResource',
|
|
|
|
permission='register',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/registration_form.jinja2'
|
|
|
|
)
|
|
|
|
def registration_form(context, request):
|
|
|
|
''' show registration form '''
|
|
|
|
form = context.get_registration_form()
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.RegistrationResource',
|
|
|
|
permission='register',
|
|
|
|
request_method='POST',
|
|
|
|
renderer='ordr:templates/account/registration_form.jinja2'
|
|
|
|
)
|
|
|
|
def registration_form_processing(context, request):
|
|
|
|
''' process registration form '''
|
|
|
|
if 'create' not in request.POST:
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
|
|
|
|
form = context.get_registration_form()
|
|
|
|
data = request.POST.items()
|
|
|
|
try:
|
|
|
|
appstruct = form.validate(data)
|
|
|
|
except deform.ValidationFailure as e:
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
# form validation successfull, create user
|
|
|
|
account = User(
|
|
|
|
username=appstruct['username'],
|
|
|
|
first_name=appstruct['first_name'],
|
|
|
|
last_name=appstruct['last_name'],
|
|
|
|
email=appstruct['email'],
|
|
|
|
role=Role.UNVALIDATED
|
|
|
|
)
|
|
|
|
account.set_password(appstruct['password'])
|
|
|
|
request.dbsession.add(account)
|
|
|
|
|
|
|
|
# create a verify-new-account token and send email
|
|
|
|
token = account.issue_token(request, TokenSubject.REGISTRATION)
|
|
|
|
notification = RegistrationNotification(request, account, {'token': token})
|
|
|
|
request.registry.notify(notification)
|
|
|
|
|
|
|
|
return HTTPFound(request.resource_url(context, 'verify'))
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.RegistrationResource',
|
|
|
|
name='verify',
|
|
|
|
permission='register',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/registration_verify.jinja2'
|
|
|
|
)
|
|
|
|
def registration_verify_email(context, request):
|
|
|
|
''' show email verification text '''
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.RegistrationTokenResource',
|
|
|
|
permission='register',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/registration_completed.jinja2'
|
|
|
|
)
|
|
|
|
def registration_completed(context, request):
|
|
|
|
''' registration is completed, awaiting activation by admin '''
|
|
|
|
token = context.model
|
|
|
|
account = token.owner
|
|
|
|
account.role = Role.NEW
|
|
|
|
request.dbsession.delete(token)
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
# forgotten password process
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.PasswordResetResource',
|
|
|
|
permission='reset',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/forgotten_password_form.jinja2'
|
|
|
|
)
|
|
|
|
def forgotten_password_form(context, request):
|
|
|
|
''' show forgotten password form '''
|
|
|
|
return {'formerror': False}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.PasswordResetResource',
|
|
|
|
permission='reset',
|
|
|
|
request_method='POST',
|
|
|
|
renderer='ordr:templates/account/forgotten_password_form.jinja2'
|
|
|
|
)
|
|
|
|
def forgotten_password_form_processing(context, request):
|
|
|
|
''' process forgotten password form '''
|
|
|
|
if 'cancel' in request.POST:
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
identifier = request.POST.get('identifier', '')
|
|
|
|
account = (
|
|
|
|
request.dbsession
|
|
|
|
.query(User)
|
|
|
|
.filter(or_(
|
|
|
|
func.lower(User.username) == identifier.lower(),
|
|
|
|
func.lower(User.email) == identifier.lower()
|
|
|
|
))
|
|
|
|
.first()
|
|
|
|
)
|
|
|
|
if account is None or not account.is_active:
|
|
|
|
return {'formerror': True}
|
|
|
|
|
|
|
|
# create a verify-new-account token and send email
|
|
|
|
token = account.issue_token(request, TokenSubject.RESET_PASSWORD)
|
|
|
|
notification = PasswordResetNotification(
|
|
|
|
request,
|
|
|
|
account,
|
|
|
|
{'token': token}
|
|
|
|
)
|
|
|
|
request.registry.notify(notification)
|
|
|
|
|
|
|
|
return HTTPFound(request.resource_url(context, 'verify'))
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.PasswordResetResource',
|
|
|
|
name='verify',
|
|
|
|
permission='reset',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/forgotten_password_verify.jinja2'
|
|
|
|
)
|
|
|
|
def forgotten_password_verify_email(context, request):
|
|
|
|
''' show email verification text '''
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.PasswordResetResource',
|
|
|
|
name='completed',
|
|
|
|
permission='reset',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/forgotten_password_completed.jinja2'
|
|
|
|
)
|
|
|
|
def forgotten_password_completed(context, request):
|
|
|
|
''' user is verified, process reset password form '''
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.PasswordResetTokenResource',
|
|
|
|
permission='reset',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/forgotten_password_reset.jinja2'
|
|
|
|
)
|
|
|
|
def reset_password_form(context, request):
|
|
|
|
''' user is verified, show reset password form '''
|
|
|
|
form = context.get_reset_form()
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.PasswordResetTokenResource',
|
|
|
|
permission='reset',
|
|
|
|
request_method='POST',
|
|
|
|
renderer='ordr:templates/account/forgotten_password_reset.jinja2'
|
|
|
|
)
|
|
|
|
def reset_password_form_processing(context, request):
|
|
|
|
''' process the password reset form '''
|
|
|
|
if 'change' not in request.POST:
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
|
|
|
|
form = context.get_reset_form()
|
|
|
|
data = request.POST.items()
|
|
|
|
try:
|
|
|
|
appstruct = form.validate(data)
|
|
|
|
except deform.ValidationFailure as e:
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
# set new password
|
|
|
|
token = context.model
|
|
|
|
account = token.owner
|
|
|
|
account.set_password(appstruct['password'])
|
|
|
|
request.dbsession.delete(token)
|
|
|
|
|
|
|
|
return HTTPFound(request.resource_url(context.__parent__, 'completed'))
|
|
|
|
|
|
|
|
|
|
|
|
# account settings
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
permission='edit',
|
|
|
|
name='settings',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/settings_form.jinja2'
|
|
|
|
)
|
|
|
|
def settings_form(context, request):
|
|
|
|
''' show the settings form '''
|
|
|
|
prefill = {
|
|
|
|
'username': request.user.username,
|
|
|
|
'first_name': request.user.first_name,
|
|
|
|
'last_name': request.user.last_name,
|
|
|
|
'email': request.user.email,
|
|
|
|
}
|
|
|
|
form = context.get_settings_form(prefill=prefill)
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
permission='edit',
|
|
|
|
name='settings',
|
|
|
|
request_method='POST',
|
|
|
|
renderer='ordr:templates/account/settings_form.jinja2'
|
|
|
|
)
|
|
|
|
def settings_form_processing(context, request):
|
|
|
|
''' process the settings form '''
|
|
|
|
if 'change' not in request.POST:
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
|
|
|
|
form = context.get_settings_form()
|
|
|
|
data = request.POST.items()
|
|
|
|
try:
|
|
|
|
appstruct = form.validate(data)
|
|
|
|
except deform.ValidationFailure as e:
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
# form validation successfull, change user
|
|
|
|
request.user.first_name = appstruct['first_name']
|
|
|
|
request.user.last_name = appstruct['last_name']
|
|
|
|
|
|
|
|
if appstruct['email'] == request.user.email:
|
|
|
|
# email was not changed
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
|
|
|
|
# create a verify-new-email token and send email
|
|
|
|
token = request.user.issue_token(
|
|
|
|
request,
|
|
|
|
TokenSubject.CHANGE_EMAIL,
|
|
|
|
payload={'email': appstruct['email']}
|
|
|
|
)
|
|
|
|
notification = ChangeEmailNotification(
|
|
|
|
request,
|
|
|
|
account,
|
|
|
|
{'token': token},
|
|
|
|
send_to=appstruct['email']
|
|
|
|
)
|
|
|
|
request.registry.notify(notification)
|
|
|
|
|
|
|
|
return HTTPFound(request.resource_url(context, 'verify'))
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.ChangeEmailTokenResource',
|
|
|
|
permission='edit',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/settings_mail_changed.jinja2'
|
|
|
|
)
|
|
|
|
def verify_email_change(context, request):
|
|
|
|
''' show email verification text '''
|
|
|
|
payload = context.model.payload
|
|
|
|
request.user.email = payload['email']
|
|
|
|
request.dbsession.delete(context.model)
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
# change password
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
permission='edit',
|
|
|
|
name='password',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/password_form.jinja2'
|
|
|
|
)
|
|
|
|
def password_form(context, request):
|
|
|
|
''' show the change password form '''
|
|
|
|
form = context.get_password_form()
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
permission='edit',
|
|
|
|
name='password',
|
|
|
|
request_method='POST',
|
|
|
|
renderer='ordr:templates/account/password_form.jinja2'
|
|
|
|
)
|
|
|
|
def password_form_processing(context, request):
|
|
|
|
''' process the change password form '''
|
|
|
|
if 'change' not in request.POST:
|
|
|
|
return HTTPFound(request.resource_url(request.root))
|
|
|
|
|
|
|
|
form = context.get_password_form()
|
|
|
|
data = request.POST.items()
|
|
|
|
try:
|
|
|
|
appstruct = form.validate(data)
|
|
|
|
except deform.ValidationFailure as e:
|
|
|
|
return {'form': form}
|
|
|
|
|
|
|
|
# form validation successfull, change the password
|
|
|
|
request.user.set_password(appstruct['password'])
|
|
|
|
return HTTPFound(request.resource_url(context, 'changed'))
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context='ordr.resources.account.AccountResource',
|
|
|
|
permission='edit',
|
|
|
|
name='changed',
|
|
|
|
request_method='GET',
|
|
|
|
renderer='ordr:templates/account/password_changed.jinja2'
|
|
|
|
)
|
|
|
|
def password_changed(context, request):
|
|
|
|
''' the password changed message '''
|
|
|
|
return {}
|