Base Application¶
This is a basic wiki-style application. A visitor can view, add, and edit pages. They can also login, logout and view information on users of the system. The URL structure is as follows:
/
/login
/logout
/users
/user/{login}
/pages
/create_page
/page/{title}
/page/{title}/edit
This demo isn’t here to teach you how to use URL Dispatch or setup a basic application. If you have any questions about how to setup this simple application with no security, please go back to the Pyramid documentation and tutorials to learn more.
Startup¶
python3 -m venv env
env/bin/pip install pyramid pyramid-mako
env/bin/python demo.py
Model¶
The application is built around a model which persists User
and
Page
objects.
Each User
of the system has a login, password, and a list of
groups to which they belong.
class User(object):
def __init__(self, login, password, groups=None):
self.login = login
self.password = password
self.groups = groups or []
def check_password(self, passwd):
return self.password == passwd
Each Page
has a title, body, and owner, as well as a web-safe
uri.
class Page(object):
def __init__(self, title, uri, body, owner):
self.title = title
self.uri = uri
self.body = body
self.owner = owner
Views¶
Most of the views are cookie cutter, but views relating to authentication have been singled out and explained in more detail.
Forbidden View¶
The forbidden view is an exception view registered for
pyramid.httpexceptions.HTTPForbidden
. When a protected resource
is accessed with invalid permissions, Pyramid will raise an an
HTTPForbidden
exception. The base application provides two
possibilities, depending on whether the user is already logged in
when the permissions checks fail. If the user is not logged in they
are redirected to the login page. However, if they were already
logged in then we know they simply do not have access, and we return
the HTTPForbidden
response (403 Forbidden).
@forbidden_view_config()
def forbidden_view(request):
# do not allow a user to login if they are already logged in
if request.authenticated_userid:
return HTTPForbidden()
loc = request.route_url('login', _query=(('next', request.path),))
return HTTPFound(location=loc)
Login View¶
The login view will accept both GET and POST requests. On a GET
it will serve up the basic login page and on POST it will look in
the request’s body for the login
and password
, validate them
and if successful redirect to the previous page. A user is successfully
logged in by calling pyramid.security.remember
which uses the
authentication policy to generate a list of headers that should
be sent back as part of the response. These headers generally set a
cookie which will allow the application to track the user on subsequent
visits.
@view_config(
route_name='login',
renderer='login.mako',
)
def login_view(request):
next = request.params.get('next') or request.route_url('home')
login = ''
did_fail = False
if 'submit' in request.POST:
login = request.POST.get('login', '')
passwd = request.POST.get('passwd', '')
user = USERS.get(login, None)
if user and user.check_password(passwd):
headers = remember(request, login)
return HTTPFound(location=next, headers=headers)
did_fail = True
return {
'login': login,
'next': next,
'failed_attempt': did_fail,
'users': USERS,
}
Logout View¶
The logout view is very simple, but it showcases the use of
pyramid.security.forget
to generate a list of headers that should
be sent back as part of the response. These headers generally will
delete the cookies set by pyramid.security.remember
.
@view_config(
route_name='logout',
)
def logout_view(request):
headers = forget(request)
loc = request.route_url('home')
return HTTPFound(location=loc, headers=headers)
Create Page View¶
Unauthenticated users cannot create pages because a Page
must
have an owner. This is protected by manually raising
HTTPForbidden
from within the create_page_view
which will
invoke the Forbidden View.
@view_config(route_name='create_page', renderer='edit_page.mako')
def create_page_view(request):
owner = request.authenticated_userid
if owner is None:
raise HTTPForbidden()
# ...