認証を追加する¶
Pyramid は authentication と authorization の
ための機能を提供しています。アプリケーションにセキュリティを追加する
ためにこれら両方の機能を利用します。私たちのアプリケーションは現在、
サーバーにアクセスできる誰もが wiki ページを見たり、編集したり、
追加したりすることが可能です。 wiki ページの追加や編集を group:editors
という名前の グループ のメンバーである限られた人々だけに許可するように
アプリケーションを変更してみましょう。しかし、依然としてサーバーに
アクセスできる誰でもページを見ることは可能です。
さらに、ログインページとすべてのページにログアウトリンクを追加します。 ログインページは、ユーザがパーミッションを必要とする任意のビューへのアクセスを 拒否された場合に、デフォルトの “403 Forbidden” ページの代わりに表示されます。
次のステップでアクセス制御を実装していきます。
- ユーザとグループを追加 (
security.py
という新しいモジュール) - ACL を追加 (
models.py
と__init__.py
) - authentication policy と authorization policy を追加
(
__init__.py
) edit_page
およびadd_page
ビューに permission 宣言を 追加 (views.py
)
次にログインとログアウトの機能を追加します:
- /login と /logout に対する route を追加 (
__init__.py
) login
およびlogout
ビューを追加 (views.py
)- ログインテンプレートを追加 (
login.pt
) - 既存のビューがレンダラーに
logged_in
フラグを返すように修正 (views.py
) - “Logout” リンクを追加して、ログイン中にページを閲覧または編集しているときに
表示されるようにする (
view.pt
,edit.pt
)
このチュートリアルステージのソースコードを以下の場所で閲覧することができます。 http://github.com/Pylons/pyramid/tree/1.3-branch/docs/tutorials/wiki2/src/authorization/.
アクセス制御¶
ユーザとグループの追加¶
新しい tutorial/tutorial/security.py
モジュールを以下の内容で作成します:
1 2 3 4 5 6 7 | USERS = {'editor':'editor',
'viewer':'viewer'}
GROUPS = {'editor':['group:editors']}
def groupfinder(userid, request):
if userid in USERS:
return GROUPS.get(userid, [])
|
groupfinder
関数は、 userid と request を受け取って、以下のどちらかを
返します:
- userid がシステムに存在すれば、グループ識別子のシーケンス (ユーザがどのグループのメンバーでもなければ空のシーケンス) を返します。
- userid がシステムに存在 しなければ 、
None
を返します。
例えば、 groupfinder('editor', request )
は [‘group:editor’] を返し、
groupfinder('viewer', request)
は [] を、
groupfinder('admin', request)
は None
を返します。
あとで、ユーザに principal (複数可) を提供する
authentication policy “コールバック” として groupfinder()
を使用します。
プロダクションシステムでは、ユーザとグループデータは、ほとんどの場合 データベースから取得されますが、ここではユーザとグループのソースを表わすために 「ダミーの」データを使用します。
ACL の追加¶
tutorial/tutorial/models.py
を開いて先頭に以下のインポート文を
追加してください:
1 2 3 4 | from pyramid.security import (
Allow,
Everyone,
)
|
以下のクラス定義を追加してください:
1 2 3 4 5 | class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
(Allow, 'group:editors', 'edit') ]
def __init__(self, request):
pass
|
Allow
(パーミッションが許可されることを意味す
るアクション) と Everyone
(すべてのリクエスト
に関連付けられる特別な principal) をインポートします。両者は ACL
を構成するために ACE エントリの中で使用されます。
ACL はリストで、 __acl__ という名前のクラス属性である必要があります。
ここでは2つの ACE エントリを持つ ACL を定義しています:
1番目のエントリは、あらゆるユーザに view パーミッションを与えます。
2番目のエントリは、 group:editors
principal に edit パーミッション
を与えます。
ACL を含む RootFactory
クラスは root factory です。
それを Pyramid アプリケーションに関連付ける必要があります。
それにより ACL が各ビューに対してリクエストの context で
(context
属性として) 提供されます。
tutorial/tutorial/__init__.py
を開いて、 Configurator
コンストラクタに root_factory
パラメータを追加してください。
それは上で作成したクラスを指します:
1 2 | config = Configurator(settings=settings,
root_factory='tutorial.models.RootFactory')
|
(ハイライトされた行は変更が必要な箇所です)
これでアプリケーションに ACL を渡せるようになりました。 ACL が 何を表わすかについての詳細は Assigning ACLs to your Resource Objects を参照してください。
認証と認可のポリシーを追加する¶
tutorial/__init__.py
を開いて、これらのインポート文を追加してください:
1 2 3 | from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from tutorial.security import groupfinder
|
そうしたら、設定にこれらのポリシーを加えてください:
1 2 3 4 5 6 7 | authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
root_factory='tutorial.models.RootFactory')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
|
(ハイライトされた行は変更が必要な箇所です)
AuthTktAuthenticationPolicy
を有効にしています。
それは、リクエストに含まれている可能性のある auth チケットと、
ACL を使用してビューに対する許可または禁止の結果を決定する
ACLAuthorizationPolicy
に基づきます。
pyramid.authentication.AuthTktAuthenticationPolicy
コンストラクタ
は 2 つの引数を受け付けます: secret
と callback
です。 secret
は、
このポリシーによって表わされる「認証チケット」機構によって使用される
暗号鍵を表わす文字列です: これは必須です。 callback
は、以前作成した
groupfinder
関数です。
パーミッション宣言を追加する¶
permission='edit'
パラメータを add_page()
と edit_page()
に対する
@view_config
デコレータに追加してください。例えば:
1 2 | @view_config(route_name='add_page', renderer='templates/edit.pt',
permission='edit')
|
(ハイライトされた行は変更が必要な箇所です)
その結果は、リクエストの時点で edit
パーミッションを所有するユーザ
だけが、それら2つのビューを起動できるということです。
permission='view'
パラメータを view_wiki()
と view_page()
に対する
@view_config
デコレータに追加してください。例えば:
1 2 | @view_config(route_name='view_page', renderer='templates/view.pt',
permission='view')
|
(ハイライトされた行は変更が必要な箇所です)
これは、誰でもこれら2つのビューを起動できるようにします。
これでアクセスを制御するのに必要とされる変更が終わりました。 続いての変更は、ログインおよびログアウト機能を追加することです。
ログイン、ログアウト¶
/login と /logout に対する route を追加する¶
tutorial/tutorial/__init__.py
に戻り、これら2つの route を追加してください:
1 2 | config.add_route('login', '/login')
config.add_route('logout', '/logout')
|
Login と Logout ビューを追加する¶
ログインフォームをレンダリングしたり、ログインフォームから送信された
データを処理して認証情報 (credentials) をチェックしたりする login
ビューを追加します。
さらに、アプリケーションに logout
ビュー callable を加えて、
それへのリンクを提供します。このビューは、ログインユーザの
資格をクリアして、フロントページにリダイレクトします。
tutorial/tutorial/views.py
の先頭に、以下のインポート文を追加してください:
1 2 3 4 5 6 7 8 9 10 11 | from pyramid.view import (
view_config,
forbidden_view_config,
)
from pyramid.security import (
remember,
forget,
)
from .security import USERS
|
(ハイライトされた行は変更が必要な箇所です)
forbidden_view_config()
は、デフォルトの 403
Forbidden ページをカスタマイズするために使用されます。
remember()
と forget()
は、
auth チケットのクッキーの作成と破棄をサポートします。
そして login
および logout
ビューを追加します:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @view_config(route_name='login', renderer='templates/login.pt')
@forbidden_view_config(renderer='templates/login.pt')
def login(request):
login_url = request.route_url('login')
referrer = request.url
if referrer == login_url:
referrer = '/' # never use the login form itself as came_from
came_from = request.params.get('came_from', referrer)
message = ''
login = ''
password = ''
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
if USERS.get(login) == password:
headers = remember(request, login)
return HTTPFound(location = came_from,
headers = headers)
message = 'Failed login'
return dict(
message = message,
url = request.application_url + '/login',
came_from = came_from,
login = login,
password = password,
)
@view_config(route_name='logout')
def logout(request):
headers = forget(request)
return HTTPFound(location = request.route_url('view_wiki'),
headers = headers)
|
login
は 2 つのデコレータでデコレートされています:
- ビュー関数を
login
ルートに関連付け、/login
を訪れたときに それが表示されるようにする@view_config
。 - ビュー関数を exception view に変換する
@forbidden_view_config
。login()
は、ユーザが許可されないビュー callable を実行しようとする 時に起動されます。例えば、もしユーザがログインせずに Wiki ページを 追加または編集しようとすれば、その続きが許可される前にログインフォーム が表示されます。
これら2つの view configuration デコレータの順番は重要ではありません。
logout()
は、 logout
ルートに関連付ける
@view_config
デコレータでデコレートされます。これは、
/logout
を訪れたときに起動されます。
login.pt
テンプレートを追加する¶
以下の内容で tutorial/tutorial/templates/login.pt
を作成してください:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
<title>Login - Pyramid tutorial wiki (based on TurboGears
20-Minute Wiki)</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
<link rel="shortcut icon"
href="${request.static_url('tutorial:static/favicon.ico')}" />
<link rel="stylesheet"
href="${request.static_url('tutorial:static/pylons.css')}"
type="text/css" media="screen" charset="utf-8" />
<!--[if lte IE 6]>
<link rel="stylesheet"
href="${request.static_url('tutorial:static/ie6.css')}"
type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="top-small">
<div class="top-small align-center">
<div>
<img width="220" height="50" alt="pyramid"
src="${request.static_url('tutorial:static/pyramid-small.png')}" />
</div>
</div>
</div>
<div id="middle">
<div class="middle align-right">
<div id="left" class="app-welcome align-left">
<b>Login</b><br/>
<span tal:replace="message"/>
</div>
<div id="right" class="app-welcome align-right"></div>
</div>
</div>
<div id="bottom">
<div class="bottom">
<form action="${url}" method="post">
<input type="hidden" name="came_from" value="${came_from}"/>
<input type="text" name="login" value="${login}"/><br/>
<input type="password" name="password"
value="${password}"/><br/>
<input type="submit" name="form.submitted" value="Log In"/>
</form>
</div>
</div>
</div>
<div id="footer">
<div class="footer"
>© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html>
上記のテンプレートは、さきほど views.py
に追加した login ビュー内で
参照されます。
logged_in フラグをレンダラーに返す¶
tutorial/tutorial/views.py
の先頭のインポートに以下の行を追加してください:
1 2 3 4 5 | from pyramid.security import (
remember,
forget,
authenticated_userid,
)
|
(ハイライトされた行は変更が必要な箇所です)
logged_in
パラメータを以下のように view_page()
, edit_page()
,
add_page()
の戻り値に追加してください:
1 2 3 4 | return dict(page = page,
content = content,
edit_url = edit_url,
logged_in = authenticated_userid(request))
|
(ハイライトされた行は変更が必要な箇所です)
authenticated_userid()
は、もしユーザが認証
されなければ None を返し、ユーザが認証されれば何らかのユーザ id を
返します。
ログインした時に “Logout” リンクを追加する¶
tutorial/tutorial/templates/edit.pt
と
tutorial/tutorial/templates/view.pt
を開いて
<div id="right" class="app-welcome align-right">
div の内側にこれを
追加してください:
<span tal:condition="logged_in">
<a href="${request.application_url}/logout">Logout</a>
</span>
logged_in
が任意のユーザ id である場合、属性 tal:condition="logged_in"
が要素に含まれるようになります。このリンクはログアウトビューを起動します。
ユーザが認証されていない場合のように logged_in
が None
なら、上記の
要素は含まれません。
変更点を確認する¶
最終的に tutorial/tutorial/__init__.py
はこのようになっているはずです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from sqlalchemy import engine_from_config
from tutorial.security import groupfinder
from .models import (
DBSession,
Base,
)
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
root_factory='tutorial.models.RootFactory')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('login', '/login')
config.add_route('logout', '/logout')
config.add_route('view_page', '/{pagename}')
config.add_route('add_page', '/add_page/{pagename}')
config.add_route('edit_page', '/{pagename}/edit_page')
config.scan()
return config.make_wsgi_app()
|
(ハイライトされた行は変更が必要な箇所です)
最終的に tutorial/tutorial/models.py
はこのようになっているはずです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | from pyramid.security import (
Allow,
Everyone,
)
from sqlalchemy import (
Column,
Integer,
Text,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
scoped_session,
sessionmaker,
)
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
class Page(Base):
""" The SQLAlchemy declarative model class for a Page object. """
__tablename__ = 'pages'
id = Column(Integer, primary_key=True)
name = Column(Text, unique=True)
data = Column(Text)
def __init__(self, name, data):
self.name = name
self.data = data
class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
(Allow, 'group:editors', 'edit') ]
def __init__(self, request):
pass
|
(ハイライトされた行は変更が必要な箇所です)
最終的に tutorial/tutorial/views.py
はこのようになっているはずです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | import re
from docutils.core import publish_parts
from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
)
from pyramid.view import (
view_config,
forbidden_view_config,
)
from pyramid.security import (
remember,
forget,
authenticated_userid,
)
from .models import (
DBSession,
Page,
)
from .security import USERS
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
@view_config(route_name='view_wiki',
permission='view')
def view_wiki(request):
return HTTPFound(location = request.route_url('view_page',
pagename='FrontPage'))
@view_config(route_name='view_page', renderer='templates/view.pt',
permission='view')
def view_page(request):
pagename = request.matchdict['pagename']
page = DBSession.query(Page).filter_by(name=pagename).first()
if page is None:
return HTTPNotFound('No such page')
def check(match):
word = match.group(1)
exists = DBSession.query(Page).filter_by(name=word).all()
if exists:
view_url = request.route_url('view_page', pagename=word)
return '<a href="%s">%s</a>' % (view_url, word)
else:
add_url = request.route_url('add_page', pagename=word)
return '<a href="%s">%s</a>' % (add_url, word)
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
edit_url = request.route_url('edit_page', pagename=pagename)
return dict(page=page, content=content, edit_url=edit_url,
logged_in=authenticated_userid(request))
@view_config(route_name='add_page', renderer='templates/edit.pt',
permission='edit')
def add_page(request):
pagename = request.matchdict['pagename']
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(pagename, body)
DBSession.add(page)
return HTTPFound(location = request.route_url('view_page',
pagename=pagename))
save_url = request.route_url('add_page', pagename=pagename)
page = Page('', '')
return dict(page=page, save_url=save_url,
logged_in=authenticated_userid(request))
@view_config(route_name='edit_page', renderer='templates/edit.pt',
permission='edit')
def edit_page(request):
pagename = request.matchdict['pagename']
page = DBSession.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
DBSession.add(page)
return HTTPFound(location = request.route_url('view_page',
pagename=pagename))
return dict(
page=page,
save_url = request.route_url('edit_page', pagename=pagename),
logged_in=authenticated_userid(request),
)
@view_config(route_name='login', renderer='templates/login.pt')
@forbidden_view_config(renderer='templates/login.pt')
def login(request):
login_url = request.route_url('login')
referrer = request.url
if referrer == login_url:
referrer = '/' # never use the login form itself as came_from
came_from = request.params.get('came_from', referrer)
message = ''
login = ''
password = ''
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
if USERS.get(login) == password:
headers = remember(request, login)
return HTTPFound(location = came_from,
headers = headers)
message = 'Failed login'
return dict(
message = message,
url = request.application_url + '/login',
came_from = came_from,
login = login,
password = password,
)
@view_config(route_name='logout')
def logout(request):
headers = forget(request)
return HTTPFound(location = request.route_url('view_wiki'),
headers = headers)
|
(ハイライトされた行は変更が必要な箇所です)
最終的に tutorial/tutorial/templates/edit.pt
テンプレートは
このようになっているはずです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
<title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
<link rel="shortcut icon"
href="${request.static_url('tutorial:static/favicon.ico')}" />
<link rel="stylesheet"
href="${request.static_url('tutorial:static/pylons.css')}"
type="text/css" media="screen" charset="utf-8" />
<!--[if lte IE 6]>
<link rel="stylesheet"
href="${request.static_url('tutorial:static/ie6.css')}"
type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="top-small">
<div class="top-small align-center">
<div>
<img width="220" height="50" alt="pyramid"
src="${request.static_url('tutorial:static/pyramid-small.png')}" />
</div>
</div>
</div>
<div id="middle">
<div class="middle align-right">
<div id="left" class="app-welcome align-left">
Editing <b><span tal:replace="page.name">Page Name
Goes Here</span></b><br/>
You can return to the
<a href="${request.application_url}">FrontPage</a>.<br/>
</div>
<div id="right" class="app-welcome align-right">
<span tal:condition="logged_in">
<a href="${request.application_url}/logout">Logout</a>
</span>
</div>
</div>
</div>
<div id="bottom">
<div class="bottom">
<form action="${save_url}" method="post">
<textarea name="body" tal:content="page.data" rows="10"
cols="60"/><br/>
<input type="submit" name="form.submitted" value="Save"/>
</form>
</div>
</div>
</div>
<div id="footer">
<div class="footer"
>© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html>
|
(ハイライトされた行は変更が必要な箇所です)
最終的に tutorial/tutorial/templates/view.pt
テンプレートは
このようになっているはずです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
<title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
<link rel="shortcut icon"
href="${request.static_url('tutorial:static/favicon.ico')}" />
<link rel="stylesheet"
href="${request.static_url('tutorial:static/pylons.css')}"
type="text/css" media="screen" charset="utf-8" />
<!--[if lte IE 6]>
<link rel="stylesheet"
href="${request.static_url('tutorial:static/ie6.css')}"
type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="top-small">
<div class="top-small align-center">
<div>
<img width="220" height="50" alt="pyramid"
src="${request.static_url('tutorial:static/pyramid-small.png')}" />
</div>
</div>
</div>
<div id="middle">
<div class="middle align-right">
<div id="left" class="app-welcome align-left">
Viewing <b><span tal:replace="page.name">Page Name
Goes Here</span></b><br/>
You can return to the
<a href="${request.application_url}">FrontPage</a>.<br/>
</div>
<div id="right" class="app-welcome align-right">
<span tal:condition="logged_in">
<a href="${request.application_url}/logout">Logout</a>
</span>
</div>
</div>
</div>
<div id="bottom">
<div class="bottom">
<div tal:replace="structure content">
Page text goes here.
</div>
<p>
<a tal:attributes="href edit_url" href="">
Edit this page
</a>
</p>
</div>
</div>
</div>
<div id="footer">
<div class="footer"
>© Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html>
|
(ハイライトされた行は変更が必要な箇所です)
ブラウザでアプリケーションを表示する¶
最終的に、ブラウザの中でアプリケーションを実行することができます (アプリケーションの起動 参照)。 ブラウザを起動して以下の各 URL にアクセスして、結果が期待通りであることを 確認してください:
http://localhost:6543/
はview_wiki
ビューを起動します。 これは常に FrontPage page オブジェクトのview_page
ビューに リダイレクトします。このビューは任意のユーザによって実行可能です。
http://localhost:6543/FrontPage
は FrontPage page オブジェクトのview_page
ビューを起動します。
http://localhost:6543/FrontPage/edit_page
は FrontPage オブジェクト に対応する edit ビューを起動します。それはeditor
ユーザだけが 実行可能です。異なるユーザ (あるいは匿名ユーザ) が起動すると、ログイン フォームが表示されます。認証情報としてユーザー名editor
、パスワードeditor
を入力すると編集ページフォームが表示されるでしょう。
http://localhost:6543/add_page/SomePageName
は、ページに対する add ビューを起動します。それはeditor
ユーザだけが実行可能です。 異なるユーザ (あるいは匿名ユーザ) が起動すると、ログインフォームが 表示されます。認証情報としてユーザー名editor
、パスワードeditor
を入力すると編集ページフォームが表示されるでしょう。
- (edit または add ページにアクセスしてログインフォームに
editor
認証を送信した結果として) ログイン後に、右上に Logout リンクが表示 されているでしょう。それをクリックするとログアウトして、 フロントページにリダイレクトされます。