def test_legacy_swhid_browse(archive_data, client, origin): snapshot = archive_data.snapshot_get_latest(origin["url"]) revision = archive_data.snapshot_get_head(snapshot) directory = archive_data.revision_get(revision)["directory"] directory_content = archive_data.directory_ls(directory) directory_file = random.choice( [e for e in directory_content if e["type"] == "file"]) legacy_swhid = gen_swhid( CONTENT, directory_file["checksums"]["sha1_git"], metadata={"origin": origin["url"]}, ) url = reverse("browse-swhid", url_args={"swhid": legacy_swhid}) resp = check_html_get_response(client, url, status_code=302) resp = check_html_get_response(client, resp["location"], status_code=200, template_used="browse/content.html") swhid = gen_swhid( CONTENT, directory_file["checksums"]["sha1_git"], metadata={ "origin": origin["url"], "visit": gen_swhid(SNAPSHOT, snapshot["id"]), "anchor": gen_swhid(REVISION, revision), }, ) assert_contains(resp, swhid)
def test_origin_snapshot_invalid_branch( client, archive_data, new_origin, new_snapshot, visit_dates, revisions ): snp_dict = new_snapshot.to_dict() archive_data.origin_add([new_origin]) for i, branch in enumerate(snp_dict["branches"].keys()): snp_dict["branches"][branch] = { "target_type": "revision", "target": hash_to_bytes(revisions[i]), } archive_data.snapshot_add([Snapshot.from_dict(snp_dict)]) visit = archive_data.origin_visit_add( [OriginVisit(origin=new_origin.url, date=visit_dates[0], type="git",)] )[0] visit_status = OriginVisitStatus( origin=new_origin.url, visit=visit.visit, date=now(), status="full", snapshot=snp_dict["id"], ) archive_data.origin_visit_status_add([visit_status]) url = reverse( "browse-origin-directory", query_params={"origin_url": new_origin.url, "branch": "invalid_branch"}, ) check_html_get_response(client, url, status_code=404, template_used="error.html")
def test_revision_request_errors(client, revision, unknown_revision, new_origin): url = reverse("browse-revision", url_args={"sha1_git": unknown_revision}) resp = check_html_get_response(client, url, status_code=404, template_used="error.html") assert_contains(resp, "Revision with sha1_git %s not found" % unknown_revision, status_code=404) url = reverse( "browse-revision", url_args={"sha1_git": revision}, query_params={"origin_url": new_origin.url}, ) resp = check_html_get_response(client, url, status_code=404, template_used="error.html") assert_contains(resp, "the origin mentioned in your request" " appears broken", status_code=404)
def test_apidoc_post_only(client): # a dedicated view accepting GET requests should have # been created to display the HTML documentation url = reverse("api-1-test-post-only-doc") check_html_get_response(client, url, status_code=200, template_used="api/apidoc.html")
def test_oidc_login_views_success(client, keycloak_mock): """ Simulate a successful login authentication with OpenID Connect authorization code flow with PKCE. """ # user initiates login process login_url = reverse("oidc-login") # should redirect to Keycloak authentication page in order # for a user to login with its username / password response = check_html_get_response(client, login_url, status_code=302) request = response.wsgi_request assert isinstance(request.user, AnonymousUser) login_data = _check_oidc_login_code_flow_data( request, response, keycloak_mock, redirect_uri=reverse("oidc-login-complete", request=request), ) # once a user has identified himself in Keycloak, he is # redirected to the 'oidc-login-complete' view to # login in Django. # 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()) login_complete_url = reverse( "oidc-login-complete", query_params={ "code": code, "state": login_data["state"], "session_state": session_state, }, ) # login process finalization, should redirect to root url by default response = check_html_get_response(client, login_complete_url, status_code=302) request = response.wsgi_request assert response["location"] == request.build_absolute_uri("/") # user should be authenticated assert isinstance(request.user, OIDCUser) # check remote user has not been saved to Django database with pytest.raises(User.DoesNotExist): User.objects.get(username=request.user.username)
def test_content_request_errors(client, invalid_sha1, unknown_content): url = reverse("browse-content", url_args={"query_string": invalid_sha1}) check_html_get_response(client, url, status_code=400, template_used="error.html") url = reverse("browse-content", url_args={"query_string": unknown_content["sha1"]}) check_html_get_response(client, url, status_code=404, template_used="browse/content.html")
def test_pull_request_branches_filtering(client, origin): # check no pull request branches are displayed in the Branches / Releases dropdown url = reverse("browse-origin-directory", query_params={"origin_url": origin.url}) resp = check_html_get_response( client, url, status_code=200, template_used="browse/directory.html" ) assert_not_contains(resp, "refs/pull/") # check no pull request branches are displayed in the branches view url = reverse("browse-origin-branches", query_params={"origin_url": origin.url}) resp = check_html_get_response( client, url, status_code=200, template_used="browse/branches.html" ) assert_not_contains(resp, "refs/pull/")
def test_directory_request_errors(client, invalid_sha1, unknown_directory): dir_url = reverse("browse-directory", url_args={"sha1_git": invalid_sha1}) check_html_get_response(client, dir_url, status_code=400, template_used="error.html") dir_url = reverse("browse-directory", url_args={"sha1_git": unknown_directory}) check_html_get_response(client, dir_url, status_code=404, template_used="error.html")
def test_content_bytes_missing(client, archive_data, mocker, content): mock_archive = mocker.patch("swh.web.browse.utils.archive") content_data = archive_data.content_get(content["sha1"]) mock_archive.lookup_content.return_value = content_data mock_archive.lookup_content_filetype.side_effect = Exception() mock_archive.lookup_content_raw.side_effect = NotFoundExc( "Content bytes not available!") url = reverse("browse-content", url_args={"query_string": content["sha1"]}) check_html_get_response(client, url, status_code=404, template_used="browse/content.html")
def test_apidoc_with_links(client): url = reverse("api-1-endpoint-links-in-doc") rv = check_html_get_response(client, url, status_code=200, template_used="api/apidoc.html") html = prettify_html(rv.content) first_link = textwrap.indent( ('<a class="reference external" href="/api/1/content/doc/">\n' " /api/1/content/\n" "</a>"), " " * 9, ) second_link = textwrap.indent( ('<a class="reference external" href="/api/1/directory/doc/">\n' " /api/1/directory/\n" "</a>"), " " * 9, ) third_link = textwrap.indent( ('<a class="reference external" ' 'href="https://archive.softwareheritage.org">\n' " archive\n" "</a>"), " " * 9, ) assert first_link in html assert second_link in html assert third_link in html
def test_content_id_optional_parts_browse(client, archive_data, content): cnt_sha1_git = content["sha1_git"] origin_url = "https://github.com/user/repo" archive_data.origin_add([Origin(url=origin_url)]) swhid = gen_swhid( CONTENT, cnt_sha1_git, metadata={ "lines": "4-20", "origin": origin_url }, ) url = reverse("browse-swhid", url_args={"swhid": swhid}) query_string = "sha1_git:" + cnt_sha1_git content_browse_url = reverse( "browse-content", url_args={"query_string": query_string}, query_params={"origin_url": origin_url}, ) content_browse_url += "#L4-L20" resp = check_html_get_response(client, url, status_code=302) assert resp["location"] == content_browse_url
def test_oidc_login_complete_wrong_code_verifier(client, keycloak_mock): keycloak_mock.set_auth_success(False) # simulate login process has been initialized session = client.session session["login_data"] = { "code_verifier": "", "state": str(uuid.uuid4()), "redirect_uri": "", "next_path": "", } session.save() # check authentication error is reported login_url = reverse( "oidc-login-complete", query_params={ "code": "some-code", "state": session["login_data"]["state"] }, ) # should render an error page response = check_html_get_response(client, login_url, status_code=500, template_used="error.html") request = response.wsgi_request assert_contains(response, "User authentication failed.", status_code=500) # no user should be logged in assert isinstance(request.user, AnonymousUser)
def test_oidc_login_complete_wrong_csrf_token(client, keycloak_mock): # simulate login process has been initialized session = client.session session["login_data"] = { "code_verifier": "", "state": str(uuid.uuid4()), "redirect_uri": "", "next_path": "", } session.save() # user initiates login process login_url = reverse("oidc-login-complete", query_params={ "code": "some-code", "state": "some-state" }) # should render an error page response = check_html_get_response(client, login_url, status_code=400, template_used="error.html") request = response.wsgi_request assert_contains(response, "Wrong CSRF token, aborting login process.", status_code=400) # no user should be logged in assert isinstance(request.user, AnonymousUser)
def test_oidc_login_complete_view_missing_parameters(client, mocker): # simulate login process has been initialized session = client.session session["login_data"] = { "code_verifier": "", "state": str(uuid.uuid4()), "redirect_uri": "", "next_path": "", } session.save() # user initiates login process login_url = reverse("oidc-login-complete") # should render an error page response = check_html_get_response(client, login_url, status_code=400, template_used="error.html") request = response.wsgi_request assert_contains(response, "Missing query parameters for authentication.", status_code=400) # no user should be logged in assert isinstance(request.user, AnonymousUser)
def test_oidc_logout_view_success(client, keycloak_mock): """ Simulate a successful logout operation with OpenID Connect. """ # login our test user client.login(code="", code_verifier="", redirect_uri="") keycloak_mock.authorization_code.assert_called() # user initiates logout oidc_logout_url = reverse("oidc-logout") # should redirect to logout page response = check_html_get_response(client, oidc_logout_url, status_code=302) request = response.wsgi_request logout_url = reverse("logout", query_params={"remote_user": 1}) assert response["location"] == request.build_absolute_uri(logout_url) # should have been logged out in Keycloak oidc_profile = keycloak_mock.login() keycloak_mock.logout.assert_called_with(oidc_profile["refresh_token"]) # check effective logout in Django assert isinstance(request.user, AnonymousUser)
def test_content_view_text(client, archive_data, content): sha1_git = content["sha1_git"] url = reverse( "browse-content", url_args={"query_string": content["sha1"]}, query_params={"path": content["path"]}, ) url_raw = reverse("browse-content-raw", url_args={"query_string": content["sha1"]}) resp = check_html_get_response(client, url, status_code=200, template_used="browse/content.html") content_display = _process_content_for_display(archive_data, content) mimetype = content_display["mimetype"] if mimetype.startswith("text/"): assert_contains(resp, '<code class="%s">' % content_display["language"]) assert_contains(resp, escape(content_display["content_data"])) assert_contains(resp, url_raw) swh_cnt_id = gen_swhid(CONTENT, sha1_git) swh_cnt_id_url = reverse("browse-swhid", url_args={"swhid": swh_cnt_id}) assert_contains(resp, swh_cnt_id) assert_contains(resp, swh_cnt_id_url) assert_not_contains(resp, "swh-metadata-popover")
def test_origin_empty_snapshot_null_revision(client, archive_data, new_origin): snapshot = Snapshot( branches={ b"HEAD": SnapshotBranch( target="refs/head/master".encode(), target_type=TargetType.ALIAS, ), b"refs/head/master": None, } ) archive_data.origin_add([new_origin]) archive_data.snapshot_add([snapshot]) visit = archive_data.origin_visit_add( [OriginVisit(origin=new_origin.url, date=now(), type="git",)] )[0] visit_status = OriginVisitStatus( origin=new_origin.url, visit=visit.visit, date=now(), status="partial", snapshot=snapshot.id, ) archive_data.origin_visit_status_add([visit_status]) url = reverse( "browse-origin-directory", query_params={"origin_url": new_origin.url}, ) resp = check_html_get_response( client, url, status_code=200, template_used="browse/directory.html" ) resp_content = resp.content.decode("utf-8") assert re.search("snapshot.*is empty", resp_content) assert not re.search("swh-tr-link", resp_content)
def test_oidc_session_expired_middleware_disabled(client, keycloak_mock): # authenticate user client.login(code="", code_verifier="", redirect_uri="") keycloak_mock.authorization_code.assert_called() url = reverse("swh-web-homepage") # visit url first to get user from response response = check_html_get_response(client, url, status_code=200) # simulate OIDC session expiration cache.delete(f"oidc_user_{response.wsgi_request.user.id}") # no redirection when session has expired check_html_get_response(client, url, status_code=200)
def test_origin_release_browse(client, archive_data, origin): snapshot = archive_data.snapshot_get_latest(origin["url"]) release = [ b for b in snapshot["branches"].values() if b["target_type"] == "release" ][-1] release_data = archive_data.release_get(release["target"]) revision_data = archive_data.revision_get(release_data["target"]) url = reverse( "browse-origin-directory", query_params={"origin_url": origin["url"], "release": release_data["name"]}, ) resp = check_html_get_response( client, url, status_code=200, template_used="browse/directory.html" ) assert_contains(resp, release_data["name"]) assert_contains(resp, release["target"]) swhid_context = { "origin": origin["url"], "visit": gen_swhid(SNAPSHOT, snapshot["id"]), "anchor": gen_swhid(RELEASE, release_data["id"]), } swh_dir_id = gen_swhid( DIRECTORY, revision_data["directory"], metadata=swhid_context ) swh_dir_id_url = reverse("browse-swhid", url_args={"swhid": swh_dir_id}) assert_contains(resp, swh_dir_id) assert_contains(resp, swh_dir_id_url)
def test_browse_origin_content_directory_empty_snapshot(client, mocker, origin): mock_snapshot_archive = mocker.patch("swh.web.browse.snapshot_context.archive") mock_get_origin_visit_snapshot = mocker.patch( "swh.web.browse.snapshot_context.get_origin_visit_snapshot" ) mock_get_origin_visit_snapshot.return_value = ([], [], {}) mock_snapshot_archive.lookup_origin.return_value = origin mock_snapshot_archive.lookup_snapshot_sizes.return_value = { "alias": 0, "revision": 0, "release": 0, } for browse_context in ("content", "directory"): url = reverse( f"browse-origin-{browse_context}", query_params={"origin_url": origin["url"], "path": "baz"}, ) resp = check_html_get_response( client, url, status_code=200, template_used=f"browse/{browse_context}.html" ) assert re.search("snapshot.*is empty", resp.content.decode("utf-8")) assert mock_get_origin_visit_snapshot.called assert mock_snapshot_archive.lookup_origin.called assert mock_snapshot_archive.lookup_snapshot_sizes.called
def test_revision_metadata_display(archive_data, client, directory, person, date): metadata = {"foo": "bar"} revision = Revision( directory=hash_to_bytes(directory), author=person, committer=person, message=b"commit message", date=TimestampWithTimezone.from_datetime(date), committer_date=TimestampWithTimezone.from_datetime(date), synthetic=False, type=RevisionType.GIT, metadata=metadata, ) archive_data.revision_add([revision]) url = reverse("browse-revision", url_args={"sha1_git": hash_to_hex(revision.id)}) resp = check_html_get_response(client, url, status_code=200, template_used="browse/revision.html") assert_contains(resp, "swh-metadata-popover") assert_contains(resp, escape(json.dumps(metadata, indent=4)))
def test_release_browse_not_found(client, archive_data, unknown_release): url = reverse("browse-release", url_args={"sha1_git": unknown_release}) resp = check_html_get_response( client, url, status_code=404, template_used="error.html" ) err_msg = "Release with sha1_git %s not found" % unknown_release assert_contains(resp, err_msg, status_code=404)
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_oidc_profile_view_anonymous_user(client): """ Non authenticated users should be redirected to login page when requesting profile view. """ url = reverse("oidc-profile") login_url = reverse("oidc-login", query_params={"next_path": url}) resp = check_html_get_response(client, url, status_code=302) assert resp["location"] == login_url
def test_origin_content_no_path(client, origin): url = reverse("browse-origin-content", query_params={"origin_url": origin["url"]}) resp = check_html_get_response( client, url, status_code=400, template_used="error.html" ) assert_contains( resp, "The path of a content must be given as query parameter.", status_code=400 )
def test_browse_visits_origin_not_found(client, new_origin): url = reverse("browse-origin-visits", query_params={"origin_url": new_origin.url}) resp = check_html_get_response( client, url, status_code=404, template_used="error.html" ) assert_contains( resp, f"Origin with url {new_origin.url} not found", status_code=404 )
def test_directory_id_browse(client, directory): swhid = gen_swhid(DIRECTORY, directory) url = reverse("browse-swhid", url_args={"swhid": swhid}) directory_browse_url = reverse("browse-directory", url_args={"sha1_git": directory}) resp = check_html_get_response(client, url, status_code=302) assert resp["location"] == directory_browse_url
def test_revision_uppercase(client, revision): url = reverse("browse-revision-uppercase-checksum", url_args={"sha1_git": revision.upper()}) resp = check_html_get_response(client, url, status_code=302) redirect_url = reverse("browse-revision", url_args={"sha1_git": revision}) assert resp["location"] == redirect_url
def test_browse_origin_content_not_found(client, origin): url = reverse( "browse-origin-content", query_params={"origin_url": origin["url"], "path": "/invalid/file/path"}, ) resp = check_html_get_response( client, url, status_code=404, template_used="browse/content.html" ) assert re.search("Directory entry.*not found", resp.content.decode("utf-8"))
def test_directory_uppercase(client, directory): url = reverse("browse-directory-uppercase-checksum", url_args={"sha1_git": directory.upper()}) resp = check_html_get_response(client, url, status_code=302) redirect_url = reverse("browse-directory", url_args={"sha1_git": directory}) assert resp["location"] == redirect_url