def test_pull_image_with_raw_http_requests(self): """ Test if a content was pulled from a registry by using raw HTTP requests. The registry offers a reference to a certified authority which generates a Bearer token. The generated Bearer token is afterwards used to pull the image. All requests are sent via aiohttp modules. """ image_path = "/v2/{}/manifests/{}".format(self.distribution.base_path, "manifest_a") latest_image_url = urljoin(self.cfg.get_base_url(), image_path) with self.assertRaises(HTTPError) as cm: requests.get( latest_image_url, headers={"Accept": MEDIA_TYPE.MANIFEST_V2} ).raise_for_status() content_response = cm.exception.response self.assertEqual(content_response.status_code, 401) authenticate_header = content_response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) content_response = requests.get( queries.realm, params={"service": queries.service, "scope": queries.scope} ) content_response.raise_for_status() content_response = requests.get( latest_image_url, auth=BearerTokenAuth(content_response.json()["token"]), headers={"Accept": MEDIA_TYPE.MANIFEST_V2}, ) content_response.raise_for_status() self.compare_config_blob_digests(content_response.json()["config"]["digest"])
def test_listing_repositories(self): """ Check if the registry correctly returns all generated repositories' names. In this test, it is also required to obtain a Bearer token. Because of that, there is caught an exception for the HTTP 401 response at first. Then, the token is retrieved from the token server and is used for requesting the list of repositories again. """ repositories_list_endpoint = urljoin(self.cfg.get_base_url(), "/v2/_catalog") with self.assertRaises(HTTPError) as cm: requests.get(repositories_list_endpoint).raise_for_status() content_response = cm.exception.response authenticate_header = content_response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) self.assertFalse(hasattr(queries, "scope")) content_response = requests.get(queries.realm, params={"service": queries.service}) content_response.raise_for_status() repositories = requests.get(repositories_list_endpoint, auth=BearerTokenAuth( content_response.json()["token"])) repositories.raise_for_status() repositories_names = [ self.distribution1.base_path, self.distribution2.base_path ] self.assertEqual(repositories.json(), {"repositories": repositories_names})
def test_api_performes_schema_conversion(self): """Verify pull via token with accepted content type.""" image_path = "/v2/{}/manifests/{}".format( self.distribution_with_repo.base_path, "latest") latest_image_url = urljoin(self.cfg.get_base_url(), image_path) with self.assertRaises(requests.HTTPError) as cm: self.client.get(latest_image_url, headers={"Accept": MEDIA_TYPE.MANIFEST_V1}) content_response = cm.exception.response self.assertEqual(content_response.status_code, 401) authenticate_header = content_response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) content_response = requests.get(queries.realm, params={ "service": queries.service, "scope": queries.scope }) content_response.raise_for_status() token = content_response.json()["token"] content_response = requests.get( latest_image_url, auth=BearerTokenAuth(token), headers={"Accept": MEDIA_TYPE.MANIFEST_V1}, ) content_response.raise_for_status() base_content_type = content_response.headers["Content-Type"].split( ";")[0] self.assertIn(base_content_type, {MEDIA_TYPE.MANIFEST_V1, MEDIA_TYPE.MANIFEST_V1_SIGNED})
def get_listed_repositories(self, auth=None): """Fetch repositories from the catalog endpoint.""" repositories_list_endpoint = urljoin(self.cfg.get_base_url(), "/v2/_catalog") with self.assertRaises(requests.HTTPError) as cm: requests.get(repositories_list_endpoint).raise_for_status() content_response = cm.exception.response authenticate_header = content_response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) self.assertEqual(queries.scope, "registry:catalog:*") content_response = requests.get(queries.realm, params={ "service": queries.service, "scope": queries.scope }, auth=auth) content_response.raise_for_status() repositories = requests.get(repositories_list_endpoint, auth=BearerTokenAuth( content_response.json()["token"])) repositories.raise_for_status() return repositories
def mount_blob(self, blob, basic_auth): """Try to mount the blob with the provided credentials.""" mount_url = f"/v2/test-2/blobs/uploads/?from=test-1&mount={blob.digest}" url = urljoin(self.cfg.get_base_url(), mount_url) if TOKEN_AUTH_DISABLED: auth = basic_auth else: response = requests.post(url, auth=basic_auth) assert response.status_code == 401 authenticate_header = response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) response = requests.get( queries.realm, params={ "service": queries.service, "scope": [queries.scope, "repository:test-1:pull"], }, auth=basic_auth, ) response.raise_for_status() token = response.json()["token"] auth = BearerTokenAuth(token) return requests.post(url, auth=auth), auth
def test_create_empty_blob_on_the_fly(self): """ Test if empty blob getscreated and served on the fly. """ blob_path = "/v2/{}/blobs/{}".format( self.distribution_with_repo.base_path, EMPTY_BLOB) empty_blob_url = urljoin(self.cfg.get_base_url(), blob_path) if TOKEN_AUTH_DISABLED: auth = () else: with self.assertRaises(requests.HTTPError) as cm: requests.get(empty_blob_url).raise_for_status() content_response = cm.exception.response self.assertEqual(content_response.status_code, 401) authenticate_header = content_response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) content_response = requests.get(queries.realm, params={ "service": queries.service, "scope": queries.scope }) content_response.raise_for_status() auth = BearerTokenAuth(content_response.json()["token"]) content_response = requests.get(empty_blob_url, auth=auth) content_response.raise_for_status() # calculate digest of the payload digest = hashlib.sha256(content_response.content).hexdigest() # compare with the digest returned in the response header header_digest = content_response.headers[ "docker-content-digest"].split(":")[1] self.assertEqual(digest, header_digest)
def test_api_performes_schema_conversion(self): """Verify pull via token with accepted content type.""" image_path = "/v2/{}/manifests/{}".format( self.distribution_with_repo.base_path, "latest") latest_image_url = urljoin(self.cfg.get_base_url(), image_path) if TOKEN_AUTH_DISABLED: auth = () else: with self.assertRaises(requests.HTTPError) as cm: self.client.get(latest_image_url, headers={"Accept": MEDIA_TYPE.MANIFEST_V1}) content_response = cm.exception.response self.assertEqual(content_response.status_code, 401) authenticate_header = content_response.headers["Www-Authenticate"] queries = AuthenticationHeaderQueries(authenticate_header) content_response = requests.get(queries.realm, params={ "service": queries.service, "scope": queries.scope }) content_response.raise_for_status() token = content_response.json()["token"] auth = BearerTokenAuth(token) content_response = requests.get( latest_image_url, auth=auth, headers={"Accept": MEDIA_TYPE.MANIFEST_V1}, ) content_response.raise_for_status() base_content_type = content_response.headers["Content-Type"].split( ";")[0] self.assertIn(base_content_type, {MEDIA_TYPE.MANIFEST_V1, MEDIA_TYPE.MANIFEST_V1_SIGNED}) header_digest = content_response.headers["Docker-Content-Digest"] converted_manifest = json.loads(content_response.content) converted_manifest.pop("signatures") manifest_string = json.dumps(converted_manifest, indent=3, sort_keys=True, separators=(",", ": ")).encode("utf-8") # the header digest should be equal to the SHA256 hash computed from # a manifest without signatures computed_digest = hashlib.sha256(manifest_string).hexdigest() self.assertEqual(computed_digest, header_digest.split(":")[1], "The manifest digests are not equal")