diff options
| -rw-r--r-- | CHANGES.rst | 4 | ||||
| -rw-r--r-- | docs/narr/security.rst | 16 | ||||
| -rw-r--r-- | docs/quick_tutorial/authentication.rst | 30 | ||||
| -rw-r--r-- | docs/quick_tutorial/authentication/tutorial/__init__.py | 18 | ||||
| -rw-r--r-- | docs/quick_tutorial/authentication/tutorial/security.py | 24 | ||||
| -rw-r--r-- | docs/quick_tutorial/authorization.rst | 15 | ||||
| -rw-r--r-- | docs/quick_tutorial/authorization/tutorial/__init__.py | 18 | ||||
| -rw-r--r-- | docs/quick_tutorial/authorization/tutorial/security.py | 38 | ||||
| -rw-r--r-- | src/pyramid/authentication.py | 163 | ||||
| -rw-r--r-- | src/pyramid/config/testing.py | 4 | ||||
| -rw-r--r-- | src/pyramid/interfaces.py | 10 | ||||
| -rw-r--r-- | src/pyramid/security.py | 4 | ||||
| -rw-r--r-- | src/pyramid/testing.py | 2 | ||||
| -rw-r--r-- | tests/pkgs/securityapp/__init__.py | 2 | ||||
| -rw-r--r-- | tests/test_security.py | 4 | ||||
| -rw-r--r-- | tests/test_testing.py | 4 |
16 files changed, 271 insertions, 85 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 383906e00..9f16b06ea 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -156,6 +156,10 @@ Backward Incompatibilities ``require_csrf`` view option to enable automatic CSRF checking. See https://github.com/Pylons/pyramid/pull/3521 +- Changed the default ``hashalg`` on + ``pyramid.authentication.AuthTktCookieHelper`` to ``sha512``. + See https://github.com/Pylons/pyramid/pull/3557 + Documentation Changes --------------------- diff --git a/docs/narr/security.rst b/docs/narr/security.rst index ac64cba0a..72c2721f6 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -69,7 +69,7 @@ A simple security policy might look like the following: from pyramid.security import Allowed, Denied class SessionSecurityPolicy: - def identify(self, request): + def authenticated_identity(self, request): """ Return app-specific user object. """ userid = request.session.get('userid') if userid is None: @@ -78,14 +78,14 @@ A simple security policy might look like the following: def authenticated_userid(self, request): """ Return a string ID for the user. """ - identity = self.identify(request) + identity = self.authenticated_identity(request) if identity is None: return None return string(identity.id) def permits(self, request, context, permission): """ Allow access to everything if signed in. """ - identity = self.identify(request) + identity = self.authenticated_identity(request) if identity is not None: return Allowed('User is signed in.') else: @@ -144,7 +144,7 @@ For example, our above security policy can leverage these helpers like so: def __init__(self): self.helper = SessionAuthenticationHelper() - def identify(self, request): + def authenticated_identity(self, request): """ Return app-specific user object. """ userid = self.helper.authenticated_userid(request) if userid is None: @@ -153,14 +153,14 @@ For example, our above security policy can leverage these helpers like so: def authenticated_userid(self, request): """ Return a string ID for the user. """ - identity = self.identify(request) + identity = self.authenticated_identity(request) if identity is None: return None return str(identity.id) def permits(self, request, context, permission): """ Allow access to everything if signed in. """ - identity = self.identify(request) + identity = self.authenticated_identity(request) if identity is not None: return Allowed('User is signed in.') else: @@ -249,7 +249,7 @@ might look like so: class SecurityPolicy: def permits(self, request, context, permission): - identity = self.identify(request) + identity = self.authenticated_identity(request) if identity is None: return Denied('User is not signed in.') @@ -698,7 +698,7 @@ A "secret" is required by various components of Pyramid. For example, the helper below might be used for a security policy and uses a secret value ``seekrit``:: - helper = AuthTktCookieHelper('seekrit', hashalg='sha512') + helper = AuthTktCookieHelper('seekrit') A :term:`session factory` also requires a secret:: diff --git a/docs/quick_tutorial/authentication.rst b/docs/quick_tutorial/authentication.rst index cd038ea36..12eb738e2 100644 --- a/docs/quick_tutorial/authentication.rst +++ b/docs/quick_tutorial/authentication.rst @@ -55,16 +55,15 @@ Steps :language: ini :linenos: -#. Get authentication (and for now, authorization policies) and login route - into the :term:`configurator` in ``authentication/tutorial/__init__.py``: +#. Create an ``authentication/tutorial/security.py`` module that can find our + user information by providing a :term:`security policy`: - .. literalinclude:: authentication/tutorial/__init__.py + .. literalinclude:: authentication/tutorial/security.py :linenos: -#. Create an ``authentication/tutorial/security.py`` module that can find our - user information by providing an *authentication policy callback*: +#. Register the ``SecurityPolicy`` with the :term:`configurator` in ``authentication/tutorial/__init__.py``: - .. literalinclude:: authentication/tutorial/security.py + .. literalinclude:: authentication/tutorial/__init__.py :linenos: #. Update the views in ``authentication/tutorial/views.py``: @@ -111,14 +110,12 @@ are you) and authorization (what are you allowed to do) are not just pluggable, but decoupled. To learn one step at a time, we provide a system that identifies users and lets them log out. -In this example we chose to use the bundled :ref:`AuthTktAuthenticationPolicy -<authentication_module>` policy. We enabled it in our configuration and -provided a ticket-signing secret in our INI file. +In this example we chose to use the bundled :class:`pyramid.authentication.AuthTktCookieHelper` helper to store the user's logged-in state in a cookie. +We enabled it in our configuration and provided a ticket-signing secret in our INI file. Our view class grew a login view. When you reached it via a ``GET`` request, it returned a login form. When reached via ``POST``, it processed the submitted -username and password against the "groupfinder" callable that we registered in -the configuration. +username and password against the ``USERS`` data store. The function ``hash_password`` uses a one-way hashing algorithm with a salt on the user's password via ``bcrypt``, instead of storing the password in plain @@ -134,6 +131,9 @@ submitted password and the user's password stored in the database. If the hashed values are equivalent, then the user is authenticated, else authentication fails. +Assuming the password was validated, we invoke :func:`pyramid.security.remember` to generate a cookie that is set in the response. +Subsequent requests return that cookie and identify the user. + In our template, we fetched the ``logged_in`` value from the view class. We use this to calculate the logged-in user, if any. In the template we can then choose to show a login link to anonymous visitors or a logout link to logged-in @@ -143,13 +143,9 @@ users. Extra credit ============ -#. What is the difference between a user and a principal? - -#. Can I use a database behind my ``groupfinder`` to look up principals? +#. Can I use a database instead of ``USERS`` to authenticate users? #. Once I am logged in, does any user-centric information get jammed onto each request? Use ``import pdb; pdb.set_trace()`` to answer this. -.. seealso:: See also :ref:`security_chapter`, - :ref:`AuthTktAuthenticationPolicy <authentication_module>`, `bcrypt - <https://pypi.org/project/bcrypt/>`_ +.. seealso:: See also :ref:`security_chapter`, :class:`pyramid.authentication.AuthTktCookieHelper`, `bcrypt <https://pypi.org/project/bcrypt/>`_ diff --git a/docs/quick_tutorial/authentication/tutorial/__init__.py b/docs/quick_tutorial/authentication/tutorial/__init__.py index efc09e760..ec8a66a23 100644 --- a/docs/quick_tutorial/authentication/tutorial/__init__.py +++ b/docs/quick_tutorial/authentication/tutorial/__init__.py @@ -1,25 +1,21 @@ -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy from pyramid.config import Configurator -from .security import groupfinder +from .security import SecurityPolicy def main(global_config, **settings): config = Configurator(settings=settings) config.include('pyramid_chameleon') - # Security policies - authn_policy = AuthTktAuthenticationPolicy( - settings['tutorial.secret'], callback=groupfinder, - hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) + config.set_security_policy( + SecurityPolicy( + secret=settings['tutorial.secret'], + ), + ) config.add_route('home', '/') config.add_route('hello', '/howdy') config.add_route('login', '/login') config.add_route('logout', '/logout') config.scan('.views') - return config.make_wsgi_app()
\ No newline at end of file + return config.make_wsgi_app() diff --git a/docs/quick_tutorial/authentication/tutorial/security.py b/docs/quick_tutorial/authentication/tutorial/security.py index e585e2642..8324000ed 100644 --- a/docs/quick_tutorial/authentication/tutorial/security.py +++ b/docs/quick_tutorial/authentication/tutorial/security.py @@ -1,4 +1,5 @@ import bcrypt +from pyramid.authentication import AuthTktCookieHelper def hash_password(pw): @@ -12,9 +13,24 @@ def check_password(pw, hashed_pw): USERS = {'editor': hash_password('editor'), 'viewer': hash_password('viewer')} -GROUPS = {'editor': ['group:editors']} -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, [])
\ No newline at end of file +class SecurityPolicy: + def __init__(self, secret): + self.authtkt = AuthTktCookieHelper(secret=secret) + + def authenticated_identity(self, request): + identity = self.authtkt.identify(request) + if identity is not None and identity['userid'] in USERS: + return identity + + def authenticated_userid(self, request): + identity = self.authenticated_identity(request) + if identity is not None: + return identity['userid'] + + def remember(self, request, userid, **kw): + return self.authtkt.remember(request, userid, **kw) + + def forget(self, request, **kw): + return self.authtkt.forget(request, **kw) diff --git a/docs/quick_tutorial/authorization.rst b/docs/quick_tutorial/authorization.rst index e80f88c51..b1ef86a17 100644 --- a/docs/quick_tutorial/authorization.rst +++ b/docs/quick_tutorial/authorization.rst @@ -55,6 +55,11 @@ Steps .. literalinclude:: authorization/tutorial/resources.py :linenos: +#. Define a ``GROUPS`` data store and the ``permits`` method of our ``SecurityPolicy``: + + .. literalinclude:: authorization/tutorial/security.py + :linenos: + #. Change ``authorization/tutorial/views.py`` to require the ``edit`` permission on the ``hello`` view and implement the forbidden view: @@ -87,8 +92,10 @@ This simple tutorial step can be boiled down to the following: - This ACL says that the ``edit`` permission is available on ``Root`` to the ``group:editors`` *principal*. -- The registered ``groupfinder`` answers whether a particular user (``editor``) - has a particular group (``group:editors``). +- The ``SecurityPolicy.effective_principals`` method answers whether a particular user (``editor``) is a member of a particular group (``group:editors``). + +- The ``SecurityPolicy.permits`` method is invoked when Pyramid wants to know whether the user is allowed to do something. + To do this, it uses the :class:`pyramid.authorization.ACLHelper` to inspect the ACL on the ``context`` and determine if the request is allowed or denied the specific permission. In summary, ``hello`` wants ``edit`` permission, ``Root`` says ``group:editors`` has ``edit`` permission. @@ -105,6 +112,10 @@ Pyramid that the ``login`` view should be used by decorating the view with Extra credit ============ +#. What is the difference between a user and a principal? + +#. Can I use a database instead of the ``GROUPS`` data store to look up principals? + #. Do I have to put a ``renderer`` in my ``@forbidden_view_config`` decorator? #. Perhaps you would like the experience of not having enough permissions diff --git a/docs/quick_tutorial/authorization/tutorial/__init__.py b/docs/quick_tutorial/authorization/tutorial/__init__.py index 8f7ab8277..255bb35ac 100644 --- a/docs/quick_tutorial/authorization/tutorial/__init__.py +++ b/docs/quick_tutorial/authorization/tutorial/__init__.py @@ -1,8 +1,6 @@ -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy from pyramid.config import Configurator -from .security import groupfinder +from .security import SecurityPolicy def main(global_config, **settings): @@ -10,17 +8,15 @@ def main(global_config, **settings): root_factory='.resources.Root') config.include('pyramid_chameleon') - # Security policies - authn_policy = AuthTktAuthenticationPolicy( - settings['tutorial.secret'], callback=groupfinder, - hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) + config.set_security_policy( + SecurityPolicy( + secret=settings['tutorial.secret'], + ), + ) config.add_route('home', '/') config.add_route('hello', '/howdy') config.add_route('login', '/login') config.add_route('logout', '/logout') config.scan('.views') - return config.make_wsgi_app()
\ No newline at end of file + return config.make_wsgi_app() diff --git a/docs/quick_tutorial/authorization/tutorial/security.py b/docs/quick_tutorial/authorization/tutorial/security.py index e585e2642..5b3e04a5f 100644 --- a/docs/quick_tutorial/authorization/tutorial/security.py +++ b/docs/quick_tutorial/authorization/tutorial/security.py @@ -1,4 +1,7 @@ import bcrypt +from pyramid.authentication import AuthTktCookieHelper +from pyramid.authorization import ACLHelper +from pyramid.security import Authenticated, Everyone def hash_password(pw): @@ -15,6 +18,35 @@ USERS = {'editor': hash_password('editor'), GROUPS = {'editor': ['group:editors']} -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, [])
\ No newline at end of file +class SecurityPolicy: + def __init__(self, secret): + self.authtkt = AuthTktCookieHelper(secret=secret) + self.acl = ACLHelper() + + def authenticated_identity(self, request): + identity = self.authtkt.identify(request) + if identity is not None and identity['userid'] in USERS: + return identity + + def authenticated_userid(self, request): + identity = self.authenticated_identity(request) + if identity is not None: + return identity['userid'] + + def remember(self, request, userid, **kw): + return self.authtkt.remember(request, userid, **kw) + + def forget(self, request, **kw): + return self.authtkt.forget(request, **kw) + + def permits(self, request, context, permission): + principals = self.effective_principals(request) + return self.acl.permits(context, principals, permission) + + def effective_principals(self, request): + principals = [Everyone] + userid = self.authenticated_userid(request) + if userid is not None: + principals += [Authenticated, 'u:' + userid] + principals += GROUPS.get(userid, []) + return principals diff --git a/src/pyramid/authentication.py b/src/pyramid/authentication.py index 0ccc646c3..2d194e309 100644 --- a/src/pyramid/authentication.py +++ b/src/pyramid/authentication.py @@ -428,9 +428,148 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy): @implementer(IAuthenticationPolicy) class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): """A :app:`Pyramid` :term:`authentication policy` which - obtains data from a Pyramid "auth ticket" cookie. See - :class:`.AuthTktCookieHelper` for documentation of the constructor - arguments. + obtains data from a Pyramid "auth ticket" cookie. + + Constructor Arguments + + ``secret`` + + The secret (a string) used for auth_tkt cookie signing. This value + should be unique across all values provided to Pyramid for various + subsystem secrets (see :ref:`admonishment_against_secret_sharing`). + Required. + + ``callback`` + + Default: ``None``. A callback passed the userid and the + request, expected to return ``None`` if the userid doesn't + exist or a sequence of principal identifiers (possibly empty) if + the user does exist. If ``callback`` is ``None``, the userid + will be assumed to exist with no principals. Optional. + + ``cookie_name`` + + Default: ``auth_tkt``. The cookie name used + (string). Optional. + + ``secure`` + + Default: ``False``. Only send the cookie back over a secure + conn. Optional. + + ``include_ip`` + + Default: ``False``. Make the requesting IP address part of + the authentication data in the cookie. Optional. + + For IPv6 this option is not recommended. The ``mod_auth_tkt`` + specification does not specify how to handle IPv6 addresses, so using + this option in combination with IPv6 addresses may cause an + incompatible cookie. It ties the authentication ticket to that + individual's IPv6 address. + + ``timeout`` + + Default: ``None``. Maximum number of seconds which a newly + issued ticket will be considered valid. After this amount of + time, the ticket will expire (effectively logging the user + out). If this value is ``None``, the ticket never expires. + Optional. + + ``reissue_time`` + + Default: ``None``. If this parameter is set, it represents the number + of seconds that must pass before an authentication token cookie is + automatically reissued as the result of a request which requires + authentication. The duration is measured as the number of seconds + since the last auth_tkt cookie was issued and 'now'. If this value is + ``0``, a new ticket cookie will be reissued on every request which + requires authentication. + + A good rule of thumb: if you want auto-expired cookies based on + inactivity: set the ``timeout`` value to 1200 (20 mins) and set the + ``reissue_time`` value to perhaps a tenth of the ``timeout`` value + (120 or 2 mins). It's nonsensical to set the ``timeout`` value lower + than the ``reissue_time`` value, as the ticket will never be reissued + if so. However, such a configuration is not explicitly prevented. + + Optional. + + ``max_age`` + + Default: ``None``. The max age of the auth_tkt cookie, in + seconds. This differs from ``timeout`` inasmuch as ``timeout`` + represents the lifetime of the ticket contained in the cookie, + while this value represents the lifetime of the cookie itself. + When this value is set, the cookie's ``Max-Age`` and + ``Expires`` settings will be set, allowing the auth_tkt cookie + to last between browser sessions. It is typically nonsensical + to set this to a value that is lower than ``timeout`` or + ``reissue_time``, although it is not explicitly prevented. + Optional. + + ``path`` + + Default: ``/``. The path for which the auth_tkt cookie is valid. + May be desirable if the application only serves part of a domain. + Optional. + + ``http_only`` + + Default: ``False``. Hide cookie from JavaScript by setting the + HttpOnly flag. Not honored by all browsers. + Optional. + + ``wild_domain`` + + Default: ``True``. An auth_tkt cookie will be generated for the + wildcard domain. If your site is hosted as ``example.com`` this + will make the cookie available for sites underneath ``example.com`` + such as ``www.example.com``. + Optional. + + ``parent_domain`` + + Default: ``False``. An auth_tkt cookie will be generated for the + parent domain of the current site. For example if your site is + hosted under ``www.example.com`` a cookie will be generated for + ``.example.com``. This can be useful if you have multiple sites + sharing the same domain. This option supercedes the ``wild_domain`` + option. + Optional. + + ``domain`` + + Default: ``None``. If provided the auth_tkt cookie will only be + set for this domain. This option is not compatible with ``wild_domain`` + and ``parent_domain``. + Optional. + + ``hashalg`` + + Default: ``sha512`` (the literal string). + + Any hash algorithm supported by Python's ``hashlib.new()`` function + can be used as the ``hashalg``. + + Cookies generated by different instances of AuthTktAuthenticationPolicy + using different ``hashalg`` options are not compatible. Switching the + ``hashalg`` will imply that all existing users with a valid cookie will + be required to re-login. + + Optional. + + ``debug`` + + Default: ``False``. If ``debug`` is ``True``, log messages to the + Pyramid debug logger about the results of various authentication + steps. The output from debugging is useful for reporting to maillist + or IRC channels when asking for support. + + ``samesite`` + + Default: ``'Lax'``. The 'samesite' option of the session cookie. Set + the value to ``None`` to turn off the samesite option. .. versionchanged:: 1.4 @@ -694,14 +833,6 @@ class AuthTktCookieHelper(object): subsystem secrets (see :ref:`admonishment_against_secret_sharing`). Required. - ``callback`` - - Default: ``None``. A callback passed the userid and the - request, expected to return ``None`` if the userid doesn't - exist or a sequence of principal identifiers (possibly empty) if - the user does exist. If ``callback`` is ``None``, the userid - will be assumed to exist with no principals. Optional. - ``cookie_name`` Default: ``auth_tkt``. The cookie name used @@ -819,12 +950,16 @@ class AuthTktCookieHelper(object): Default: ``False``. If ``debug`` is ``True``, log messages to the Pyramid debug logger about the results of various authentication steps. The output from debugging is useful for reporting to maillist - or IRC channels when asking for support. + or IRC channels when asking for support. Optional. ``samesite`` Default: ``'Lax'``. The 'samesite' option of the session cookie. Set - the value to ``None`` to turn off the samesite option. + the value to ``None`` to turn off the samesite option. Optional. + + .. versionchanged:: 2.0 + + The default ``hashalg`` was changed from ``md5`` to ``sha512``. """ @@ -858,7 +993,7 @@ class AuthTktCookieHelper(object): http_only=False, path="/", wild_domain=True, - hashalg='md5', + hashalg='sha512', parent_domain=False, domain=None, samesite='Lax', diff --git a/src/pyramid/config/testing.py b/src/pyramid/config/testing.py index db1aefb24..ea3f92d17 100644 --- a/src/pyramid/config/testing.py +++ b/src/pyramid/config/testing.py @@ -32,8 +32,8 @@ class TestingConfiguratorMixin(object): :attr:`pyramid.request.Request.authenticated_userid` will have this value as well. :type userid: str - :param identity: If provided, the policy's ``identify`` method will - return this value. As a result, + :param identity: If provided, the policy's ``authenticated_identity`` + method will return this value. As a result, :attr:`pyramid.request.Request.authenticated_identity`` will have this value. :type identity: object diff --git a/src/pyramid/interfaces.py b/src/pyramid/interfaces.py index c4160cc2b..1f089216f 100644 --- a/src/pyramid/interfaces.py +++ b/src/pyramid/interfaces.py @@ -483,16 +483,16 @@ class IViewMapperFactory(Interface): class ISecurityPolicy(Interface): + def authenticated_identity(request): + """ Return the :term:`identity` of the current user. The object can be + of any shape, such as a simple ID string or an ORM object. + """ + def authenticated_userid(request): """ Return a :term:`userid` string identifying the trusted and verified user, or ``None`` if unauthenticated. """ - def identify(request): - """ Return the :term:`identity` of the current user. The object can be - of any shape, such as a simple ID string or an ORM object. - """ - def permits(request, context, permission): """ Return an instance of :class:`pyramid.security.Allowed` if a user of the given identity is allowed the ``permission`` in the current diff --git a/src/pyramid/security.py b/src/pyramid/security.py index 5e803aa0a..c0d01e0a7 100644 --- a/src/pyramid/security.py +++ b/src/pyramid/security.py @@ -300,7 +300,7 @@ class SecurityAPIMixin: policy = _get_security_policy(self) if policy is None: return None - return policy.identify(self) + return policy.authenticated_identity(self) @property def authenticated_userid(self): @@ -431,7 +431,7 @@ class LegacySecurityPolicy: def _get_authz_policy(self, request): return request.registry.getUtility(IAuthorizationPolicy) - def identify(self, request): + def authenticated_identity(self, request): return self.authenticated_userid(request) def authenticated_userid(self, request): diff --git a/src/pyramid/testing.py b/src/pyramid/testing.py index a03f2678e..af02872dd 100644 --- a/src/pyramid/testing.py +++ b/src/pyramid/testing.py @@ -51,7 +51,7 @@ class DummySecurityPolicy(object): self.remember_result = remember_result self.forget_result = forget_result - def identify(self, request): + def authenticated_identity(self, request): return self.identity def authenticated_userid(self, request): diff --git a/tests/pkgs/securityapp/__init__.py b/tests/pkgs/securityapp/__init__.py index 6c9025e7d..facc37878 100644 --- a/tests/pkgs/securityapp/__init__.py +++ b/tests/pkgs/securityapp/__init__.py @@ -3,7 +3,7 @@ from pyramid.security import Allowed, Denied class SecurityPolicy: - def identify(self, request): + def authenticated_identity(self, request): raise NotImplementedError() # pragma: no cover def authenticated_userid(self, request): diff --git a/tests/test_security.py b/tests/test_security.py index fa3d165ea..db5861562 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -479,7 +479,7 @@ class TestLegacySecurityPolicy(unittest.TestCase): policy = LegacySecurityPolicy() _registerAuthenticationPolicy(request.registry, 'userid') - self.assertEqual(policy.identify(request), 'userid') + self.assertEqual(policy.authenticated_identity(request), 'userid') def test_remember(self): from pyramid.security import LegacySecurityPolicy @@ -532,7 +532,7 @@ class DummySecurityPolicy: def __init__(self, result): self.result = result - def identify(self, request): + def authenticated_identity(self, request): return self.result def authenticated_userid(self, request): diff --git a/tests/test_testing.py b/tests/test_testing.py index a5746b59d..dbda76454 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -27,9 +27,9 @@ class TestDummySecurityPolicy(unittest.TestCase): klass = self._getTargetClass() return klass(userid, identity, permissive) - def test_identify(self): + def test_authenticated_identity(self): policy = self._makeOne('user', 'identity') - self.assertEqual(policy.identify(None), 'identity') + self.assertEqual(policy.authenticated_identity(None), 'identity') def test_authenticated_userid(self): policy = self._makeOne('user') |
