summaryrefslogtreecommitdiff
path: root/docs/quick_tutorial
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2016-09-28 20:38:15 -0500
committerMichael Merickel <michael@merickel.org>2016-09-28 20:38:15 -0500
commit8ce5736478f035cc14cfd247e7d936dc4db1f7b5 (patch)
tree48d48dc3433e915df60d2549cebab64762fdd4fd /docs/quick_tutorial
parente8c66a339e9f7d83bd2408952de53ef30dba0794 (diff)
parent7391d365ce68677c588cf60c1431d29a786c6e82 (diff)
downloadpyramid-8ce5736478f035cc14cfd247e7d936dc4db1f7b5.tar.gz
pyramid-8ce5736478f035cc14cfd247e7d936dc4db1f7b5.tar.bz2
pyramid-8ce5736478f035cc14cfd247e7d936dc4db1f7b5.zip
Merge branch 'master' into exception_only
Diffstat (limited to 'docs/quick_tutorial')
-rw-r--r--docs/quick_tutorial/authentication.rst35
-rw-r--r--docs/quick_tutorial/authentication/setup.py3
-rw-r--r--docs/quick_tutorial/authentication/tutorial/security.py16
-rw-r--r--docs/quick_tutorial/authentication/tutorial/views.py7
-rw-r--r--docs/quick_tutorial/authorization/setup.py3
-rw-r--r--docs/quick_tutorial/authorization/tutorial/security.py16
-rw-r--r--docs/quick_tutorial/authorization/tutorial/views.py7
-rw-r--r--docs/quick_tutorial/debugtoolbar.rst2
-rw-r--r--docs/quick_tutorial/forms.rst10
-rw-r--r--docs/quick_tutorial/hello_world.rst2
-rw-r--r--docs/quick_tutorial/ini.rst2
-rw-r--r--docs/quick_tutorial/requirements.rst7
-rw-r--r--docs/quick_tutorial/routing.rst2
-rw-r--r--docs/quick_tutorial/scaffolds.rst6
-rw-r--r--docs/quick_tutorial/static_assets.rst2
-rw-r--r--docs/quick_tutorial/unit_testing.rst2
16 files changed, 95 insertions, 27 deletions
diff --git a/docs/quick_tutorial/authentication.rst b/docs/quick_tutorial/authentication.rst
index acff97f3b..892beb3ec 100644
--- a/docs/quick_tutorial/authentication.rst
+++ b/docs/quick_tutorial/authentication.rst
@@ -1,7 +1,7 @@
.. _qtut_authentication:
==============================
-20: Logins With Authentication
+20: Logins with authentication
==============================
Login views that authenticate a username and password against a list of users.
@@ -34,6 +34,18 @@ Steps
.. code-block:: bash
$ cd ..; cp -r view_classes authentication; cd authentication
+
+#. Add ``bcrypt`` as a dependency in ``authentication/setup.py``:
+
+ .. literalinclude:: authentication/setup.py
+ :language: python
+ :emphasize-lines: 5-6
+ :linenos:
+
+#. We can now install our project in development mode:
+
+ .. code-block:: bash
+
$ $VENV/bin/pip install -e .
#. Put the security hash in the ``authentication/development.ini``
@@ -96,8 +108,8 @@ Unlike many web frameworks, Pyramid includes a built-in but optional security
model for authentication and authorization. This security system is intended to
be flexible and support many needs. In this security model, authentication (who
are you) and authorization (what are you allowed to do) are not just pluggable,
-but de-coupled. To learn one step at a time, we provide a system that
-identifies users and lets them log out.
+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
@@ -108,6 +120,20 @@ 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.
+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
+text. This is considered to be a "best practice" for security.
+
+.. note::
+ There are alternative libraries to ``bcrypt`` if it is an issue on your
+ system. Just make sure that the library uses an algorithm approved for
+ storing passwords securely.
+
+The function ``check_password`` will compare the two hashed values of the
+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.
+
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
@@ -125,4 +151,5 @@ Extra credit
request? Use ``import pdb; pdb.set_trace()`` to answer this.
.. seealso:: See also :ref:`security_chapter`,
- :ref:`AuthTktAuthenticationPolicy <authentication_module>`.
+ :ref:`AuthTktAuthenticationPolicy <authentication_module>`, `bcrypt
+ <https://pypi.python.org/pypi/bcrypt>`_
diff --git a/docs/quick_tutorial/authentication/setup.py b/docs/quick_tutorial/authentication/setup.py
index 2221b72e9..7a6ff4226 100644
--- a/docs/quick_tutorial/authentication/setup.py
+++ b/docs/quick_tutorial/authentication/setup.py
@@ -2,7 +2,8 @@ from setuptools import setup
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'bcrypt'
]
setup(name='tutorial',
diff --git a/docs/quick_tutorial/authentication/tutorial/security.py b/docs/quick_tutorial/authentication/tutorial/security.py
index ab90bab2c..e585e2642 100644
--- a/docs/quick_tutorial/authentication/tutorial/security.py
+++ b/docs/quick_tutorial/authentication/tutorial/security.py
@@ -1,5 +1,17 @@
-USERS = {'editor': 'editor',
- 'viewer': 'viewer'}
+import bcrypt
+
+
+def hash_password(pw):
+ pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
+ return pwhash.decode('utf8')
+
+def check_password(pw, hashed_pw):
+ expected_hash = hashed_pw.encode('utf8')
+ return bcrypt.checkpw(pw.encode('utf8'), expected_hash)
+
+
+USERS = {'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer')}
GROUPS = {'editor': ['group:editors']}
diff --git a/docs/quick_tutorial/authentication/tutorial/views.py b/docs/quick_tutorial/authentication/tutorial/views.py
index ab46eb2dd..b07538d5e 100644
--- a/docs/quick_tutorial/authentication/tutorial/views.py
+++ b/docs/quick_tutorial/authentication/tutorial/views.py
@@ -9,7 +9,10 @@ from pyramid.view import (
view_defaults
)
-from .security import USERS
+from .security import (
+ USERS,
+ check_password
+)
@view_defaults(renderer='home.pt')
@@ -40,7 +43,7 @@ class TutorialViews:
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
- if USERS.get(login) == password:
+ if check_password(password, USERS.get(login)):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
diff --git a/docs/quick_tutorial/authorization/setup.py b/docs/quick_tutorial/authorization/setup.py
index 2221b72e9..7a6ff4226 100644
--- a/docs/quick_tutorial/authorization/setup.py
+++ b/docs/quick_tutorial/authorization/setup.py
@@ -2,7 +2,8 @@ from setuptools import setup
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'bcrypt'
]
setup(name='tutorial',
diff --git a/docs/quick_tutorial/authorization/tutorial/security.py b/docs/quick_tutorial/authorization/tutorial/security.py
index ab90bab2c..e585e2642 100644
--- a/docs/quick_tutorial/authorization/tutorial/security.py
+++ b/docs/quick_tutorial/authorization/tutorial/security.py
@@ -1,5 +1,17 @@
-USERS = {'editor': 'editor',
- 'viewer': 'viewer'}
+import bcrypt
+
+
+def hash_password(pw):
+ pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
+ return pwhash.decode('utf8')
+
+def check_password(pw, hashed_pw):
+ expected_hash = hashed_pw.encode('utf8')
+ return bcrypt.checkpw(pw.encode('utf8'), expected_hash)
+
+
+USERS = {'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer')}
GROUPS = {'editor': ['group:editors']}
diff --git a/docs/quick_tutorial/authorization/tutorial/views.py b/docs/quick_tutorial/authorization/tutorial/views.py
index 43d14455a..b2dc905c0 100644
--- a/docs/quick_tutorial/authorization/tutorial/views.py
+++ b/docs/quick_tutorial/authorization/tutorial/views.py
@@ -10,7 +10,10 @@ from pyramid.view import (
forbidden_view_config
)
-from .security import USERS
+from .security import (
+ USERS,
+ check_password
+)
@view_defaults(renderer='home.pt')
@@ -42,7 +45,7 @@ class TutorialViews:
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
- if USERS.get(login) == password:
+ if check_password(password, USERS.get(login)):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
diff --git a/docs/quick_tutorial/debugtoolbar.rst b/docs/quick_tutorial/debugtoolbar.rst
index aaf904390..b02363d40 100644
--- a/docs/quick_tutorial/debugtoolbar.rst
+++ b/docs/quick_tutorial/debugtoolbar.rst
@@ -90,7 +90,7 @@ temporarily.
.. seealso:: See also :ref:`pyramid_debugtoolbar <toolbar:overview>`.
-Extra Credit
+Extra credit
============
#. Why don't we add ``pyramid_debugtoolbar`` to the list of
diff --git a/docs/quick_tutorial/forms.rst b/docs/quick_tutorial/forms.rst
index 66e77491d..84ceb13d6 100644
--- a/docs/quick_tutorial/forms.rst
+++ b/docs/quick_tutorial/forms.rst
@@ -41,6 +41,7 @@ Steps
pulls in Colander as a dependency:
.. literalinclude:: forms/setup.py
+ :emphasize-lines: 5-6
:linenos:
#. We can now install our project in development mode:
@@ -74,13 +75,18 @@ Steps
:language: html
:linenos:
-#. Finally, a template at ``forms/tutorial/wikipage_view.pt`` for viewing a
- wiki page:
+#. Add a template at ``forms/tutorial/wikipage_view.pt`` for viewing a wiki
+ page:
.. literalinclude:: forms/tutorial/wikipage_view.pt
:language: html
:linenos:
+#. Our tests in ``forms/tutorial/tests.py`` don't run, so let's modify them:
+
+ .. literalinclude:: forms/tutorial/tests.py
+ :linenos:
+
#. Run the tests:
.. code-block:: bash
diff --git a/docs/quick_tutorial/hello_world.rst b/docs/quick_tutorial/hello_world.rst
index 4e35da7bb..56dccde58 100644
--- a/docs/quick_tutorial/hello_world.rst
+++ b/docs/quick_tutorial/hello_world.rst
@@ -88,7 +88,7 @@ Pyramid development. Building an application from loosely-coupled parts via
revisit regularly in this *Quick Tutorial*.
-Extra Credit
+Extra credit
============
#. Why do we do this:
diff --git a/docs/quick_tutorial/ini.rst b/docs/quick_tutorial/ini.rst
index fba5ce29e..9a65d66d1 100644
--- a/docs/quick_tutorial/ini.rst
+++ b/docs/quick_tutorial/ini.rst
@@ -120,7 +120,7 @@ filesystem for changes to relevant code (Python files, the INI file, etc.) and,
when something changes, restart the application. Very handy during development.
-Extra Credit
+Extra credit
============
#. If you don't like configuration and/or ``.ini`` files, could you do this
diff --git a/docs/quick_tutorial/requirements.rst b/docs/quick_tutorial/requirements.rst
index 62dd570fc..afa8ed104 100644
--- a/docs/quick_tutorial/requirements.rst
+++ b/docs/quick_tutorial/requirements.rst
@@ -19,7 +19,7 @@ virtual environment.)
This *Quick Tutorial* is based on:
-* **Python 3.5**. Pyramid fully supports Python 3.3+ and Python 2.7+. This
+* **Python 3.5**. Pyramid fully supports Python 3.4+ and Python 2.7+. This
tutorial uses **Python 3.5** but runs fine under Python 2.7.
* **venv**. We believe in virtual environments. For this tutorial, we use
@@ -156,7 +156,7 @@ environment variable.
.. code-block:: doscon
# Windows
- c:\> c:\Python35\python3 -m venv %VENV%
+ c:\> c:\Python35\python -m venv %VENV%
.. seealso:: See also Python 3's :mod:`venv module <python:venv>` and Python
2's `virtualenv <https://virtualenv.pypa.io/en/latest/>`_ package.
@@ -179,6 +179,9 @@ time of its release.
# Windows
c:\> %VENV%\Scripts\pip install --upgrade pip setuptools
+.. seealso:: See also :ref:`Why use $VENV/bin/pip instead of source
+ bin/activate, then pip <venv-bin-pip-vs-source-bin-activate>`.
+
.. _install-pyramid:
diff --git a/docs/quick_tutorial/routing.rst b/docs/quick_tutorial/routing.rst
index 27c8c2c22..d88adfa1e 100644
--- a/docs/quick_tutorial/routing.rst
+++ b/docs/quick_tutorial/routing.rst
@@ -79,7 +79,7 @@ Steps
.. code-block:: bash
- $ $VENV/bin/$VENV/bin/py.test tutorial/tests.py -q
+ $ $VENV/bin/py.test tutorial/tests.py -q
..
2 passed in 0.39 seconds
diff --git a/docs/quick_tutorial/scaffolds.rst b/docs/quick_tutorial/scaffolds.rst
index 7845f2b71..ad002f4fd 100644
--- a/docs/quick_tutorial/scaffolds.rst
+++ b/docs/quick_tutorial/scaffolds.rst
@@ -38,9 +38,9 @@ Steps
$ $VENV/bin/pcreate --list
Available scaffolds:
- alchemy: Pyramid SQLAlchemy project using url dispatch
- starter: Pyramid starter project
- zodb: Pyramid ZODB project using traversal
+ alchemy: Pyramid project using SQLAlchemy, SQLite, URL dispatch, and Jinja2
+ starter: Pyramid starter project using URL dispatch and Chameleon
+ zodb: Pyramid project using ZODB, traversal, and Chameleon
#. Tell ``pcreate`` to use the ``starter`` scaffold to make our project:
diff --git a/docs/quick_tutorial/static_assets.rst b/docs/quick_tutorial/static_assets.rst
index 65b34f8f9..b8482492d 100644
--- a/docs/quick_tutorial/static_assets.rst
+++ b/docs/quick_tutorial/static_assets.rst
@@ -47,7 +47,7 @@ Steps
.. code-block:: bash
- $ $VENV/bin/$VENV/bin/py.test tutorial/tests.py -q
+ $ $VENV/bin/py.test tutorial/tests.py -q
....
4 passed in 0.50 seconds
diff --git a/docs/quick_tutorial/unit_testing.rst b/docs/quick_tutorial/unit_testing.rst
index 56fd2b297..7c85d5289 100644
--- a/docs/quick_tutorial/unit_testing.rst
+++ b/docs/quick_tutorial/unit_testing.rst
@@ -92,7 +92,7 @@ necessary when your test needs to make use of the ``config`` object (it's a
Configurator) to add stuff to the configuration state before calling the view.
-Extra Credit
+Extra credit
============
#. Change the test to assert that the response status code should be ``404``