diff options
| author | Tres Seaver <tseaver@palladion.com> | 2024-06-10 12:09:42 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-10 12:09:42 -0400 |
| commit | ef0f6861e5b439afe43983f6c7437c30a413a34d (patch) | |
| tree | de670102b0123f2eea2ef399fd1e61cdfc5676b4 | |
| parent | 72f61853beda8e21b669c3520e43fe3e5b224ba3 (diff) | |
| parent | 1ebd9884e712463057de38fb4948a56c0c0982c5 (diff) | |
| download | pyramid-ef0f6861e5b439afe43983f6c7437c30a413a34d.tar.gz pyramid-ef0f6861e5b439afe43983f6c7437c30a413a34d.tar.bz2 pyramid-ef0f6861e5b439afe43983f6c7437c30a413a34d.zip | |
Merge pull request #3760 from Pylons/tseaver-qt_cleanup
docs: quick tutorial cleanups
17 files changed, 62 insertions, 276 deletions
diff --git a/docs/quick_tutorial/authentication.rst b/docs/quick_tutorial/authentication.rst index 3f6df17de..da76f3ec7 100644 --- a/docs/quick_tutorial/authentication.rst +++ b/docs/quick_tutorial/authentication.rst @@ -137,7 +137,7 @@ Subsequent requests return that cookie and identify the user. In our template, we fetched the ``logged_in`` value from the view class. We use this to calculate the logged-in user, if any. In the template we can then choose to show a login link to anonymous visitors or a logout link to logged-in -users. +users, including their login name. Extra credit diff --git a/docs/quick_tutorial/authentication/tutorial/home.pt b/docs/quick_tutorial/authentication/tutorial/home.pt index ed911b673..0e8508558 100644 --- a/docs/quick_tutorial/authentication/tutorial/home.pt +++ b/docs/quick_tutorial/authentication/tutorial/home.pt @@ -8,8 +8,10 @@ <div> <a tal:condition="view.logged_in is None" href="${request.application_url}/login">Log In</a> - <a tal:condition="view.logged_in is not None" - href="${request.application_url}/logout">Logout</a> + <span tal:condition="view.logged_in is not None"> + <a href="${request.application_url}/logout">Logout</a> + as ${view.logged_in} + </span> </div> <h1>Hi ${name}</h1> diff --git a/docs/quick_tutorial/authentication/tutorial/login.pt b/docs/quick_tutorial/authentication/tutorial/login.pt index 9e5bfe2ad..db8080fc8 100644 --- a/docs/quick_tutorial/authentication/tutorial/login.pt +++ b/docs/quick_tutorial/authentication/tutorial/login.pt @@ -8,8 +8,6 @@ <span tal:replace="message"/> <form action="${url}" method="post"> - <input type="hidden" name="came_from" - value="${came_from}"/> <label for="login">Username</label> <input type="text" id="login" name="login" diff --git a/docs/quick_tutorial/authentication/tutorial/views.py b/docs/quick_tutorial/authentication/tutorial/views.py index b2d9354ec..7c57d6371 100644 --- a/docs/quick_tutorial/authentication/tutorial/views.py +++ b/docs/quick_tutorial/authentication/tutorial/views.py @@ -33,10 +33,6 @@ class TutorialViews: def login(self): request = self.request login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use login form itself as came_from - came_from = request.params.get('came_from', referrer) message = '' login = '' password = '' @@ -46,7 +42,7 @@ class TutorialViews: hashed_pw = USERS.get(login) if hashed_pw and check_password(password, hashed_pw): headers = remember(request, login) - return HTTPFound(location=came_from, + return HTTPFound(location=request.route_url("home"), headers=headers) message = 'Failed login' @@ -54,7 +50,6 @@ class TutorialViews: name='Login', message=message, url=request.application_url + '/login', - came_from=came_from, login=login, password=password, ) diff --git a/docs/quick_tutorial/authorization.rst b/docs/quick_tutorial/authorization.rst index b1ef86a17..9a5b7c738 100644 --- a/docs/quick_tutorial/authorization.rst +++ b/docs/quick_tutorial/authorization.rst @@ -104,9 +104,17 @@ Of course, this only applies on ``Root``. Some other part of the site (a.k.a. *context*) might have a different ACL. If you are not logged in and visit ``/howdy``, you need to get shown the login -screen. How does Pyramid know what is the login page to use? We explicitly told -Pyramid that the ``login`` view should be used by decorating the view with -``@forbidden_view_config``. +screen. How does Pyramid know what is the login page to use? We defined an +explicit "forbidden view", decorating that view with +``@forbidden_view_config``, and then had it store the information about the +route being protected in the request's session, before redirecting to the +login view. + +.. note:: + + We use the session to store the ``came_from`` information, rather than a + hidden form input, in order to avoid trusting user-supplied data (from the + form or query string) when constructing redirect URLs. Extra credit diff --git a/docs/quick_tutorial/authorization/tutorial/__init__.py b/docs/quick_tutorial/authorization/tutorial/__init__.py index 255bb35ac..f59d5ab6d 100644 --- a/docs/quick_tutorial/authorization/tutorial/__init__.py +++ b/docs/quick_tutorial/authorization/tutorial/__init__.py @@ -1,11 +1,16 @@ from pyramid.config import Configurator +from pyramid.session import SignedCookieSessionFactory from .security import SecurityPolicy def main(global_config, **settings): - config = Configurator(settings=settings, - root_factory='.resources.Root') + my_session_factory = SignedCookieSessionFactory('itsaseekreet') + config = Configurator( + settings=settings, + root_factory='.resources.Root', + session_factory=my_session_factory, + ) config.include('pyramid_chameleon') config.set_security_policy( diff --git a/docs/quick_tutorial/authorization/tutorial/home.pt b/docs/quick_tutorial/authorization/tutorial/home.pt index ed911b673..0e8508558 100644 --- a/docs/quick_tutorial/authorization/tutorial/home.pt +++ b/docs/quick_tutorial/authorization/tutorial/home.pt @@ -8,8 +8,10 @@ <div> <a tal:condition="view.logged_in is None" href="${request.application_url}/login">Log In</a> - <a tal:condition="view.logged_in is not None" - href="${request.application_url}/logout">Logout</a> + <span tal:condition="view.logged_in is not None"> + <a href="${request.application_url}/logout">Logout</a> + as ${view.logged_in} + </span> </div> <h1>Hi ${name}</h1> diff --git a/docs/quick_tutorial/authorization/tutorial/login.pt b/docs/quick_tutorial/authorization/tutorial/login.pt index 9e5bfe2ad..db8080fc8 100644 --- a/docs/quick_tutorial/authorization/tutorial/login.pt +++ b/docs/quick_tutorial/authorization/tutorial/login.pt @@ -8,8 +8,6 @@ <span tal:replace="message"/> <form action="${url}" method="post"> - <input type="hidden" name="came_from" - value="${came_from}"/> <label for="login">Username</label> <input type="text" id="login" name="login" diff --git a/docs/quick_tutorial/authorization/tutorial/views.py b/docs/quick_tutorial/authorization/tutorial/views.py index b9c828086..9b3bbe42a 100644 --- a/docs/quick_tutorial/authorization/tutorial/views.py +++ b/docs/quick_tutorial/authorization/tutorial/views.py @@ -30,33 +30,58 @@ class TutorialViews: def hello(self): return {'name': 'Hello View'} + @forbidden_view_config() + def forbidden(self): + request = self.request + session = request.session + if request.matched_route is not None: + session['came_from'] = { + 'route_name': request.matched_route.name, + 'route_kwargs': request.matchdict, + } + if request.authenticated_userid is not None: + session['message'] = ( + f'User {request.authenticated_userid} is not allowed ' + f'to see this resource. Please log in as another user.' + ) + else: + if 'came_from' in session: + del session['came_from'] + + return HTTPFound(request.route_url('login')) + @view_config(route_name='login', renderer='login.pt') - @forbidden_view_config(renderer='login.pt') def login(self): request = self.request + session = request.session login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' + came_from = session.get('came_from') + message = session.get('message', '') login = '' password = '' + if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] hashed_pw = USERS.get(login) if hashed_pw and check_password(password, hashed_pw): headers = remember(request, login) - return HTTPFound(location=came_from, - headers=headers) + + if came_from is not None: + return_to = request.route_url( + came_from['route_name'], **came_from['route_kwargs'], + ) + else: + return_to = request.route_url('home') + + return HTTPFound(location=return_to, headers=headers) + message = 'Failed login' return dict( name='Login', message=message, url=request.application_url + '/login', - came_from=came_from, login=login, password=password, ) diff --git a/docs/quick_tutorial/retail_forms/development.ini b/docs/quick_tutorial/retail_forms/development.ini deleted file mode 100644 index 78d7479e7..000000000 --- a/docs/quick_tutorial/retail_forms/development.ini +++ /dev/null @@ -1,9 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true -pyramid.includes = - pyramid_debugtoolbar - -[server:main] -use = egg:waitress#main -listen = localhost:6543 diff --git a/docs/quick_tutorial/retail_forms/setup.py b/docs/quick_tutorial/retail_forms/setup.py deleted file mode 100644 index dda0a2cc4..000000000 --- a/docs/quick_tutorial/retail_forms/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -from setuptools import setup - -# List of dependencies installed via `pip install -e .` -# by virtue of the Setuptools `install_requires` value below. -requires = [ - 'deform', - 'pyramid', - 'pyramid_chameleon', - 'waitress', -] - -setup( - name='tutorial', - install_requires=requires, - entry_points={ - 'paste.app_factory': [ - 'main = tutorial:main' - ], - }, -) diff --git a/docs/quick_tutorial/retail_forms/tutorial/__init__.py b/docs/quick_tutorial/retail_forms/tutorial/__init__.py deleted file mode 100644 index dff7457cf..000000000 --- a/docs/quick_tutorial/retail_forms/tutorial/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.include('pyramid_chameleon') - config.add_route('wiki_view', '/') - config.add_route('wikipage_add', '/add') - config.add_route('wikipage_view', '/{uid}') - config.add_route('wikipage_edit', '/{uid}/edit') - config.add_static_view('deform_static', 'deform:static/') - config.scan('.views') - return config.make_wsgi_app() diff --git a/docs/quick_tutorial/retail_forms/tutorial/tests.py b/docs/quick_tutorial/retail_forms/tutorial/tests.py deleted file mode 100644 index 5a2c40904..000000000 --- a/docs/quick_tutorial/retail_forms/tutorial/tests.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest - -from pyramid import testing - - -class TutorialViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_home(self): - from .views import WikiViews - - request = testing.DummyRequest() - inst = WikiViews(request) - response = inst.wiki_view() - self.assertEqual(len(response['pages']), 3) - - -class TutorialFunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - - app = main({}) - from webtest import TestApp - - self.testapp = TestApp(app) - - def tearDown(self): - testing.tearDown() - - def test_home(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'<title>Wiki: View</title>', res.body) diff --git a/docs/quick_tutorial/retail_forms/tutorial/views.py b/docs/quick_tutorial/retail_forms/tutorial/views.py deleted file mode 100644 index c6f4e3877..000000000 --- a/docs/quick_tutorial/retail_forms/tutorial/views.py +++ /dev/null @@ -1,96 +0,0 @@ -import colander -import deform.widget - -from pyramid.httpexceptions import HTTPFound -from pyramid.view import view_config - -pages = { - '100': dict(uid='100', title='Page 100', body='<em>100</em>'), - '101': dict(uid='101', title='Page 101', body='<em>101</em>'), - '102': dict(uid='102', title='Page 102', body='<em>102</em>') -} - -class WikiPage(colander.MappingSchema): - title = colander.SchemaNode(colander.String()) - body = colander.SchemaNode( - colander.String(), - widget=deform.widget.RichTextWidget() - ) - - -class WikiViews: - def __init__(self, request): - self.request = request - - @property - def wiki_form(self): - schema = WikiPage() - return deform.Form(schema, buttons=('submit',)) - - @property - def reqts(self): - return self.wiki_form.get_widget_resources() - - @view_config(route_name='wiki_view', renderer='wiki_view.pt') - def wiki_view(self): - return dict(pages=pages.values()) - - @view_config(route_name='wikipage_add', - renderer='wikipage_addedit.pt') - def wikipage_add(self): - form = self.wiki_form - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = self.wiki_form.validate(controls) - except deform.ValidationFailure as e: - # Form is NOT valid - return dict(form=e.render()) - - # Form is valid, make a new identifier and add to list - last_uid = int(sorted(pages.keys())[-1]) - new_uid = str(last_uid + 1) - pages[new_uid] = dict( - uid=new_uid, title=appstruct['title'], - body=appstruct['body'] - ) - - # Now visit new page - url = self.request.route_url('wikipage_view', uid=new_uid) - return HTTPFound(url) - - return dict(form=form) - - @view_config(route_name='wikipage_view', renderer='wikipage_view.pt') - def wikipage_view(self): - uid = self.request.matchdict['uid'] - page = pages[uid] - return dict(page=page) - - @view_config(route_name='wikipage_edit', - renderer='wikipage_addedit.pt') - def wikipage_edit(self): - uid = self.request.matchdict['uid'] - page = pages[uid] - - wiki_form = self.wiki_form - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = wiki_form.validate(controls) - except deform.ValidationFailure as e: - return dict(page=page, form=e.render()) - - # Change the content and redirect to the view - page['title'] = appstruct['title'] - page['body'] = appstruct['body'] - - url = self.request.route_url('wikipage_view', - uid=page['uid']) - return HTTPFound(url) - - form = wiki_form.render(page) - - return dict(page=page, form=form)
\ No newline at end of file diff --git a/docs/quick_tutorial/retail_forms/tutorial/wiki_view.pt b/docs/quick_tutorial/retail_forms/tutorial/wiki_view.pt deleted file mode 100644 index 9e3afe495..000000000 --- a/docs/quick_tutorial/retail_forms/tutorial/wiki_view.pt +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>Wiki: View</title> -</head> -<body> -<h1>Wiki</h1> - -<a href="${request.route_url('wikipage_add')}">Add - WikiPage</a> -<ul> - <li tal:repeat="page pages"> - <a href="${request.route_url('wikipage_view', uid=page.uid)}"> - ${page.title} - </a> - </li> -</ul> -</body> -</html>
\ No newline at end of file diff --git a/docs/quick_tutorial/retail_forms/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/retail_forms/tutorial/wikipage_addedit.pt deleted file mode 100644 index 586f4c44b..000000000 --- a/docs/quick_tutorial/retail_forms/tutorial/wikipage_addedit.pt +++ /dev/null @@ -1,37 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>WikiPage: Add/Edit</title> - <tal:block tal:repeat="reqt view.reqts['css']"> - <link rel="stylesheet" type="text/css" - href="${request.static_url('deform:static/' + reqt)}"/> - </tal:block> - <tal:block tal:repeat="reqt view.reqts['js']"> - <script src="${request.static_url('deform:static/' + reqt)}" - type="text/javascript"></script> - </tal:block> -</head> -<body> -<h1>Wiki</h1> - -<div class="row" - tal:repeat="field form"> - <div class="span2"> - ${structure:field.title} - <span class="req" tal:condition="field.required">*</span> - </div> - <div class="span2"> - ${structure:field.serialize()} - </div> - <ul tal:condition="field.error"> - <li tal:repeat="error field.error.messages()"> - ${structure:error} - </li> - </ul> -</div> - -<script type="text/javascript"> - deform.load() -</script> -</body> -</html> diff --git a/docs/quick_tutorial/retail_forms/tutorial/wikipage_view.pt b/docs/quick_tutorial/retail_forms/tutorial/wikipage_view.pt deleted file mode 100644 index cb9ff526e..000000000 --- a/docs/quick_tutorial/retail_forms/tutorial/wikipage_view.pt +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>WikiPage: View</title> -</head> -<body> -<a href="${request.route_url('wiki_view')}"> - Up -</a> | -<a href="${request.route_url('wikipage_edit', uid=page.uid)}"> - Edit -</a> - -<h1>${page.title}</h1> -<p>${structure: page.body}</p> -</body> -</html>
\ No newline at end of file |
