From 7adc44fa2b4bfa5b4230d8646e734ba262ec1ce2 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 7 Jan 2020 23:48:51 -0600 Subject: demonstrate an identity_cache --- docs/tutorials/wiki2/authentication.rst | 23 ++++++++++++++++++---- docs/tutorials/wiki2/authorization.rst | 2 +- .../wiki2/src/authentication/tutorial/security.py | 7 ++++++- .../wiki2/src/authorization/tutorial/security.py | 7 ++++++- .../tutorials/wiki2/src/tests/tutorial/security.py | 7 ++++++- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/docs/tutorials/wiki2/authentication.rst b/docs/tutorials/wiki2/authentication.rst index a4937d93e..381868e71 100644 --- a/docs/tutorials/wiki2/authentication.rst +++ b/docs/tutorials/wiki2/authentication.rst @@ -51,15 +51,30 @@ It also handles authorization, which we'll cover in the next chapter (if you're Identifying the current user is done in a couple steps: -1. The ``MySecurityPolicy.authenticated_identity`` method asks the cookie helper to pull the identity from the request. +1. :app:`Pyramid` invokes a method on the policy requesting identity, userid, or permission to perform an operation. + +1. The policy starts by calling :meth:`pyramid.request.RequestLocalCache.get_or_create` to load the identity. + +1. The ``MySecurityPolicy.load_identity`` method asks the cookie helper to pull the identity from the request. This value is ``None`` if the cookie is missing or the content cannot be verified. -2. We then translate the identity into a ``tutorial.models.User`` object by looking for a record in the database. -This is a good spot to confirm that the user is actually allowed to access our application. -For example, maybe they were marked deleted or banned and we should return ``None`` instead of the ``user`` object. +1. The policy then translates the identity into a ``tutorial.models.User`` object by looking for a record in the database. + This is a good spot to confirm that the user is actually allowed to access our application. + For example, maybe they were marked deleted or banned and we should return ``None`` instead of the ``user`` object. + +1. The result is stored in the ``identity_cache`` which ensures that subsequent invocations return the same identity object for the request. Finally, :attr:`pyramid.request.Request.authenticated_identity` contains either ``None`` or a ``tutorial.models.User`` instance and that value is aliased to ``request.user`` for convenience in our application. +Note the usage of the ``identity_cache`` is optional, but it has several advantages in most scenarios: + +- It improves performance as the identity is necessary for many operations during the lifetime of a request. + +- It provides consistency across method invocations to ensure the identity does not change while processing the request. + +It is up to individual security policies and applications to determine the best approach with respect to caching. +Applications is long-running requests may want to avoid caching the identity, or tracking some extra metadata to re-verify it periodically against the authentication source. + Configure the app ~~~~~~~~~~~~~~~~~ diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index e8f95f8cf..001bde935 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -40,7 +40,7 @@ Open the file ``tutorial/security.py`` and edit it as follows: .. literalinclude:: src/authorization/tutorial/security.py :linenos: - :emphasize-lines: 2,4-7,15,37-48 + :emphasize-lines: 2,5-8,17,42-53 :language: python Only the highlighted lines need to be added. diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/security.py b/docs/tutorials/wiki2/src/authentication/tutorial/security.py index 48149d6e5..1027ddd0a 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/security.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/security.py @@ -1,5 +1,6 @@ from pyramid.authentication import AuthTktCookieHelper from pyramid.csrf import CookieCSRFStoragePolicy +from pyramid.request import RequestLocalCache from . import models @@ -7,8 +8,9 @@ from . import models class MySecurityPolicy: def __init__(self, secret): self.authtkt = AuthTktCookieHelper(secret) + self.identity_cache = RequestLocalCache(self.load_identity) - def authenticated_identity(self, request): + def load_identity(self, request): identity = self.authtkt.identify(request) if identity is None: return None @@ -17,6 +19,9 @@ class MySecurityPolicy: user = request.dbsession.query(models.User).get(userid) return user + def authenticated_identity(self, request): + return self.identity_cache.get_or_create(request) + def authenticated_userid(self, request): user = self.authenticated_identity(request) if user is not None: diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py index 448183c95..7a99fb9e9 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py @@ -1,6 +1,7 @@ from pyramid.authentication import AuthTktCookieHelper from pyramid.authorization import ACLHelper from pyramid.csrf import CookieCSRFStoragePolicy +from pyramid.request import RequestLocalCache from pyramid.security import ( Authenticated, Everyone, @@ -12,9 +13,10 @@ from . import models class MySecurityPolicy: def __init__(self, secret): self.authtkt = AuthTktCookieHelper(secret) + self.identity_cache = RequestLocalCache(self.load_identity) self.acl = ACLHelper() - def authenticated_identity(self, request): + def load_identity(self, request): identity = self.authtkt.identify(request) if identity is None: return None @@ -23,6 +25,9 @@ class MySecurityPolicy: user = request.dbsession.query(models.User).get(userid) return user + def authenticated_identity(self, request): + return self.identity_cache.get_or_create(request) + def authenticated_userid(self, request): user = self.authenticated_identity(request) if user is not None: diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security.py b/docs/tutorials/wiki2/src/tests/tutorial/security.py index 448183c95..7a99fb9e9 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/security.py @@ -1,6 +1,7 @@ from pyramid.authentication import AuthTktCookieHelper from pyramid.authorization import ACLHelper from pyramid.csrf import CookieCSRFStoragePolicy +from pyramid.request import RequestLocalCache from pyramid.security import ( Authenticated, Everyone, @@ -12,9 +13,10 @@ from . import models class MySecurityPolicy: def __init__(self, secret): self.authtkt = AuthTktCookieHelper(secret) + self.identity_cache = RequestLocalCache(self.load_identity) self.acl = ACLHelper() - def authenticated_identity(self, request): + def load_identity(self, request): identity = self.authtkt.identify(request) if identity is None: return None @@ -23,6 +25,9 @@ class MySecurityPolicy: user = request.dbsession.query(models.User).get(userid) return user + def authenticated_identity(self, request): + return self.identity_cache.get_or_create(request) + def authenticated_userid(self, request): user = self.authenticated_identity(request) if user is not None: -- cgit v1.2.3