Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
pyramid.pdf
Скачиваний:
11
Добавлен:
24.03.2015
Размер:
3.82 Mб
Скачать

36.7. ADDING AUTHORIZATION

Visiting http://localhost:6543/FrontPage/edit_page in a browser invokes the edit view for the FrontPage Page resource.

Visiting http://localhost:6543/add_page/SomePageName in a browser invokes the add view for a Page.

To generate an error, visit http://localhost:6543/add_page which will generate an IndexError for the expression request.subpath[0]. You’ll see an interactive traceback facility provided by pyramid_debugtoolbar.

36.7 Adding Authorization

Our application currently allows anyone with access to the server to view, edit, and add pages to our wiki. For purposes of demonstration we’ll change our application to allow people who are members of a group named group:editors to add and edit wiki pages but we’ll continue allowing anyone with access to the server to view pages. Pyramid provides facilities for authorization and authentication. We’ll make use of both features to provide security to our application.

We will add an authentication policy and an authorization policy to our application registry, add a security.py module and give our root resource an ACL.

Then we will add login and logout views, and modify the existing views to make them return a logged_in flag to the renderer and add permission declarations to their view_config decorators.

Finally, we will add a login.pt template and change the existing view.pt and edit.pt to show a “Logout” link when not logged in.

The source code for this tutorial stage can be browsed via http://github.com/Pylons/pyramid/tree/1.3- branch/docs/tutorials/wiki/src/authorization/.

36.7.1 Add Authentication and Authorization Policies

We’ll change our package’s __init__.py file to enable an AuthTktAuthenticationPolicy and an ACLAuthorizationPolicy to enable declarative security checking. We need to import the new policies:

1

2

3

from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy

from .security import groupfinder

Then, we’ll add those policies to the configuration:

417

36. ZODB + TRAVERSAL WIKI TUTORIAL

1authn_policy = AuthTktAuthenticationPolicy(secret=’sosecret’,

2

callback=groupfinder)

3authz_policy = ACLAuthorizationPolicy()

4config = Configurator(root_factory=root_factory, settings=settings)

5config.set_authentication_policy(authn_policy)

6config.set_authorization_policy(authz_policy)

Note that the creation of an AuthTktAuthenticationPolicy requires two arguments: secret and callback. secret is a string representing an encryption key used by the “authentication ticket” machinery represented by this policy: it is required. The callback is a reference to a groupfinder function in the tutorial package’s security.py file. We haven’t added that module yet, but we’re about to.

When you’re done, your __init__.py will look like so:

1

from pyramid.config import Configurator

2

from pyramid_zodbconn import get_connection

3

 

4

from pyramid.authentication import AuthTktAuthenticationPolicy

5

from pyramid.authorization import ACLAuthorizationPolicy

6

 

7

from .models import appmaker

8

from .security import groupfinder

9

10def root_factory(request):

11conn = get_connection(request)

12return appmaker(conn.root())

13

14def main(global_config, **settings):

15""" This function returns a WSGI application.

16"""

17authn_policy = AuthTktAuthenticationPolicy(secret=’sosecret’,

18

callback=groupfinder)

19authz_policy = ACLAuthorizationPolicy()

20config = Configurator(root_factory=root_factory, settings=settings)

21config.set_authentication_policy(authn_policy)

22config.set_authorization_policy(authz_policy)

23config.add_static_view(’static’, ’static’, cache_max_age=3600)

24config.scan()

25return config.make_wsgi_app()

418

36.7. ADDING AUTHORIZATION

36.7.2 Add security.py

Add a security.py module within your package (in the same directory as __init__.py, views.py, etc.) with the following content:

1 USERS = {’editor’:’editor’,

2’viewer’:’viewer’}

3 GROUPS = {’editor’:[’group:editors’]}

4

5 def groupfinder(userid, request):

6if userid in USERS:

7return GROUPS.get(userid, [])

The groupfinder function defined here is an authentication policy “callback”; it is a callable that accepts a userid and a request. If the userid exists in the system, the callback will return a sequence of group identifiers (or an empty sequence if the user isn’t a member of any groups). If the userid does not exist in the system, the callback will return None. In a production system, user and group data will most often come from a database, but here we use “dummy” data to represent user and groups sources. Note that the editor user is a member of the group:editors group in our dummy group data (the GROUPS data structure).

36.7.3 Give Our Root Resource an ACL

We need to give our root resource object an ACL. This ACL will be sufficient to provide enough information to the Pyramid security machinery to challenge a user who doesn’t have appropriate credentials when he attempts to invoke the add_page or edit_page views.

We need to perform some imports at module scope in our models.py file:

1 from pyramid.security import Allow

2 from pyramid.security import Everyone

Our root resource object is a Wiki instance. We’ll add the following line at class scope to our Wiki class:

1

2

__acl__ = [ (Allow, Everyone, ’view’),

(Allow, ’group:editors’, ’edit’) ]

419

36. ZODB + TRAVERSAL WIKI TUTORIAL

It’s only happenstance that we’re assigning this ACL at class scope. An ACL can be attached to an object instance too; this is how “row level security” can be achieved in Pyramid applications. We actually only need one ACL for the entire system, however, because our security requirements are simple, so this feature is not demonstrated.

Our resulting models.py file will now look like so:

1

from persistent import Persistent

2

from persistent.mapping import PersistentMapping

3

 

4

from pyramid.security import Allow

5

from pyramid.security import Everyone

6

 

7

class Wiki(PersistentMapping):

8__name__ = None

9__parent__ = None

10

__acl__ = [ (Allow,

Everyone, ’view’),

11

(Allow,

’group:editors’, ’edit’) ]

12

13class Page(Persistent):

14def __init__(self, data):

15self.data = data

16

17def appmaker(zodb_root):

18if not ’app_root’ in zodb_root:

19app_root = Wiki()

20frontpage = Page(’This is the front page’)

21app_root[’FrontPage’] = frontpage

22frontpage.__name__ = ’FrontPage’

23frontpage.__parent__ = app_root

24zodb_root[’app_root’] = app_root

25import transaction

26transaction.commit()

27return zodb_root[’app_root’]

36.7.4 Add Login and Logout Views

We’ll add a login view which renders a login form and processes the post from the login form, checking credentials.

We’ll also add a logout view to our application and provide a link to it. This view will clear the credentials of the logged in user and redirect back to the front page.

We’ll add these views to the existing views.py file we have in our project. Here’s what the login view callable will look like:

420

 

36.7. ADDING AUTHORIZATION

 

 

1

@view_config(context=’.models.Wiki’, name=’login’,

2

renderer=’templates/login.pt’)

3

@forbidden_view_config(renderer=’templates/login.pt’)

4

def login(request):

5

login_url = request.resource_url(request.context, ’login’)

6referrer = request.url

7if referrer == login_url:

8 referrer = ’/’ # never use the login form itself as came_from

9 came_from = request.params.get(’came_from’, referrer)

10message = ’’

11login = ’’

12password = ’’

13if ’form.submitted’ in request.params:

14login = request.params[’login’]

15password = request.params[’password’]

16if USERS.get(login) == password:

17

headers = remember(request,

login)

18

return HTTPFound(location =

came_from,

19

headers = headers)

20

message = ’Failed login’

 

21

 

 

22return dict(

23message = message,

24url = request.application_url + ’/login’,

25came_from = came_from,

26login = login,

27password = password,

28)

Here’s what the logout view callable will look like:

1 @view_config(context=’.models.Wiki’, name=’logout’) 2 def logout(request):

3headers = forget(request)

4return HTTPFound(location = request.resource_url(request.context),

5

headers = headers)

Note that the login view callable has two view configuration decorators. The order of these decorators is unimportant. Each just adds a different view configuration for the login view callable.

The first view configuration decorator configures the login view callable so it will be invoked when someone visits /login (when the context is a Wiki and the view name is login). The second decorator, named forbidden_view_config specifies a forbidden view. This configures our login view to be presented to the user when Pyramid detects that a view invocation can not be authorized. Because we’ve

421

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]