def test_dummy_auth(build_image, container): """ Test that the client is able to. - Once authenticated, pass a correctly formatted Mount Header """ test_logger.info("Start of test_dummy_auth") assert wait_for_site(JHUB_URL) is True with requests.Session() as s: # login user = "******" # next to hub/home, else the login by # default will return 500 because it will # try and start a server right away with the new user # Which fails because the default # spawner requires a matching local username login_response = s.post( JHUB_URL + "/hub/login?next=/hub/home", data={ "username": user, "password": "******" }, ) assert login_response.status_code == 200 resp = s.get(JHUB_URL + "/hub/home") assert resp.status_code == 200 test_logger.info("End of test_dummy_auth")
def test_basic_cert_user_header_auth(build_image, container): """ Test that the client is able to. Once authenticated, pass a correctly formatted Mount Header """ test_logger.info("Start of test_basic_cert_user_header_auth") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.session() as session: # Auth requests remote_user = ( "/C=DK/ST=NA/L=NA/O=NBI/OU=NA/CN=Name" "/[email protected]" ) auth_header = {"Remote-User": remote_user} auth_response = session.get( "".join([JHUB_HUB_URL, "/home"]), headers=auth_header ) assert auth_response.status_code == 200 auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=auth_header ) assert auth_response.status_code == 200 # TODO, validate username is actual email regex test_logger.info("End of test_basic_cert_user_header_auth")
def test_default_header_config(build_image, container): """ Test that an authenticated client is able to pass a correctly formatted Mount Header """ test_logger.info("Start of test_default_header_config") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.session() as session: # Auth requests remote_user = "******" auth_header = {"Remote-User": remote_user} auth_response = session.get( "".join([JHUB_HUB_URL, "/home"]), headers=auth_header ) assert auth_response.status_code == 200 auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=auth_header ) assert auth_response.status_code == 200 test_logger.info("End of test_default_header_config")
def test_remote_oid_user_header_auth(build_image, container): """ Test that the client is able to. Once authenticated, pass a correctly formatted Mount Header """ test_logger.info("Start of test_remote_oid_user_header_auth") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.session() as session: # Auth requests remote_user = "******" auth_header = {"Remote-User": remote_user} auth_response = session.get( "".join([JHUB_HUB_URL, "/home"]), headers=auth_header ) assert auth_response.status_code == 200 auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=auth_header ) assert auth_response.status_code == 200 test_logger.info("End of test_remote_oid_user_header_auth")
def test_custom_data_header_auth(build_image, container): """ Test that the client is able to. Once authenticated, pass a correctly formatted Mount Header """ test_logger.info("Start of test_custom_data_header_auth") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.session() as session: # Auth requests remote_user = "******" data_dict = { "HOST": "hostaddr", "USERNAME": "******", "PATH": "@host.localhost:", } auth_data_header = {"Remote-User": remote_user, "Mount": json.dumps(data_dict)} auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=auth_data_header ) assert auth_response.status_code == 200 test_logger.info("End of test_custom_data_header_auth")
def test_auth_hub(build_image, container): """ Test that the client is able to, Not access the home path without being authed Authenticate with the Remote-User header """ test_logger.info("Start of test_auth_hub") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.Session() as session: # Auth requests user_cert = ( "/C=DK/ST=NA/L=NA/O=NBI/OU=NA/CN=Name" "/[email protected]" ) other_user = "******" cert_auth_header = {"Remote-User": user_cert} other_auth_header = {"Remote-User": other_user} auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=cert_auth_header ) assert auth_response.status_code == 200 auth_response = session.get( "".join([JHUB_HUB_URL, "/home"]), headers=other_auth_header ) assert auth_response.status_code == 200 test_logger.info("End of test_auth_hub")
def test_auth_data_header(build_image, container): """ Test that the client is able to. Once authenticated, pass a correctly formatted custom Data header """ # not ideal, wait for the jhub container to start, update with proper check test_logger.info("Start of test_auth_data_header") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.Session() as session: no_auth_mount = session.post("".join([JHUB_HUB_URL, "/data"])) assert no_auth_mount.status_code == 403 # Auth requests user_cert = ( "/C=DK/ST=NA/L=NA/O=NBI/OU=NA/CN=Name" "/[email protected]" ) cert_auth_header = {"Remote-User": user_cert} auth_response = session.get( "".join([JHUB_HUB_URL, "/home"]), headers=cert_auth_header ) assert auth_response.status_code == 200 auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=cert_auth_header ) assert auth_response.status_code == 200 wrong_header = {"Mount": "SDfssdfsesdfsfdsdfsxv"} # Random key set correct_dict = { "HOST": "hostaddr", "USERNAME": "******", "PATH": "@host.localhost:", } correct_header = {"Mount": str(correct_dict)} # Invalid mount header auth_mount_response = session.post( "".join([JHUB_HUB_URL, "/data"]), headers=wrong_header ) assert auth_mount_response.status_code == 403 # Valid mount header auth_mount_response = session.post( "".join([JHUB_HUB_URL, "/data"]), headers=correct_header ) assert auth_mount_response.status_code == 200 test_logger.info("End of test_auth_data_header")
def test_json_data_post(build_image, network, container): """ Test that the client is able to submit a json data to the authenticated user. """ test_logger.info("Start of test_json_data_post") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.session() as session: # Auth requests remote_user = "******" auth_header = { "Remote-User": remote_user, } auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=auth_header ) assert auth_response.status_code == 200 # Post json data_str = "blablabla" data_dict = { "HOST": "hostaddr", "USERNAME": "******", "PATH": "@host.localhost:", } env_data = {"StringData": data_str, "JsonData": data_dict} json_data = {"data": env_data} post_response = session.post( "".join([JHUB_HUB_URL, "/user-data"]), json=json_data ) assert post_response.status_code == 200 test_logger.info("End of test_json_data_post")
def test_auth_state_header_auth(build_image, network, container): """ Test that the client is able to. Test that auth_state recieves the specified test data headers. """ test_logger.info("Start of test_auth_state_header_auth") client = docker.from_env() service_name = "jupyterhub" if not wait_for_container(client, service_name, minutes=5): raise RuntimeError(JUPYTERHUB_START_ERROR) assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.session() as session: # Auth requests remote_user = "******" data_str = "blablabla" data_dict = { "HOST": "hostaddr", "USERNAME": "******", "PATH": "@host.localhost:", } env_data = {"StringData": data_str, "JsonData": data_dict} auth_data_header = { "Remote-User": remote_user, } # Cast to json data types before submission auth_data_header.update( {env_key: json.dumps(env_val) for env_key, env_val in env_data.items()} ) auth_response = session.post( "".join([JHUB_HUB_URL, "/login"]), headers=auth_data_header ) assert auth_response.status_code == 200 # Spawn with auth_state spawn_response = session.post("".join([JHUB_HUB_URL, "/spawn"])) assert spawn_response.status_code == 200 test_logger.info("Spawn POST response message: {}".format(spawn_response.text)) assert spawn_response.status_code == 200 target_container_name = "{}-{}".format("jupyter", remote_user) wait_min = 5 if not wait_for_container(client, target_container_name, minutes=wait_min): raise RuntimeError( "No container with name: {} appeared within: {} minutes".format( service_name, wait_min ) ) spawned_container = get_container(client, target_container_name) # Validate that the container has the passed environment values defined # in env_data envs = { env.split("=")[0]: env.split("=")[1] for env in spawned_container.attrs["Config"]["Env"] } for data_key, data_value in env_data.items(): assert data_key in envs assert envs[data_key] == str(data_value) # Shutdown the container # Delete the spawned service delete_headers = {"Referer": urljoin(JHUB_URL, "/hub/"), "Origin": JHUB_URL} jhub_user = get_container_user(spawned_container) assert jhub_user is not None delete_url = urljoin(JHUB_URL, "/hub/api/users/{}/server".format(jhub_user)) deleted = delete(session, delete_url, headers=delete_headers) assert deleted # Remove the stopped container spawned_container.stop() spawned_container.wait() spawned_container.remove() deleted_container = get_container(client, target_container_name) assert deleted_container is None test_logger.info("End of test_auth_state_header_auth")
def test_remote_auth_hub(image, swarm, network, make_service): """Test that logging in as a new user creates a new docker service.""" test_logger.info("Start of service testing") make_service(remote_hub_service) client = docker.from_env() # jupyterhub service should be running at this point services_before_spawn = client.services.list() test_logger.info("Pre test services: {}".format(services_before_spawn)) username = "******" # Auth header test_logger.info("Authenticating with user: {}".format(username)) headers = {"Remote-User": username} assert wait_for_site(JHUB_URL, valid_status_code=401) is True with requests.Session() as s: # Login login_response = s.post(JHUB_URL + "/hub/login", headers=headers) test_logger.info("Login response message: {}".format( login_response.text)) assert login_response.status_code == 200 # Spawn a notebook spawn_form_resp = s.get(JHUB_URL + "/hub/spawn") test_logger.info("Spawn page message: {}".format(spawn_form_resp.text)) assert spawn_form_resp.status_code == 200 assert "Select a notebook image" in spawn_form_resp.text payload = {"dockerimage": "nielsbohr/base-notebook:latest"} spawn_resp = s.post(JHUB_URL + "/hub/spawn", data=payload) test_logger.info("Spawn POST response message: {}".format( spawn_resp.text)) assert spawn_resp.status_code == 200 # Get spawned service target_service_name = "{}-".format("jupyter") spawned_service = get_service(client, target_service_name) assert spawned_service is not None # Get the service api url service_url = get_service_url(spawned_service) # If failed the service might not be running if not service_url: test_logger.info("Properly failed to start the service correctly") assert service_url is not None # Combine with the base jhub URL jhub_service_api = urljoin(JHUB_URL, service_url) # Write to user home new_file = "write_test.ipynb" data = json.dumps({"name": new_file}) test_logger.info("Looking for xsrf in: {}".format(s.cookies)) # Refresh csrf token assert wait_for_session(s, jhub_service_api, require_xsrf=True) assert "_xsrf" in s.cookies xsrf_token = s.cookies["_xsrf"] service_api_url = get_service_api_url(spawned_service, postfix_url="contents/") jhub_service_content = urljoin(JHUB_URL, service_api_url) # Write to home xsrf_headers = {"X-XSRFToken": xsrf_token} resp = s.put( "".join([jhub_service_content, new_file]), data=data, headers=xsrf_headers, ) assert resp.status_code == 201 # Remove via the web interface refresh_csrf(s, jhub_service_api) assert "_xsrf" in s.cookies delete_headers = { "Referer": urljoin(JHUB_URL, "/hub/home"), "Origin": JHUB_URL, } jhub_user = get_service_user(spawned_service) delete_url = urljoin(JHUB_URL, "/hub/api/users/{}/server".format(jhub_user)) # Wait for the server to finish deleting deleted = delete(s, delete_url, headers=delete_headers) assert deleted deleted_service = get_service(client, target_service_name) assert deleted_service is None
def test_creates_service(image, swarm, network, make_service): """Test that logging in as a new user creates a new docker service.""" test_logger.info("Start of service testing") make_service(hub_service) client = docker.from_env() # jupyterhub service should be running at this point services_before_spawn = client.services.list() test_logger.info("Pre test services: {}".format(services_before_spawn)) username = "******" password = "******" test_logger.info("Authenticating with user: {}".format(username)) assert wait_for_site(JHUB_URL) is True with requests.Session() as s: # login test_logger.info("Authenticating with user: {}".format(username)) login_response = s.post( JHUB_URL + "/hub/login?next=", data={ "username": username, "password": password }, ) test_logger.info("Login response message: {}".format( login_response.text)) assert login_response.status_code == 200 # Spawn a notebook spawn_form_resp = s.get(JHUB_URL + "/hub/spawn") test_logger.info("Spawn page message: {}".format(spawn_form_resp.text)) assert spawn_form_resp.status_code == 200 assert "Select a notebook image" in spawn_form_resp.text payload = {"dockerimage": "nielsbohr/base-notebook:latest"} spawn_resp = s.post(JHUB_URL + "/hub/spawn", data=payload) test_logger.info("Spawn POST response message: {}".format( spawn_resp.text)) assert spawn_resp.status_code == 200 services = client.services.list() test_logger.info("Post spawn services: {}".format(services)) target_service_name = "{}-{}-{}".format("jupyter", username, "1") spawned_service = get_service(client, target_service_name) assert spawned_service is not None # Verify that a task is succesfully running running_task = wait_for_service_task( client, spawned_service, filters={"desired-state": "running"}) assert running_task # wait for user home home_resp = s.get(JHUB_URL + "/user/{}/tree?".format(username)) assert home_resp.status_code == 200 # Remove via the web interface delete_headers = { "Referer": urljoin(JHUB_URL, "/hub/home"), "Origin": JHUB_URL } jhub_user = get_service_user(spawned_service) delete_url = urljoin(JHUB_URL, "/hub/api/users/{}/server".format(jhub_user)) pending = True num_wait, max_wait = 0, 15 while pending or num_wait > max_wait: num_wait += 1 resp = s.delete(delete_url, headers=delete_headers) test_logger.info( "Response from removing the user server: {}".format(resp.text)) if resp.status_code == 204: pending = False time.sleep(1) assert resp.status_code == 204 # double check it is gone deleted_service = get_service(client, target_service_name) assert deleted_service is None test_logger.info("End of test service")
def test_image_selection(image, swarm, network, make_service): """Test that the spawner allows for dynamic image selection""" test_logger.info("Start of the image selection test") make_service(hub_service) client = docker.from_env() # jupyterhub service should be running at this point services_before_spawn = client.services.list() test_logger.info("Pre test services: {}".format(services_before_spawn)) username = "******" password = "******" test_logger.info("Authenticating with user: {}".format(username)) assert wait_for_site(JHUB_URL) is True with requests.Session() as s: # login test_logger.info("Authenticating with user: {}".format(username)) login_response = s.post( urljoin(JHUB_URL, "/hub/login"), data={ "username": username, "password": password }, ) test_logger.info("Login response message: {}".format( login_response.text)) assert login_response.status_code == 200 # Spawn a notebook spawn_form_resp = s.get(JHUB_URL + "/hub/spawn") test_logger.info("Spawn page message: {}".format(spawn_form_resp.text)) assert spawn_form_resp.status_code == 200 assert "Select a notebook image" in spawn_form_resp.text user_image = "nielsbohr/base-notebook:latest" user_image_name = "Basic Python Notebook" payload = {"name": user_image_name, "image": user_image} json_payload = json.dumps(payload) spawn_resp = s.post( JHUB_URL + "/hub/spawn/{}".format(username), files={"select_image": ( None, json_payload, )}, ) test_logger.info("Spawn POST response message: {}".format( spawn_resp.text)) assert spawn_resp.status_code == 200 target_service_name = "{}-{}-{}".format("jupyter", username, "1") spawned_service = get_service(client, target_service_name) assert spawned_service is not None # Verify that a task is succesfully running running_task = wait_for_service_task( client, spawned_service, filters={"desired-state": "running"}) assert running_task # Verify that the image is correct service_image = get_task_image(running_task) assert service_image == user_image service_labels = get_service_labels(spawned_service) assert service_labels is not None assert service_labels["image_name"] == user_image_name # Delete the spawned service delete_headers = { "Referer": urljoin(JHUB_URL, "/hub/home"), "Origin": JHUB_URL } jhub_user = get_service_user(spawned_service) delete_url = urljoin(JHUB_URL, "/hub/api/users/{}/server".format(jhub_user)) deleted = delete(s, delete_url, headers=delete_headers) assert deleted deleted_service = get_service(client, target_service_name) assert deleted_service is None # Spawn a second service with a different name but the same image ## # Spawn a notebook second_spawn_form_resp = s.get(JHUB_URL + "/hub/spawn") test_logger.info("Spawn page message: {}".format( second_spawn_form_resp.text)) assert second_spawn_form_resp.status_code == 200 assert "Select a notebook image" in second_spawn_form_resp.text second_image_name = "Basic Python Notebook 2" selection_payload = {"name": second_image_name, "image": user_image} json_second_payload = json.dumps(selection_payload) spawn_resp = s.post( JHUB_URL + "/hub/spawn/{}".format(username), files={"select_image": ( None, json_second_payload, )}, ) test_logger.info("Spawn POST response message: {}".format( spawn_resp.text)) assert spawn_resp.status_code == 200 second_target_service_name = "{}-{}-{}".format("jupyter", username, "1") second_spawned_service = get_service(client, second_target_service_name) assert second_spawned_service is not None # Verify that a task is succesfully running second_running_task = wait_for_service_task( client, second_spawned_service, filters={"desired-state": "running"}) assert second_running_task # Verify that the image is correct second_service_image = get_task_image(second_running_task) assert second_service_image == user_image second_service_labels = get_service_labels(second_spawned_service) assert second_service_labels is not None assert second_service_labels["image_name"] == second_image_name # Delete the second spawned service deleted = delete(s, delete_url, headers=delete_headers) assert deleted deleted_service = get_service(client, second_target_service_name) assert deleted_service is None