def test_badge_endpoints_have_cors_header(client, origin, release): url = reverse("swh-badge", url_args={ "object_type": "origin", "object_id": origin["url"] }) resp = check_http_get_response( client, url, status_code=200, content_type="image/svg+xml", http_origin="https://example.org", ) assert ACCESS_CONTROL_ALLOW_ORIGIN in resp release_swhid = str( QualifiedSWHID(object_type=ObjectType.RELEASE, object_id=hash_to_bytes(release))) url = reverse("swh-badge-swhid", url_args={"object_swhid": release_swhid}) resp = check_http_get_response( client, url, status_code=200, content_type="image/svg+xml", http_origin="https://example.org", ) assert ACCESS_CONTROL_ALLOW_ORIGIN in resp
def test_content_raw_text(client, archive_data, content): url = reverse("browse-content-raw", url_args={"query_string": content["sha1"]}) resp = check_http_get_response(client, url, status_code=200, content_type="text/plain") content_data = archive_data.content_get_data(content["sha1"])["data"] assert resp["Content-Type"] == "text/plain" assert resp["Content-disposition"] == ("filename=%s_%s" % ("sha1", content["sha1"])) assert resp.content == content_data filename = content["path"].split("/")[-1] url = reverse( "browse-content-raw", url_args={"query_string": content["sha1"]}, query_params={"filename": filename}, ) resp = check_http_get_response(client, url, status_code=200, content_type="text/plain") assert resp["Content-Type"] == "text/plain" assert resp["Content-disposition"] == "filename=%s" % filename assert resp.content == content_data
def test_content_raw_bin(client, archive_data, content): url = reverse("browse-content-raw", url_args={"query_string": content["sha1"]}) resp = check_http_get_response(client, url, status_code=200, content_type="application/octet-stream") filename = content["path"].split("/")[-1] content_data = archive_data.content_get_data(content["sha1"])["data"] assert resp["Content-Type"] == "application/octet-stream" assert resp["Content-disposition"] == "attachment; filename=%s_%s" % ( "sha1", content["sha1"], ) assert resp.content == content_data url = reverse( "browse-content-raw", url_args={"query_string": content["sha1"]}, query_params={"filename": filename}, ) resp = check_http_get_response(client, url, status_code=200, content_type="application/octet-stream") assert resp["Content-Type"] == "application/octet-stream" assert resp["Content-disposition"] == "attachment; filename=%s" % filename assert resp.content == content_data
def test_oidc_list_bearer_tokens_anonymous_user(client): """ Anonymous user should be refused access with forbidden response. """ url = reverse("oidc-list-bearer-tokens", query_params={ "draw": 1, "start": 0, "length": 10 }) check_http_get_response(client, url, status_code=403)
def test_badge_errors( client, unknown_content, unknown_directory, new_origin, unknown_release, unknown_revision, unknown_snapshot, invalid_sha1, ): for object_type, object_id in ( ("content", unknown_content["sha1_git"]), ("directory", unknown_directory), ("origin", new_origin), ("release", unknown_release), ("revision", unknown_revision), ("snapshot", unknown_snapshot), ): url_args = {"object_type": object_type, "object_id": object_id} url = reverse("swh-badge", url_args=url_args) resp = check_http_get_response(client, url, status_code=200, content_type="image/svg+xml") _check_generated_badge(resp, **url_args, error="not found") for object_type, object_id in ( (ObjectType.CONTENT, invalid_sha1), (ObjectType.DIRECTORY, invalid_sha1), (ObjectType.RELEASE, invalid_sha1), (ObjectType.REVISION, invalid_sha1), (ObjectType.SNAPSHOT, invalid_sha1), ): url_args = { "object_type": object_type.name.lower(), "object_id": object_id } url = reverse("swh-badge", url_args=url_args) resp = check_http_get_response(client, url, status_code=200, content_type="image/svg+xml") _check_generated_badge(resp, **url_args, error="invalid id") object_swhid = f"swh:1:{object_type.value}:{object_id}" url = reverse("swh-badge-swhid", url_args={"object_swhid": object_swhid}) resp = check_http_get_response(client, url, status_code=200, content_type="image/svg+xml") _check_generated_badge(resp, "", "", error="invalid id")
def test_graph_endpoint_no_authentication_for_vpn_users( api_client, requests_mock): graph_query = "stats" url = reverse("api-1-graph", url_args={"graph_query": graph_query}) requests_mock.get( get_config()["graph"]["server_url"] + graph_query, json={}, headers={"Content-Type": "application/json"}, ) check_http_get_response(api_client, url, status_code=200, server_name=SWH_WEB_INTERNAL_SERVER_NAME)
def test_api_content_uppercase(api_client, content): url = reverse("api-1-content-uppercase-checksum", url_args={"q": content["sha1"].upper()}) rv = check_http_get_response(api_client, url, status_code=302) redirect_url = reverse("api-1-content", url_args={"q": content["sha1"]}) assert rv["location"] == redirect_url
def test_graph_ndjson_response(api_client, keycloak_mock, requests_mock): _authenticate_graph_user(api_client, keycloak_mock) graph_query = "visit/paths/swh:1:dir:644dd466d8ad527ea3a609bfd588a3244e6dafcb" response_ndjson = textwrap.dedent("""\ ["swh:1:dir:644dd466d8ad527ea3a609bfd588a3244e6dafcb",\ "swh:1:cnt:acfb7cabd63b368a03a9df87670ece1488c8bce0"] ["swh:1:dir:644dd466d8ad527ea3a609bfd588a3244e6dafcb",\ "swh:1:cnt:2a0837708151d76edf28fdbb90dc3eabc676cff3"] ["swh:1:dir:644dd466d8ad527ea3a609bfd588a3244e6dafcb",\ "swh:1:cnt:eaf025ad54b94b2fdda26af75594cfae3491ec75"] """) requests_mock.get( get_config()["graph"]["server_url"] + graph_query, text=response_ndjson, headers={ "Content-Type": "application/x-ndjson", "Transfer-Encoding": "chunked", }, ) url = reverse("api-1-graph", url_args={"graph_query": graph_query}) resp = check_http_get_response(api_client, url, status_code=200) assert isinstance(resp, StreamingHttpResponse) assert resp["Content-Type"] == "application/x-ndjson" assert b"".join(resp.streaming_content) == response_ndjson.encode()
def test_api_vault_cook_uppercase_hash(api_client, directory, revision): for obj_type, obj_id in ( ("directory", directory), ("revision_gitfast", revision), ): url = reverse( f"api-1-vault-cook-{obj_type}-uppercase-checksum", url_args={f"{obj_type[:3]}_id": obj_id.upper()}, ) rv = check_http_post_response(api_client, url, data={"email": "*****@*****.**"}, status_code=302) redirect_url = reverse(f"api-1-vault-cook-{obj_type}", url_args={f"{obj_type[:3]}_id": obj_id}) assert rv["location"] == redirect_url fetch_url = reverse( f"api-1-vault-fetch-{obj_type}-uppercase-checksum", url_args={f"{obj_type[:3]}_id": obj_id.upper()}, ) rv = check_http_get_response(api_client, fetch_url, status_code=302) redirect_url = reverse( f"api-1-vault-fetch-{obj_type}", url_args={f"{obj_type[:3]}_id": obj_id}, ) assert rv["location"] == redirect_url
def test_graph_endpoint_needs_permission(api_client, keycloak_mock, requests_mock): graph_query = "stats" url = reverse("api-1-graph", url_args={"graph_query": graph_query}) oidc_profile = keycloak_mock.login() api_client.credentials( HTTP_AUTHORIZATION=f"Bearer {oidc_profile['refresh_token']}") check_http_get_response(api_client, url, status_code=403) _authenticate_graph_user(api_client, keycloak_mock) requests_mock.get( get_config()["graph"]["server_url"] + graph_query, json={}, headers={"Content-Type": "application/json"}, ) check_http_get_response(api_client, url, status_code=200)
def _generate_and_test_bearer_token(client, kc_oidc_mock): # user authenticates client.login(code="code", code_verifier="code-verifier", redirect_uri="redirect-uri") # user initiates bearer token generation flow url = reverse("oidc-generate-bearer-token") response = check_http_get_response(client, url, status_code=302) request = response.wsgi_request redirect_uri = reverse("oidc-generate-bearer-token-complete", request=request) # check login data and redirection to Keycloak is valid login_data = _check_oidc_login_code_flow_data( request, response, kc_oidc_mock, redirect_uri=redirect_uri, scope="openid offline_access", ) # once a user has identified himself in Keycloak, he is # redirected to the 'oidc-generate-bearer-token-complete' view # to get and save bearer token # generate authorization code / session state in the same # manner as Keycloak code = f"{str(uuid.uuid4())}.{str(uuid.uuid4())}.{str(uuid.uuid4())}" session_state = str(uuid.uuid4()) token_complete_url = reverse( "oidc-generate-bearer-token-complete", query_params={ "code": code, "state": login_data["state"], "session_state": session_state, }, ) nb_tokens = len(OIDCUserOfflineTokens.objects.all()) response = check_html_get_response(client, token_complete_url, status_code=302) request = response.wsgi_request # check token has been generated and saved encrypted to database assert len(OIDCUserOfflineTokens.objects.all()) == nb_tokens + 1 encrypted_token = OIDCUserOfflineTokens.objects.last().offline_token secret = get_config()["secret_key"].encode() salt = request.user.sub.encode() decrypted_token = decrypt_data(encrypted_token, secret, salt) oidc_profile = kc_oidc_mock.authorization_code(code=code, redirect_uri=redirect_uri) assert decrypted_token.decode("ascii") == oidc_profile["refresh_token"] # should redirect to tokens management Web UI assert response["location"] == reverse("oidc-profile") + "#tokens" return decrypted_token
def test_api_revision_uppercase(api_client, revision): url = reverse("api-1-revision-uppercase-checksum", url_args={"sha1_git": revision.upper()}) resp = check_http_get_response(api_client, url, status_code=302) redirect_url = reverse("api-1-revision", url_args={"sha1_git": revision}) assert resp["location"] == redirect_url
def test_api_revision_raw_ok(api_client, archive_data, revision): url = reverse("api-1-revision-raw-message", url_args={"sha1_git": revision}) expected_message = archive_data.revision_get(revision)["message"] rv = check_http_get_response(api_client, url, status_code=200) assert rv["Content-Type"] == "application/octet-stream" assert rv.content == expected_message.encode()
def test_layout_without_oidc_auth_enabled(client, mocker): config = deepcopy(get_config()) config["keycloak"]["server_url"] = "" mock_get_config = mocker.patch("swh.web.common.utils.get_config") mock_get_config.return_value = config url = reverse("swh-web-homepage") resp = check_http_get_response(client, url, status_code=200) assert_contains(resp, reverse("login"))
def test_api_directory_uppercase(api_client, directory): url = reverse( "api-1-directory-uppercase-checksum", url_args={"sha1_git": directory.upper()} ) resp = check_http_get_response(api_client, url, status_code=302) redirect_url = reverse("api-1-directory", url_args={"sha1_git": directory}) assert resp["location"] == redirect_url
def test_api_content_raw_text(api_client, archive_data, content): url = reverse("api-1-content-raw", url_args={"q": "sha1:%s" % content["sha1"]}) rv = check_http_get_response(api_client, url, status_code=200) assert rv["Content-Type"] == "application/octet-stream" assert (rv["Content-disposition"] == "attachment; filename=content_sha1_%s_raw" % content["sha1"]) expected_data = archive_data.content_get_data(content["sha1"]) assert rv.content == expected_data["data"]
def test_api_snapshot_uppercase(api_client, snapshot): url = reverse("api-1-snapshot-uppercase-checksum", url_args={"snapshot_id": snapshot.upper()}) resp = check_http_get_response(api_client, url, status_code=302) redirect_url = reverse("api-1-snapshot-uppercase-checksum", url_args={"snapshot_id": snapshot}) assert resp["location"] == redirect_url
def test_content_raw_no_utf8_text(client, content): url = reverse("browse-content-raw", url_args={"query_string": content["sha1"]}) resp = check_http_get_response(client, url, status_code=200, content_type="text/plain") _, encoding = get_mimetype_and_encoding_for_content(resp.content) assert encoding == content["encoding"]
def _test_badge_endpoints(client, object_type: str, object_id: str): url_args = {"object_type": object_type, "object_id": object_id} url = reverse("swh-badge", url_args=url_args) resp = check_http_get_response(client, url, status_code=200, content_type="image/svg+xml") _check_generated_badge(resp, **url_args) if object_type != "origin": obj_swhid = str( QualifiedSWHID( object_type=ObjectType[object_type.upper()], object_id=hash_to_bytes(object_id), )) url = reverse("swh-badge-swhid", url_args={"object_swhid": obj_swhid}) resp = check_http_get_response(client, url, status_code=200, content_type="image/svg+xml") _check_generated_badge(resp, **url_args)
def test_reject_pending_save_request(client, mocker): mock_scheduler = mocker.patch("swh.web.common.origin_save.scheduler") visit_type = "git" origin_url = "https://wikipedia.com" save_request_url = reverse( "api-1-save-origin", url_args={ "visit_type": visit_type, "origin_url": origin_url }, ) response = check_http_post_response(client, save_request_url, status_code=200) assert response.data["save_request_status"] == SAVE_REQUEST_PENDING reject_request_url = reverse( "admin-origin-save-request-reject", url_args={ "visit_type": visit_type, "origin_url": origin_url }, ) check_not_login(client, reject_request_url) client.login(username=_user_name, password=_user_password) response = check_http_post_response(client, reject_request_url, status_code=200) tasks_data = [{ "priority": "high", "policy": "oneshot", "type": "load-git", "arguments": { "kwargs": { "repo_url": origin_url }, "args": [] }, "status": "next_run_not_scheduled", "id": 1, }] mock_scheduler.create_tasks.return_value = tasks_data mock_scheduler.get_tasks.return_value = tasks_data response = check_http_get_response(client, save_request_url, status_code=200) assert response.data[0]["save_request_status"] == SAVE_REQUEST_REJECTED
def test_api_vault_cook(api_client, mocker, directory, revision): mock_archive = mocker.patch("swh.web.api.views.vault.archive") for obj_type, obj_id in ( ("directory", directory), ("revision_gitfast", revision), ): fetch_url = reverse( f"api-1-vault-fetch-{obj_type}", url_args={f"{obj_type[:3]}_id": obj_id}, ) stub_cook = { "type": obj_type, "progress_msg": None, "task_id": 1, "task_status": "done", "object_id": obj_id, } stub_fetch = b"content" mock_archive.vault_cook.return_value = stub_cook mock_archive.vault_fetch.return_value = stub_fetch email = "*****@*****.**" url = reverse( f"api-1-vault-cook-{obj_type}", url_args={f"{obj_type[:3]}_id": obj_id}, query_params={"email": email}, ) rv = check_api_post_responses(api_client, url, data=None, status_code=200) assert rv.data == { "fetch_url": rv.wsgi_request.build_absolute_uri(fetch_url), "obj_type": obj_type, "progress_message": None, "id": 1, "status": "done", "obj_id": obj_id, } mock_archive.vault_cook.assert_called_with( obj_type, hashutil.hash_to_bytes(obj_id), email) rv = check_http_get_response(api_client, fetch_url, status_code=200) assert rv["Content-Type"] == "application/gzip" assert rv.content == stub_fetch mock_archive.vault_fetch.assert_called_with( obj_type, hashutil.hash_to_bytes(obj_id))
def test_graph_json_response(api_client, keycloak_mock, requests_mock): _authenticate_graph_user(api_client, keycloak_mock) graph_query = "stats" requests_mock.get( get_config()["graph"]["server_url"] + graph_query, json=_response_json, headers={"Content-Type": "application/json"}, ) url = reverse("api-1-graph", url_args={"graph_query": graph_query}) resp = check_http_get_response(api_client, url, status_code=200) assert resp.content_type == "application/json" assert resp.content == json.dumps(_response_json).encode()
def test_drf_django_session_auth_success(keycloak_mock, client): """ Check user gets authenticated when querying the web api through a web browser. """ url = reverse("api-1-stat-counters") client.login(code="", code_verifier="", redirect_uri="") response = check_http_get_response(client, url, status_code=200) request = response.wsgi_request # user should be authenticated assert isinstance(request.user, OIDCUser) # check remoter used has not been saved to Django database with pytest.raises(User.DoesNotExist): User.objects.get(username=request.user.username)
def test_graph_text_plain_response(api_client, keycloak_mock, requests_mock): _authenticate_graph_user(api_client, keycloak_mock) graph_query = "leaves/swh:1:dir:432d1b21c1256f7408a07c577b6974bbdbcc1323" response_text = textwrap.dedent("""\ swh:1:cnt:1d3dace0a825b0535c37c53ed669ef817e9c1b47 swh:1:cnt:6d5b280f4e33589ae967a7912a587dd5cb8dedaa swh:1:cnt:91bef238bf01356a550d416d14bb464c576ac6f4 swh:1:cnt:58a8b925a463b87d49639fda282b8f836546e396 swh:1:cnt:fd32ee0a87e16ccc853dfbeb7018674f9ce008c0 swh:1:cnt:ab7c39871872589a4fc9e249ebc927fb1042c90d swh:1:cnt:93073c02bf3869845977527de16af4d54765838d swh:1:cnt:4251f795b52c54c447a97c9fe904d8b1f993b1e0 swh:1:cnt:c6e7055424332006d07876ffeba684e7e284b383 swh:1:cnt:8459d8867dc3b15ef7ae9683e21cccc9ab2ec887 swh:1:cnt:5f9981d52202815aa947f85b9dfa191b66f51138 swh:1:cnt:00a685ec51bcdf398c15d588ecdedb611dbbab4b swh:1:cnt:e1cf1ea335106a0197a2f92f7804046425a7d3eb swh:1:cnt:07069b38087f88ec192d2c9aff75a502476fd17d swh:1:cnt:f045ee845c7f14d903a2c035b2691a7c400c01f0 """) requests_mock.get( get_config()["graph"]["server_url"] + graph_query, text=response_text, headers={ "Content-Type": "text/plain", "Transfer-Encoding": "chunked" }, ) url = reverse("api-1-graph", url_args={"graph_query": graph_query}) resp = check_http_get_response(api_client, url, status_code=200, content_type="text/plain") assert isinstance(resp, StreamingHttpResponse) assert b"".join(resp.streaming_content) == response_text.encode()
def test_oidc_list_bearer_tokens(client, keycloak_mock): """ User with correct credentials should be allowed to list his tokens. """ nb_tokens = 3 for _ in range(nb_tokens): _generate_and_test_bearer_token(client, keycloak_mock) url = reverse("oidc-list-bearer-tokens", query_params={ "draw": 1, "start": 0, "length": 10 }) response = check_http_get_response(client, url, status_code=200) tokens_data = list( reversed(json.loads(response.content.decode("utf-8"))["data"])) for oidc_token in OIDCUserOfflineTokens.objects.all(): assert (oidc_token.creation_date.isoformat() == tokens_data[ oidc_token.id - 1]["creation_date"])
def test_api_endpoints_have_cors_headers(client, content, directory, revision): url = reverse("api-1-stat-counters") resp = check_http_get_response(client, url, status_code=200, http_origin="https://example.org") assert ACCESS_CONTROL_ALLOW_ORIGIN in resp swhids = [ gen_swhid(CONTENT, content["sha1_git"]), gen_swhid(DIRECTORY, directory), gen_swhid(REVISION, revision), ] url = reverse("api-1-known") ac_request_method = "POST" ac_request_headers = "Content-Type" resp = client.options( url, HTTP_ORIGIN="https://example.org", HTTP_ACCESS_CONTROL_REQUEST_METHOD=ac_request_method, HTTP_ACCESS_CONTROL_REQUEST_HEADERS=ac_request_headers, ) assert resp.status_code == 200 assert ACCESS_CONTROL_ALLOW_ORIGIN in resp assert ACCESS_CONTROL_ALLOW_METHODS in resp assert ac_request_method in resp[ACCESS_CONTROL_ALLOW_METHODS] assert ACCESS_CONTROL_ALLOW_HEADERS in resp assert ac_request_headers.lower() in resp[ACCESS_CONTROL_ALLOW_HEADERS] resp = resp = check_http_post_response(client, url, data=swhids, status_code=200, http_origin="https://example.org") assert ACCESS_CONTROL_ALLOW_ORIGIN in resp
def test_layout_with_staging_ribbon(client): url = reverse("swh-web-homepage") resp = check_http_get_response( client, url, status_code=200, server_name=random.choice(STAGING_SERVER_NAMES), ) assert_contains(resp, "swh-corner-ribbon")
def test_layout_with_oidc_auth_enabled(client): url = reverse("swh-web-homepage") resp = check_http_get_response(client, url, status_code=200) assert_contains(resp, reverse("oidc-login"))
def test_layout_without_staging_ribbon(client): url = reverse("swh-web-homepage") resp = check_http_get_response(client, url, status_code=200) assert_not_contains(resp, "swh-corner-ribbon")
def test_old_save_url_redirection(client): url = reverse("browse-origin-save") redirect_url = reverse("origin-save") resp = check_http_get_response(client, url, status_code=302) assert resp["location"] == redirect_url