From cd2c7567d40ee134e558c536e33c1d49de45bcef Mon Sep 17 00:00:00 2001 From: Leticia Portella Date: Wed, 9 Jan 2019 11:47:39 +0000 Subject: [PATCH 1/3] Add password encriptation on saving UserInfo --- nativeauthenticator/nativeauthenticator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nativeauthenticator/nativeauthenticator.py b/nativeauthenticator/nativeauthenticator.py index 7da21b2..87c321a 100644 --- a/nativeauthenticator/nativeauthenticator.py +++ b/nativeauthenticator/nativeauthenticator.py @@ -1,3 +1,4 @@ +import bcrypt from jupyterhub.orm import User from jupyterhub.auth import Authenticator @@ -27,7 +28,8 @@ def get_or_create_user(self, username, password): user = User(name=username, admin=False) self.db.add(user) - user_info = UserInfo(username=username, password=password) + encoded_pw = bcrypt.hashpw(password.encode(),bcrypt.gensalt()) + user_info = UserInfo(username=username, password=encoded_pw) self.db.add(user_info) return user From 66ac5fd560dd3068fff00a3fae621f9781d04d2c Mon Sep 17 00:00:00 2001 From: Leticia Portella Date: Wed, 9 Jan 2019 12:21:24 +0000 Subject: [PATCH 2/3] Add password check on authenticator --- nativeauthenticator/nativeauthenticator.py | 6 ++++-- nativeauthenticator/orm.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/nativeauthenticator/nativeauthenticator.py b/nativeauthenticator/nativeauthenticator.py index 87c321a..808a826 100644 --- a/nativeauthenticator/nativeauthenticator.py +++ b/nativeauthenticator/nativeauthenticator.py @@ -20,7 +20,9 @@ def __init__(self, *args, **kwargs): @gen.coroutine def authenticate(self, handler, data): - return data['username'] + user = UserInfo.find(self.db, data['username']) + if user.is_valid_password(data['password']): + return data['username'] def get_or_create_user(self, username, password): user = User.find(self.db, username) @@ -28,7 +30,7 @@ def get_or_create_user(self, username, password): user = User(name=username, admin=False) self.db.add(user) - encoded_pw = bcrypt.hashpw(password.encode(),bcrypt.gensalt()) + encoded_pw = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) user_info = UserInfo(username=username, password=encoded_pw) self.db.add(user_info) return user diff --git a/nativeauthenticator/orm.py b/nativeauthenticator/orm.py index d6f7c17..150ccce 100644 --- a/nativeauthenticator/orm.py +++ b/nativeauthenticator/orm.py @@ -1,3 +1,4 @@ +import bcrypt from sqlalchemy import ( Column, Integer, String ) @@ -18,3 +19,7 @@ def find(cls, db, username): Returns None if not found. """ return db.query(cls).filter(cls.username == username).first() + + def is_valid_password(self, password): + encoded_pw = bcrypt.hashpw(password.encode(), self.password) + return encoded_pw == self.password From 7ba4b77fffcd5e1a6bfca2246c3190702c85b9be Mon Sep 17 00:00:00 2001 From: Leticia Portella Date: Wed, 9 Jan 2019 16:19:14 +0000 Subject: [PATCH 3/3] Adding more tests for authenticate and get_or_create --- dev-requirements.txt | 1 + nativeauthenticator/nativeauthenticator.py | 2 +- .../tests/test_authenticator.py | 41 ++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index b628bc2..3d34a7d 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,3 +3,4 @@ flake8 pytest pytest-asyncio notebook==5.7.2 +bcrypt diff --git a/nativeauthenticator/nativeauthenticator.py b/nativeauthenticator/nativeauthenticator.py index 808a826..777d53d 100644 --- a/nativeauthenticator/nativeauthenticator.py +++ b/nativeauthenticator/nativeauthenticator.py @@ -21,7 +21,7 @@ def __init__(self, *args, **kwargs): @gen.coroutine def authenticate(self, handler, data): user = UserInfo.find(self.db, data['username']) - if user.is_valid_password(data['password']): + if user and user.is_valid_password(data['password']): return data['username'] def get_or_create_user(self, username, password): diff --git a/nativeauthenticator/tests/test_authenticator.py b/nativeauthenticator/tests/test_authenticator.py index 60ebb9c..f47d14f 100644 --- a/nativeauthenticator/tests/test_authenticator.py +++ b/nativeauthenticator/tests/test_authenticator.py @@ -1,8 +1,9 @@ import pytest -from unittest.mock import Mock +from jupyterhub.orm import User from jupyterhub.tests.mocking import MockHub from nativeauthenticator import NativeAuthenticator +from ..orm import UserInfo @pytest.fixture @@ -23,14 +24,44 @@ def app(): pytestmark = pytestmark(pytest.mark.usefixtures("tmpcwd")) -async def test_basic(tmpcwd, app): +async def test_auth_get_or_create(tmpcwd, app): + '''Test if method get_or_create_user creates a new user''' auth = NativeAuthenticator(db=app.db) - response = await auth.authenticate(Mock(), {'username': 'name', - 'password': '123'}) - assert response == 'name' + user = auth.get_or_create_user('John Snow', 'password') + main_user = User.find(app.db, 'John Snow') + user_info = UserInfo.find(app.db, 'John Snow') + assert user == main_user + assert user_info.username == 'John Snow' + + +async def test_failed_authentication_user_doesnt_exist(tmpcwd, app): + '''Test if authentication fails with a unexistent user''' + auth = NativeAuthenticator(db=app.db) + response = await auth.authenticate(app, {'username': 'name', + 'password': '123'}) + assert not response + + +async def test_failed_authentication_wrong_password(tmpcwd, app): + '''Test if authentication fails with a wrong password''' + auth = NativeAuthenticator(db=app.db) + auth.get_or_create_user('John Snow', 'password') + response = await auth.authenticate(app, {'username': 'John Snow', + 'password': '123'}) + assert not response + + +async def test_succeded_authentication(tmpcwd, app): + '''Test a successfull authentication''' + auth = NativeAuthenticator(db=app.db) + user = auth.get_or_create_user('John Snow', 'password') + response = await auth.authenticate(app, {'username': 'John Snow', + 'password': 'password'}) + assert response == user.name async def test_handlers(app): + '''Test if all handlers are available on the Authenticator''' auth = NativeAuthenticator(db=app.db) handlers = auth.get_handlers(app) assert handlers[1][0] == '/signup'