def test_auth_test(self): c = AppConfig() c.update_server_config(authentication__type="test") c.update_server_config( multi_dataset__dataroot=dict( a1=dict(dataroot=self.dataset_dataroot, base_url="auth"), a2=dict(dataroot=self.dataset_dataroot, base_url="no-auth"), ) ) # specialize the configs c.add_dataroot_config("a1", app__authentication_enable=True, user_annotations__enable=True) c.add_dataroot_config("a2", app__authentication_enable=False, user_annotations__enable=False) c.complete_config() with test_server(app_config=c) as server: session = requests.Session() # auth datasets config = session.get(f"{server}/auth/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/auth/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue(config["config"]["authentication"]["requires_client_login"]) self.assertTrue(config["config"]["parameters"]["annotations"]) login_uri = config["config"]["authentication"]["login"] logout_uri = config["config"]["authentication"]["logout"] self.assertEqual(login_uri, "/login?dataset=auth/pbmc3k.cxg") self.assertEqual(logout_uri, "/logout?dataset=auth/pbmc3k.cxg") r = session.get(f"{server}/{login_uri}") # check that the login redirect worked self.assertEqual(r.history[0].status_code, 302) self.assertEqual(r.url, f"{server}/auth/pbmc3k.cxg/") config = session.get(f"{server}/auth/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/auth/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertTrue(userinfo["userinfo"]["is_authenticated"]) self.assertEqual(userinfo["userinfo"]["username"], "test_account") self.assertTrue(config["config"]["parameters"]["annotations"]) r = session.get(f"{server}/{logout_uri}") # check that the logout redirect worked self.assertEqual(r.history[0].status_code, 302) self.assertEqual(r.url, f"{server}/auth/pbmc3k.cxg/") config = session.get(f"{server}/auth/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/auth/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue(config["config"]["parameters"]["annotations"]) # no-auth datasets config = session.get(f"{server}/no-auth/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/no-auth/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertIsNone(userinfo) self.assertFalse(config["config"]["parameters"]["annotations"])
def test_get_api_base_url_works(self): # test the api_base_url feature, and that it can contain a path config = AppConfig() backend_port = find_available_port("localhost", 10000) config.update_server_config( app__flask_secret_key="secret", app__api_base_url= f"http://localhost:{backend_port}/additional/path", multi_dataset__dataroot=f"{PROJECT_ROOT}/example-dataset", ) config.complete_config() with test_server(["-p", str(backend_port)], app_config=config) as server: session = requests.Session() self.assertEqual(server, f"http://localhost:{backend_port}") response = session.get( f"{server}/additional/path/d/pbmc3k.h5ad/api/v0.2/config") self.assertEqual(response.status_code, 200) data_config = response.json() self.assertEqual(data_config["config"]["displayNames"]["dataset"], "pbmc3k") # test the health check at the correct url response = session.get(f"{server}/additional/path/health") assert response.json()["status"] == "pass"
def test_mulitdatasets_work_e2e(self): # test that multi dataroots work end to end self.config.update_server_config( multi_dataset__dataroot=dict( s1=dict(dataroot=f"{PROJECT_ROOT}/example-dataset", base_url="set1/1/2"), s2=dict(dataroot=f"{FIXTURES_ROOT}", base_url="set2"), s3=dict(dataroot=f"{FIXTURES_ROOT}", base_url="set3"), ) ) # Change this default to test if the dataroot overrides below work. self.config.update_default_dataset_config(app__about_legal_tos="tos_default.html") # specialize the configs for set1 self.config.add_dataroot_config( "s1", user_annotations__enable=False, diffexp__enable=True, app__about_legal_tos="tos_set1.html" ) # specialize the configs for set2 self.config.add_dataroot_config( "s2", user_annotations__enable=True, diffexp__enable=False, app__about_legal_tos="tos_set2.html" ) # no specializations for set3 (they get the default dataset config) self.config.complete_config() with test_server(app_config=self.config) as server: session = requests.Session() response = session.get(f"{server}/set1/1/2/pbmc3k.h5ad/api/v0.2/config") data_config = response.json() assert data_config["config"]["displayNames"]["dataset"] == "pbmc3k" assert data_config["config"]["parameters"]["annotations"] is False assert data_config["config"]["parameters"]["disable-diffexp"] is False assert data_config["config"]["parameters"]["about_legal_tos"] == "tos_set1.html" response = session.get(f"{server}/set2/pbmc3k.cxg/api/v0.2/config") data_config = response.json() assert data_config["config"]["displayNames"]["dataset"] == "pbmc3k" assert data_config["config"]["parameters"]["annotations"] is True assert data_config["config"]["parameters"]["about_legal_tos"] == "tos_set2.html" response = session.get(f"{server}/set3/pbmc3k.cxg/api/v0.2/config") data_config = response.json() assert data_config["config"]["displayNames"]["dataset"] == "pbmc3k" assert data_config["config"]["parameters"]["annotations"] is True assert data_config["config"]["parameters"]["disable-diffexp"] is False assert data_config["config"]["parameters"]["about_legal_tos"] == "tos_default.html" response = session.get(f"{server}/health") assert response.json()["status"] == "pass" # access a dataset (no slash) response = session.get(f"{server}/set2/pbmc3k.cxg") self.assertEqual(response.status_code, 200) # access a dataset (with slash) response = session.get(f"{server}/set2/pbmc3k.cxg/") self.assertEqual(response.status_code, 200)
def test_environment_variable(self): configfile = self.custom_external_config( environment=[ dict(name="DATAPATH", path=["server", "single_dataset", "datapath"], required=True), dict(name="DIFFEXP", path=["dataset", "diffexp", "enable"], required=True), ], config_file_name="environment_external_config.yaml", ) env = os.environ env["DATAPATH"] = f"{FIXTURES_ROOT}/pbmc3k.cxg" env["DIFFEXP"] = "False" with test_server(command_line_args=["-c", configfile], env=env) as server: session = requests.Session() response = session.get(f"{server}/api/v0.2/config") data_config = response.json() self.assertEqual(data_config["config"]["displayNames"]["dataset"], "pbmc3k") self.assertTrue( data_config["config"]["parameters"]["disable-diffexp"]) env["DATAPATH"] = f"{FIXTURES_ROOT}/a95c59b4-7f5d-4b80-ad53-a694834ca18b.h5ad" env["DIFFEXP"] = "True" with test_server(command_line_args=["-c", configfile], env=env) as server: session = requests.Session() response = session.get(f"{server}/api/v0.2/config") data_config = response.json() self.assertEqual(data_config["config"]["displayNames"]["dataset"], "a95c59b4-7f5d-4b80-ad53-a694834ca18b") self.assertFalse( data_config["config"]["parameters"]["disable-diffexp"])
def test_auth_none(self): c = AppConfig() c.update_server_config( authentication__type=None, multi_dataset__dataroot=self.dataset_dataroot ) c.update_default_dataset_config(user_annotations__enable=False) c.complete_config() with test_server(app_config=c) as server: session = requests.Session() config = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertNotIn("authentication", config["config"]) self.assertIsNone(userinfo)
def test_auth_session(self): c = AppConfig() c.update_server_config( authentication__type="session", multi_dataset__dataroot=self.dataset_dataroot ) c.update_default_dataset_config(user_annotations__enable=True) c.complete_config() with test_server(app_config=c) as server: session = requests.Session() config = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertFalse(config["config"]["authentication"]["requires_client_login"]) self.assertTrue(userinfo["userinfo"]["is_authenticated"]) self.assertEqual(userinfo["userinfo"]["username"], "anonymous")
def test_auth_test_single(self): app_config = AppConfig() app_config.update_server_config(app__flask_secret_key="secret") app_config.update_server_config( authentication__type="test", single_dataset__datapath=f"{self.dataset_dataroot}/pbmc3k.cxg") app_config.update_server_config( authentication__insecure_test_environment=True) app_config.complete_config() with test_server(app_config=app_config) as server: session = requests.Session() config = session.get(f"{server}/api/v0.2/config").json() userinfo = session.get(f"{server}/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue( config["config"]["authentication"]["requires_client_login"]) self.assertTrue(config["config"]["parameters"]["annotations"]) login_uri = config["config"]["authentication"]["login"] logout_uri = config["config"]["authentication"]["logout"] self.assertEqual(login_uri, "/login") self.assertEqual(logout_uri, "/logout") response = session.get(f"{server}/{login_uri}") # check that the login redirect worked self.assertEqual(response.history[0].status_code, 302) self.assertEqual(response.url, f"{server}/") config = session.get(f"{server}/api/v0.2/config").json() userinfo = session.get(f"{server}/api/v0.2/userinfo").json() self.assertTrue(userinfo["userinfo"]["is_authenticated"]) self.assertEqual(userinfo["userinfo"]["username"], "test_account") self.assertTrue(config["config"]["parameters"]["annotations"]) response = session.get(f"{server}/{logout_uri}") # check that the logout redirect worked self.assertEqual(response.history[0].status_code, 302) self.assertEqual(response.url, f"{server}/") config = session.get(f"{server}/api/v0.2/config").json() userinfo = session.get(f"{server}/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue(config["config"]["parameters"]["annotations"])
def auth_flow(self, app_config, cookie_key=None): app_config.update_server_config( app__api_base_url="local", authentication__type="oauth", authentication__params_oauth__oauth_api_base_url=f"http://*****:*****@email.com") self.assertTrue(config["config"]["parameters"]["annotations"]) if cookie_key: cookie = session.cookies.get(cookie_key) token = json.loads(base64.b64decode(cookie)) access_token_before = token.get("access_token") id_token_before = token.get("id_token") # let the token expire time.sleep(TOKEN_EXPIRES + 1) # check that refresh works session.get(login_uri) userinfo = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertTrue(userinfo["userinfo"]["is_authenticated"]) self.assertEqual(userinfo["userinfo"]["username"], "fake_user") cookie = session.cookies.get(cookie_key) token = json.loads(base64.b64decode(cookie)) access_token_after = token.get("access_token") id_token_after = token.get("id_token") self.assertNotEqual(access_token_before, access_token_after) self.assertNotEqual(id_token_before, id_token_after) # invalid cookie is rejected session.cookies.set(cookie_key, "TEST_" + cookie) self.assertTrue(cookie_key in session.cookies) response = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo") # this is not an error, the invalid cookie is just ignored. self.assertEqual(response.status_code, 200) userinfo = response.json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) # invalid id_token is rejected test_token = token test_token["id_token"] = "TEST_" + id_token_after encoded_cookie = base64.b64encode(json.dumps(test_token).encode()).decode() session.cookies.set(cookie_key, encoded_cookie) response = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo") # this is not an error, the invalid id_token is just ignored. self.assertEqual(response.status_code, 200) userinfo = response.json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) r = session.get(logout_uri) # check that the logout redirect worked self.assertEqual(r.history[0].status_code, 302) self.assertEqual(r.url, f"{server}/d/pbmc3k.cxg/") config = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get(f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue(config["config"]["parameters"]["annotations"])
def test_multi_dataset(self): c = AppConfig() # test for illegal url_dataroots for illegal in ("../b", "!$*", "\\n", "", "(bad)"): c.update_server_config( multi_dataset__dataroot={"tag": {"base_url": illegal, "dataroot": "{PROJECT_ROOT}/example-dataset"}} ) with self.assertRaises(ConfigurationError): c.complete_config() # test for legal url_dataroots for legal in ("d", "this.is-okay_", "a/b"): c.update_server_config( multi_dataset__dataroot={"tag": {"base_url": legal, "dataroot": "{PROJECT_ROOT}/example-dataset"}} ) c.complete_config() # test that multi dataroots work end to end c.update_server_config( multi_dataset__dataroot=dict( s1=dict(dataroot=f"{PROJECT_ROOT}/example-dataset", base_url="set1/1/2"), s2=dict(dataroot=f"{PROJECT_ROOT}/server/test/test_datasets", base_url="set2"), s3=dict(dataroot=f"{PROJECT_ROOT}/server/test/test_datasets", base_url="set3"), ) ) # Change this default to test if the dataroot overrides below work. c.update_default_dataset_config(app__about_legal_tos="tos_default.html") # specialize the configs for set1 c.add_dataroot_config( "s1", user_annotations__enable=False, diffexp__enable=True, app__about_legal_tos="tos_set1.html" ) # specialize the configs for set2 c.add_dataroot_config( "s2", user_annotations__enable=True, diffexp__enable=False, app__about_legal_tos="tos_set2.html" ) # no specializations for set3 (they get the default dataset config) c.complete_config() with test_server(app_config=c) as server: session = requests.Session() r = session.get(f"{server}/set1/1/2/pbmc3k.h5ad/api/v0.2/config") data_config = r.json() assert data_config["config"]["displayNames"]["dataset"] == "pbmc3k" assert data_config["config"]["parameters"]["annotations"] is False assert data_config["config"]["parameters"]["disable-diffexp"] is False assert data_config["config"]["parameters"]["about_legal_tos"] == "tos_set1.html" r = session.get(f"{server}/set2/pbmc3k.cxg/api/v0.2/config") data_config = r.json() assert data_config["config"]["displayNames"]["dataset"] == "pbmc3k" assert data_config["config"]["parameters"]["annotations"] is True assert data_config["config"]["parameters"]["about_legal_tos"] == "tos_set2.html" r = session.get(f"{server}/set3/pbmc3k.cxg/api/v0.2/config") data_config = r.json() assert data_config["config"]["displayNames"]["dataset"] == "pbmc3k" assert data_config["config"]["parameters"]["annotations"] is True assert data_config["config"]["parameters"]["disable-diffexp"] is False assert data_config["config"]["parameters"]["about_legal_tos"] == "tos_default.html" r = session.get(f"{server}/health") assert r.json()["status"] == "pass"
def auth_flow(self, app_config, cookie_key=None): with test_server(app_config=app_config) as server: session = requests.Session() # auth datasets config = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue( config["config"]["authentication"]["requires_client_login"]) self.assertTrue(config["config"]["parameters"]["annotations"]) login_uri = config["config"]["authentication"]["login"] logout_uri = config["config"]["authentication"]["logout"] self.assertEqual(login_uri, "/login?dataset=d/pbmc3k.cxg/") self.assertEqual(logout_uri, "/logout") r = session.get(f"{server}/{login_uri}") # check that the login redirect worked self.assertEqual(r.history[0].status_code, 302) self.assertEqual(r.url, f"{server}/d/pbmc3k.cxg/") config = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertTrue(userinfo["userinfo"]["is_authenticated"]) self.assertEqual(userinfo["userinfo"]["username"], "fake_user") self.assertTrue(config["config"]["parameters"]["annotations"]) if cookie_key: cookie = session.cookies.get(cookie_key) token = json.loads(base64.b64decode(cookie)) access_token_before = token.get("access_token") expires_at_before = token.get("expires_at") # let the token expire time.sleep(TOKEN_EXPIRES + 1) # check that refresh works session.get(f"{server}/{login_uri}") userinfo = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertTrue(userinfo["userinfo"]["is_authenticated"]) self.assertEqual(userinfo["userinfo"]["username"], "fake_user") cookie = session.cookies.get(cookie_key) token = json.loads(base64.b64decode(cookie)) access_token_after = token.get("access_token") expires_at_after = token.get("expires_at") self.assertNotEqual(access_token_before, access_token_after) self.assertTrue( expires_at_after - expires_at_before > TOKEN_EXPIRES) r = session.get(f"{server}/{logout_uri}") # check that the logout redirect worked self.assertEqual(r.history[0].status_code, 302) self.assertEqual(r.url, f"{server}") config = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/config").json() userinfo = session.get( f"{server}/d/pbmc3k.cxg/api/v0.2/userinfo").json() self.assertFalse(userinfo["userinfo"]["is_authenticated"]) self.assertIsNone(userinfo["userinfo"]["username"]) self.assertTrue(config["config"]["parameters"]["annotations"])