From e48644fb44efba9ea09c5b5075caf4decd47fb69 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Mon, 16 Jul 2018 13:19:35 -0400 Subject: [PATCH 1/6] Create authz users on login --- atst/app.py | 4 +-- atst/handlers/dev.py | 22 ++++++++++++++-- atst/handlers/login_redirect.py | 12 ++++++++- config/base.ini | 2 +- tests/conftest.py | 4 +-- tests/mocks.py | 46 ++++++++++++++++++++++++++++++++- tests/test_auth.py | 2 +- 7 files changed, 82 insertions(+), 10 deletions(-) diff --git a/atst/app.py b/atst/app.py index ed1be4a6..f36b4c23 100644 --- a/atst/app.py +++ b/atst/app.py @@ -26,7 +26,7 @@ def make_app(config, deps, **kwargs): url( r"/login-redirect", LoginRedirect, - {"sessions": deps["sessions"], "authnid_client": deps["authnid_client"]}, + {"sessions": deps["sessions"], "authnid_client": deps["authnid_client"], "authz_client": deps["authz_client"]}, name="login_redirect", ), url(r"/home", Main, {"page": "home"}, name="home"), @@ -88,7 +88,7 @@ def make_app(config, deps, **kwargs): url( r"/login-dev", Dev, - {"action": "login", "sessions": deps["sessions"]}, + {"action": "login", "sessions": deps["sessions"], "authz_client": deps["authz_client"]}, name="dev-login", ) ] diff --git a/atst/handlers/dev.py b/atst/handlers/dev.py index 7ee34799..8267e1ff 100644 --- a/atst/handlers/dev.py +++ b/atst/handlers/dev.py @@ -1,15 +1,33 @@ +import tornado.gen + from atst.handler import BaseHandler class Dev(BaseHandler): - def initialize(self, action, sessions): + def initialize(self, action, sessions, authz_client): self.action = action self.sessions = sessions + self.authz_client = authz_client + @tornado.gen.coroutine def get(self): user = { "id": "164497f6-c1ea-4f42-a5ef-101da278c012", "first_name": "Test", - "last_name": "User" + "last_name": "User", } + user_permissions = yield self.get_or_fetch_user_permissions(user["id"]) + user["atat_permissions"] = user_permissions self.login(user) + + @tornado.gen.coroutine + def get_or_fetch_user_permissions(self, user_id): + response = yield self.authz_client.post( + "/users", json={"id": user_id, "atat_role": "ccpo"}, raise_error=False + ) + if response.code == 200: + return response.json["atat_permissions"] + elif response.code == 409: + # User already exists + response = yield self.authz_client.get("/users/{}".format(user_id)) + return response.json["atat_permissions"] diff --git a/atst/handlers/login_redirect.py b/atst/handlers/login_redirect.py index 9fa9998f..d535aef7 100644 --- a/atst/handlers/login_redirect.py +++ b/atst/handlers/login_redirect.py @@ -3,9 +3,10 @@ from atst.handler import BaseHandler class LoginRedirect(BaseHandler): - def initialize(self, authnid_client, sessions): + def initialize(self, authnid_client, sessions, authz_client): self.authnid_client = authnid_client self.sessions = sessions + self.authz_client = authz_client @tornado.gen.coroutine def get(self): @@ -13,6 +14,8 @@ class LoginRedirect(BaseHandler): if token: user = yield self._fetch_user_info(token) if user: + authz_user = yield self.create_authz_user(user["id"]) + user["atat_permissions"] = authz_user["atat_permissions"] self.login(user) else: self.write_error(401) @@ -35,3 +38,10 @@ class LoginRedirect(BaseHandler): else: raise error + + @tornado.gen.coroutine + def create_authz_user(self, user_id): + response = yield self.authz_client.post( + "/users", json={"id": user_id, "atat_role": "ccpo"} + ) + return response.json diff --git a/config/base.ini b/config/base.ini index 993745f3..6a367c63 100644 --- a/config/base.ini +++ b/config/base.ini @@ -2,7 +2,7 @@ PORT=8000 ENVIRONMENT = dev DEBUG = true -AUTHZ_BASE_URL = http://localhost +AUTHZ_BASE_URL = http://localhost:8002 AUTHNID_BASE_URL= https://localhost:8001 COOKIE_SECRET = some-secret-please-replace SECRET = change_me_into_something_secret diff --git a/tests/conftest.py b/tests/conftest.py index a8c45680..469a2100 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,14 +1,14 @@ import pytest from atst.app import make_app, make_deps, make_config -from tests.mocks import MockApiClient, MockRequestsClient +from tests.mocks import MockApiClient, MockRequestsClient, MockAuthzClient from atst.sessions import DictSessions @pytest.fixture def app(): TEST_DEPS = { - "authz_client": MockApiClient("authz"), + "authz_client": MockAuthzClient("authz"), "requests_client": MockRequestsClient("requests"), "authnid_client": MockApiClient("authnid"), "sessions": DictSessions(), diff --git a/tests/mocks.py b/tests/mocks.py index 2fefbf64..b916d677 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -49,7 +49,7 @@ class MockRequestsClient(MockApiClient): "id": "66b8ef71-86d3-48ef-abc2-51bfa1732b6b", "creator": "49903ae7-da4a-49bf-a6dc-9dff5d004238", "body": {}, - "status": "incomplete" + "status": "incomplete", } return self._get_response("GET", path, 200, json=json) @@ -61,3 +61,47 @@ class MockRequestsClient(MockApiClient): "body": {}, } return self._get_response("POST", path, 202, json=json) + + +class MockAuthzClient(MockApiClient): + @tornado.gen.coroutine + def post(self, path, **kwargs): + json = { + "atat_permissions": [ + "view_original_jedi_request", + "review_and_approve_jedi_workspace_request", + "modify_atat_role_permissions", + "create_csp_role", + "delete_csp_role", + "deactivate_csp_role", + "modify_csp_role_permissions", + "view_usage_report", + "view_usage_dollars", + "add_and_assign_csp_roles", + "remove_csp_roles", + "request_new_csp_role", + "assign_and_unassign_atat_role", + "view_assigned_atat_role_configurations", + "view_assigned_csp_role_configurations", + "deactivate_workspace", + "view_atat_permissions", + "transfer_ownership_of_workspace", + "add_application_in_workspace", + "delete_application_in_workspace", + "deactivate_application_in_workspace", + "view_application_in_workspace", + "rename_application_in_workspace", + "add_environment_in_application", + "delete_environment_in_application", + "deactivate_environment_in_application", + "view_environment_in_application", + "rename_environment_in_application", + "add_tag_to_workspace", + "remove_tag_from_workspace", + ], + "atat_role": "ccpo", + "id": "164497f6-c1ea-4f42-a5ef-101da278c012", + "username": None, + "workspace_roles": [], + } + return self._get_response("POST", path, 200, json=json) diff --git a/tests/test_auth.py b/tests/test_auth.py index b5c420e2..ab880ed4 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -3,7 +3,7 @@ import pytest import tornado.web import tornado.gen -MOCK_USER = {"user": {"id": "438567dd-25fa-4d83-a8cc-8aa8366cb24a"}} +MOCK_USER = {"id": "438567dd-25fa-4d83-a8cc-8aa8366cb24a"} @tornado.gen.coroutine def _fetch_user_info(c, t): return MOCK_USER From 53ab37dc6853860ce356eabbbfee8a6edc30ae36 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Mon, 16 Jul 2018 14:53:21 -0400 Subject: [PATCH 2/6] Check for permission before listing requests --- atst/handlers/request.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/atst/handlers/request.py b/atst/handlers/request.py index 6c6322d0..4d2e88e6 100644 --- a/atst/handlers/request.py +++ b/atst/handlers/request.py @@ -27,9 +27,15 @@ class Request(BaseHandler): @tornado.gen.coroutine def get(self): user = self.get_current_user() - response = yield self.requests_client.get( - "/users/{}/requests".format(user["id"]) - ) - requests = response.json["requests"] + + if "review_and_approve_jedi_workspace_request" in user["atat_permissions"]: + response = yield self.requests_client.get("/requests") + requests = response.json + else: + response = yield self.requests_client.get( + "/requests?creator_id={}".format(user["id"]) + ) + requests = response.json["requests"] + mapped_requests = [map_request(user, request) for request in requests] self.render("requests.html.to", page=self.page, requests=mapped_requests) From 37ee8f9c7a29940203ae58eaf33f4e7c883be161 Mon Sep 17 00:00:00 2001 From: dandds Date: Mon, 16 Jul 2018 15:58:43 -0400 Subject: [PATCH 3/6] confirm user site perms are stored in session --- tests/test_auth.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_auth.py b/tests/test_auth.py index ab880ed4..3ee23e33 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -73,3 +73,6 @@ def test_valid_login_creates_session(app, monkeypatch, http_client, base_url): raise_error=False, ) assert len(app.sessions.sessions) == 1 + session = list(app.sessions.sessions.values())[0] + assert "atat_permissions" in session["user"] + assert isinstance(session["user"]["atat_permissions"], list) From ff0a50b615a2c625c8e6e0fa7a14c324065d1eca Mon Sep 17 00:00:00 2001 From: richard-dds Date: Mon, 16 Jul 2018 16:55:08 -0400 Subject: [PATCH 4/6] Factor out fetch_requests --- atst/handlers/request.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/atst/handlers/request.py b/atst/handlers/request.py index 4d2e88e6..d9bec160 100644 --- a/atst/handlers/request.py +++ b/atst/handlers/request.py @@ -27,15 +27,17 @@ class Request(BaseHandler): @tornado.gen.coroutine def get(self): user = self.get_current_user() + requests = yield self.fetch_requests(user) + mapped_requests = [map_request(user, request) for request in requests] + self.render("requests.html.to", page=self.page, requests=mapped_requests) + @tornado.gen.coroutine + def fetch_requests(self, user): if "review_and_approve_jedi_workspace_request" in user["atat_permissions"]: response = yield self.requests_client.get("/requests") - requests = response.json else: response = yield self.requests_client.get( "/requests?creator_id={}".format(user["id"]) ) - requests = response.json["requests"] - mapped_requests = [map_request(user, request) for request in requests] - self.render("requests.html.to", page=self.page, requests=mapped_requests) + return response.json["requests"] From 83bbad7b866ec3d7ee43a9a8a466bdd12d8b29b4 Mon Sep 17 00:00:00 2001 From: dandds Date: Tue, 17 Jul 2018 14:24:39 -0400 Subject: [PATCH 5/6] update call to fetch request from requests-queue --- atst/handlers/request_new.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/atst/handlers/request_new.py b/atst/handlers/request_new.py index 63e615af..6140a7a4 100644 --- a/atst/handlers/request_new.py +++ b/atst/handlers/request_new.py @@ -52,9 +52,7 @@ class RequestNew(BaseHandler): if request_id: response = yield self.requests_client.get( - "/users/{}/requests/{}".format( - self.get_current_user()["id"], request_id - ), + "/requests/{}".format(request_id), raise_error=False, ) if response.ok: @@ -76,14 +74,6 @@ class RequestNew(BaseHandler): can_submit=jedi_flow.can_submit ) - @tornado.gen.coroutine - def get_request(self, request_id): - request = yield self.requests_client.get( - "/users/{}/requests/{}".format(self.get_current_user()["id"], request_id), - raise_error=False, - ) - return request - class JEDIRequestFlow(object): def __init__( From a0d5e679f62032359fc63309bcbb46337ddb7810 Mon Sep 17 00:00:00 2001 From: dandds Date: Wed, 18 Jul 2018 09:27:25 -0400 Subject: [PATCH 6/6] put initial permissions fetching in BaseHandler --- atst/handler.py | 13 ++++++++++++- atst/handlers/dev.py | 16 +--------------- atst/handlers/login_redirect.py | 11 +---------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/atst/handler.py b/atst/handler.py index 2ad1acaf..8c500069 100644 --- a/atst/handler.py +++ b/atst/handler.py @@ -6,16 +6,26 @@ helpers = {"assets": environment} class BaseHandler(tornado.web.RequestHandler): + def get_template_namespace(self): ns = super(BaseHandler, self).get_template_namespace() helpers["config"] = self.application.config ns.update(helpers) return ns + @tornado.gen.coroutine def login(self, user): + user["atat_permissions"] = yield self._get_user_permissions(user["id"]) session_id = self.sessions.start_session(user) self.set_secure_cookie("atat", session_id) - self.redirect("/home") + return self.redirect("/home") + + @tornado.gen.coroutine + def _get_user_permissions(self, user_id): + response = yield self.authz_client.post( + "/users", json={"id": user_id, "atat_role": "ccpo"} + ) + return response.json["atat_permissions"] def get_current_user(self): cookie = self.get_secure_cookie("atat") @@ -24,6 +34,7 @@ class BaseHandler(tornado.web.RequestHandler): session = self.application.sessions.get_session(cookie) except SessionNotFoundError: return None + else: return None diff --git a/atst/handlers/dev.py b/atst/handlers/dev.py index 8267e1ff..38c71419 100644 --- a/atst/handlers/dev.py +++ b/atst/handlers/dev.py @@ -16,18 +16,4 @@ class Dev(BaseHandler): "first_name": "Test", "last_name": "User", } - user_permissions = yield self.get_or_fetch_user_permissions(user["id"]) - user["atat_permissions"] = user_permissions - self.login(user) - - @tornado.gen.coroutine - def get_or_fetch_user_permissions(self, user_id): - response = yield self.authz_client.post( - "/users", json={"id": user_id, "atat_role": "ccpo"}, raise_error=False - ) - if response.code == 200: - return response.json["atat_permissions"] - elif response.code == 409: - # User already exists - response = yield self.authz_client.get("/users/{}".format(user_id)) - return response.json["atat_permissions"] + yield self.login(user) diff --git a/atst/handlers/login_redirect.py b/atst/handlers/login_redirect.py index d535aef7..59fb8751 100644 --- a/atst/handlers/login_redirect.py +++ b/atst/handlers/login_redirect.py @@ -14,9 +14,7 @@ class LoginRedirect(BaseHandler): if token: user = yield self._fetch_user_info(token) if user: - authz_user = yield self.create_authz_user(user["id"]) - user["atat_permissions"] = authz_user["atat_permissions"] - self.login(user) + yield self.login(user) else: self.write_error(401) @@ -38,10 +36,3 @@ class LoginRedirect(BaseHandler): else: raise error - - @tornado.gen.coroutine - def create_authz_user(self, user_id): - response = yield self.authz_client.post( - "/users", json={"id": user_id, "atat_role": "ccpo"} - ) - return response.json