From 0905d2015e35e827c3fdb2135695710b80d549a5 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 8 Oct 2013 11:50:11 -0500 Subject: Subclass HTTPBadCSRFToken from HTTPBadRequest and have request.session.check_csrf_token use the new exception. This supports a more fine-grained exception trapping. --- docs/api/httpexceptions.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/api/httpexceptions.rst b/docs/api/httpexceptions.rst index 6a08d1048..0fdd0f0e9 100644 --- a/docs/api/httpexceptions.rst +++ b/docs/api/httpexceptions.rst @@ -7,9 +7,12 @@ .. attribute:: status_map - A mapping of integer status code to exception class (eg. the - integer "401" maps to - :class:`pyramid.httpexceptions.HTTPUnauthorized`). + A mapping of integer status code to HTTP exception class (eg. the integer + "401" maps to :class:`pyramid.httpexceptions.HTTPUnauthorized`). All + mapped exception classes are children of :class:`pyramid.httpexceptions`, + i.e. the :ref:`pyramid_specific_http_exceptions` such as + :class:`pyramid.httpexceptions.HTTPBadRequest.BadCSRFToken` are not + mapped. .. autofunction:: exception_response @@ -106,3 +109,13 @@ .. autoclass:: HTTPVersionNotSupported .. autoclass:: HTTPInsufficientStorage + + +.. _pyramid_specific_http_exceptions: + +Pyramid-specific HTTP Exceptions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each Pyramid-specific HTTP exception has the status code of it's parent. + + .. autoclass:: HTTPBadCSRFToken -- cgit v1.2.3 From cd218d2934c87260bbb10620e3b419b275fe6244 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 14 Oct 2013 16:05:50 +0200 Subject: make these tests pass on python 3.2+ --- docs/tutorials/wiki/src/tests/tutorial/tests.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py index c435a4519..5add04c20 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/tests.py +++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py @@ -158,11 +158,11 @@ class FunctionalTests(unittest.TestCase): def test_FrontPage(self): res = self.testapp.get('/FrontPage', status=200) - self.assertTrue('FrontPage' in res.body) + self.assertTrue(b'FrontPage' in res.body) def test_unexisting_page(self): res = self.testapp.get('/SomePage', status=404) - self.assertTrue('Not Found' in res.body) + self.assertTrue(b'Not Found' in res.body) def test_successful_log_in(self): res = self.testapp.get( self.viewer_login, status=302) @@ -170,48 +170,48 @@ class FunctionalTests(unittest.TestCase): def test_failed_log_in(self): res = self.testapp.get( self.viewer_wrong_login, status=200) - self.assertTrue('login' in res.body) + self.assertTrue(b'login' in res.body) def test_logout_link_present_when_logged_in(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/FrontPage', status=200) - self.assertTrue('Logout' in res.body) + self.assertTrue(b'Logout' in res.body) def test_logout_link_not_present_after_logged_out(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/FrontPage', status=200) res = self.testapp.get('/logout', status=302) - self.assertTrue('Logout' not in res.body) + self.assertTrue(b'Logout' not in res.body) def test_anonymous_user_cannot_edit(self): res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue('Login' in res.body) + self.assertTrue(b'Login' in res.body) def test_anonymous_user_cannot_add(self): res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue('Login' in res.body) + self.assertTrue(b'Login' in res.body) def test_viewer_user_cannot_edit(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue('Login' in res.body) + self.assertTrue(b'Login' in res.body) def test_viewer_user_cannot_add(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue('Login' in res.body) + self.assertTrue(b'Login' in res.body) def test_editors_member_user_can_edit(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue('Editing' in res.body) + self.assertTrue(b'Editing' in res.body) def test_editors_member_user_can_add(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue('Editing' in res.body) + self.assertTrue(b'Editing' in res.body) def test_editors_member_user_can_view(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get('/FrontPage', status=200) - self.assertTrue('FrontPage' in res.body) + self.assertTrue(b'FrontPage' in res.body) -- cgit v1.2.3 From f23f38db37e8e323512424e5715a40dc2dce9ab8 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 14 Oct 2013 16:08:02 +0200 Subject: Revert "make these tests pass on python 3.2+" This reverts commit cd218d2934c87260bbb10620e3b419b275fe6244. --- docs/tutorials/wiki/src/tests/tutorial/tests.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py index 5add04c20..c435a4519 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/tests.py +++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py @@ -158,11 +158,11 @@ class FunctionalTests(unittest.TestCase): def test_FrontPage(self): res = self.testapp.get('/FrontPage', status=200) - self.assertTrue(b'FrontPage' in res.body) + self.assertTrue('FrontPage' in res.body) def test_unexisting_page(self): res = self.testapp.get('/SomePage', status=404) - self.assertTrue(b'Not Found' in res.body) + self.assertTrue('Not Found' in res.body) def test_successful_log_in(self): res = self.testapp.get( self.viewer_login, status=302) @@ -170,48 +170,48 @@ class FunctionalTests(unittest.TestCase): def test_failed_log_in(self): res = self.testapp.get( self.viewer_wrong_login, status=200) - self.assertTrue(b'login' in res.body) + self.assertTrue('login' in res.body) def test_logout_link_present_when_logged_in(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/FrontPage', status=200) - self.assertTrue(b'Logout' in res.body) + self.assertTrue('Logout' in res.body) def test_logout_link_not_present_after_logged_out(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/FrontPage', status=200) res = self.testapp.get('/logout', status=302) - self.assertTrue(b'Logout' not in res.body) + self.assertTrue('Logout' not in res.body) def test_anonymous_user_cannot_edit(self): res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue(b'Login' in res.body) + self.assertTrue('Login' in res.body) def test_anonymous_user_cannot_add(self): res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue(b'Login' in res.body) + self.assertTrue('Login' in res.body) def test_viewer_user_cannot_edit(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue(b'Login' in res.body) + self.assertTrue('Login' in res.body) def test_viewer_user_cannot_add(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue(b'Login' in res.body) + self.assertTrue('Login' in res.body) def test_editors_member_user_can_edit(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue(b'Editing' in res.body) + self.assertTrue('Editing' in res.body) def test_editors_member_user_can_add(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue(b'Editing' in res.body) + self.assertTrue('Editing' in res.body) def test_editors_member_user_can_view(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get('/FrontPage', status=200) - self.assertTrue(b'FrontPage' in res.body) + self.assertTrue('FrontPage' in res.body) -- cgit v1.2.3 From 8df7a71d99bbeb7819e8a2752012d51202669aa6 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Oct 2013 01:30:58 -0500 Subject: update the docs --- docs/api/session.rst | 8 ++++++-- docs/narr/sessions.rst | 19 +++++++++---------- docs/quick_tour/package/hello_world/__init__.py | 4 ++-- docs/quick_tour/package/hello_world/init.py | 4 ++-- docs/quick_tutorial/sessions/tutorial/__init__.py | 6 +++--- 5 files changed, 22 insertions(+), 19 deletions(-) (limited to 'docs') diff --git a/docs/api/session.rst b/docs/api/session.rst index 31bc196ad..dde9d20e9 100644 --- a/docs/api/session.rst +++ b/docs/api/session.rst @@ -5,12 +5,16 @@ .. automodule:: pyramid.session - .. autofunction:: UnencryptedCookieSessionFactoryConfig - .. autofunction:: signed_serialize .. autofunction:: signed_deserialize .. autofunction:: check_csrf_token + .. autofunction:: SignedCookieSessionFactory + + .. autofunction:: UnencryptedCookieSessionFactoryConfig + + .. autofunction:: BaseCookieSessionFactory + diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst index 358977089..1d914f9ea 100644 --- a/docs/narr/sessions.rst +++ b/docs/narr/sessions.rst @@ -43,24 +43,23 @@ limitations: It is digitally signed, however, and thus its data cannot easily be tampered with. -You can configure this session factory in your :app:`Pyramid` -application by using the ``session_factory`` argument to the -:class:`~pyramid.config.Configurator` class: +You can configure this session factory in your :app:`Pyramid` application +by using the :meth:`pyramid.config.Configurator.set_session_factory`` method. .. code-block:: python :linenos: - from pyramid.session import UnencryptedCookieSessionFactoryConfig - my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet') - + from pyramid.session import SignedCookieSessionFactory + my_session_factory = SignedCookieSessionFactory('itsaseekreet') + from pyramid.config import Configurator - config = Configurator(session_factory = my_session_factory) + config = Configurator() + config.set_session_factory(my_session_factory) .. warning:: - Note the very long, very explicit name for - ``UnencryptedCookieSessionFactoryConfig``. It's trying to tell you that - this implementation is, by default, *unencrypted*. You should not use it + By default the :func:`~pyramid.session.SignedCookieSessionFactory` + implementation is *unencrypted*. You should not use it when you keep sensitive information in the session object, as the information can be easily read by both users of your application and third parties who have access to your users' network traffic. And if you use this diff --git a/docs/quick_tour/package/hello_world/__init__.py b/docs/quick_tour/package/hello_world/__init__.py index 6e66bf40a..4a4fbec30 100644 --- a/docs/quick_tour/package/hello_world/__init__.py +++ b/docs/quick_tour/package/hello_world/__init__.py @@ -1,7 +1,7 @@ from pyramid.config import Configurator from pyramid_jinja2 import renderer_factory # Start Sphinx Include 1 -from pyramid.session import UnencryptedCookieSessionFactoryConfig +from pyramid.session import SignedCookieSessionFactory # End Sphinx Include 1 from hello_world.models import get_root @@ -16,7 +16,7 @@ def main(global_config, **settings): settings.setdefault('jinja2.i18n.domain', 'hello_world') # Start Sphinx Include 2 - my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet') + my_session_factory = SignedCookieSessionFactory('itsaseekreet') config = Configurator(root_factory=get_root, settings=settings, session_factory=my_session_factory) # End Sphinx Include 2 diff --git a/docs/quick_tour/package/hello_world/init.py b/docs/quick_tour/package/hello_world/init.py index 9d7ec43d8..5b5f6a118 100644 --- a/docs/quick_tour/package/hello_world/init.py +++ b/docs/quick_tour/package/hello_world/init.py @@ -1,7 +1,7 @@ from pyramid.config import Configurator from pyramid_jinja2 import renderer_factory # Start Sphinx 1 -from pyramid.session import UnencryptedCookieSessionFactoryConfig +from pyramid.session import SignedCookieSessionFactory # End Sphinx 1 from hello_world.models import get_root @@ -22,7 +22,7 @@ def main(global_config, **settings): # End Include # Start Sphinx Include 2 - my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet') + my_session_factory = SignedCookieSessionFactory('itsaseekreet') config = Configurator(session_factory=my_session_factory) # End Sphinx Include 2 diff --git a/docs/quick_tutorial/sessions/tutorial/__init__.py b/docs/quick_tutorial/sessions/tutorial/__init__.py index ecf57bb32..9ddc2e1b1 100644 --- a/docs/quick_tutorial/sessions/tutorial/__init__.py +++ b/docs/quick_tutorial/sessions/tutorial/__init__.py @@ -1,9 +1,9 @@ from pyramid.config import Configurator -from pyramid.session import UnencryptedCookieSessionFactoryConfig +from pyramid.session import SignedCookieSessionFactory def main(global_config, **settings): - my_session_factory = UnencryptedCookieSessionFactoryConfig( + my_session_factory = SignedCookieSessionFactory( 'itsaseekreet') config = Configurator(settings=settings, session_factory=my_session_factory) @@ -11,4 +11,4 @@ def main(global_config, **settings): config.add_route('home', '/') config.add_route('hello', '/howdy') config.scan('.views') - return config.make_wsgi_app() \ No newline at end of file + return config.make_wsgi_app() -- cgit v1.2.3 From 6b0889cc8f3711d5f77cb663f8f2fa432eb3ad06 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Oct 2013 01:52:11 -0500 Subject: update doc references --- docs/api/exceptions.rst | 2 ++ docs/api/httpexceptions.rst | 13 ------------- 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'docs') diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst index ab158f18d..0c630571f 100644 --- a/docs/api/exceptions.rst +++ b/docs/api/exceptions.rst @@ -5,6 +5,8 @@ .. automodule:: pyramid.exceptions + .. autoclass:: BadCSRFToken + .. autoclass:: PredicateMismatch .. autoclass:: Forbidden diff --git a/docs/api/httpexceptions.rst b/docs/api/httpexceptions.rst index 0fdd0f0e9..b50f10beb 100644 --- a/docs/api/httpexceptions.rst +++ b/docs/api/httpexceptions.rst @@ -10,9 +10,6 @@ A mapping of integer status code to HTTP exception class (eg. the integer "401" maps to :class:`pyramid.httpexceptions.HTTPUnauthorized`). All mapped exception classes are children of :class:`pyramid.httpexceptions`, - i.e. the :ref:`pyramid_specific_http_exceptions` such as - :class:`pyramid.httpexceptions.HTTPBadRequest.BadCSRFToken` are not - mapped. .. autofunction:: exception_response @@ -109,13 +106,3 @@ .. autoclass:: HTTPVersionNotSupported .. autoclass:: HTTPInsufficientStorage - - -.. _pyramid_specific_http_exceptions: - -Pyramid-specific HTTP Exceptions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Each Pyramid-specific HTTP exception has the status code of it's parent. - - .. autoclass:: HTTPBadCSRFToken -- cgit v1.2.3 From b04ae5ac814266eb77d4a09c749e5e0394a11a1c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Oct 2013 03:43:05 -0500 Subject: modify the docs for the renderer interfaces --- docs/api/interfaces.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index 1dea5fab0..d8d935afd 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -50,7 +50,10 @@ Other Interfaces .. autointerface:: IRendererInfo :members: - .. autointerface:: ITemplateRenderer + .. autointerface:: IRendererFactory + :members: + + .. autointerface:: IRenderer :members: .. autointerface:: IViewMapperFactory -- cgit v1.2.3 From 777112d521e337fefc2e0c217add7ac283d087b3 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Oct 2013 03:48:01 -0500 Subject: link to the public renderer interfaces --- docs/narr/renderers.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst index 740c81555..4f8c4bf77 100644 --- a/docs/narr/renderers.rst +++ b/docs/narr/renderers.rst @@ -480,8 +480,11 @@ Adding a New Renderer You may add a new renderer by creating and registering a :term:`renderer factory`. -A renderer factory implementation is typically a class with the -following interface: +A renderer factory implementation should conform to the +:class:`pyramid.interfaces.IRendererFactory` interface. It should be capable +of creating an object that conforms to the +:class:`pyramid.interfaces.IRenderer` interface. A typical class that follows +this setup is as follows: .. code-block:: python :linenos: -- cgit v1.2.3 From e521f14cc4d986c2ad400abff3d6cb7ff784b775 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 19 Oct 2013 15:57:33 -0400 Subject: add admonishment against secret sharing --- docs/narr/security.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'docs') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 6517fedf8..9884bb1dc 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -669,3 +669,31 @@ following interface: After you do so, you can pass an instance of such a class into the :class:`~pyramid.config.Configurator.set_authorization_policy` method at configuration time to use it. + +.. _admonishment_against_secret_sharing: + +Admomishment Against Secret-Sharing +----------------------------------- + +A "secret" is required by various components of Pyramid. For example, the +:term:`authentication policy` below uses a secret value ``seekrit``:: + + authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512') + +A :term:`session factory` also requires a secret:: + + my_session_factory = SignedCookieSessionFactory('itsaseekreet') + +It is tempting to use the same secret for multiple Pyramid subsystems. For +example, you might be tempted to use the value ``seekrit`` as the secret for +both the authentication policy and the session factory defined above. This is +a bad idea, because in both cases, these secrets are used to sign the payload +of the data. + +If you use the same secret for two different parts of your application for +signing purposes, it may allow an attacker to get his chosen plaintext signed, +which would allow the attacker to control the content of the payload. Re-using +a secret across two different subsystems might drop the security of signing to +zero. Keys should not be re-used across different contexts where an attacker +has the possibility of providing a chosen plaintext. + -- cgit v1.2.3 From 94360dffe85332733f35f2fb3ab32de3fedd787e Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 19 Oct 2013 16:00:40 -0400 Subject: mon --- docs/narr/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 9884bb1dc..e85ed823a 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -672,7 +672,7 @@ configuration time to use it. .. _admonishment_against_secret_sharing: -Admomishment Against Secret-Sharing +Admonishment Against Secret-Sharing ----------------------------------- A "secret" is required by various components of Pyramid. For example, the -- cgit v1.2.3 From 3c2f95e8049bbd45b144d454daa68005361828b2 Mon Sep 17 00:00:00 2001 From: Matt Russell Date: Thu, 24 Oct 2013 23:52:42 +0100 Subject: Security APIs on pyramid.request.Request The pyramid.security Authorization API function has_permission is made available on the request. The pyramid.security Authentication API functions are now available as properties (unauthenticated_userid, authenticated_userid, effective_principals) and methods (remember_userid, forget_userid) on pyramid.request.Request. Backwards compatibility: For each of the APIs moved to request method or property, the original API in the pyramid.security module proxies to the request. Reworked tests to check module level b/c wrappers call through to mixins for each API. Tests that check no reg on request now do the right thing. Use a response callback to set the request headers for forget_userid and remember_userid. Update docs. Attempt to improve a documentation section referencing the pyramid.security.has_permission function in docs/narr/resources.rst Ensures backwards compatiblity for `pyramid.security.forget` and `pyramid.security.remember`. --- docs/narr/resources.rst | 12 ++++++------ docs/narr/security.rst | 4 ++-- docs/narr/testing.rst | 2 +- docs/narr/viewconfig.rst | 2 +- docs/tutorials/wiki/authorization.rst | 4 ++-- docs/tutorials/wiki2/authorization.rst | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index b1bb611e5..34d75f2cc 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -201,7 +201,7 @@ location-aware resources. These APIs include (but are not limited to) :func:`~pyramid.traversal.resource_path`, :func:`~pyramid.traversal.resource_path_tuple`, or :func:`~pyramid.traversal.traverse`, :func:`~pyramid.traversal.virtual_root`, -and (usually) :func:`~pyramid.security.has_permission` and +and (usually) :meth:`~pyramid.request.Request.has_permission` and :func:`~pyramid.security.principals_allowed_by_permission`. In general, since so much :app:`Pyramid` infrastructure depends on @@ -695,10 +695,10 @@ The APIs provided by :ref:`location_module` are used against resources. These can be used to walk down a resource tree, or conveniently locate one resource "inside" another. -Some APIs in :ref:`security_module` accept a resource object as a parameter. -For example, the :func:`~pyramid.security.has_permission` API accepts a +Some APIs on the :class:`pyramid.request.Request` accept a resource object as a parameter. +For example, the :meth:`~pyramid.request.Request.has_permission` API accepts a resource object as one of its arguments; the ACL is obtained from this -resource or one of its ancestors. Other APIs in the :mod:`pyramid.security` -module also accept :term:`context` as an argument, and a context is always a -resource. +resource or one of its ancestors. Other security related APIs on the +:class:`pyramid.request.Request` class also accept :term:`context` as an argument, +and a context is always a resource. diff --git a/docs/narr/security.rst b/docs/narr/security.rst index e85ed823a..9e6fb6c82 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -550,7 +550,7 @@ also contain security debugging information in its body. Debugging Imperative Authorization Failures ------------------------------------------- -The :func:`pyramid.security.has_permission` API is used to check +The :meth:`pyramid.request.Request.has_permission` API is used to check security within view functions imperatively. It returns instances of objects that are effectively booleans. But these objects are not raw ``True`` or ``False`` objects, and have information attached to them @@ -563,7 +563,7 @@ one of :data:`pyramid.security.ACLAllowed`, ``msg`` attribute, which is a string indicating why the permission was denied or allowed. Introspecting this information in the debugger or via print statements when a call to -:func:`~pyramid.security.has_permission` fails is often useful. +:meth:`~pyramid.request.Request.has_permission` fails is often useful. .. index:: single: authentication policy (creating) diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index 88d6904c7..3f5d5ae6c 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -229,7 +229,7 @@ function. otherwise it would fail when run normally. Without doing anything special during a unit test, the call to -:func:`~pyramid.security.has_permission` in this view function will always +:meth:`~pyramid.request.Request.has_permission` in this view function will always return a ``True`` value. When a :app:`Pyramid` application starts normally, it will populate a :term:`application registry` using :term:`configuration declaration` calls made against a :term:`Configurator`. But if this diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index 7c76116f7..e5a2c1ade 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -435,7 +435,7 @@ configured view. If specified, this value should be a :term:`principal` identifier or a sequence of principal identifiers. If the - :func:`pyramid.security.effective_principals` method indicates that every + :meth:`pyramid.request.Request.effective_principals` method indicates that every principal named in the argument list is present in the current request, this predicate will return True; otherwise it will return False. For example: ``effective_principals=pyramid.security.Authenticated`` or diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 460a852e0..2bd8c1f1c 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -207,8 +207,8 @@ need to be added.) :meth:`~pyramid.view.forbidden_view_config` will be used to customize the default 403 Forbidden page. -:meth:`~pyramid.security.remember` and -:meth:`~pyramid.security.forget` help to create and +:meth:`~pyramid.request.Request.remember_userid` and +:meth:`~pyramid.request.Request.forget_userid` help to create and expire an auth ticket cookie. Now add the ``login`` and ``logout`` views: diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index cf20db6d7..2b4263610 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -230,8 +230,8 @@ head of ``tutorial/tutorial/views.py``: :meth:`~pyramid.view.forbidden_view_config` will be used to customize the default 403 Forbidden page. -:meth:`~pyramid.security.remember` and -:meth:`~pyramid.security.forget` help to create and +:meth:`~pyramid.request.Request.remember_userid` and +:meth:`~pyramid.request.Request.forget_userid` help to create and expire an auth ticket cookie. Now add the ``login`` and ``logout`` views: -- cgit v1.2.3 From 0184b527725cfb634e4d57a1b033450fa8b24502 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 28 Oct 2013 15:26:31 -0400 Subject: Bring change log, API docs, and deprecations in line with normal policies/processes --- docs/api/request.rst | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/api/request.rst b/docs/api/request.rst index 72abddb68..3d1fe020c 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -11,7 +11,10 @@ :exclude-members: add_response_callback, add_finished_callback, route_url, route_path, current_route_url, current_route_path, static_url, static_path, - model_url, resource_url, set_property + model_url, resource_url, set_property, + effective_principals, authenticated_userid, + unauthenticated_userid, has_permission, forget_userid, + remember_userid .. attribute:: context @@ -161,6 +164,42 @@ request, the value of this attribute will be ``None``. See :ref:`matched_route`. + .. attribute:: authenticated_userid + + .. versionadded:: 1.5 + + A property which returns the userid of the currently authenticated user + or ``None`` if there is no :term:`authentication policy` in effect or + there is no currently authenticated user. This differs from + :meth:`~pyramid.request.Request.unauthenticated_userid`, because the + effective authentication policy will have ensured that a record + associated with the userid exists in persistent storage; if it has + not, this value will be ``None``. + + .. attribute:: unauthenticated_userid + + .. versionadded:: 1.5 + + A property which returns a value which represents the *claimed* (not + verified) user id of the credentials present in the request. ``None`` if + there is no :term:`authentication policy` in effect or there is no user + data associated with the current request. This differs from + :meth:`~pyramid.request.Request.authenticated_userid`, because the + effective authentication policy will not ensure that a record associated + with the userid exists in persistent storage. Even if the userid + does not exist in persistent storage, this value will be the value + of the userid *claimed* by the request data. + + .. attribute:: effective_principals + + .. versionadded:: 1.5 + + A property which returns the list of 'effective' :term:`principal` + identifiers for this request. This will include the userid of the + currently authenticated user if a user is currently authenticated. If no + :term:`authentication policy` is in effect, this will return a sequence + containing only the :attr:`pyramid.security.Everyone` principal. + .. method:: invoke_subrequest(request, use_tweens=False) .. versionadded:: 1.4a1 @@ -215,6 +254,12 @@ request provided by e.g. the ``pshell`` environment. For more information, see :ref:`subrequest_chapter`. + .. automethod:: remember_userid + + .. automethod:: forget_userid + + .. automethod:: has_permission + .. automethod:: add_response_callback .. automethod:: add_finished_callback -- cgit v1.2.3 From 696e0e3bd257fdace57adbb4c3d331af377d9e5b Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 19:47:52 -0400 Subject: fix zodb tutorial wrt request-based authentication and authorization apis --- docs/tutorials/wiki/authorization.rst | 34 +++++++--------------- .../wiki/src/authorization/tutorial/views.py | 22 +++++--------- 2 files changed, 18 insertions(+), 38 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 2bd8c1f1c..bba303d7f 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -56,10 +56,10 @@ returns one of these values: return ``None``. For example, ``groupfinder('editor', request )`` returns ``['group:editor']``, -``groupfinder('viewer', request)`` returns ``[]``, and ``groupfinder('admin', request)`` -returns ``None``. We will use ``groupfinder()`` as an :term:`authentication policy` -"callback" that will provide the :term:`principal` or principals -for a user. +``groupfinder('viewer', request)`` returns ``[]``, and ``groupfinder('admin', +request)`` returns ``None``. We will use ``groupfinder()`` as an +:term:`authentication policy` "callback" that will provide the +:term:`principal` or principals for a user. In a production system, user and group data will most often come from a database, but here we use "dummy" @@ -197,15 +197,15 @@ Add the following import statements to the head of ``tutorial/tutorial/views.py``: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 6-13,15-17 + :lines: 6-11 :linenos: - :emphasize-lines: 3,6-9,11 + :emphasize-lines: 3,6 :language: python (Only the highlighted lines, with other necessary modifications, need to be added.) -:meth:`~pyramid.view.forbidden_view_config` will be used +:func:`~pyramid.view.forbidden_view_config` will be used to customize the default 403 Forbidden page. :meth:`~pyramid.request.Request.remember_userid` and :meth:`~pyramid.request.Request.forget_userid` help to create and @@ -214,7 +214,7 @@ expire an auth ticket cookie. Now add the ``login`` and ``logout`` views: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 82-120 + :lines: 76-102 :linenos: :language: python @@ -251,18 +251,6 @@ in ``views.py``. Return a logged_in flag to the renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add the following line to the import at the head of -``tutorial/tutorial/views.py``: - -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 11-15 - :linenos: - :emphasize-lines: 4 - :language: python - -(Only the highlighted line and a trailing comma on the preceding -line need to be added.) - Add a ``logged_in`` parameter to the return value of ``view_page()``, ``edit_page()`` and ``add_page()``, like this: @@ -274,12 +262,12 @@ like this: return dict(page = page, content = content, edit_url = edit_url, - logged_in = authenticated_userid(request)) + logged_in = request.authenticated_userid) (Only the highlighted line and a trailing comma on the preceding line need to be added.) -:meth:`~pyramid.security.authenticated_userid()` will return ``None`` +:attr:`~pyramid.request.Request.authenticated_userid` will return ``None`` if the user is not authenticated, or a user id if the user is authenticated. @@ -329,7 +317,7 @@ when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: - :emphasize-lines: 8,11-15,17,24,29,48,52,68,72,80,82-120 + :emphasize-lines: 8,11,18,23,42,46,62,66,74,80,76-107 :language: python (Only the highlighted lines need to be added.) diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py index 77956b1e3..57529ac8d 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py @@ -8,12 +8,6 @@ from pyramid.view import ( forbidden_view_config, ) -from pyramid.security import ( - remember, - forget, - authenticated_userid, - ) - from .security import USERS from .models import Page @@ -45,7 +39,7 @@ def view_page(context, request): edit_url = request.resource_url(context, 'edit_page') return dict(page = context, content = content, edit_url = edit_url, - logged_in = authenticated_userid(request)) + logged_in = request.authenticated_userid) @view_config(name='add_page', context='.models.Wiki', renderer='templates/edit.pt', @@ -65,7 +59,7 @@ def add_page(context, request): page.__parent__ = context return dict(page=page, save_url=save_url, - logged_in=authenticated_userid(request)) + logged_in=request.authenticated_userid) @view_config(name='edit_page', context='.models.Page', renderer='templates/edit.pt', @@ -77,7 +71,7 @@ def edit_page(context, request): return dict(page=context, save_url=request.resource_url(context, 'edit_page'), - logged_in=authenticated_userid(request)) + logged_in=request.authenticated_userid) @view_config(context='.models.Wiki', name='login', renderer='templates/login.pt') @@ -95,9 +89,8 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) + request.remember_userid(login) + return HTTPFound(location=came_from) message = 'Failed login' return dict( @@ -110,6 +103,5 @@ def login(request): @view_config(context='.models.Wiki', name='logout') def logout(request): - headers = forget(request) - return HTTPFound(location = request.resource_url(request.context), - headers = headers) + request.forget_userid() + return HTTPFound(location=request.resource_url(request.context)) -- cgit v1.2.3 From f436d7f5cd19e94378737096d9d21635b157fc46 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:03:49 -0400 Subject: copy forward views.py changes to tests step --- docs/tutorials/wiki/src/tests/tutorial/views.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py index 77956b1e3..57529ac8d 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki/src/tests/tutorial/views.py @@ -8,12 +8,6 @@ from pyramid.view import ( forbidden_view_config, ) -from pyramid.security import ( - remember, - forget, - authenticated_userid, - ) - from .security import USERS from .models import Page @@ -45,7 +39,7 @@ def view_page(context, request): edit_url = request.resource_url(context, 'edit_page') return dict(page = context, content = content, edit_url = edit_url, - logged_in = authenticated_userid(request)) + logged_in = request.authenticated_userid) @view_config(name='add_page', context='.models.Wiki', renderer='templates/edit.pt', @@ -65,7 +59,7 @@ def add_page(context, request): page.__parent__ = context return dict(page=page, save_url=save_url, - logged_in=authenticated_userid(request)) + logged_in=request.authenticated_userid) @view_config(name='edit_page', context='.models.Page', renderer='templates/edit.pt', @@ -77,7 +71,7 @@ def edit_page(context, request): return dict(page=context, save_url=request.resource_url(context, 'edit_page'), - logged_in=authenticated_userid(request)) + logged_in=request.authenticated_userid) @view_config(context='.models.Wiki', name='login', renderer='templates/login.pt') @@ -95,9 +89,8 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) + request.remember_userid(login) + return HTTPFound(location=came_from) message = 'Failed login' return dict( @@ -110,6 +103,5 @@ def login(request): @view_config(context='.models.Wiki', name='logout') def logout(request): - headers = forget(request) - return HTTPFound(location = request.resource_url(request.context), - headers = headers) + request.forget_userid() + return HTTPFound(location=request.resource_url(request.context)) -- cgit v1.2.3 From 3657ba974660677050fe4a62441c2073bd71203c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:08:58 -0400 Subject: fix wiki2 tutorial wrt request-method security APIs --- docs/tutorials/wiki2/authorization.rst | 27 +++++++--------------- .../wiki2/src/authorization/tutorial/views.py | 22 ++++++------------ docs/tutorials/wiki2/src/tests/tutorial/views.py | 22 ++++++------------ 3 files changed, 22 insertions(+), 49 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index 2b4263610..830cb0277 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -221,14 +221,14 @@ Add the following import statements to the head of ``tutorial/tutorial/views.py``: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 9-16,18,24-25 + :lines: 9-12,19 :linenos: - :emphasize-lines: 3,6-9,11 + :emphasize-lines: 3,5 :language: python (Only the highlighted lines need to be added.) -:meth:`~pyramid.view.forbidden_view_config` will be used +:func:`~pyramid.view.forbidden_view_config` will be used to customize the default 403 Forbidden page. :meth:`~pyramid.request.Request.remember_userid` and :meth:`~pyramid.request.Request.forget_userid` help to create and @@ -237,7 +237,7 @@ expire an auth ticket cookie. Now add the ``login`` and ``logout`` views: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 91-123 + :lines: 85-115 :linenos: :language: python @@ -274,17 +274,6 @@ added to ``views.py``. Return a logged_in flag to the renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add the following line to the import at the head of -``tutorial/tutorial/views.py``: - -.. literalinclude:: src/authorization/tutorial/views.py - :lines: 14-18 - :linenos: - :emphasize-lines: 4 - :language: python - -(Only the highlighted line needs to be added.) - Add a ``logged_in`` parameter to the return value of ``view_page()``, ``edit_page()`` and ``add_page()``, like this: @@ -296,12 +285,12 @@ like this: return dict(page = page, content = content, edit_url = edit_url, - logged_in = authenticated_userid(request)) + logged_in = request.authenticated_userid) (Only the highlighted line needs to be added.) -The :meth:`~pyramid.security.authenticated_userid` method will return None -if the user is not authenticated. +The :attr:`~pyramid.request.Request.authenticated_userid` property will return +``None`` if the user is not authenticated. Add a "Logout" link when logged in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -349,7 +338,7 @@ when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: - :emphasize-lines: 11,14-18,25,31,37,58,61,73,76,88,91-117,119-123 + :emphasize-lines: 11,19,25,31,52,55,67,70,82,85-115 :language: python (Only the highlighted lines need to be added.) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py index b6dbbf5f6..110d738c2 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views.py @@ -11,12 +11,6 @@ from pyramid.view import ( forbidden_view_config, ) -from pyramid.security import ( - remember, - forget, - authenticated_userid, - ) - from .models import ( DBSession, Page, @@ -55,7 +49,7 @@ def view_page(request): 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)) + logged_in=request.authenticated_userid) @view_config(route_name='add_page', renderer='templates/edit.pt', permission='edit') @@ -70,7 +64,7 @@ def add_page(request): save_url = request.route_url('add_page', pagename=pagename) page = Page(name='', data='') return dict(page=page, save_url=save_url, - logged_in=authenticated_userid(request)) + logged_in=request.authenticated_userid) @view_config(route_name='edit_page', renderer='templates/edit.pt', permission='edit') @@ -85,7 +79,7 @@ def edit_page(request): return dict( page=page, save_url = request.route_url('edit_page', pagename=pagename), - logged_in=authenticated_userid(request), + logged_in=request.authenticated_userid, ) @view_config(route_name='login', renderer='templates/login.pt') @@ -103,9 +97,8 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) + request.remember_userid(login) + return HTTPFound(location = came_from) message = 'Failed login' return dict( @@ -118,7 +111,6 @@ def login(request): @view_config(route_name='logout') def logout(request): - headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) + request.forget_userid() + return HTTPFound(location = request.route_url('view_wiki')) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views.py b/docs/tutorials/wiki2/src/tests/tutorial/views.py index b6dbbf5f6..110d738c2 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views.py @@ -11,12 +11,6 @@ from pyramid.view import ( forbidden_view_config, ) -from pyramid.security import ( - remember, - forget, - authenticated_userid, - ) - from .models import ( DBSession, Page, @@ -55,7 +49,7 @@ def view_page(request): 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)) + logged_in=request.authenticated_userid) @view_config(route_name='add_page', renderer='templates/edit.pt', permission='edit') @@ -70,7 +64,7 @@ def add_page(request): save_url = request.route_url('add_page', pagename=pagename) page = Page(name='', data='') return dict(page=page, save_url=save_url, - logged_in=authenticated_userid(request)) + logged_in=request.authenticated_userid) @view_config(route_name='edit_page', renderer='templates/edit.pt', permission='edit') @@ -85,7 +79,7 @@ def edit_page(request): return dict( page=page, save_url = request.route_url('edit_page', pagename=pagename), - logged_in=authenticated_userid(request), + logged_in=request.authenticated_userid, ) @view_config(route_name='login', renderer='templates/login.pt') @@ -103,9 +97,8 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) + request.remember_userid(login) + return HTTPFound(location = came_from) message = 'Failed login' return dict( @@ -118,7 +111,6 @@ def login(request): @view_config(route_name='logout') def logout(request): - headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) + request.forget_userid() + return HTTPFound(location = request.route_url('view_wiki')) -- cgit v1.2.3 From e1838557e6721b5b42f1267b134b626099703c2c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:14:52 -0400 Subject: not methods, attrs --- docs/api/request.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/api/request.rst b/docs/api/request.rst index 3d1fe020c..661cdfc91 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -171,7 +171,7 @@ A property which returns the userid of the currently authenticated user or ``None`` if there is no :term:`authentication policy` in effect or there is no currently authenticated user. This differs from - :meth:`~pyramid.request.Request.unauthenticated_userid`, because the + :attr:`~pyramid.request.Request.unauthenticated_userid`, because the effective authentication policy will have ensured that a record associated with the userid exists in persistent storage; if it has not, this value will be ``None``. @@ -184,7 +184,7 @@ verified) user id of the credentials present in the request. ``None`` if there is no :term:`authentication policy` in effect or there is no user data associated with the current request. This differs from - :meth:`~pyramid.request.Request.authenticated_userid`, because the + :attr:`~pyramid.request.Request.authenticated_userid`, because the effective authentication policy will not ensure that a record associated with the userid exists in persistent storage. Even if the userid does not exist in persistent storage, this value will be the value -- cgit v1.2.3 From a91c19837f5ce579ce2a5bf68ddee30cfaebe034 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:19:23 -0400 Subject: note deprecation --- docs/narr/threadlocals.rst | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'docs') diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index a90ee4905..afe56de3e 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -29,17 +29,16 @@ of a thread local or a global is usually just a way to avoid passing some value around between functions, which is itself usually a very bad idea, at least if code readability counts as an important concern. -For historical reasons, however, thread local variables are indeed -consulted by various :app:`Pyramid` API functions. For example, -the implementation of the :mod:`pyramid.security` function named -:func:`~pyramid.security.authenticated_userid` retrieves the thread -local :term:`application registry` as a matter of course to find an +For historical reasons, however, thread local variables are indeed consulted by +various :app:`Pyramid` API functions. For example, the implementation of the +:mod:`pyramid.security` function named +:func:`~pyramid.security.authenticated_userid` (deprecated as of 1.5) retrieves +the thread local :term:`application registry` as a matter of course to find an :term:`authentication policy`. It uses the -:func:`pyramid.threadlocal.get_current_registry` function to -retrieve the application registry, from which it looks up the -authentication policy; it then uses the authentication policy to -retrieve the authenticated user id. This is how :app:`Pyramid` -allows arbitrary authentication policies to be "plugged in". +:func:`pyramid.threadlocal.get_current_registry` function to retrieve the +application registry, from which it looks up the authentication policy; it then +uses the authentication policy to retrieve the authenticated user id. This is +how :app:`Pyramid` allows arbitrary authentication policies to be "plugged in". When they need to do so, :app:`Pyramid` internals use two API functions to retrieve the :term:`request` and :term:`application -- cgit v1.2.3 From 675e0d4cf01840740490c03a2e3704b0b7d98de3 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:24:34 -0400 Subject: convert remember/forget to request-method-based --- docs/quick_tutorial/authentication/tutorial/views.py | 17 +++++------------ docs/quick_tutorial/authorization/tutorial/views.py | 17 +++++------------ 2 files changed, 10 insertions(+), 24 deletions(-) (limited to 'docs') diff --git a/docs/quick_tutorial/authentication/tutorial/views.py b/docs/quick_tutorial/authentication/tutorial/views.py index 3038b6d9b..240a23d3e 100644 --- a/docs/quick_tutorial/authentication/tutorial/views.py +++ b/docs/quick_tutorial/authentication/tutorial/views.py @@ -1,9 +1,4 @@ from pyramid.httpexceptions import HTTPFound -from pyramid.security import ( - remember, - forget, - authenticated_userid - ) from pyramid.view import ( view_config, view_defaults @@ -16,7 +11,7 @@ from .security import USERS class TutorialViews: def __init__(self, request): self.request = request - self.logged_in = authenticated_userid(request) + self.logged_in = request.authenticated_userid @view_config(route_name='home') def home(self): @@ -41,9 +36,8 @@ class TutorialViews: login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, - headers=headers) + request.remember_userid(login) + return HTTPFound(location=came_from) message = 'Failed login' return dict( @@ -58,7 +52,6 @@ class TutorialViews: @view_config(route_name='logout') def logout(self): request = self.request - headers = forget(request) + request.forget_userid() url = request.route_url('home') - return HTTPFound(location=url, - headers=headers) + return HTTPFound(location=url) diff --git a/docs/quick_tutorial/authorization/tutorial/views.py b/docs/quick_tutorial/authorization/tutorial/views.py index 92c1946ba..2ce2c37b4 100644 --- a/docs/quick_tutorial/authorization/tutorial/views.py +++ b/docs/quick_tutorial/authorization/tutorial/views.py @@ -1,9 +1,4 @@ from pyramid.httpexceptions import HTTPFound -from pyramid.security import ( - remember, - forget, - authenticated_userid - ) from pyramid.view import ( view_config, view_defaults, @@ -17,7 +12,7 @@ from .security import USERS class TutorialViews: def __init__(self, request): self.request = request - self.logged_in = authenticated_userid(request) + self.logged_in = request.authenticated_userid @view_config(route_name='home') def home(self): @@ -43,9 +38,8 @@ class TutorialViews: login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, - headers=headers) + request.remember_userid(login) + return HTTPFound(location=came_from) message = 'Failed login' return dict( @@ -60,7 +54,6 @@ class TutorialViews: @view_config(route_name='logout') def logout(self): request = self.request - headers = forget(request) + request.forget_userid() url = request.route_url('home') - return HTTPFound(location=url, - headers=headers) + return HTTPFound(location=url) -- cgit v1.2.3 From 3bd1fa5dd792d639615e5125b73caef8c65a0a30 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:28:53 -0400 Subject: new api --- docs/narr/testing.rst | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'docs') diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index 3f5d5ae6c..5a5bf8fad 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -214,11 +214,10 @@ function. .. code-block:: python :linenos: - from pyramid.security import has_permission from pyramid.httpexceptions import HTTPForbidden def view_fn(request): - if not has_permission('edit', request.context, request): + if request.has_permission('edit'): raise HTTPForbidden return {'greeting':'hello'} @@ -229,15 +228,16 @@ function. otherwise it would fail when run normally. Without doing anything special during a unit test, the call to -:meth:`~pyramid.request.Request.has_permission` in this view function will always -return a ``True`` value. When a :app:`Pyramid` application starts normally, -it will populate a :term:`application registry` using :term:`configuration -declaration` calls made against a :term:`Configurator`. But if this -application registry is not created and populated (e.g. by initializing the -configurator with an authorization policy), like when you invoke application -code via a unit test, :app:`Pyramid` API functions will tend to either fail -or return default results. So how do you test the branch of the code in this -view function that raises :exc:`~pyramid.httpexceptions.HTTPForbidden`? +:meth:`~pyramid.request.Request.has_permission` in this view function will +always return a ``True`` value. When a :app:`Pyramid` application starts +normally, it will populate a :term:`application registry` using +:term:`configuration declaration` calls made against a :term:`Configurator`. +But if this application registry is not created and populated (e.g. by +initializing the configurator with an authorization policy), like when you +invoke application code via a unit test, :app:`Pyramid` API functions will tend +to either fail or return default results. So how do you test the branch of the +code in this view function that raises +:exc:`~pyramid.httpexceptions.HTTPForbidden`? The testing API provided by :app:`Pyramid` allows you to simulate various application registry registrations for use under a unit testing framework @@ -287,12 +287,12 @@ Its third line registers a "dummy" "non-permissive" authorization policy using the :meth:`~pyramid.config.Configurator.testing_securitypolicy` method, which is a special helper method for unit testing. -We then create a :class:`pyramid.testing.DummyRequest` object which simulates -a WebOb request object API. A :class:`pyramid.testing.DummyRequest` is a -request object that requires less setup than a "real" :app:`Pyramid` request. -We call the function being tested with the manufactured request. When the -function is called, :func:`pyramid.security.has_permission` will call the -"dummy" authentication policy we've registered through +We then create a :class:`pyramid.testing.DummyRequest` object which simulates a +WebOb request object API. A :class:`pyramid.testing.DummyRequest` is a request +object that requires less setup than a "real" :app:`Pyramid` request. We call +the function being tested with the manufactured request. When the function is +called, :meth:`pyramid.request.Request.has_permission` will call the "dummy" +authentication policy we've registered through :meth:`~pyramid.config.Configurator.testing_securitypolicy`, which denies access. We check that the view function raises a :exc:`~pyramid.httpexceptions.HTTPForbidden` error. -- cgit v1.2.3 From 2885a7b96545c037109d7999319f74869a640050 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 30 Oct 2013 20:40:12 -0400 Subject: fix failing test (unrelated to security stuff) --- docs/tutorials/wiki2/src/tests/tutorial/tests.py | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests.py b/docs/tutorials/wiki2/src/tests/tutorial/tests.py index 4ee30685e..c50e05b6d 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests.py @@ -26,27 +26,6 @@ def _registerRoutes(config): config.add_route('add_page', 'add_page/{pagename}') -class PageModelTests(unittest.TestCase): - - def setUp(self): - self.session = _initTestingDB() - - def tearDown(self): - self.session.remove() - - def _getTargetClass(self): - from tutorial.models import Page - return Page - - def _makeOne(self, name='SomeName', data='some data'): - return self._getTargetClass()(name, data) - - def test_constructor(self): - instance = self._makeOne() - self.assertEqual(instance.name, 'SomeName') - self.assertEqual(instance.data, 'some data') - - class ViewWikiTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() -- cgit v1.2.3 From 19d5fe09bb37d3694f63884eb5a95158f4252473 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 7 Nov 2013 00:00:38 -0600 Subject: document add_adapter --- docs/api/renderers.rst | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'docs') diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst index ea000ad02..0caca02b4 100644 --- a/docs/api/renderers.rst +++ b/docs/api/renderers.rst @@ -13,8 +13,12 @@ .. autoclass:: JSON + .. automethod:: add_adapter + .. autoclass:: JSONP + .. automethod:: add_adapter + .. attribute:: null_renderer An object that can be used in advanced integration cases as input to the -- cgit v1.2.3 From 0dcd56c2c30863c6683c0cf442aa73dfdcd11b13 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 9 Nov 2013 17:11:16 -0500 Subject: undeprecate remember/forget functions and remove remember_userid/forget_userid methods from request --- docs/api/request.rst | 7 +------ .../authentication/tutorial/views.py | 15 ++++++++++---- .../quick_tutorial/authorization/tutorial/views.py | 15 ++++++++++---- docs/tutorials/wiki/authorization.rst | 19 +++++++++--------- .../wiki/src/authorization/tutorial/views.py | 16 +++++++++++---- docs/tutorials/wiki/src/tests/tutorial/views.py | 16 +++++++++++---- docs/tutorials/wiki2/authorization.rst | 16 +++++++-------- .../wiki2/src/authorization/tutorial/views.py | 22 ++++++++++++++------- docs/tutorials/wiki2/src/tests/tutorial/views.py | 23 ++++++++++++++-------- 9 files changed, 94 insertions(+), 55 deletions(-) (limited to 'docs') diff --git a/docs/api/request.rst b/docs/api/request.rst index 661cdfc91..b7604020e 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -13,8 +13,7 @@ current_route_path, static_url, static_path, model_url, resource_url, set_property, effective_principals, authenticated_userid, - unauthenticated_userid, has_permission, forget_userid, - remember_userid + unauthenticated_userid, has_permission .. attribute:: context @@ -254,10 +253,6 @@ request provided by e.g. the ``pshell`` environment. For more information, see :ref:`subrequest_chapter`. - .. automethod:: remember_userid - - .. automethod:: forget_userid - .. automethod:: has_permission .. automethod:: add_response_callback diff --git a/docs/quick_tutorial/authentication/tutorial/views.py b/docs/quick_tutorial/authentication/tutorial/views.py index 240a23d3e..ab46eb2dd 100644 --- a/docs/quick_tutorial/authentication/tutorial/views.py +++ b/docs/quick_tutorial/authentication/tutorial/views.py @@ -1,4 +1,9 @@ from pyramid.httpexceptions import HTTPFound +from pyramid.security import ( + remember, + forget, + ) + from pyramid.view import ( view_config, view_defaults @@ -36,8 +41,9 @@ class TutorialViews: login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - request.remember_userid(login) - return HTTPFound(location=came_from) + headers = remember(request, login) + return HTTPFound(location=came_from, + headers=headers) message = 'Failed login' return dict( @@ -52,6 +58,7 @@ class TutorialViews: @view_config(route_name='logout') def logout(self): request = self.request - request.forget_userid() + headers = forget(request) url = request.route_url('home') - return HTTPFound(location=url) + return HTTPFound(location=url, + headers=headers) diff --git a/docs/quick_tutorial/authorization/tutorial/views.py b/docs/quick_tutorial/authorization/tutorial/views.py index 2ce2c37b4..43d14455a 100644 --- a/docs/quick_tutorial/authorization/tutorial/views.py +++ b/docs/quick_tutorial/authorization/tutorial/views.py @@ -1,4 +1,9 @@ from pyramid.httpexceptions import HTTPFound +from pyramid.security import ( + remember, + forget, + ) + from pyramid.view import ( view_config, view_defaults, @@ -38,8 +43,9 @@ class TutorialViews: login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - request.remember_userid(login) - return HTTPFound(location=came_from) + headers = remember(request, login) + return HTTPFound(location=came_from, + headers=headers) message = 'Failed login' return dict( @@ -54,6 +60,7 @@ class TutorialViews: @view_config(route_name='logout') def logout(self): request = self.request - request.forget_userid() + headers = forget(request) url = request.route_url('home') - return HTTPFound(location=url) + return HTTPFound(location=url, + headers=headers) diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index bba303d7f..62b1164e3 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -197,24 +197,24 @@ Add the following import statements to the head of ``tutorial/tutorial/views.py``: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 6-11 + :lines: 6-13,15-17 :linenos: - :emphasize-lines: 3,6 + :emphasize-lines: 3,6-9,11 :language: python (Only the highlighted lines, with other necessary modifications, need to be added.) -:func:`~pyramid.view.forbidden_view_config` will be used +:meth:`~pyramid.view.forbidden_view_config` will be used to customize the default 403 Forbidden page. -:meth:`~pyramid.request.Request.remember_userid` and -:meth:`~pyramid.request.Request.forget_userid` help to create and +:meth:`~pyramid.security.remember` and +:meth:`~pyramid.security.forget` help to create and expire an auth ticket cookie. Now add the ``login`` and ``logout`` views: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 76-102 + :lines: 82-120 :linenos: :language: python @@ -267,9 +267,8 @@ like this: (Only the highlighted line and a trailing comma on the preceding line need to be added.) -:attr:`~pyramid.request.Request.authenticated_userid` will return ``None`` -if the user is not authenticated, or a user id if the user is -authenticated. +The :meth:`pyramid.request.Request.authenticated_userid` will be ``None`` if +the user is not authenticated, or a user id if the user is authenticated. Add a "Logout" link when logged in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -317,7 +316,7 @@ when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: - :emphasize-lines: 8,11,18,23,42,46,62,66,74,80,76-107 + :emphasize-lines: 8,11-15,17,24,29,48,52,68,72,80,82-120 :language: python (Only the highlighted lines need to be added.) diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py index 57529ac8d..62e96e0e7 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py @@ -8,6 +8,12 @@ from pyramid.view import ( forbidden_view_config, ) +from pyramid.security import ( + remember, + forget, + ) + + from .security import USERS from .models import Page @@ -89,8 +95,9 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - request.remember_userid(login) - return HTTPFound(location=came_from) + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) message = 'Failed login' return dict( @@ -103,5 +110,6 @@ def login(request): @view_config(context='.models.Wiki', name='logout') def logout(request): - request.forget_userid() - return HTTPFound(location=request.resource_url(request.context)) + headers = forget(request) + return HTTPFound(location = request.resource_url(request.context), + headers = headers) diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py index 57529ac8d..62e96e0e7 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki/src/tests/tutorial/views.py @@ -8,6 +8,12 @@ from pyramid.view import ( forbidden_view_config, ) +from pyramid.security import ( + remember, + forget, + ) + + from .security import USERS from .models import Page @@ -89,8 +95,9 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - request.remember_userid(login) - return HTTPFound(location=came_from) + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) message = 'Failed login' return dict( @@ -103,5 +110,6 @@ def login(request): @view_config(context='.models.Wiki', name='logout') def logout(request): - request.forget_userid() - return HTTPFound(location=request.resource_url(request.context)) + headers = forget(request) + return HTTPFound(location = request.resource_url(request.context), + headers = headers) diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index 830cb0277..1e5d0dcbf 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -221,23 +221,23 @@ Add the following import statements to the head of ``tutorial/tutorial/views.py``: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 9-12,19 + :lines: 9-19 :linenos: - :emphasize-lines: 3,5 + :emphasize-lines: 3,6-9,11 :language: python (Only the highlighted lines need to be added.) -:func:`~pyramid.view.forbidden_view_config` will be used +:meth:`~pyramid.view.forbidden_view_config` will be used to customize the default 403 Forbidden page. -:meth:`~pyramid.request.Request.remember_userid` and -:meth:`~pyramid.request.Request.forget_userid` help to create and +:meth:`~pyramid.security.remember` and +:meth:`~pyramid.security.forget` help to create and expire an auth ticket cookie. Now add the ``login`` and ``logout`` views: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 85-115 + :lines: 91-123 :linenos: :language: python @@ -289,7 +289,7 @@ like this: (Only the highlighted line needs to be added.) -The :attr:`~pyramid.request.Request.authenticated_userid` property will return +The :meth:`~pyramid.request.Request.authenticated_userid` property will be ``None`` if the user is not authenticated. Add a "Logout" link when logged in @@ -338,7 +338,7 @@ when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: - :emphasize-lines: 11,19,25,31,52,55,67,70,82,85-115 + :emphasize-lines: 11,14-19,25,31,37,58,61,73,76,88,91-117,119-123 :language: python (Only the highlighted lines need to be added.) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py index 110d738c2..e954d5a31 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views.py @@ -11,12 +11,18 @@ from pyramid.view import ( forbidden_view_config, ) +from pyramid.security import ( + remember, + forget, + ) + +from .security import USERS + 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+)") @@ -78,8 +84,8 @@ def edit_page(request): pagename=pagename)) return dict( page=page, - save_url = request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid, + save_url=request.route_url('edit_page', pagename=pagename), + logged_in=request.authenticated_userid ) @view_config(route_name='login', renderer='templates/login.pt') @@ -97,8 +103,9 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - request.remember_userid(login) - return HTTPFound(location = came_from) + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) message = 'Failed login' return dict( @@ -111,6 +118,7 @@ def login(request): @view_config(route_name='logout') def logout(request): - request.forget_userid() - return HTTPFound(location = request.route_url('view_wiki')) + headers = forget(request) + return HTTPFound(location = request.route_url('view_wiki'), + headers = headers) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views.py b/docs/tutorials/wiki2/src/tests/tutorial/views.py index 110d738c2..41bea4785 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views.py @@ -11,12 +11,18 @@ from pyramid.view import ( forbidden_view_config, ) +from pyramid.security import ( + remember, + forget, + ) + +from .security import USERS + 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+)") @@ -78,8 +84,8 @@ def edit_page(request): pagename=pagename)) return dict( page=page, - save_url = request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid, + save_url=request.route_url('edit_page', pagename=pagename), + logged_in=request.authenticated_userid ) @view_config(route_name='login', renderer='templates/login.pt') @@ -97,8 +103,9 @@ def login(request): login = request.params['login'] password = request.params['password'] if USERS.get(login) == password: - request.remember_userid(login) - return HTTPFound(location = came_from) + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) message = 'Failed login' return dict( @@ -111,6 +118,6 @@ def login(request): @view_config(route_name='logout') def logout(request): - request.forget_userid() - return HTTPFound(location = request.route_url('view_wiki')) - + headers = forget(request) + return HTTPFound(location = request.route_url('view_wiki'), + headers = headers) -- cgit v1.2.3 From daa0964624bb4d2818cad62351418c09a7326b5a Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 11 Nov 2013 23:23:58 -0600 Subject: easiest commit ever --- docs/narr/paste.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/narr/paste.rst b/docs/narr/paste.rst index 3427b6d53..f1fb70869 100644 --- a/docs/narr/paste.rst +++ b/docs/narr/paste.rst @@ -87,7 +87,7 @@ configuration object and *returns* an instance of our application. .. _defaults_section_of_pastedeploy_file: -``[DEFAULTS]`` Section of a PasteDeploy ``.ini`` File +``[DEFAULT]`` Section of a PasteDeploy ``.ini`` File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can add a ``[DEFAULT]`` section to your PasteDeploy ``.ini`` file. Such -- cgit v1.2.3 From 04e6bf670e3968864fd858e3d3fa310d601a2420 Mon Sep 17 00:00:00 2001 From: Antti Haapala Date: Sat, 16 Nov 2013 00:31:48 +0200 Subject: Enhanced the narrative documentation for tweens. --- docs/narr/hooks.rst | 87 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 19 deletions(-) (limited to 'docs') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 0c450fad7..b26a46272 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -963,8 +963,8 @@ For full details, please read the `Venusian documentation .. _registering_tweens: -Registering "Tweens" --------------------- +Registering Tweens +------------------ .. versionadded:: 1.2 Tweens @@ -976,23 +976,76 @@ feature that may be used by Pyramid framework extensions, to provide, for example, Pyramid-specific view timing support bookkeeping code that examines exceptions before they are returned to the upstream WSGI application. Tweens behave a bit like :term:`WSGI` :term:`middleware` but they have the benefit of -running in a context in which they have access to the Pyramid -:term:`application registry` as well as the Pyramid rendering machinery. +running in a context in which they have access to the Pyramid :term:`request`, +:term:`response` and :term:`application registry` as well as the Pyramid +rendering machinery. -Creating a Tween Factory -~~~~~~~~~~~~~~~~~~~~~~~~ +Creating a Tween +~~~~~~~~~~~~~~~~ -To make use of tweens, you must construct a "tween factory". A tween factory +To create a tween, you must write a "tween factory". A tween factory must be a globally importable callable which accepts two arguments: ``handler`` and ``registry``. ``handler`` will be the either the main Pyramid request handling function or another tween. ``registry`` will be the Pyramid :term:`application registry` represented by this Configurator. A -tween factory must return a tween when it is called. +tween factory must return the tween (a callable object) when it is called. -A tween is a callable which accepts a :term:`request` object and returns -a :term:`response` object. +A tween is called with a single argument, ``request``, which is the +:term:`request` created by Pyramid's router when it receives a WSGI request. +A tween should return a :term:`response`, usually the one generated by the +downstream Pyramid application. -Here's an example of a tween factory: +You can write the tween factory as a simple closure-returning function: + +.. code-block:: python + :linenos: + + def simple_tween_factory(handler, registry): + # one-time configuration code goes here + + def simple_tween(request): + # code to be executed for each request before + # the actual application code goes here + + response = handler(request) + + # code to be executed for each request after + # the actual application code goes here + + return response + + return handler + +Alternatively, the tween factory can be a class with the ``__call__`` magic method: + +.. code-block:: python + :linenos: + + class simple_tween_factory(object): + def __init__(handler, registry): + self.handler = handler + self.registry = registry + + # one-time configuration code goes here + + def __call__(self, request): + # code to be executed for each request before + # the actual application code goes here + + response = self.handler(request) + + # code to be executed for each request after + # the actual application code goes here + + return response + +The closure style performs slightly better and enables you to conditionally +omit the tween from the request processing pipeline (see the following timing +tween example), whereas the class style makes it easier to have shared mutable +state, and it allows subclassing. + +Here's a complete example of a tween that logs the time spent processing each +request: .. code-block:: python :linenos: @@ -1022,12 +1075,6 @@ Here's an example of a tween factory: # handler return handler -If you remember, a tween is an object which accepts a :term:`request` object -and which returns a :term:`response` argument. The ``request`` argument to a -tween will be the request created by Pyramid's router when it receives a WSGI -request. The response object will be generated by the downstream Pyramid -application and it should be returned by the tween. - In the above example, the tween factory defines a ``timing_tween`` tween and returns it if ``asbool(registry.settings.get('do_timing'))`` is true. It otherwise simply returns the handler it was given. The ``registry.settings`` @@ -1132,8 +1179,10 @@ Allowable values for ``under`` or ``over`` (or both) are: fallbacks if the desired tween is not included, as well as compatibility with multiple other tweens. -Effectively, ``under`` means "closer to the main Pyramid application than", -``over`` means "closer to the request ingress than". +Effectively, ``over`` means "closer to the request ingress than" and +``under`` means "closer to the main Pyramid application than". +You can think of an onion with outer layers over the inner layers, +the application being under all the layers at the center. For example, the following call to :meth:`~pyramid.config.Configurator.add_tween` will attempt to place the -- cgit v1.2.3 From a0ef135f2ad6e4425eec7ffcc0839b605347c346 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 16 Nov 2013 05:06:38 -0500 Subject: 80 chars --- docs/narr/hooks.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index b26a46272..84dd2143c 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -1016,7 +1016,8 @@ You can write the tween factory as a simple closure-returning function: return handler -Alternatively, the tween factory can be a class with the ``__call__`` magic method: +Alternatively, the tween factory can be a class with the ``__call__`` magic +method: .. code-block:: python :linenos: -- cgit v1.2.3 From 392a6c7df93b67d6889680133fda0f744970d61f Mon Sep 17 00:00:00 2001 From: Antti Haapala Date: Sun, 17 Nov 2013 00:11:37 +0200 Subject: Removed extra indentation from some examples (:linenos: should be indented with the same indentation as the rest of the code block) --- docs/designdefense.rst | 2 +- docs/narr/events.rst | 6 +++--- docs/narr/extending.rst | 2 +- docs/narr/hooks.rst | 20 ++++++++++---------- docs/narr/hybrid.rst | 2 +- docs/narr/i18n.rst | 8 ++++---- docs/narr/introduction.rst | 6 +++--- docs/narr/introspector.rst | 4 ++-- docs/narr/logging.rst | 2 +- docs/narr/resources.rst | 2 +- docs/narr/scaffolding.rst | 2 +- docs/narr/upgrading.rst | 4 ++-- docs/narr/urldispatch.rst | 10 +++++----- docs/narr/vhosting.rst | 4 ++-- docs/narr/views.rst | 2 +- docs/tutorials/wiki/authorization.rst | 4 ++-- 16 files changed, 40 insertions(+), 40 deletions(-) (limited to 'docs') diff --git a/docs/designdefense.rst b/docs/designdefense.rst index bbce3e29c..2f3c14881 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -1078,7 +1078,7 @@ The contents of ``app2.py``: The contents of ``config.py``: .. code-block:: python - :linenos: + :linenos: L = [] diff --git a/docs/narr/events.rst b/docs/narr/events.rst index 2accb3dbe..50484761d 100644 --- a/docs/narr/events.rst +++ b/docs/narr/events.rst @@ -172,7 +172,7 @@ track of the information that subscribers will need. Here are some example custom event classes: .. code-block:: python - :linenos: + :linenos: class DocCreated(object): def __init__(self, doc, request): @@ -196,7 +196,7 @@ also use custom events with :ref:`subscriber predicates event with a decorator: .. code-block:: python - :linenos: + :linenos: from pyramid.events import subscriber from .events import DocCreated @@ -215,7 +215,7 @@ To fire your custom events use the accessed as ``request.registry.notify``. For example: .. code-block:: python - :linenos: + :linenos: from .events import DocCreated diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index a60a49fea..8462a9da7 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -234,7 +234,7 @@ For example, if the original application has the following ``configure_views`` configuration method: .. code-block:: python - :linenos: + :linenos: def configure_views(config): config.add_view('theoriginalapp.views.theview', name='theview') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 84dd2143c..14009a094 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -363,7 +363,7 @@ and modify the set of :term:`renderer globals` before they are passed to a that can be used for this purpose. For example: .. code-block:: python - :linenos: + :linenos: from pyramid.events import subscriber from pyramid.events import BeforeRender @@ -998,7 +998,7 @@ downstream Pyramid application. You can write the tween factory as a simple closure-returning function: .. code-block:: python - :linenos: + :linenos: def simple_tween_factory(handler, registry): # one-time configuration code goes here @@ -1020,7 +1020,7 @@ Alternatively, the tween factory can be a class with the ``__call__`` magic method: .. code-block:: python - :linenos: + :linenos: class simple_tween_factory(object): def __init__(handler, registry): @@ -1049,7 +1049,7 @@ Here's a complete example of a tween that logs the time spent processing each request: .. code-block:: python - :linenos: + :linenos: # in a module named myapp.tweens @@ -1101,7 +1101,7 @@ Here's an example of registering a tween factory as an "implicit" tween in a Pyramid application: .. code-block:: python - :linenos: + :linenos: from pyramid.config import Configurator config = Configurator() @@ -1135,7 +1135,7 @@ chain (the tween generated by the very last tween factory added) as its request handler function. For example: .. code-block:: python - :linenos: + :linenos: from pyramid.config import Configurator @@ -1379,7 +1379,7 @@ route predicate factory is most often a class with a constructor method. For example: .. code-block:: python - :linenos: + :linenos: class ContentTypePredicate(object): def __init__(self, val, config): @@ -1442,7 +1442,7 @@ with a subscriber that subscribes to the :class:`pyramid.events.NewRequest` event type. .. code-block:: python - :linenos: + :linenos: class RequestPathStartsWith(object): def __init__(self, val, config): @@ -1471,7 +1471,7 @@ previously registered ``request_path_startswith`` predicate in a call to :meth:`~pyramid.config.Configurator.add_subscriber`: .. code-block:: python - :linenos: + :linenos: # define a subscriber in your code @@ -1487,7 +1487,7 @@ Here's the same subscriber/predicate/event-type combination used via :class:`~pyramid.events.subscriber`. .. code-block:: python - :linenos: + :linenos: from pyramid.events import subscriber diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index a29ccb2ac..4a3258d35 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -63,7 +63,7 @@ An application that uses only traversal will have view configuration declarations that look like this: .. code-block:: python - :linenos: + :linenos: # config is an instance of pyramid.config.Configurator diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst index b62c16ff0..c9b782c08 100644 --- a/docs/narr/i18n.rst +++ b/docs/narr/i18n.rst @@ -309,7 +309,7 @@ In particular, add the ``Babel`` and ``lingua`` distributions to the application's ``setup.py`` file: .. code-block:: python - :linenos: + :linenos: setup(name="mypackage", # ... @@ -370,7 +370,7 @@ file of a ``pcreate`` -generated :app:`Pyramid` application has stanzas in it that look something like the following: .. code-block:: ini - :linenos: + :linenos: [compile_catalog] directory = myproject/locale @@ -398,7 +398,7 @@ that you'd like the domain of your translations to be ``mydomain`` instead, change the ``setup.cfg`` file stanzas to look like so: .. code-block:: ini - :linenos: + :linenos: [compile_catalog] directory = myproject/locale @@ -1041,7 +1041,7 @@ if no locale can be determined. Here's an implementation of a simple locale negotiator: .. code-block:: python - :linenos: + :linenos: def my_locale_negotiator(request): locale_name = request.params.get('my_locale') diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index a9c5fdfbd..8acbab3a0 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -336,7 +336,7 @@ For example, instead of returning a ``Response`` object from a ``render_to_response`` call: .. code-block:: python - :linenos: + :linenos: from pyramid.renderers import render_to_response @@ -347,7 +347,7 @@ For example, instead of returning a ``Response`` object from a You can return a Python dictionary: .. code-block:: python - :linenos: + :linenos: from pyramid.view import view_config @@ -827,7 +827,7 @@ Here's an example of using Pyramid's introspector from within a view callable: .. code-block:: python - :linenos: + :linenos: from pyramid.view import view_config from pyramid.response import Response diff --git a/docs/narr/introspector.rst b/docs/narr/introspector.rst index 3c0a6744f..a7bde4cf7 100644 --- a/docs/narr/introspector.rst +++ b/docs/narr/introspector.rst @@ -24,7 +24,7 @@ Here's an example of using Pyramid's introspector from within a view callable: .. code-block:: python - :linenos: + :linenos: from pyramid.view import view_config from pyramid.response import Response @@ -100,7 +100,7 @@ its ``__getitem__``, ``get``, ``keys``, ``values``, or ``items`` methods. For example: .. code-block:: python - :linenos: + :linenos: route_intr = introspector.get('routes', 'edit_user') pattern = route_intr['pattern'] diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst index b3bfb8a1e..75428d513 100644 --- a/docs/narr/logging.rst +++ b/docs/narr/logging.rst @@ -179,7 +179,7 @@ file, simply create a logger object using the ``__name__`` builtin and call methods on it. .. code-block:: python - :linenos: + :linenos: import logging log = logging.getLogger(__name__) diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index 34d75f2cc..f3ff1dc4c 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -83,7 +83,7 @@ works against resource instances. Here's a sample resource tree, represented by a variable named ``root``: .. code-block:: python - :linenos: + :linenos: class Resource(dict): pass diff --git a/docs/narr/scaffolding.rst b/docs/narr/scaffolding.rst index 534b2caf4..9952b6818 100644 --- a/docs/narr/scaffolding.rst +++ b/docs/narr/scaffolding.rst @@ -112,7 +112,7 @@ want to have extension scaffolds that can work across Pyramid 1.0.X, 1.1.X, defining your scaffold template: .. code-block:: python - :linenos: + :linenos: try: # pyramid 1.0.X # "pyramid.paster.paste_script..." doesn't exist past 1.0.X diff --git a/docs/narr/upgrading.rst b/docs/narr/upgrading.rst index 64343ca3e..eb3194a65 100644 --- a/docs/narr/upgrading.rst +++ b/docs/narr/upgrading.rst @@ -137,7 +137,7 @@ In the above case, it's line #3 in the ``myproj.views`` module (``from pyramid.view import static``) that is causing the problem: .. code-block:: python - :linenos: + :linenos: from pyramid.view import view_config @@ -148,7 +148,7 @@ The deprecation warning tells me how to fix it, so I can change the code to do things the newer way: .. code-block:: python - :linenos: + :linenos: from pyramid.view import view_config diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 61849c3c0..96ee5758e 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -492,7 +492,7 @@ The simplest route declaration which configures a route match to *directly* result in a particular view callable being invoked: .. code-block:: python - :linenos: + :linenos: config.add_route('idea', 'site/{id}') config.add_view('mypackage.views.site_view', route_name='idea') @@ -901,7 +901,7 @@ Details of the route matching decision for a particular request to the which you started the application from. For example: .. code-block:: text - :linenos: + :linenos: $ PYRAMID_DEBUG_ROUTEMATCH=true $VENV/bin/pserve development.ini Starting server in PID 13586. @@ -1060,7 +1060,7 @@ A custom route predicate may also *modify* the ``match`` dictionary. For instance, a predicate might do some type conversion of values: .. code-block:: python - :linenos: + :linenos: def integers(*segment_names): def predicate(info, request): @@ -1086,7 +1086,7 @@ To avoid the try/except uncertainty, the route pattern can contain regular expressions specifying requirements for that marker. For instance: .. code-block:: python - :linenos: + :linenos: def integers(*segment_names): def predicate(info, request): @@ -1128,7 +1128,7 @@ name. The ``pattern`` attribute is the route pattern. An example of using the route in a set of route predicates: .. code-block:: python - :linenos: + :linenos: def twenty_ten(info, request): if info['route'].name in ('ymd', 'ym', 'y'): diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index d37518052..53f6888b3 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -109,7 +109,7 @@ An example of an Apache ``mod_proxy`` configuration that will host the is below: .. code-block:: apache - :linenos: + :linenos: NameVirtualHost *:80 @@ -130,7 +130,7 @@ For a :app:`Pyramid` application running under :term:`mod_wsgi`, the same can be achieved using ``SetEnv``: .. code-block:: apache - :linenos: + :linenos: SetEnv HTTP_X_VHM_ROOT /cms diff --git a/docs/narr/views.rst b/docs/narr/views.rst index b2dd549ce..a746eb043 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -536,7 +536,7 @@ The following types work as view callables in this style: e.g.: .. code-block:: python - :linenos: + :linenos: from pyramid.response import Response diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 62b1164e3..93cd0c18e 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -149,8 +149,8 @@ to the ``@view_config`` decorator for ``add_page()`` and ``edit_page()``, for example: .. code-block:: python - :linenos: - :emphasize-lines: 3 + :linenos: + :emphasize-lines: 3 @view_config(name='add_page', context='.models.Wiki', renderer='templates/edit.pt', -- cgit v1.2.3 From bd897bbcfd936d36ad9877a2f00792454767b7fb Mon Sep 17 00:00:00 2001 From: Antti Haapala Date: Sun, 17 Nov 2013 01:02:29 +0200 Subject: Fixed indentation issues --- docs/narr/scaffolding.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/narr/scaffolding.rst b/docs/narr/scaffolding.rst index 9952b6818..f924d0d62 100644 --- a/docs/narr/scaffolding.rst +++ b/docs/narr/scaffolding.rst @@ -39,9 +39,9 @@ named ``__init__.py`` with something like the following: from pyramid.scaffolds import PyramidTemplate - class CoolExtensionTemplate(PyramidTemplate): - _template_dir = 'coolextension_scaffold' - summary = 'My cool extension' + class CoolExtensionTemplate(PyramidTemplate): + _template_dir = 'coolextension_scaffold' + summary = 'My cool extension' Once this is done, within the ``scaffolds`` directory, create a template directory. Our example used a template directory named @@ -89,7 +89,7 @@ For example: [pyramid.scaffold] coolextension=coolextension.scaffolds:CoolExtensionTemplate """ - ) + ) Run your distribution's ``setup.py develop`` or ``setup.py install`` command. After that, you should be able to see your scaffolding template -- cgit v1.2.3 From fab44e1e402efbf37fc58875974e9ae42827446e Mon Sep 17 00:00:00 2001 From: Antti Haapala Date: Sun, 17 Nov 2013 19:08:18 +0200 Subject: Should return the simple_tween here, not the handler. --- docs/narr/hooks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 14009a094..f2542f1d7 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -1014,7 +1014,7 @@ You can write the tween factory as a simple closure-returning function: return response - return handler + return simple_tween Alternatively, the tween factory can be a class with the ``__call__`` magic method: -- cgit v1.2.3 From 4caf2b0a553b09009299f23057820dd447ec5705 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 18 Nov 2013 07:37:10 -0500 Subject: update whatsnew in 1.5 --- docs/whatsnew-1.5.rst | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'docs') diff --git a/docs/whatsnew-1.5.rst b/docs/whatsnew-1.5.rst index 57f93cbff..23613896a 100644 --- a/docs/whatsnew-1.5.rst +++ b/docs/whatsnew-1.5.rst @@ -316,6 +316,48 @@ The feature additions in Pyramid 1.5 follow. - :func:`pyramid.path.package_name` no longer thows an exception when resolving the package name for namespace packages that have no ``__file__`` attribute. +- An authorization API has been added as a method of the request: + :meth:`pyramid.request.Request.has_permission`. It is a method-based + alternative to the :func:`pyramid.security.has_permission` API and works + exactly the same. The older API is now deprecated. + +- Property API attributes have been added to the request for easier access to + authentication data: :attr:`pyramid.request.Request.authenticated_userid`, + :attr:`pyramid.request.Request.unauthenticated_userid`, and + :attr:`pyramid.request.Request.effective_principals`. These are analogues, + respectively, of :func:`pyramid.security.authenticated_userid`, + :func:`pyramid.security.unauthenticated_userid`, and + :func:`pyramid.security.effective_principals`. They operate exactly the + same, except they are attributes of the request instead of functions + accepting a request. They are properties, so they cannot be assigned to. + The older function-based APIs are now deprecated. + +- Pyramid's console scripts (``pserve``, ``pviews``, etc) can now be run + directly, allowing custom arguments to be sent to the python interpreter + at runtime. For example:: + + python -3 -m pyramid.scripts.pserve development.ini + +- Added a specific subclass of :class:`pyramid.httpexceptions.HTTPBadRequest` + named :class:`pyramid.exceptions.BadCSRFToken` which will now be raised in + response to failures in the ``check_csrf_token`` view predicate. See + https://github.com/Pylons/pyramid/pull/1149 + +- Added a new ``SignedCookieSessionFactory`` which is very similar to the + ``UnencryptedCookieSessionFactoryConfig`` but with a clearer focus on + signing content. The custom serializer arguments to this function should + only focus on serializing, unlike its predecessor which required the + serializer to also perform signing. + See https://github.com/Pylons/pyramid/pull/1142 + +- Added a new ``BaseCookieSessionFactory`` which acts as a generic cookie + factory that can be used by framework implementors to create their own + session implementations. It provides a reusable API which focuses strictly + on providing a dictionary-like object that properly handles renewals, + timeouts, and conformance with the ``ISession`` API. + See https://github.com/Pylons/pyramid/pull/1142 + + Other Backwards Incompatibilities --------------------------------- @@ -404,6 +446,13 @@ Other Backwards Incompatibilities Pyramid narrative documentation instead of providing renderer globals values to the configurator. +- The key/values in the ``_query`` parameter of + :meth:`pyramid.request.Request.route_url` and the ``query`` parameter of + :meth:`pyramid.request.Request.resource_url` (and their variants), used to + encode a value of ``None`` as the string ``'None'``, leaving the resulting + query string to be ``a=b&key=None``. The value is now dropped in this + situation, leaving a query string of ``a=b&key=``. See + https://github.com/Pylons/pyramid/issues/1119 Deprecations ------------ @@ -417,12 +466,36 @@ Deprecations a deprecation warning when used. It had been docs-deprecated in 1.4 but did not issue a deprecation warning when used. +- :func:`pyramid.security.has_permission` is now deprecated in favor of using + :meth:`pyramid.request.Request.has_permission`. + +- The :func:`pyramid.security.authenticated_userid`, + :func:`pyramid.security.unauthenticated_userid`, and + :func:`pyramid.security.effective_principals` functions have been + deprecated. Use :attr:`pyramid.request.Request.authenticated_userid`, + :attr:`pyramid.request.Request.unauthenticated_userid` and + :attr:`pyramid.request.Request.effective_principals` instead. + +- Deprecate the ``pyramid.interfaces.ITemplateRenderer`` interface. It was + ill-defined and became unused when Mako and Chameleon template bindings were + split into their own packages. + +- The ``pyramid.session.UnencryptedCookieSessionFactoryConfig`` API has been + deprecated and is superseded by the + ``pyramid.session.SignedCookieSessionFactory``. Note that while the cookies + generated by the ``UnencryptedCookieSessionFactoryConfig`` + are compatible with cookies generated by old releases, cookies generated by + the SignedCookieSessionFactory are not. See + https://github.com/Pylons/pyramid/pull/1142 + Documentation Enhancements -------------------------- - A new documentation chapter named :ref:`quick_tour` was added. It describes starting out with Pyramid from a high level. +- Added a :ref:`quick_tutorial` to go with the Quick Tour + - Many other enhancements. -- cgit v1.2.3