summaryrefslogtreecommitdiff
path: root/docs/quick_tutorial
diff options
context:
space:
mode:
Diffstat (limited to 'docs/quick_tutorial')
-rw-r--r--docs/quick_tutorial/authentication.rst30
-rw-r--r--docs/quick_tutorial/authentication/tutorial/__init__.py18
-rw-r--r--docs/quick_tutorial/authentication/tutorial/security.py24
-rw-r--r--docs/quick_tutorial/authorization.rst15
-rw-r--r--docs/quick_tutorial/authorization/tutorial/__init__.py18
-rw-r--r--docs/quick_tutorial/authorization/tutorial/security.py38
6 files changed, 95 insertions, 48 deletions
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