diff options
| author | Chris McDonough <chrism@plope.com> | 2013-10-28 15:26:31 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2013-10-28 15:26:31 -0400 |
| commit | 0184b527725cfb634e4d57a1b033450fa8b24502 (patch) | |
| tree | 403ada69ee4747ce113e896720d8634d7947938f | |
| parent | 0f424ae492c4b7f11d526149046fc6467a54e438 (diff) | |
| download | pyramid-0184b527725cfb634e4d57a1b033450fa8b24502.tar.gz pyramid-0184b527725cfb634e4d57a1b033450fa8b24502.tar.bz2 pyramid-0184b527725cfb634e4d57a1b033450fa8b24502.zip | |
Bring change log, API docs, and deprecations in line with normal policies/processes
| -rw-r--r-- | CHANGES.txt | 56 | ||||
| -rw-r--r-- | docs/api/request.rst | 47 | ||||
| -rw-r--r-- | pyramid/config/routes.py | 8 | ||||
| -rw-r--r-- | pyramid/config/testing.py | 8 | ||||
| -rw-r--r-- | pyramid/config/views.py | 8 | ||||
| -rw-r--r-- | pyramid/request.py | 21 | ||||
| -rw-r--r-- | pyramid/security.py | 185 | ||||
| -rw-r--r-- | pyramid/testing.py | 14 | ||||
| -rw-r--r-- | pyramid/tests/test_security.py | 96 |
9 files changed, 345 insertions, 98 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 61f3b63f7..20b7726c4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,13 +4,34 @@ Unreleased Features -------- -- The :mod:``pyramid.security`` authentication API methods should now be - accessed via the request. The ``pyramid.security`` authoriztion API function - :meth:`has_permission` should now be accessed via the request. - The methods :meth:``pyramid.request.Request.forget_userid``, - meth:``pyramid.request.Request.remember_userid`` now automatically - set the headers on the response, as returned by the corrosponding - method of the current request's :term:``authentication policy``. +- Authentication and authorization APIs have been added as as methods of the + request: ``request.has_permission``, ``request.forget_userid``, and + ``request.remember_userid``. + + ``request.has_permission`` is a method-based alternative to the + ``pyramid.security.has_permission`` API and works exactly the same. The + older API is now deprecated. + + ``request.forget_userid`` and ``request.remember_userid`` are method-based + alternatives to ``pyramid.security.forget`` and + ``pyramid.security.remember``. These do not work exacly the same as their + function counterparts, however. These methods automatically set the headers + returned by the authentication policy on the response, whereas the older + function-based APIs returned a sequence of headers and required the caller to + set those headers. The older function-based API still works but is now + deprecated. + +- Property API attributes have been added to the request for easier access to + authentication data: ``request.authenticated_userid``, + ``request.unauthenticated_userid``, and ``request.effective_principals``. + + These are analogues, respectively, of + ``pyramid.security.authenticated_userid``, + ``pyramid.security.unauthenticated_userid``, and + ``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 @@ -105,6 +126,27 @@ Deprecations the SignedCookieSessionFactory are not. See https://github.com/Pylons/pyramid/pull/1142 +- The ``pyramid.security.has_permission`` API is now deprecated. Instead, use + the newly-added ``has_permission`` method of the request object. + +- The ``pyramid.security.forget`` API is now deprecated. Instead, use + the newly-added ``forget_userid`` method of the request object. + +- The ``pyramid.security.remember`` API is now deprecated. Instead, use + the newly-added ``remember_userid`` method of the request object. + +- The ``pyramid.security.effective_principals`` API is now deprecated. + Instead, use the newly-added ``effective_principals`` attribute of the + request object. + +- The ``pyramid.security.authenticated_userid`` API is now deprecated. + Instead, use the newly-added ``authenticated_userid`` attribute of the + request object. + +- The ``pyramid.security.unauthenticated_userid`` API is now deprecated. + Instead, use the newly-added ``unauthenticated_userid`` attribute of the + request object. + 1.5a2 (2013-09-22) ================== 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 diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index 5a671c819..4fd207600 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -237,10 +237,10 @@ class RoutesConfiguratorMixin(object): If specified, this value should be a :term:`principal` identifier or a sequence of principal identifiers. If the - :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: + :attr:`pyramid.request.Request.effective_principals` property + 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 ``effective_principals=('fred', 'group:admins')``. diff --git a/pyramid/config/testing.py b/pyramid/config/testing.py index a006c4767..5df726a31 100644 --- a/pyramid/config/testing.py +++ b/pyramid/config/testing.py @@ -47,14 +47,14 @@ class TestingConfiguratorMixin(object): ``groupids`` argument. The authentication policy will return the userid identifier implied by the ``userid`` argument and the group ids implied by the ``groupids`` argument when the - :meth:`pyramid.request.Request.authenticated_userid` or - :meth:`pyramid.request.Request.effective_principals` APIs are + :attr:`pyramid.request.Request.authenticated_userid` or + :attr:`pyramid.request.Request.effective_principals` APIs are used. This function is most useful when testing code that uses the APIs named :meth:`pyramid.request.Request.has_permission`, - :meth:`pyramid.request.Request.authenticated_userid`, - :meth:`pyramid.request.Request.effective_principals`, and + :attr:`pyramid.request.Request.authenticated_userid`, + :attr:`pyramid.request.Request.effective_principals`, and :func:`pyramid.security.principals_allowed_by_permission`. .. versionadded:: 1.4 diff --git a/pyramid/config/views.py b/pyramid/config/views.py index b0cd785f5..a3f885504 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -1017,10 +1017,10 @@ class ViewsConfiguratorMixin(object): If specified, this value should be a :term:`principal` identifier or a sequence of principal identifiers. If the - :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: + :attr:`pyramid.request.Request.effective_principals` property + 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 ``effective_principals=('fred', 'group:admins')``. diff --git a/pyramid/request.py b/pyramid/request.py index da640ea7d..188e968ac 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -21,7 +21,10 @@ from pyramid.compat import ( from pyramid.decorator import reify from pyramid.i18n import LocalizerRequestMixin from pyramid.response import Response -from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin +from pyramid.security import ( + AuthenticationAPIMixin, + AuthorizationAPIMixin, + ) from pyramid.url import URLMethodsMixin from pyramid.util import InstancePropertyMixin @@ -137,13 +140,15 @@ class CallbackMethodsMixin(object): callback(self) @implementer(IRequest) -class Request(BaseRequest, - URLMethodsMixin, - CallbackMethodsMixin, - InstancePropertyMixin, - LocalizerRequestMixin, - AuthenticationAPIMixin, - AuthorizationAPIMixin): +class Request( + BaseRequest, + URLMethodsMixin, + CallbackMethodsMixin, + InstancePropertyMixin, + LocalizerRequestMixin, + AuthenticationAPIMixin, + AuthorizationAPIMixin, + ): """ A subclass of the :term:`WebOb` Request class. An instance of this class is created by the :term:`router` and is provided to a diff --git a/pyramid/security.py b/pyramid/security.py index b5e0a2c78..27612206a 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -1,3 +1,4 @@ +from zope.deprecation import deprecated from zope.interface import providedBy from pyramid.interfaces import ( @@ -37,57 +38,152 @@ def _get_registry(request): reg = get_current_registry() # b/c return reg -# b/c def has_permission(permission, context, request): - """ Backwards compatible wrapper. + """ + A function that calls + :meth:`pyramid.request.Request.has_permission` and returns its result. + + .. deprecated:: 1.5 + Use :meth:`pyramid.request.Request.has_permission` instead. - Delegates to the :meth:``pyramid.request.Request.has_permission`` method. + .. versionchanged:: 1.5a3 + If context is None, then attempt to use the context attribute + of self, if not set then the AttributeError is propergated. """ return request.has_permission(permission, context) -# b/c -def authenticated_userid(request): - """ Backwards compatible wrapper. +deprecated( + 'has_permission', + 'As of Pyramid 1.5 the "pyramid.security.has_permission" API is now ' + 'deprecated. It will be removed in Pyramd 1.8. Use the ' + '"has_permission" method of the Pyramid request instead.' + ) + - Delegates to the - :meth:``pyramid.request.Request.authenticated_userid`` method. +def authenticated_userid(request): + """ + A function that returns the value of the property + :attr:`pyramid.request.Request.authenticated_userid`. + + .. deprecated:: 1.5 + Use :attr:`pyramid.request.Request.authenticated_userid` instead. """ return request.authenticated_userid -# b/c -def unauthenticated_userid(request): - """ Backwards compatible wrapper. +deprecated( + 'authenticated_userid', + 'As of Pyramid 1.5 the "pyramid.security.authenticated_userid" API is now ' + 'deprecated. It will be removed in Pyramd 1.8. Use the ' + '"authenticated_userid" attribute of the Pyramid request instead.' + ) - Delegates to the - :meth:``pyramid.request.Request.unauthenticated_userid`` method. +def unauthenticated_userid(request): + """ + A function that returns the value of the property + :attr:`pyramid.request.Request.unauthenticated_userid`. + + .. deprecated:: 1.5 + Use :attr:`pyramid.request.Request.unauthenticated_userid` instead. """ return request.unauthenticated_userid -# b/c -def effective_principals(request): - """ Backwards compatible wrapper. +deprecated( + 'unauthenticated_userid', + 'As of Pyramid 1.5 the "pyramid.security.unauthenticated_userid" API is ' + 'now deprecated. It will be removed in Pyramd 1.8. Use the ' + '"unauthenticated_userid" attribute of the Pyramid request instead.' + ) - Delegates to the - :meth:``pyramid.request.Request.effective_principals`` method. +def effective_principals(request): + """ + A function that returns the value of the property + :attr:`pyramid.request.Request.effective_principals`. + + .. deprecated:: 1.5 + Use :attr:`pyramid.request.Request.effective_principals` instead. """ return request.effective_principals -# b/c -def remember(request, principal, **kw): - """ Backwards compatible wrapper. +deprecated( + 'effective_principals', + 'As of Pyramid 1.5 the "pyramid.security.effective_principals" API is ' + 'now deprecated. It will be removed in Pyramd 1.8. Use the ' + '"effective_principals" attribute of the Pyramid request instead.' + ) - Delegates to the :meth:``pyramid.request.Request.remember_userid`` method. - """ +def remember(request, principal, **kw): + """ + Returns a sequence of header tuples (e.g. ``[('Set-Cookie', + 'foo=abc')]``) on this request's response. + These headers are suitable for 'remembering' a set of credentials + implied by the data passed as ``principal`` and ``*kw`` using the + current :term:`authentication policy`. Common usage might look + like so within the body of a view function (``response`` is + assumed to be a :term:`WebOb` -style :term:`response` object + computed previously by the view code):: + + .. code-block:: python + + from pyramid.security import remember + headers = remember(request, 'chrism', password='123', max_age='86400') + response.headerlist.extend(headers) + return response + + If no :term:`authentication policy` is in use, this function will + do nothing. If used, the composition and + meaning of ``**kw`` must be agreed upon by the calling code and + the effective authentication policy. + + .. deprecated:: 1.5 + Use :meth:`pyramid.request.Request.remember_userid` instead. + but be sure to read its docs first; the remember_userid method is not an + exact analog of the remember function, because it sets headers instead + of returning them. + """ return request._remember_userid(principal, **kw) -# b/c -def forget(request): - """ Backwards compatible wrapper. +deprecated( + 'remember', + 'As of Pyramid 1.5 the "pyramid.security.remember" API is ' + 'now deprecated. It will be removed in Pyramd 1.8. Use the ' + '"remember_userid" method of the Pyramid request instead, but be sure to ' + 'read the docs first; the remember_userid method is not an exact analog of ' + 'the remember function, because it sets headers instead of returning them.' + ) - Delegates to the :meth:``pyramid.request.Request.forget_userid`` method. +def forget(request): + """ + Return a sequence of header tuples (e.g. ``[('Set-Cookie', + 'foo=abc')]``) suitable for 'forgetting' the set of credentials + possessed by the currently authenticated user. A common usage + might look like so within the body of a view function + (``response`` is assumed to be an :term:`WebOb` -style + :term:`response` object computed previously by the view code):: + + from pyramid.security import forget + headers = forget(request) + response.headerlist.extend(headers) + return response + + If no :term:`authentication policy` is in use, this function will + always return an empty sequence. + + .. deprecated:: 1.5 + Use :meth:`pyramid.request.Request.forget_userid` instead. + but be sure to read its docs first; the forget_userid method is not an + exact analog of the forget function, because it sets headers instead + of returning them. """ return request._forget_userid() +deprecated( + 'forget', + 'As of Pyramid 1.5 the "pyramid.security.forget" API is ' + 'now deprecated. It will be removed in Pyramd 1.8. Use the ' + '"forget_user" method of the Pyramid request instead, but be sure to ' + 'read the docs first; the forget_userid method is not an exact analog of ' + 'the forget function, because it sets headers instead of returning them.' + ) def principals_allowed_by_permission(context, permission): """ Provided a ``context`` (a resource object), and a ``permission`` @@ -234,7 +330,10 @@ class AuthenticationAPIMixin(object): def authenticated_userid(self): """ Return 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.""" + there is no currently authenticated user. + + .. versionadded:: 1.5 + """ policy = self._get_authentication_policy() if policy is None: return None @@ -248,7 +347,10 @@ class AuthenticationAPIMixin(object): associated with the current request. This differs from :func:`~pyramid.security.authenticated_userid`, because the effective authentication policy will not ensure that a record associated with the - userid exists in persistent storage.""" + userid exists in persistent storage. + + .. versionadded:: 1.5 + """ policy = self._get_authentication_policy() if policy is None: return None @@ -260,7 +362,10 @@ class AuthenticationAPIMixin(object): for the ``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 an empty sequence.""" + this will return an empty sequence. + + .. versionadded:: 1.5 + """ policy = self._get_authentication_policy() if policy is None: return [Everyone] @@ -290,7 +395,11 @@ class AuthenticationAPIMixin(object): If no :term:`authentication policy` is in use, this function will do nothing. If used, the composition and meaning of ``**kw`` must be agreed upon by the calling code and - the effective authentication policy.""" + the effective authentication policy. + + .. versionadded:: 1.5 + + """ headers = self._remember_userid(principal, **kw) callback = lambda req, response: response.headerlist.extend(headers) self.add_response_callback(callback) @@ -315,9 +424,12 @@ class AuthenticationAPIMixin(object): request.forget_userid() If no :term:`authentication policy` is in use, this function will - be a noop.""" + be a noop. + + .. versionadded:: 1.5 + """ headers = self._forget_userid() - callback = lambda req, response: response.headerlist.extend(headers) + callback = lambda req, response: response.headerlist.extend(headers) self.add_response_callback(callback) class AuthorizationAPIMixin(object): @@ -333,15 +445,14 @@ class AuthorizationAPIMixin(object): unconditionally if no authentication policy has been registered for this request. - .. versionchanged:: 1.5a3 - If context is None, then attempt to use the context attribute - of self, if not set then the AttributeError is propergated. - :param permission: Does this request have the given permission? :type permission: unicode, str :param context: Typically a resource of a regsitered type. :type context: object :returns: `pyramid.security.PermitsResult` + + .. versionadded:: 1.5 + """ if context is None: context = self.context diff --git a/pyramid/testing.py b/pyramid/testing.py index 2416c7b34..b3460d8aa 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -283,12 +283,14 @@ class DummySession(dict): return token @implementer(IRequest) -class DummyRequest(URLMethodsMixin, - CallbackMethodsMixin, - InstancePropertyMixin, - LocalizerRequestMixin, - AuthenticationAPIMixin, - AuthorizationAPIMixin): +class DummyRequest( + URLMethodsMixin, + CallbackMethodsMixin, + InstancePropertyMixin, + LocalizerRequestMixin, + AuthenticationAPIMixin, + AuthorizationAPIMixin, + ): """ A DummyRequest object (incompletely) imitates a :term:`request` object. The ``params``, ``environ``, ``headers``, ``path``, and diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py index 4b40feaf3..b685ddc97 100644 --- a/pyramid/tests/test_security.py +++ b/pyramid/tests/test_security.py @@ -242,9 +242,17 @@ class AuthenticationAPIMixinTest(object): class TestAuthenticatedUserId(AuthenticationAPIMixinTest, unittest.TestCase): def test_backward_compat_delegates_to_mixin(self): - request = self._makeFakeOne() - from pyramid.security import authenticated_userid - self.assertEqual(authenticated_userid(request), 'authenticated_userid') + from zope.deprecation import __show__ + try: + __show__.off() + request = self._makeFakeOne() + from pyramid.security import authenticated_userid + self.assertEqual( + authenticated_userid(request), + 'authenticated_userid' + ) + finally: + __show__.on() def test_no_authentication_policy(self): request = self._makeOne() @@ -265,10 +273,17 @@ class TestAuthenticatedUserId(AuthenticationAPIMixinTest, unittest.TestCase): class TestUnAuthenticatedUserId(AuthenticationAPIMixinTest, unittest.TestCase): def test_backward_compat_delegates_to_mixin(self): - request = self._makeFakeOne() - from pyramid.security import unauthenticated_userid - self.assertEqual(unauthenticated_userid(request), - 'unauthenticated_userid') + from zope.deprecation import __show__ + try: + __show__.off() + request = self._makeFakeOne() + from pyramid.security import unauthenticated_userid + self.assertEqual( + unauthenticated_userid(request), + 'unauthenticated_userid', + ) + finally: + __show__.on() def test_no_authentication_policy(self): request = self._makeOne() @@ -290,8 +305,16 @@ class TestUnAuthenticatedUserId(AuthenticationAPIMixinTest, unittest.TestCase): class TestEffectivePrincipals(AuthenticationAPIMixinTest, unittest.TestCase): def test_backward_compat_delegates_to_mixin(self): request = self._makeFakeOne() - from pyramid.security import effective_principals - self.assertEqual(effective_principals(request), 'effective_principals') + from zope.deprecation import __show__ + try: + __show__.off() + from pyramid.security import effective_principals + self.assertEqual( + effective_principals(request), + 'effective_principals' + ) + finally: + __show__.on() def test_no_authentication_policy(self): from pyramid.security import Everyone @@ -322,10 +345,17 @@ class TestRememberUserId(ResponseCallbackTestMixin, unittest.TestCase): principal = 'the4th' def test_backward_compat_delegates_to_mixin(self): - request = self._makeFakeOne() - from pyramid.security import remember - self.assertEqual(remember(request, 'matt'), - [('X-Pyramid-Test', 'remember_userid')]) + from zope.deprecation import __show__ + try: + __show__.off() + request = self._makeFakeOne() + from pyramid.security import remember + self.assertEqual( + remember(request, 'matt'), + [('X-Pyramid-Test', 'remember_userid')] + ) + finally: + __show__.on() def test_with_no_authentication_policy(self): request = self._makeOne() @@ -357,10 +387,17 @@ class TestForgetUserId(ResponseCallbackTestMixin, unittest.TestCase): return request def test_backward_compat_delegates_to_mixin(self): - request = self._makeFakeOne() - from pyramid.security import forget - self.assertEqual(forget(request), - [('X-Pyramid-Test', 'forget_userid')]) + from zope.deprecation import __show__ + try: + __show__.off() + request = self._makeFakeOne() + from pyramid.security import forget + self.assertEqual( + forget(request), + [('X-Pyramid-Test', 'forget_userid')], + ) + finally: + __show__.on() def test_with_no_authentication_policy(self): request = self._makeOne() @@ -401,16 +438,21 @@ class TestHasPermission(unittest.TestCase): return mixin def test_delegates_to_mixin(self): - mixin = self._makeOne() - from pyramid.security import has_permission - self.called_has_permission = False - - def mocked_has_permission(*args, **kw): - self.called_has_permission = True - - mixin.has_permission = mocked_has_permission - has_permission('view', object(), mixin) - self.assertTrue(self.called_has_permission) + from zope.deprecation import __show__ + try: + __show__.off() + mixin = self._makeOne() + from pyramid.security import has_permission + self.called_has_permission = False + + def mocked_has_permission(*args, **kw): + self.called_has_permission = True + + mixin.has_permission = mocked_has_permission + has_permission('view', object(), mixin) + self.assertTrue(self.called_has_permission) + finally: + __show__.on() def test_no_authentication_policy(self): request = self._makeOne() |
