Browse files

Finish the Pyramid for Pylons Users guide.

  • Loading branch information...
mikeorr committed Jun 4, 2012
1 parent 48f7568 commit 3d5936fc9dc170459ab3ff722668a1bb41fe8e1a
Showing with 397 additions and 36 deletions.
  1. +4 −0 links.rst
  2. +160 −0 pylons/auth.rst
  3. +12 −0 pylons/deployment.rst
  4. +4 −4 pylons/index.rst
  5. +31 −10 pylons/migrate.rst
  6. +186 −0 pylons/other.rst
  7. +0 −22 pylons/unfinished.rst
@@ -34,3 +34,7 @@
.. _Deform:
.. _pyramid_simpleform:
+.. _Kotti:
+.. _Ptah:
+.. _Khufu:
@@ -0,0 +1,160 @@
+Authentication and Authorization
+*This chapter is contributed by Eric Rasmussen.*
+Pyramid has built-in authentication and authorization capibalities that make it
+easy to restrict handler actions. Here is an overview of the steps you'll
+generally need to take:
+1) Create a root factory in your model that associates allow/deny directives
+ with groups and permissions
+2) Create users and groups in your model
+3) Create a callback function to retrieve a list of groups a user is subscribed to based on their user ID
+4) Make a "forbidden view" that will be invoked when a Forbidden exception is
+ raised.
+5) Create a login action that will check the username/password and remember the
+ user if successful
+6) Restrict access to handler actions by passing in a
+ permission='somepermission' argument to ``@view_config``.
+7) Wire it all together in your config
+You can get started by adding an import statement and custom root factory to
+your model::
+ from import Allow, Everyone
+ class RootFactory(object):
+ __acl__ = [ (Allow, Everyone, "everybody"),
+ (Allow, "basic", "entry"),
+ (Allow, "secured", ("entry", "topsecret"))
+ ]
+ def __init__(self, request):
+ pass
+The custom root factory generates objects that will be used as the context of
+requests sent to your web application. The first attribute of the root factory
+is the ACL, or access control list. It's a list of tuples that contain a
+directive to handle the request (such as Allow or Deny), the group that is
+granted or denied access to the resource, and a permission (or optionally a
+tuple of permissions) to be associated with that group.
+The example access control list above indicates that we will allow everyone to
+view pages with the 'everybody' permission, members of the basic group to view
+pages restricted with the 'entry' permission, and members of the secured group
+to view pages restricted with either the 'entry' or 'topsecret' permissions.
+The special principal 'Everyone' is a built-in feature that allows any person
+visiting your site (known as a principal) access to a given resource.
+For a user to login, you can create a handler that validates the login and
+password (or any additional criteria) submitted through a form. You'll
+typically want to add the following imports::
+ from pyramid.httpexceptions import HTTPFound
+ from import remember, forget
+Once you validate a user's login and password against the model, you can set
+the headers to "remember" the user's ID, and then you can redirect the user to
+the home page or url they were trying to access::
+ # retrieve the userid from the model on valid login
+ headers = remember(self.request, userid)
+ return HTTPFound(location=someurl, headers=headers)
+Note that in the call to the remember function, we're passing in the user ID we
+retrieved from the database and stored in the variable 'userid' (an arbitrary
+name used here as an example). However, you could just as easily pass in a
+username or other unique identifier. Whatever you decide to "remember" is what
+will be passed to the groupfinder callback function that returns a list of
+groups a user belongs to. If you import ``authenticated_userid``, which is a
+useful way to retrieve user information in a handler action, it will return the
+information you set the headers to "remember".
+To log a user out, you "forget" them, and use HTTPFound to redirect to another
+ headers = forget(self.request)
+ return HTTPFound(location=someurl, headers=headers)
+Before you restrict a handler action with a permission, you will need a
+callback function to return a list of groups that a user ID belongs to. Here is
+one way to implement it in your model, in this case assuming you have a Groups
+object with a groupname attribute and a Users object with a mygroups relation
+to Groups::
+ def groupfinder(userid, request):
+ user = Users.by_id(userid)
+ return [g.groupname for g in user.mygroups]
+As an example, you could now import and use the @action decorator to restrict
+by permission, and authenticated_userid to retrieve the user's ID from the
+ from pyramid_handlers import action
+ from import authenticated_userid
+ from models import Users
+ class MainHandler(object):
+ def __init__(self, request):
+ self.request = request
+ @action(renderer="welcome.html", permission="entry")
+ def index(self):
+ userid = authenticated_userid(self.request)
+ user = Users.by_id(userid)
+ username = user.username
+ return {"currentuser": username}
+This gives us a very simple way to restrict handler actions and also obtain
+information about the user. This example assumes we have a Users class with a
+convenience class method called by_id to return the user object. You can then
+access any of the object's attributes defined in your model (such as username,
+email address, etc.), and pass those to a template as dictionary key/values in
+your return statement.
+If you would like a specific handler action to be called when a forbidden
+exception is raised, you need to add a forbidden view. This was covered
+earlier, but for completelness::
+ @view_config(renderer='myapp:templates/forbidden.html',
+ context='pyramid.exceptions.Forbidden')
+ @action(renderer='forbidden.html')
+ def forbidden(request):
+ ...
+The last step is to configure to use your auth policy. Make sure to
+add these imports::
+ from pyramid.authentication import AuthTktAuthenticationPolicy
+ from pyramid.authorization import ACLAuthorizationPolicy
+ from .models import groupfinder
+In your main function you'll want to define your auth policies so you can
+include them in the call to Configurator::
+ authn_policy = AuthTktAuthenticationPolicy('secretstring',
+ callback=groupfinder)
+ authz_policy = ACLAuthorizationPolicy()
+ config = Configurator(settings=settings,
+ root_factory='myapp.models.RootFactory',
+ authentication_policy=authn_policy,
+ authorization_policy=authz_policy)
+ config.scan()
+The capabilities for authentication and authorization in Pyramid are very easy
+to get started with compared to using Pylons and repoze.what. The advantage is
+easier to maintain code and built-in methods to handle common tasks like
+remembering or forgetting users, setting permissions, and easily modifying the
+groupfinder callback to work with your model. For cases where it's manageable
+to set permissions in advance in your root factory and restrict individual
+handler actions, this is by far the simplest way to get up and running while
+still offering robust user and group management capabilities through your
+However, if your application requires the ability to create/edit/delete
+permissions (not just access through group membership), or you require the use
+of advanced predicates, you can either build your own auth system (see the
+Pyramid docs for details) or integrate an existing system like repoze.what.
+You can also use "repoze.who" with Pyramid's authorization system if you want to
+use Who's authenticators and configuration.
@@ -0,0 +1,12 @@
+Deployment is the same for Pyramid as for Pylons. Specify the desired WSGI
+server in the "[server:main]" and run "pserve" with it. The default server in
+Pyramid is Waitress, compared to PasteHTTPServer in Pylons.
+Waitress' advantage is that it runs on Python 3. Its disadvantage is that it
+doesn't seek and destroy stuck threads like PasteHTTPServer does. If you're
+like me, that's enough reason not to use Waitress in production. You can switch
+to PasteHTTPServer or CherryPy server if you wish, or use a method like
+mod_wsgi that doesn't require a Python HTTP server.
@@ -1,13 +1,11 @@
Pyramid for Pylons Users
-:Updated: 2012-04-30
+:Updated: 2012-06-12
:Versions: Pyramid 1.3
:Author: Mike Orr
-*XXX This guide is under construction....*
This guide discusses how Pyramid 1.3 differs from Pylons 1, and a few ways to
make it more like Pylons. The guide may also be helpful to readers coming from
Django or another Rails-like framework. The author has been a Pylons developer
@@ -36,8 +34,10 @@ skim through the rest of the manual to see which sections cover which topics.
+ deployment
+ auth
+ other
- unfinished
.. include:: ../links.rst
@@ -1,17 +1,38 @@
Migrating an Existing Pylons Application
-If you're upgrading a large Pylons application to Pyramid, you can do it one
-route at a time and have it fall back to the Pylons application for URLs which
-don't exist. There are a few ways to set this up. One is to use Paste Cascade
--- the same way Pylons serves static files. In the INI file, make the main
-application ``paste.cascade.Cascade``, delegating to the Pyramid application
-first and falling back to the Pylons app.
+There are two general ways to port a Pylons application to Pyramid. One is to
+start from scratch, expressing the application's behavior in Pyramid. Many
+aspects such as the models, templates, and static files can be used unchanged
+or mostly unchanged. Other aspects like such as the controllers and globals
+will have to be rewritten. The route map can be ported to the new syntax, or
+you can take the opportunity to restructure your routes.
-Another way is to wrap the Pylons application in a Pyramid view. See
-`pyramid.wsgiapp.wsgiapp2`_ and `Porting an Existing WSGI Application to
+The other way is to port one URL at a time, and let Pyramid serve the ported
+URLs and Pylons serve the unported URLs. There are several ways to do this:
+* Run both the Pyramid and Python applications in Apache, and use mod_rewrite
+ to send different URLs to different applications.
+* Set up ``paste.cascade`` in the INI file, so that it will first try one
+ application and then the other if the URL returns "Not Found". (This is how
+ Pylons serves static files.)
+* Wrap the Pylons application in a Pyramid view. See pyramid.wsgiapp.wsgiapp2_.
+Also see the `Porting Applications to Pyramid`_ section in the Cookbook.
+*Caution:* running a Pyramid and a Pylons application simultaneously may bring up
+some tricky issues such as coordiating database connections, sessions, data
+files, etc. These are beyond the scope of this Guide.
+You'll also have to choose whether to write the Pyramid application in Python 2
+or 3. Pyramid 1.3 runs on Python 3, along with Mako and SQLAlchemy, and the
+Waitress and CherryPy HTTP servers (but not PasteHTTPServer). But not all
+optional libraries have been ported yet, and your application may depend on
+libraries which haven't been.
+.. include:: ../links.rst
.. _pyramid.wsgiapp.wsgiapp2:
-.. _Porting an Existing WSGI Application to Pyramid:
+.. _Porting Applications to Pyramid: ../porting/index.html
Oops, something went wrong.

0 comments on commit 3d5936f

Please sign in to comment.