Chris McDonough
2013-11-09 0dcd56c2c30863c6683c0cf442aa73dfdcd11b13
undeprecate remember/forget functions and remove remember_userid/forget_userid methods from request
13 files modified
548 ■■■■■ changed files
CHANGES.txt 14 ●●●● patch | view | raw | blame | history
docs/api/request.rst 7 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/authentication/tutorial/views.py 15 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/authorization/tutorial/views.py 15 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/authorization.rst 19 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/tutorial/views.py 16 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/tutorial/views.py 16 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/authorization.rst 16 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/views.py 22 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/views.py 23 ●●●●● patch | view | raw | blame | history
pyramid/security.py 139 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_testing.py 32 ●●●● patch | view | raw | blame | history
pyramid/tests/test_security.py 214 ●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -4,22 +4,12 @@
Features
--------
- Authentication and authorization APIs have been added as as methods of the
  request: ``request.has_permission``, ``request.forget_userid``, and
  ``request.remember_userid``.
- An authorization API has been added as a method of the
  request: ``request.has_permission``.
  ``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``,
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
@@ -253,10 +252,6 @@
      subrequest is invoked.  This means that it's not available for use on a
      request provided by e.g. the ``pshell`` environment.  For more
      information, see :ref:`subrequest_chapter`.
   .. automethod:: remember_userid
   .. automethod:: forget_userid
   .. automethod:: has_permission
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 @@
            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 @@
    @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)
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 @@
            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 @@
    @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)
docs/tutorials/wiki/authorization.rst
@@ -197,24 +197,24 @@
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 @@
(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 @@
.. 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.)
docs/tutorials/wiki/src/authorization/tutorial/views.py
@@ -8,6 +8,12 @@
    forbidden_view_config,
    )
from pyramid.security import (
    remember,
    forget,
    )
from .security import USERS
from .models import Page
@@ -89,8 +95,9 @@
        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 @@
@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)
docs/tutorials/wiki/src/tests/tutorial/views.py
@@ -8,6 +8,12 @@
    forbidden_view_config,
    )
from pyramid.security import (
    remember,
    forget,
    )
from .security import USERS
from .models import Page
@@ -89,8 +95,9 @@
        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 @@
@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)
docs/tutorials/wiki2/authorization.rst
@@ -221,23 +221,23 @@
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 @@
(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 @@
.. 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.)
docs/tutorials/wiki2/src/authorization/tutorial/views.py
@@ -11,12 +11,18 @@
    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 @@
                                                      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 @@
        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 @@
@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)
    
docs/tutorials/wiki2/src/tests/tutorial/views.py
@@ -11,12 +11,18 @@
    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 @@
                                                      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 @@
        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 @@
@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)
pyramid/security.py
@@ -38,6 +38,10 @@
        reg = get_current_registry() # b/c
    return reg
def _get_authentication_policy(request):
    registry = _get_registry(request)
    return registry.queryUtility(IAuthenticationPolicy)
def has_permission(permission, context, request):
    """
    A function that calls
@@ -113,8 +117,8 @@
def remember(request, principal, **kw):
    """
    Returns a sequence of header tuples (e.g. ``[('Set-Cookie',
    'foo=abc')]``) on this request's response.
    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
@@ -126,6 +130,7 @@
       from pyramid.security import remember
       headers = remember(request, 'chrism', password='123', max_age='86400')
       response = request.response
       response.headerlist.extend(headers)
       return response
@@ -133,23 +138,11 @@
    always return an empty sequence. 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)
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.'
    )
    policy = _get_authentication_policy(request)
    if policy is None:
        return []
    return policy.remember(request, principal, **kw)
def forget(request):
    """
@@ -169,21 +162,12 @@
    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.
       Use :meth:`pyramid.request.Request.get_logout_headers` instead.
    """            
    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.'
    )
    policy = _get_authentication_policy(request)
    if policy is None:
        return []
    return policy.forget(request)
def principals_allowed_by_permission(context, permission):
    """ Provided a ``context`` (a resource object), and a ``permission``
@@ -371,97 +355,6 @@
            return [Everyone]
        return policy.effective_principals(self)
    
    # b/c
    def _remember_userid(self, principal, **kw):
        policy = self._get_authentication_policy()
        if policy is None:
            return []
        return policy.remember(self, principal, **kw)
    def remember_userid(self, principal, on_exception=False, **kw):
        """ Using a response callback, sets authentication headers on the
        response eventually returned by the view executed by this request
        suitable for loggin a user in.  These headers are used 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:
        .. code-block:: python
           request.remember_userid('chrism', password='123', max_age='86400')
        This method always returns ``None``; it is called only for its side
        effects.
        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.
        One special keyword value is understood by this method:
        ``on_exception``.  Usually if an exception occurs within the same
        request after this method is called, the headers provided by the
        authentication policy will not be set on the response.  If
        ``on_exception`` is passed, and as ``True``, then the headers will be
        set on the response even if an exception is later raised.  By default
        this value is ``False``.
        .. versionadded:: 1.5
        """
        def callback(req, response):
            # do not set the headers on an exception unless explicitly
            # instructed
            exc = getattr(req, 'exception', None)
            if exc is None or on_exception:
                # NB: this call to _remember_userid should be exactly here
                # because some policies actually add another response callback
                # when their remember method is called, and we dont want them
                # to do that if there's an exception in the default case.
                headers = req._remember_userid(principal, **kw)
                response.headerlist.extend(headers)
        self.add_response_callback(callback)
    # b/c
    def _forget_userid(self):
        policy = self._get_authentication_policy()
        if policy is None:
            return []
        return policy.forget(self)
    def forget_userid(self, on_exception=False):
        """ Using a response callback, sets authentication headers suitable for
        logging a user out on the response returned by the view executed during
        this request based on the current :term:`authentication policy`.
        If no :term:`authentication policy` is in use, this function will
        be a noop.
        This method always returns ``None``; it is called only for its side
        effects.
        One special keyword value is understood by this method:
        ``on_exception``.  Usually if an exception occurs within the same
        request after this method is called, the headers provided by the
        authentication policy will not be set on the response.  If
        ``on_exception`` is passed, and as ``True``, then the headers will be
        set on the response even if an exception is later raised.  By default
        this value is ``False``.
        .. versionadded:: 1.5
        """
        def callback(req, response):
            exc = getattr(req, 'exception', None)
            if exc is None or on_exception:
                # NB: this call to _forget_userid should be exactly here
                # because some policies actually add another response callback
                # when their forget method is called, and we dont want them
                # to do that if there's an exception in the default case.
                headers = req._forget_userid()
                response.headerlist.extend(headers)
        self.add_response_callback(callback)
class AuthorizationAPIMixin(object):
    def has_permission(self, permission, context=None):
pyramid/tests/test_config/test_testing.py
@@ -25,31 +25,28 @@
        self.assertEqual(ut.permissive, False)
    def test_testing_securitypolicy_remember_result(self):
        from pyramid.security import remember
        config = self._makeOne(autocommit=True)
        pol = config.testing_securitypolicy(
            'user', ('group1', 'group2'),
            permissive=False,
            remember_result=[('X-Pyramid-Test', True)])
            permissive=False, remember_result=True)
        request = DummyRequest()
        request.registry = config.registry
        request.remember_userid('fred')
        val = remember(request, 'fred')
        self.assertEqual(pol.remembered, 'fred')
        val = dict(request.response.headerlist).get('X-Pyramid-Test')
        self.assertEqual(val, True)
    def test_testing_securitypolicy_forget_result(self):
        from pyramid.security import forget
        config = self._makeOne(autocommit=True)
        pol = config.testing_securitypolicy(
            'user', ('group1', 'group2'),
            permissive=False,
            forget_result=[('X-Pyramid-Test', True)])
            permissive=False, forget_result=True)
        request = DummyRequest()
        request.registry = config.registry
        request.response = DummyResponse()
        request.forget_userid()
        val = forget(request)
        self.assertEqual(pol.forgotten, True)
        val = dict(request.response.headerlist).get('X-Pyramid-Test')
        self.assertTrue(val)
        self.assertEqual(val, True)
    def test_testing_resources(self):
        from pyramid.traversal import find_resource
@@ -200,24 +197,9 @@
class DummyEvent:
    pass
class DummyResponse(object):
    def __init__(self):
        self.headers = []
    @property
    def headerlist(self):
        return self.headers
class DummyRequest(AuthenticationAPIMixin, AuthorizationAPIMixin):
    subpath = ()
    matchdict = None
    def __init__(self, environ=None):
        if environ is None:
            environ = {}
        self.environ = environ
        self.params = {}
        self.cookies = {}
        self.response = DummyResponse()
        
    def add_response_callback(self, callback):
        callback(self, self.response)
pyramid/tests/test_security.py
@@ -127,6 +127,69 @@
        result = self._callFUT(context, 'view')
        self.assertEqual(result, 'yo')
class TestRemember(unittest.TestCase):
    def setUp(self):
        testing.setUp()
    def tearDown(self):
        testing.tearDown()
    def _callFUT(self, *arg):
        from pyramid.security import remember
        return remember(*arg)
    def test_no_authentication_policy(self):
        request = _makeRequest()
        result = self._callFUT(request, 'me')
        self.assertEqual(result, [])
    def test_with_authentication_policy(self):
        request = _makeRequest()
        registry = request.registry
        _registerAuthenticationPolicy(registry, 'yo')
        result = self._callFUT(request, 'me')
        self.assertEqual(result, [('X-Pyramid-Test', 'me')])
    def test_with_authentication_policy_no_reg_on_request(self):
        from pyramid.threadlocal import get_current_registry
        registry = get_current_registry()
        request = _makeRequest()
        del request.registry
        _registerAuthenticationPolicy(registry, 'yo')
        result = self._callFUT(request, 'me')
        self.assertEqual(result, [('X-Pyramid-Test', 'me')])
class TestForget(unittest.TestCase):
    def setUp(self):
        testing.setUp()
    def tearDown(self):
        testing.tearDown()
    def _callFUT(self, *arg):
        from pyramid.security import forget
        return forget(*arg)
    def test_no_authentication_policy(self):
        request = _makeRequest()
        result = self._callFUT(request)
        self.assertEqual(result, [])
    def test_with_authentication_policy(self):
        request = _makeRequest()
        _registerAuthenticationPolicy(request.registry, 'yo')
        result = self._callFUT(request)
        self.assertEqual(result, [('X-Pyramid-Test', 'logout')])
    def test_with_authentication_policy_no_reg_on_request(self):
        from pyramid.threadlocal import get_current_registry
        registry = get_current_registry()
        request = _makeRequest()
        del request.registry
        _registerAuthenticationPolicy(registry, 'yo')
        result = self._callFUT(request)
        self.assertEqual(result, [('X-Pyramid-Test', 'logout')])
class TestViewExecutionPermitted(unittest.TestCase):
    def setUp(self):
        testing.setUp()
@@ -312,149 +375,6 @@
        _registerAuthenticationPolicy(registry, 'yo')
        self.assertEqual(request.effective_principals, 'yo')
class TestRememberUserId(unittest.TestCase):
    principal = 'the4th'
    def setUp(self):
        testing.setUp()
    def tearDown(self):
        testing.tearDown()
    def assert_response_headers(self, request, expected_headers):
        request._process_response_callbacks(request.response)
        headers = request.response.headerlist
        self.assertEqual(list(expected_headers), list(headers))
    def test_backward_compat_delegates_to_mixin(self):
        from zope.deprecation import __show__
        try:
            __show__.off()
            request = _makeFakeRequest()
            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 = _makeRequest()
        headers_before = request.response.headerlist
        request.remember_userid(self.principal)
        self.assert_response_headers(request, headers_before)
    def test_with_authentication_policy(self):
        request = _makeRequest()
        headers_before = request.response.headerlist
        expected_headers = headers_before[:] + [(_TEST_HEADER, self.principal)]
        _registerAuthenticationPolicy(request.registry, self.principal)
        request.remember_userid(self.principal)
        self.assert_response_headers(request, expected_headers)
    def test_with_authentication_policy_no_reg_on_request(self):
        from pyramid.threadlocal import get_current_registry
        registry = get_current_registry()
        request = _makeRequest()
        del request.registry
        _registerAuthenticationPolicy(registry, self.principal)
        headers_before = request.response.headerlist
        request.remember_userid(self.principal)
        expected_headers = headers_before[:] + [(_TEST_HEADER, self.principal)]
        self.assert_response_headers(request, expected_headers)
    def test_request_has_exception_attr_no_on_exception_flag(self):
        request = _makeRequest()
        headers_before = request.response.headerlist
        _registerAuthenticationPolicy(request.registry, self.principal)
        request.exception = True
        request.remember_userid(self.principal)
        self.assert_response_headers(request, headers_before)
    def test_request_has_exception_attr_with_on_exception_flag(self):
        request = _makeRequest()
        headers_before = request.response.headerlist
        _registerAuthenticationPolicy(request.registry, self.principal)
        request.exception = True
        request.remember_userid(self.principal, on_exception=True)
        expected_headers = headers_before[:] + [(_TEST_HEADER, self.principal)]
        self.assert_response_headers(request, expected_headers)
class TestForgetUserId(unittest.TestCase):
    principal = 'me-not'
    def setUp(self):
        testing.setUp()
    def tearDown(self):
        testing.tearDown()
    def assert_response_headers(self, request, expected_headers):
        request._process_response_callbacks(request.response)
        headers = request.response.headerlist
        self.assertEqual(list(expected_headers), list(headers))
    def _makeOne(self):
        request = _makeRequest()
        request.response.headers.add(_TEST_HEADER, self.principal)
        return request
    def test_backward_compat_delegates_to_mixin(self):
        from zope.deprecation import __show__
        try:
            __show__.off()
            request = _makeFakeRequest()
            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()
        headers_before = request.response.headerlist
        request.forget_userid()
        self.assert_response_headers(request, headers_before)
    def test_with_authentication_policy(self):
        request = self._makeOne()
        headers_before = request.response.headerlist
        expected_headers = headers_before[:] + [(_TEST_HEADER, 'forget_userid')]
        _registerAuthenticationPolicy(request.registry, self.principal)
        request.forget_userid()
        self.assert_response_headers(request, expected_headers)
    def test_with_authentication_policy_no_reg_on_request(self):
        from pyramid.threadlocal import get_current_registry
        registry = get_current_registry()
        request = self._makeOne()
        del request.registry
        _registerAuthenticationPolicy(registry, self.principal)
        headers_before = request.response.headerlist
        request.forget_userid()
        expected_headers = headers_before[:] + [(_TEST_HEADER, 'forget_userid')]
        self.assert_response_headers(request, expected_headers)
    def test_request_has_exception_attr_no_on_exception_flag(self):
        request = self._makeOne()
        headers_before = request.response.headerlist
        _registerAuthenticationPolicy(request.registry, self.principal)
        request.exception = True
        request.forget_userid()
        self.assert_response_headers(request, headers_before)
    def test_request_has_exception_attr_with_on_exception_flag(self):
        request = self._makeOne()
        headers_before = request.response.headerlist
        _registerAuthenticationPolicy(request.registry, self.principal)
        request.exception = True
        request.forget_userid(on_exception=True)
        expected_headers = headers_before[:] + [(_TEST_HEADER, 'forget_userid')]
        self.assert_response_headers(request, expected_headers)
class TestHasPermission(unittest.TestCase):
    def setUp(self):
        testing.setUp()
@@ -548,7 +468,7 @@
        return headers
    def forget(self, request):
        headers = [(_TEST_HEADER, 'forget_userid')]
        headers = [(_TEST_HEADER, 'logout')]
        self._header_forgotten = headers[0]
        return headers
@@ -594,12 +514,6 @@
        @property
        def effective_principals(req):
            return 'effective_principals'
        def _forget_userid(req):
            return [('X-Pyramid-Test', 'forget_userid')]
        def _remember_userid(req, principal, **kw):
            return [('X-Pyramid-Test', 'remember_userid')]
    return FakeRequest({})