From b01a0233aa03b4b5a9ddd640a7a114f68d1c763d Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 6 Dec 2016 16:36:56 +0100 Subject: Changed wiki tutorial to showcase passwrd hashing with bcrypt. Relates to #2204 --- docs/tutorials/wiki/src/authorization/setup.py | 1 + .../tutorials/wiki/src/authorization/tutorial/security.py | 15 +++++++++++++-- docs/tutorials/wiki/src/authorization/tutorial/views.py | 4 ++-- docs/tutorials/wiki/src/tests/setup.py | 1 + docs/tutorials/wiki/src/tests/tutorial/security.py | 15 +++++++++++++-- docs/tutorials/wiki/src/tests/tutorial/tests.py | 11 +++++++++++ docs/tutorials/wiki/src/tests/tutorial/views.py | 4 ++-- 7 files changed, 43 insertions(+), 8 deletions(-) (limited to 'docs/tutorials/wiki/src') diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py index beeed75c9..68e3c0abd 100644 --- a/docs/tutorials/wiki/src/authorization/setup.py +++ b/docs/tutorials/wiki/src/authorization/setup.py @@ -18,6 +18,7 @@ requires = [ 'ZODB3', 'waitress', 'docutils', + 'bcrypt', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/authorization/tutorial/security.py b/docs/tutorials/wiki/src/authorization/tutorial/security.py index d88c9c71f..4115c780c 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/security.py @@ -1,5 +1,16 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} +import bcrypt + + +def hash_password(pw): + return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + +def check_password(expected_hash, pw): + if expected_hash is not None: + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return False + +USERS = {'editor': hash_password('editor'), + 'viewer': hash_password('viewer')} GROUPS = {'editor':['group:editors']} def groupfinder(userid, request): diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py index c271d2cc1..e4560dfe1 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py @@ -14,7 +14,7 @@ from pyramid.security import ( ) -from .security import USERS +from .security import USERS, check_password from .models import Page # regular expression used to find WikiWords @@ -94,7 +94,7 @@ def login(request): if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: + if check_password(USERS.get(login), password): headers = remember(request, login) return HTTPFound(location=came_from, headers=headers) diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py index beeed75c9..68e3c0abd 100644 --- a/docs/tutorials/wiki/src/tests/setup.py +++ b/docs/tutorials/wiki/src/tests/setup.py @@ -18,6 +18,7 @@ requires = [ 'ZODB3', 'waitress', 'docutils', + 'bcrypt', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/tests/tutorial/security.py b/docs/tutorials/wiki/src/tests/tutorial/security.py index d88c9c71f..4115c780c 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki/src/tests/tutorial/security.py @@ -1,5 +1,16 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} +import bcrypt + + +def hash_password(pw): + return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + +def check_password(expected_hash, pw): + if expected_hash is not None: + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return False + +USERS = {'editor': hash_password('editor'), + 'viewer': hash_password('viewer')} GROUPS = {'editor':['group:editors']} def groupfinder(userid, request): diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py index 04beaea44..098e9c1bd 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/tests.py +++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py @@ -122,6 +122,17 @@ class EditPageTests(unittest.TestCase): self.assertEqual(response.location, 'http://example.com/') self.assertEqual(context.data, 'Hello yo!') +class SecurityTests(unittest.TestCase): + def test_hashing(self): + from .security import hash_password, check_password + password = 'secretpassword' + hashed_password = hash_password(password) + self.assertTrue(check_password(hashed_password, password)) + + self.assertFalse(check_password(hashed_password, 'attackerpassword')) + + self.assertFalse(check_password(None, password)) + class FunctionalTests(unittest.TestCase): viewer_login = '/login?login=viewer&password=viewer' \ diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py index c271d2cc1..e4560dfe1 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki/src/tests/tutorial/views.py @@ -14,7 +14,7 @@ from pyramid.security import ( ) -from .security import USERS +from .security import USERS, check_password from .models import Page # regular expression used to find WikiWords @@ -94,7 +94,7 @@ def login(request): if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: + if check_password(USERS.get(login), password): headers = remember(request, login) return HTTPFound(location=came_from, headers=headers) -- cgit v1.2.3 From b4abcd1f596297eb083e855d5e9a158d9e108c81 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 7 Dec 2016 09:43:22 +0100 Subject: Hashing helpers now deal in unicode. Fixed wording. Added link to bcrypt and a footnote from wiki2 example. --- docs/tutorials/wiki/src/authorization/tutorial/security.py | 6 ++++-- docs/tutorials/wiki/src/tests/tutorial/security.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'docs/tutorials/wiki/src') diff --git a/docs/tutorials/wiki/src/authorization/tutorial/security.py b/docs/tutorials/wiki/src/authorization/tutorial/security.py index 4115c780c..cbb3acd5d 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/security.py @@ -2,11 +2,13 @@ import bcrypt def hash_password(pw): - return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + # return unicode instead of bytes because databases handle it better + return hashed_pw.decode('utf-8') def check_password(expected_hash, pw): if expected_hash is not None: - return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8')) return False USERS = {'editor': hash_password('editor'), diff --git a/docs/tutorials/wiki/src/tests/tutorial/security.py b/docs/tutorials/wiki/src/tests/tutorial/security.py index 4115c780c..cbb3acd5d 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki/src/tests/tutorial/security.py @@ -2,11 +2,13 @@ import bcrypt def hash_password(pw): - return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + # return unicode instead of bytes because databases handle it better + return hashed_pw.decode('utf-8') def check_password(expected_hash, pw): if expected_hash is not None: - return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8')) return False USERS = {'editor': hash_password('editor'), -- cgit v1.2.3