Merge PRs #174 #175 and #172 into unstable/v1

This commit is contained in:
Sviatoslav Sydorenko 2023-08-10 18:57:00 +02:00
commit ade57f54dc
No known key found for this signature in database
GPG key ID: 9345E8FEA89CA455
3 changed files with 61 additions and 12 deletions

View file

@ -25,7 +25,8 @@ tag, or opt-in to [use a full Git commit SHA] and Dependabot.
### Trusted publishing ### Trusted publishing
> **NOTE**: Trusted publishing is sometimes referred to by its > [!NOTE]
> Trusted publishing is sometimes referred to by its
> underlying technology -- OpenID Connect, or OIDC for short. > underlying technology -- OpenID Connect, or OIDC for short.
> If you see references to "OIDC publishing" in the context of PyPI, > If you see references to "OIDC publishing" in the context of PyPI,
> this is what they're referring to. > this is what they're referring to.
@ -61,10 +62,11 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@release/v1
``` ```
> **Pro tip**: instead of using branch pointers, like `unstable/v1`, pin > [!NOTE]
versions of Actions that you use to tagged versions or sha1 commit identifiers. > Pro tip: instead of using branch pointers, like `unstable/v1`, pin versions of
This will make your workflows more secure and better reproducible, saving you > Actions that you use to tagged versions or sha1 commit identifiers.
from sudden and unpleasant surprises. > This will make your workflows more secure and better reproducible, saving you
> from sudden and unpleasant surprises.
Other indices that support trusted publishing can also be used, like TestPyPI: Other indices that support trusted publishing can also be used, like TestPyPI:
@ -76,7 +78,8 @@ Other indices that support trusted publishing can also be used, like TestPyPI:
``` ```
_(don't forget to update the environment name to `testpypi` or similar!)_ _(don't forget to update the environment name to `testpypi` or similar!)_
> **Pro tip**: only set the `id-token: write` permission in the job that does > [!NOTE]
> Pro tip: only set the `id-token: write` permission in the job that does
> publishing, not globally. Also, try to separate building from publishing > publishing, not globally. Also, try to separate building from publishing
> — this makes sure that any scripts maliciously injected into the build > — this makes sure that any scripts maliciously injected into the build
> or test environment won't be able to elevate privileges while flying under > or test environment won't be able to elevate privileges while flying under
@ -96,7 +99,8 @@ This GitHub Action [has nothing to do with _building package
distributions_]. Users are responsible for preparing dists for upload distributions_]. Users are responsible for preparing dists for upload
by putting them into the `dist/` folder prior to running this Action. by putting them into the `dist/` folder prior to running this Action.
> **IMPORTANT**: Since this GitHub Action is docker-based, it can only > [!IMPORTANT]
> Since this GitHub Action is docker-based, it can only
> be used from within GNU/Linux based jobs in GitHub Actions CI/CD > be used from within GNU/Linux based jobs in GitHub Actions CI/CD
> workflows. This is by design and is unlikely to change due to a number > workflows. This is by design and is unlikely to change due to a number
> of considerations we rely on. > of considerations we rely on.
@ -187,9 +191,10 @@ default) setting as follows:
skip-existing: true skip-existing: true
``` ```
> **Pro tip**: try to avoid enabling this setting where possible. If you > [!NOTE]
have steps for publishing to both PyPI and TestPyPI, consider only using > Pro tip: try to avoid enabling this setting where possible. If you
it for the latter, having the former fail loudly on duplicates. > have steps for publishing to both PyPI and TestPyPI, consider only using
> it for the latter, having the former fail loudly on duplicates.
### For Debugging ### For Debugging

View file

@ -1,3 +1,5 @@
import base64
import json
import os import os
import sys import sys
from http import HTTPStatus from http import HTTPStatus
@ -50,6 +52,25 @@ _SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE = """
Token request failed: the server refused the request for the following reasons: Token request failed: the server refused the request for the following reasons:
{reasons} {reasons}
This generally indicates a trusted publisher configuration error, but could
also indicate an internal error on GitHub or PyPI's part.
{rendered_claims}
"""
_RENDERED_CLAIMS = """
The claims rendered below are **for debugging purposes only**. You should **not**
use them to configure a trusted publisher unless they already match your expectations.
If a claim is not present in the claim set, then it is rendered as `MISSING`.
* `sub`: `{sub}`
* `repository`: `{repository}`
* `repository_owner`: `{repository_owner}`
* `repository_owner_id`: `{repository_owner_id}`
* `job_workflow_ref`: `{job_workflow_ref}`
* `ref`: `{ref}`
""" """
# Rendered if the package index's token response isn't valid JSON. # Rendered if the package index's token response isn't valid JSON.
@ -121,6 +142,23 @@ def assert_successful_audience_call(resp: requests.Response, domain: str):
) )
def render_claims(token: str) -> str:
_, payload, _ = token.split(".", 2)
claims = json.loads(base64.urlsafe_b64decode(payload))
def _get(name: str) -> str: # noqa: WPS430
return claims.get(name, "MISSING")
return _RENDERED_CLAIMS.format(
sub=_get("sub"),
repository=_get("repository"),
repository_owner=_get("repository_owner"),
repository_owner_id=_get("repository_owner_id"),
job_workflow_ref=_get("job_workflow_ref"),
ref=_get("ref"),
)
repository_url = get_normalized_input("repository-url") repository_url = get_normalized_input("repository-url")
repository_domain = urlparse(repository_url).netloc repository_domain = urlparse(repository_url).netloc
token_exchange_url = f"https://{repository_domain}/_/oidc/github/mint-token" token_exchange_url = f"https://{repository_domain}/_/oidc/github/mint-token"
@ -165,7 +203,13 @@ if not mint_token_resp.ok:
for error in mint_token_payload["errors"] for error in mint_token_payload["errors"]
) )
die(_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE.format(reasons=reasons)) rendered_claims = render_claims(oidc_token)
die(
_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE.format(
reasons=reasons, rendered_claims=rendered_claims,
),
)
pypi_token = mint_token_payload.get("token") pypi_token = mint_token_payload.get("token")
if pypi_token is None: if pypi_token is None:

View file

@ -14,7 +14,7 @@ cffi==1.15.1
# via cryptography # via cryptography
charset-normalizer==3.2.0 charset-normalizer==3.2.0
# via requests # via requests
cryptography==41.0.2 cryptography==41.0.3
# via secretstorage # via secretstorage
docutils==0.20.1 docutils==0.20.1
# via readme-renderer # via readme-renderer