diff --git a/atst/api_client.py b/atst/api_client.py index 3a68f707..c7e35fe7 100644 --- a/atst/api_client.py +++ b/atst/api_client.py @@ -33,7 +33,7 @@ class ApiClient(object): kwargs['body'] = dumps(kwargs['json']) del kwargs['json'] headers = kwargs.get('headers', {}) - headers['Content-Type'] = 'application-json' + headers['Content-Type'] = 'application/json' kwargs['headers'] = headers response = yield self.client.fetch(url, method=method, **kwargs) diff --git a/atst/app.py b/atst/app.py index 4c09faf0..92d188db 100644 --- a/atst/app.py +++ b/atst/app.py @@ -17,6 +17,7 @@ ENV = os.getenv("TORNADO_ENV", "dev") def make_app(config): authz_client = ApiClient(config["default"]["AUTHZ_BASE_URL"]) + authnid_client = ApiClient(config["default"]["AUTHNID_BASE_URL"]) routes = [ url(r"/", Login, {"page": "login"}, name="main"), @@ -52,6 +53,7 @@ def make_app(config): cookie_secret=config["default"]["COOKIE_SECRET"], debug=config['default'].getboolean('DEBUG') ) + app.authnid_client = authnid_client return app diff --git a/atst/handler.py b/atst/handler.py index 446c8df9..dbd95430 100644 --- a/atst/handler.py +++ b/atst/handler.py @@ -22,11 +22,13 @@ helpers = { def authenticated(method): @functools.wraps(method) + @tornado.gen.coroutine def wrapper(self, *args, **kwargs): if not self.current_user: if self.get_cookie('bearer-token'): bearer_token = self.get_cookie('bearer-token') - if validate_login_token(bearer_token): + valid = yield validate_login_token(self.application.authnid_client, bearer_token) + if valid: self._start_session() else: raise NotImplementedError @@ -39,9 +41,16 @@ def authenticated(method): return method(self, *args, **kwargs) return wrapper -def validate_login_token(token): - # check against authnid - pass +@tornado.gen.coroutine +def validate_login_token(client, token): + try: + response = yield client.post('/api/v1/validate', raise_error=False, json={"token": token}) + return response.code == 200 + except tornado.httpclient.HTTPError as error: + if error.response.code == 401: + return False + else: + raise error class BaseHandler(tornado.web.RequestHandler): diff --git a/config/base.ini b/config/base.ini index fb4a4daa..9747c782 100644 --- a/config/base.ini +++ b/config/base.ini @@ -1,6 +1,7 @@ [default] +PORT=8000 ENVIRONMENT = dev DEBUG = true AUTHZ_BASE_URL = http://localhost -PORT = 8000 +AUTHNID_BASE_URL= http://localhost COOKIE_SECRET = some-secret-please-replace diff --git a/tests/test_auth.py b/tests/test_auth.py index 756e9c00..fa636b67 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,5 +1,35 @@ import pytest import tornado.web +from concurrent.futures import ThreadPoolExecutor +from atst.handler import validate_login_token + + +class MockApiResponse(): + + def __init__(self, code, json): + self.code = code + self.json = json + + +@pytest.mark.gen_test +def test_successful_validate_login_token(monkeypatch, app): + monkeypatch.setattr( + "atst.api_client.ApiClient.get", + lambda x, + y, + json=None: MockApiResponse(200, {"status": "success"}), + ) + assert validate_login_token(app.authnid_client, "abc-123") + + +@pytest.mark.gen_test +def test_unsuccessful_validate_login_token(monkeypatch, app): + monkeypatch.setattr( + "atst.api_client.ApiClient.get", + lambda x,y,json=None: MockApiResponse(401, {"status": "error"}), + ) + valid = yield validate_login_token(app.authnid_client, "abc-123") + assert not valid @pytest.mark.gen_test @@ -11,25 +41,32 @@ def test_redirects_when_not_logged_in(http_client, base_url): assert response.error assert response.headers["Location"] == "/login" + @pytest.mark.gen_test def test_login_with_valid_bearer_token(app, monkeypatch, http_client, base_url): - monkeypatch.setattr("atst.handler.validate_login_token", lambda t: True) - response = yield http_client.fetch( - base_url + "/home", headers={"Cookie": "bearer-token=anything"} - ) - assert response.headers['Set-Cookie'].startswith('atst') - assert response.code == 200 - assert not response.error + with ThreadPoolExecutor(max_workers=1) as executor: + monkeypatch.setattr( + "atst.handler.validate_login_token", + lambda c,t: executor.submit(lambda: True), + ) + response = yield http_client.fetch( + base_url + "/home", headers={"Cookie": "bearer-token=anything"} + ) + assert response.headers["Set-Cookie"].startswith("atst") + assert response.code == 200 + assert not response.error + @pytest.mark.gen_test def test_login_with_via_dev_endpoint(app, monkeypatch, http_client, base_url): response = yield http_client.fetch( base_url + "/login-dev", raise_error=False, follow_redirects=False ) - assert response.headers['Set-Cookie'].startswith('atst') + assert response.headers["Set-Cookie"].startswith("atst") assert response.code == 302 assert response.headers["Location"] == "/home" + @pytest.mark.gen_test @pytest.mark.skip(reason="need to work out auth error user paths") def test_login_with_invalid_bearer_token(monkeypatch, http_client, base_url):