You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
11 KiB
368 lines
11 KiB
''' account registration, login, logout and settings ''' |
|
|
|
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 ChangedEmail, CompleteRegistration, PasswordReset |
|
from ordr2.models.account import User, Role, TokenSubject |
|
from ordr2.schemas.account import ( |
|
RegistrationSchema, |
|
ResetPasswordSchema, |
|
AccountSettingsSchema |
|
) |
|
|
|
|
|
PROPOSED_PASSWORD_LENGTH = 12 |
|
|
|
|
|
# login and logout |
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='login', |
|
request_method='GET', |
|
permission='login', |
|
renderer='ordr2:templates/account/login.jinja2' |
|
) |
|
def login_form(context, request): |
|
return {} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='login', |
|
request_method='POST', |
|
permission='login', |
|
renderer='ordr2:templates/account/login.jinja2' |
|
) |
|
def login(context, request): |
|
''' loging in a user ''' |
|
username = request.POST.get('username') |
|
password = request.POST.get('password') |
|
|
|
# Form validation is not done for login forms, |
|
# either the data represents a user or not. |
|
user = request.dbsession.query(User).filter_by(username=username).first() |
|
if user is not None: |
|
if user.is_active and user.check_password(password): |
|
headers = remember(request, user.id) |
|
return HTTPFound( |
|
request.resource_url(request.root, 'orders'), |
|
headers=headers |
|
) |
|
request.session.flash( |
|
'You entered the wrong username or password', |
|
'error' |
|
) |
|
|
|
return {} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='logout', |
|
permission='logout' |
|
) |
|
def logout(context, request): |
|
''' log out an user ''' |
|
headers = forget(request) |
|
return HTTPFound(request.resource_url(request.root), headers=headers) |
|
|
|
|
|
# account registration |
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='register', |
|
request_method='GET', |
|
permission='register', |
|
renderer='ordr2:templates/account/register.jinja2' |
|
) |
|
def registration_form(context, request): |
|
''' the new user registraion page ''' |
|
form = RegistrationSchema.as_form(request) |
|
return {'form': form} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='register', |
|
request_method='POST', |
|
permission='register', |
|
renderer='ordr2:templates/account/register.jinja2' |
|
) |
|
def registration_form_processing(context, request): |
|
''' registration form processing ''' |
|
if 'Cancel' in request.POST: |
|
return HTTPFound(request.resource_url(request.root)) |
|
|
|
# validate the form data |
|
form = RegistrationSchema.as_form(request) |
|
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.USER_REGISTRATION) |
|
notification = CompleteRegistration(request, account, {'token': token}) |
|
request.registry.notify(notification) |
|
|
|
# issue a warning on a short password |
|
if len(appstruct['password']) < PROPOSED_PASSWORD_LENGTH: |
|
request.session.flash( |
|
'You should really consider a longer password', |
|
'warning' |
|
) |
|
|
|
return HTTPFound(request.resource_url(context, 'registered')) |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='registered', |
|
permission='register', |
|
renderer='ordr2:templates/account/registration_confirmation.jinja2' |
|
) |
|
def registration_confirmation(context, request): |
|
''' email to verify registration was sent ''' |
|
return {} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.RegistrationToken', |
|
permission='register', |
|
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( |
|
'Username or email address unknown', |
|
'error' |
|
) |
|
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 {} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.ForgottenPasswordToken', |
|
request_method='GET', |
|
permission='reset password', |
|
renderer='ordr2:templates/account/reset_password.jinja2' |
|
) |
|
def reset_password_form(context, request): |
|
form = ResetPasswordSchema.as_form(request) |
|
return {'form': form} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.ForgottenPasswordToken', |
|
request_method='POST', |
|
permission='reset password', |
|
renderer='ordr2:templates/account/reset_password.jinja2' |
|
) |
|
def reset_password_form_processing(context, request): |
|
if 'Cancel' in request.POST: |
|
return HTTPFound(request.resource_url(request.root)) |
|
|
|
# validate the form data |
|
form = ResetPasswordSchema.as_form(request) |
|
data = request.POST.items() |
|
try: |
|
appstruct = form.validate(data) |
|
except deform.ValidationFailure as e: |
|
return {'form': form} |
|
|
|
# set the new password |
|
context.model.owner.set_password(appstruct['password']) |
|
|
|
# delete the token |
|
request.dbsession.delete(context.model) |
|
|
|
# issue a warning on a short password |
|
if len(appstruct['password']) < PROPOSED_PASSWORD_LENGTH: |
|
request.session.flash( |
|
'You should really consider a longer password', |
|
'warning' |
|
) |
|
|
|
request.session.flash('Your password was changed', 'ok') |
|
return HTTPFound(request.resource_url(context.__parent__, 'login')) |
|
|
|
|
|
# account settings |
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
request_method='GET', |
|
name='settings', |
|
permission='settings', |
|
renderer='ordr2:templates/account/settings.jinja2' |
|
) |
|
def settings_form(context, request): |
|
prefill = { |
|
'general': { |
|
'username': context.model.username, |
|
'first_name': context.model.first_name, |
|
'last_name': context.model.last_name, |
|
'email': context.model.email, |
|
'role': str(context.model.role) |
|
} |
|
} |
|
form = AccountSettingsSchema.as_form(request) |
|
form.set_appstruct(prefill) |
|
return {'form': form} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
request_method='POST', |
|
name='settings', |
|
permission='settings', |
|
renderer='ordr2:templates/account/settings.jinja2' |
|
) |
|
def settings_form_processing(context, request): |
|
if 'Cancel' in request.POST: |
|
return HTTPFound(request.resource_url(request.root)) |
|
|
|
# validate the form data |
|
form = AccountSettingsSchema.as_form(request) |
|
data = request.POST.items() |
|
try: |
|
appstruct = form.validate(data) |
|
except deform.ValidationFailure as e: |
|
return {'form': form} |
|
|
|
context.model.first_name = appstruct['general']['first_name'] |
|
context.model.last_name = appstruct['general']['last_name'] |
|
|
|
new_password = appstruct['change_password']['new_password'] |
|
if new_password: |
|
context.model.set_password(new_password) |
|
request.session.flash( |
|
'Password updated sucessfully', |
|
'success' |
|
) |
|
# issue a warning on a short password |
|
if len(new_password) < PROPOSED_PASSWORD_LENGTH: |
|
request.session.flash( |
|
'You should really consider a longer password', |
|
'warning' |
|
) |
|
|
|
# email address changed |
|
# the email address is not updated directly, |
|
# but a email sent to confirm the new address |
|
new_email = appstruct['general']['email'] |
|
if new_email != context.model.email: |
|
# create a verify-new-email token and send email |
|
token = context.model.issue_token( |
|
request, |
|
TokenSubject.CHANGE_EMAIL, |
|
{'new_email': new_email} |
|
) |
|
notification = ChangedEmail( |
|
request, |
|
context.model, |
|
{'token': token}, |
|
send_to=new_email |
|
) |
|
request.registry.notify(notification) |
|
return HTTPFound(request.resource_url(context, 'verify-new-email')) |
|
|
|
return HTTPFound(request.resource_url(request.root)) |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.AccountResource', |
|
name='verify-new-email', |
|
permission='settings', |
|
renderer='ordr2:templates/account/email_confirmation.jinja2' |
|
) |
|
def email_confirmation(context, request): |
|
''' email sent to new address ''' |
|
return {} |
|
|
|
|
|
@view_config( |
|
context='ordr2:resources.account.EmailVerificationToken', |
|
permission='settings' |
|
) |
|
def email_change_confirmed(context, request): |
|
''' changed email address is confirmed ''' |
|
context.model.owner.email = context.model.payload['new_email'] |
|
request.dbsession.delete(context.model) |
|
request.session.flash('Email change sucessful', 'success') |
|
return HTTPFound(request.resource_url(request.root, 'login'))
|
|
|