summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2016-02-16 23:42:04 -0600
committerMichael Merickel <michael@merickel.org>2016-02-16 23:42:04 -0600
commit2fa90465bfdd213b6ce51ca8de6eaf9b614c283e (patch)
treefbbdb3550c4502cca1fd28620ea56d41dc74a968 /docs
parentf2c43689b50152d55ddc98e8f56754ee61f9a8c7 (diff)
downloadpyramid-2fa90465bfdd213b6ce51ca8de6eaf9b614c283e.tar.gz
pyramid-2fa90465bfdd213b6ce51ca8de6eaf9b614c283e.tar.bz2
pyramid-2fa90465bfdd213b6ce51ca8de6eaf9b614c283e.zip
add first cut at source for authorization chapter
Diffstat (limited to 'docs')
-rw-r--r--docs/tutorials/wiki2/src/authorization/development.ini2
-rw-r--r--docs/tutorials/wiki2/src/authorization/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py8
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/user.py6
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/routes.py50
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/security.py51
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja26
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja210
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja24
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja24
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py23
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/default.py53
12 files changed, 126 insertions, 93 deletions
diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini
index 99c4ff0fe..f3079727e 100644
--- a/docs/tutorials/wiki2/src/authorization/development.ini
+++ b/docs/tutorials/wiki2/src/authorization/development.ini
@@ -17,6 +17,8 @@ pyramid.includes =
sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite
+auth.secret = seekrit
+
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini
index cb1db3211..686dba48a 100644
--- a/docs/tutorials/wiki2/src/authorization/production.ini
+++ b/docs/tutorials/wiki2/src/authorization/production.ini
@@ -14,6 +14,8 @@ pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite
+auth.secret = real-seekrit
+
[server:main]
use = egg:waitress#main
host = 0.0.0.0
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 8eacdee5a..f5c033b8b 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -7,13 +7,7 @@ def main(global_config, **settings):
config = Configurator(settings=settings)
config.include('pyramid_jinja2')
config.include('.models')
+ config.include('.routes')
config.include('.security')
- 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()
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
index 25b0a8187..6fb32a1b2 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
@@ -18,10 +18,12 @@ class User(Base):
password_hash = Column(Text)
def set_password(self, pw):
- pwhash = bcrypt.hashpw(pw, bcrypt.gensalt())
+ pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
self.password_hash = pwhash
def check_password(self, pw):
if self.password_hash is not None:
- return bcrypt.hashpw(pw, self.password_hash) == self.password_hash
+ expected_hash = self.password_hash.encode('utf8')
+ actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash)
+ return expected_hash == actual_hash
return False
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py
new file mode 100644
index 000000000..c7c3a2120
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py
@@ -0,0 +1,50 @@
+from pyramid.httpexceptions import HTTPNotFound
+from pyramid.security import (
+ Allow,
+ Everyone,
+)
+
+from .models import Page
+
+def includeme(config):
+ 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}', factory=page_factory)
+ config.add_route('add_page', '/add_page/{pagename}',
+ factory=new_page_factory)
+ config.add_route('edit_page', '/{pagename}/edit_page',
+ factory=page_factory)
+
+def new_page_factory(request):
+ pagename = request.matchdict['pagename']
+ return NewPage(pagename)
+
+class NewPage(object):
+ def __init__(self, pagename):
+ self.pagename = pagename
+
+ def __acl__(self):
+ return [
+ (Allow, 'role:editor', 'create'),
+ (Allow, 'role:basic', 'create'),
+ ]
+
+def page_factory(request):
+ pagename = request.matchdict['pagename']
+ page = request.dbsession.query(Page).filter_by(name=pagename).first()
+ if page is None:
+ raise HTTPNotFound
+ return PageResource(page)
+
+class PageResource(object):
+ def __init__(self, page):
+ self.page = page
+
+ def __acl__(self):
+ return [
+ (Allow, Everyone, 'view'),
+ (Allow, 'role:editor', 'edit'),
+ (Allow, str(self.page.creator_id), 'edit'),
+ ]
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py
index 7bceabf3f..25cff7b05 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py
@@ -1,51 +1,40 @@
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
-
from pyramid.security import (
- Allow,
Authenticated,
Everyone,
)
+from .models import User
-USERS = {
- 'editor': 'editor',
- 'viewer': 'viewer',
-}
-
-GROUPS = {
- 'editor': ['group:editors'],
-}
class MyAuthenticationPolicy(AuthTktAuthenticationPolicy):
def authenticated_userid(self, request):
- userid = self.unauthenticated_userid(request)
- if userid in USERS:
- return userid
+ user = request.user
+ if user is not None:
+ return user.id
def effective_principals(self, request):
principals = [Everyone]
- userid = self.authenticated_userid(request)
- if userid is not None:
+ user = request.user
+ if user is not None:
principals.append(Authenticated)
- principals.append(userid)
-
- groups = GROUPS.get(userid, [])
- principals.extend(groups)
+ principals.append(str(user.id))
+ principals.append('role:' + user.role)
return principals
-class RootFactory(object):
- __acl__ = [
- (Allow, Everyone, 'view'),
- (Allow, 'group:editors', 'edit'),
- ]
-
- def __init__(self, request):
- pass
+def get_user(request):
+ user_id = request.unauthenticated_userid
+ if user_id is not None:
+ user = request.dbsession.query(User).get(user_id)
+ return user
def includeme(config):
- authn_policy = MyAuthenticationPolicy('sosecret', hashalg='sha512')
- authz_policy = ACLAuthorizationPolicy()
- config.set_root_factory(RootFactory)
+ settings = config.get_settings()
+ authn_policy = MyAuthenticationPolicy(
+ settings['auth.secret'],
+ hashalg='sha512',
+ )
config.set_authentication_policy(authn_policy)
- config.set_authorization_policy(authz_policy)
+ config.set_authorization_policy(ACLAuthorizationPolicy())
+ config.add_request_method(get_user, 'user', reify=True)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2
index e47b3aabf..7db25c674 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2
@@ -1,17 +1,17 @@
{% extends 'layout.jinja2' %}
-{% block title %}Edit {{page.name}} - {% endblock title %}
+{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %}
{% block content %}
<p>
-Editing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
+Editing <strong>{{pagename}}</strong>
</p>
<p>You can return to the
<a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
<form action="{{ save_url }}" method="post">
<div class="form-group">
- <textarea class="form-control" name="body" rows="10" cols="60">{{ page.data }}</textarea>
+ <textarea class="form-control" name="body" rows="10" cols="60">{{ pagedata }}</textarea>
</div>
<div class="form-group">
<button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2
index 82a144abf..44d14304e 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2
@@ -8,7 +8,7 @@
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="{{request.static_url('tutorial:static/pyramid-16x16.png')}}">
- <title>{% block title %}{% if page.name %} {{page.name}} - {% endif %}{% endblock title %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <title>{% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
@@ -33,9 +33,13 @@
</div>
<div class="col-md-10">
<div class="content">
- {% if request.authenticated_userid is not none %}
+ {% if request.user is none %}
<p class="pull-right">
- <a href="{{ request.route_url('logout') }}">Logout</a>
+ <a href="{{ request.route_url('login') }}">Login</a>
+ </p>
+ {% else %}
+ <p class="pull-right">
+ {{request.user.name}} <a href="{{request.route_url('logout')}}">Logout</a>
</p>
{% endif %}
{% block content %}{% endblock %}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2
index 99d369173..1806de0ff 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2
@@ -10,14 +10,14 @@
{{ message }}
</p>
<form action="{{ url }}" method="post">
-<input type="hidden" name="came_from" value="{{ came_from }}">
+<input type="hidden" name="next" value="{{ next_url }}">
<div class="form-group">
<label for="login">Username</label>
<input type="text" name="login" value="{{ login }}">
</div>
<div class="form-group">
<label for="password">Password</label>
- <input type="password" name="password" value="{{ password }}">
+ <input type="password" name="password">
</div>
<div class="form-group">
<button type="submit" name="form.submitted" value="Log In" class="btn btn-default">Log In</button>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2
index c582ce1f9..94419e228 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2
@@ -1,5 +1,7 @@
{% extends 'layout.jinja2' %}
+{% block subtitle %}{{page.name}} - {% endblock subtitle %}
+
{% block content %}
<p>{{ content|safe }}</p>
<p>
@@ -8,7 +10,7 @@
</a>
</p>
<p>
- Viewing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
+ Viewing <strong>{{page.name}}</strong>, created by <strong>{{page.creator.name}}</strong>.
</p>
<p>You can return to the
<a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py
index 08aa2bfad..d3db34132 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py
@@ -8,33 +8,28 @@ from pyramid.view import (
view_config,
)
-from ..security.default import USERS
+from ..models import User
-@view_config(route_name='login', renderer='templates/login.jinja2')
+@view_config(route_name='login', renderer='../templates/login.jinja2')
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)
+ next_url = request.params.get('next', request.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)
+ user = request.dbsession.query(User).filter_by(name=login).first()
+ if user is not None and user.check_password(password):
+ headers = remember(request, user.id)
+ return HTTPFound(location=next_url, headers=headers)
message = 'Failed login'
return dict(
message=message,
url=request.route_url('login'),
- came_from=came_from,
+ next_url=next_url,
login=login,
- password=password,
)
@view_config(route_name='logout')
@@ -45,5 +40,5 @@ def logout(request):
@forbidden_view_config()
def forbidden_view(request):
- next_url = request.route_url('login', _query={'came_from': request.url})
+ next_url = request.route_url('login', _query={'next': request.url})
return HTTPFound(location=next_url)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
index f74059be0..9358993ea 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
@@ -2,19 +2,15 @@ import cgi
import re
from docutils.core import publish_parts
-from pyramid.httpexceptions import (
- HTTPFound,
- HTTPNotFound,
- )
+from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config
from ..models import Page
-
# 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')
+@view_config(route_name='view_wiki')
def view_wiki(request):
next_url = request.route_url('view_page', pagename='FrontPage')
return HTTPFound(location=next_url)
@@ -22,12 +18,9 @@ def view_wiki(request):
@view_config(route_name='view_page', renderer='../templates/view.jinja2',
permission='view')
def view_page(request):
- pagename = request.matchdict['pagename']
- page = request.dbsession.query(Page).filter_by(name=pagename).first()
- if page is None:
- raise HTTPNotFound('No such page')
+ page = request.context.page
- def check(match):
+ def add_link(match):
word = match.group(1)
exists = request.dbsession.query(Page).filter_by(name=word).all()
if exists:
@@ -38,34 +31,34 @@ def view_page(request):
return '<a href="%s">%s</a>' % (add_url, cgi.escape(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)
+ content = wikiwords.sub(add_link, content)
+ edit_url = request.route_url('edit_page', pagename=page.name)
return dict(page=page, content=content, edit_url=edit_url)
+@view_config(route_name='edit_page', renderer='../templates/edit.jinja2',
+ permission='edit')
+def edit_page(request):
+ page = request.context.page
+ if 'form.submitted' in request.params:
+ page.data = request.params['body']
+ next_url = request.route_url('view_page', pagename=page.name)
+ return HTTPFound(location=next_url)
+ return dict(
+ pagename=page.name,
+ pagedata=page.data,
+ save_url=request.route_url('edit_page', pagename=page.name),
+ )
+
@view_config(route_name='add_page', renderer='../templates/edit.jinja2',
permission='create')
def add_page(request):
- pagename = request.matchdict['pagename']
+ pagename = request.context.pagename
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(name=pagename, data=body)
+ page.creator = request.user
request.dbsession.add(page)
next_url = request.route_url('view_page', pagename=pagename)
return HTTPFound(location=next_url)
save_url = request.route_url('add_page', pagename=pagename)
- page = Page(name='', data='')
- return dict(page=page, save_url=save_url)
-
-@view_config(route_name='edit_page', renderer='../templates/edit.jinja2',
- permission='edit')
-def edit_page(request):
- pagename = request.matchdict['pagename']
- page = request.dbsession.query(Page).filter_by(name=pagename).one()
- if 'form.submitted' in request.params:
- page.data = request.params['body']
- next_url = request.route_url('view_page', pagename=pagename)
- return HTTPFound(location=next_url)
- return dict(
- page=page,
- save_url=request.route_url('edit_page', pagename=pagename),
- )
+ return dict(pagename=pagename, pagedata='', save_url=save_url)