tailbone/tailbone/views/auth.py
Lance Edgar 6bf60365ba Let any 'admin' user elevate to 'root' for full system access
But otherwise, let the Administrator role be "normal" and have perms of
its own.  Hopefully cuts down on unwanted screen noise for admins.
2016-10-18 16:59:38 -05:00

188 lines
5.9 KiB
Python

# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Auth Views
"""
from __future__ import unicode_literals, absolute_import
from rattail.db.auth import authenticate_user, set_user_password
import formencode
from pyramid.httpexceptions import HTTPFound
from pyramid.security import remember, forget, authenticated_userid
from pyramid_simpleform import Form
from webhelpers.html import literal
from webhelpers.html import tags
from tailbone.db import Session
from tailbone.forms.simpleform import FormRenderer
def forbidden(request):
"""
Access forbidden view.
This is triggered whenever access is not allowed for an otherwise
appropriate view.
"""
msg = literal("You do not have permission to do that.")
if not authenticated_userid(request):
msg += literal("&nbsp; (Perhaps you should %s?)" %
tags.link_to("log in", request.route_url('login')))
# Store current URL in session, for smarter redirect after login.
request.session['next_url'] = request.current_route_url()
request.session.flash(msg, allow_duplicate=False)
return HTTPFound(location=request.get_referrer())
class UserLogin(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
username = formencode.validators.NotEmpty()
password = formencode.validators.NotEmpty()
def login(request):
"""
The login view, responsible for displaying and handling the login form.
"""
referrer = request.get_referrer()
# Redirect if already logged in.
if request.user:
return HTTPFound(location=referrer)
form = Form(request, schema=UserLogin)
if form.validate():
user = authenticate_user(Session(),
form.data['username'],
form.data['password'])
if user:
headers = remember(request, user.uuid)
# Treat URL from session as referrer, if available.
referrer = request.session.pop('next_url', referrer)
return HTTPFound(location=referrer, headers=headers)
request.session.flash("Invalid username or password")
return {'form': FormRenderer(form), 'referrer': referrer}
def logout(request):
"""
View responsible for logging out the current user.
This deletes/invalidates the current session and then redirects to the
login page.
"""
request.session.delete()
request.session.invalidate()
headers = forget(request)
referrer = request.get_referrer()
return HTTPFound(location=referrer, headers=headers)
def become_root(request):
"""
Elevate the current request to 'root' for full system access.
"""
request.session['is_root'] = True
request.session.flash("You have been elevated to 'root' and now have full system access", 'error')
return HTTPFound(location=request.get_referrer())
def stop_root(request):
"""
Lower the current request from 'root' back to normal access.
"""
request.session['is_root'] = False
request.session.flash("Your normal system access has been restored")
return HTTPFound(location=request.get_referrer())
class CurrentPasswordCorrect(formencode.validators.FancyValidator):
def _to_python(self, value, state):
user = state
if not authenticate_user(Session, user.username, value):
raise formencode.Invalid("The password is incorrect.", value, state)
return value
class ChangePassword(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
current_password = formencode.All(
formencode.validators.NotEmpty(),
CurrentPasswordCorrect())
new_password = formencode.validators.NotEmpty()
confirm_password = formencode.validators.NotEmpty()
chained_validators = [formencode.validators.FieldsMatch(
'new_password', 'confirm_password')]
def change_password(request):
"""
Allows a user to change his or her password.
"""
if not request.user:
return HTTPFound(location=request.route_url('home'))
form = Form(request, schema=ChangePassword, state=request.user)
if form.validate():
set_user_password(request.user, form.data['new_password'])
return HTTPFound(location=request.get_referrer())
return {'form': FormRenderer(form)}
def add_routes(config):
config.add_route('login', '/login')
config.add_route('logout', '/logout')
config.add_route('become_root', '/root/yes')
config.add_route('stop_root', '/root/no')
config.add_route('change_password', '/change-password')
def includeme(config):
add_routes(config)
config.add_forbidden_view(forbidden)
config.add_view(login, route_name='login',
renderer='/login.mako')
config.add_view(logout, route_name='logout')
config.add_view(become_root, route_name='become_root')
config.add_view(stop_root, route_name='stop_root')
config.add_view(change_password, route_name='change_password',
renderer='/change_password.mako')