Skip to content

Commit 909198a

Browse files
authored
Merge pull request #133 from ewjoachim/new-format
2 parents beb9548 + d4d04ae commit 909198a

25 files changed

+2315
-1371
lines changed

.github/workflows/ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ jobs:
2222
script: tests
2323
- python_version: "3.10"
2424
script: tests
25+
- python_version: "3.11"
26+
script: tests
2527

2628
name: "py${{ matrix.python_version }} / ${{ matrix.script }}"
2729
runs-on: ubuntu-latest

.readthedocs.yml

+9-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ version: 2
88
sphinx:
99
fail_on_warning: true
1010

11-
python:
12-
install:
13-
- requirements: docs/requirements.txt
11+
build:
12+
os: ubuntu-20.04
13+
tools:
14+
python: '3.10'
15+
jobs:
16+
post_install:
17+
- pip install -U poetry
18+
- poetry config virtualenvs.create false
19+
- poetry install --with docs

CONTRIBUTING.rst

-15
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,6 @@ Build with:
8282
$ tox -e docs
8383
$ python -m webbrowser docs/_build/html/index.html
8484
85-
Run spell checking on the documentation (optional):
86-
87-
.. code-block:: console
88-
89-
$ sudo apt install enchant
90-
$ tox -e docs-spelling
91-
92-
Because of outdated software and version incompatibilities, spell checking is not
93-
checked in the CI, and we don't require people to run it in their PR. Though, it's
94-
always a nice thing to do. Feel free to include any spell fix in your PR, even if it's
95-
not related to your PR (but please put it in a dedicated commit).
96-
97-
If you need to add words to the spell checking dictionary, it's in
98-
``docs/spelling_wordlist.txt``. Make sure the file is alphabetically sorted.
99-
10085
If Sphinx's console output is localized and you would rather have it in English,
10186
use the environment variable ``LC_ALL=C.utf-8`` (either exported or attached to the
10287
``tox`` process)

README.rst

+6-3
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,15 @@ Here's an example:
5757
token = pypitoken.Token.load("pypi-foobartoken")
5858
5959
print(token.restrictions)
60-
# [NoopRestriction()]
60+
# [ProjectIDsRestriction(project_ids=["00000000-0000-0000-0000-000000000000"])]
6161
62-
token.restrict(projects=["requests"])
62+
token.restrict(project_names=["requests"])
6363
6464
print(token.restrictions)
65-
# [NoopRestriction(), ProjectsRestriction(projects=["requests"])]
65+
# [
66+
# ProjectIDsRestriction(project_ids=["00000000-0000-0000-0000-000000000000"]),
67+
# ProjectNamesRestriction(project_names=["requests"]),
68+
# ]
6669
6770
token.dump()
6871
# pypi-newfoobartoken

docs/conf.py

-11
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,8 @@
3434
extensions = [
3535
"sphinx.ext.napoleon",
3636
"sphinx.ext.autodoc",
37-
"sphinx_autodoc_typehints",
3837
"sphinx_github_changelog",
3938
]
40-
try:
41-
import sphinxcontrib.spelling # noqa
42-
except ImportError:
43-
pass
44-
else:
45-
extensions.append("sphinxcontrib.spelling")
4639

4740
# Add any paths that contain templates here, relative to this directory.
4841
templates_path = ["_templates"]
@@ -60,10 +53,6 @@
6053
# https://github.com/sphinx-doc/sphinx/issues/7418
6154
suppress_warnings = ["ref.term"]
6255

63-
# -- Spell check -------------------------------------------------------------
64-
65-
spelling_word_list_filename = "spelling_wordlist.txt"
66-
6756
# -- Options for HTML output -------------------------------------------------
6857

6958
# The theme to use for HTML and HTML Help pages. See the documentation for

docs/discussions.rst

+68-54
Original file line numberDiff line numberDiff line change
@@ -64,26 +64,50 @@ then from the database, the key and user are extracted. The macaroon is checked
6464
the key and caveats are checked against the upload information. If the macaroon is
6565
valid, then PyPI checks if the user has upload rights on the package, and then proceeds.
6666

67-
The caveats are json-encoded strings, and as of May 2022, they come in 3 flavors:
68-
69-
- ``"{"version": 1, "permissions": "user"}"`` (note: it's the string ``"user"`` here,
70-
not a placeholder for the username), which is always met. It's represented in this
71-
library by the class `NoopRestriction`,
72-
- ``"{"version": 1, "permissions": {"projects": ["<project name>", ...]}}"`` (note:
73-
``"<project name>"`` is a placeholder here). It's met if the project we upload
74-
is among the ones listed in the caveats. It's represented by the
75-
`ProjectsRestriction`.
76-
- ``"{"nbf": <timestamp>, "exp": <timestamp>}"``. It's met if we try uploading
77-
the project between ``nbf`` (included) and ``exp`` (excluded). It's
78-
represented by the
79-
`DateRestriction`.
80-
81-
The projects restriction that PyPI implements is limited to:
82-
83-
- a single project per token (even if the restriction format allows multiple projects)
84-
- creating tokens for existing projects you own (even though it's perfectly possible
85-
to create tokens for projects that don't exist yet, or for which you don't have
86-
upload permission yet).
67+
The caveats are json-encoded strings, and as of October 2022, they come in 7 flavors:
68+
4 new caveats and 3 legacy caveats.
69+
The legacy caveat are represented by classes prefixed by ``Legacy``.
70+
71+
The types of caveats are:
72+
73+
- ``[0, <timestamp: int>, <timestamp: int>]`` is met if we try uploading the
74+
project between ``nbf`` (included) and ``exp`` (excluded). It's represented
75+
by the class `DateRestriction`. Legacy format is ``"{"nbf": <timestamp: int>,
76+
"exp": <timestamp: int>}"`` and the corresponding class is
77+
`LegacyDateRestriction`.
78+
79+
- ``[1, [<project_name: str>, ...]]`` is met if the project we upload is among
80+
the ones listed in the caveats. It's represented by the class
81+
`ProjectNamesRestriction`. Legacy format is ``{"version": 1, "permissions":
82+
{"projects": [<project_name: str>, ...]}}`` and the corresponding class is
83+
`LegacyProjectNamesRestriction`.
84+
85+
- ``[2, [<project_id: str>, ...]]`` is met if the project we upload is among
86+
the ones listed in the caveats. It's represented by the class
87+
`ProjectIDsRestriction`. There is no legacy equivalent.
88+
89+
- ``[3, <user_id: str>]`` is met if the user triggering the upload is the
90+
one associated whit that UUID. It's represented by the class
91+
`UserIDRestriction`
92+
93+
- ``{"version": 1, "permissions": "user"}`` which is always met. It's
94+
represented in this library by the class `LegacyNoopRestriction`.
95+
96+
Within the PyPI website as of October 2022, one may generate tokens. Those tokens are
97+
either associated with a single project or with the how account.
98+
99+
If they are associated to a single project, they will come with a
100+
`ProjectNamesRestriction` with a single value (the normalized name of the project)
101+
and a `ProjectIDsRestriction` with a single value (the ID if the project).
102+
103+
If they are not associated with a project, they will come with a `UserIDRestriction`
104+
associated with the user who generated the token.
105+
106+
If they were generated before August 2022, they may come with legacy restrictions.
107+
108+
There is currently no way in the website's interface to generate tokens with
109+
a `DateRestriction` or associated with multiple projects, but if such a token
110+
was received, PyPI would interpret it correctly.
87111

88112
.. note::
89113

@@ -93,27 +117,27 @@ The projects restriction that PyPI implements is limited to:
93117
Do we really need an abstraction layer over PyMacaroons?
94118
--------------------------------------------------------
95119

96-
Yes ? No ? Maybe ? What's clear is that the implementation in PyPI is adding some
97-
unnecessary complexity, and the community would benefit from a single source of
98-
macaroons, so that the same codebase will be responsible for serializing and
99-
deserializing the custom PyPI token restrictions.
120+
Yes ? No ? Maybe ? What's clear is that the community would benefit from a
121+
single source of macaroons, so that the same codebase would be responsible for
122+
serializing and deserializing the custom PyPI token restrictions.
100123

101124
Can we add new restrictions?
102125
----------------------------
103126

104-
As long as PyPI doesn't use ``pypitoken`` to generate tokens, it's not very useful
105-
to implement new restrictions. But once we get it merged, then we'll want to add plenty
106-
of new and smart restrictions (based on time, upload file name and hash, etc)
127+
As long as PyPI doesn't use ``pypitoken`` to generate tokens, it's not very
128+
useful to implement new restrictions here that would not already be supported
129+
by PyPI. We're doing our best to have this library follow developments of PyPI
130+
itself, and provide feature-parity.
107131

108-
The restrictions planned for the future are:
132+
In discussions around PyPI development, the following restrictions that have
133+
been mentionned:
109134

110135
- Version-based restriction
111136
- Filename-based restriction
112137
- Hash-sum-based restriction
113138
- IP-based restriction
114139
- One-time-use restriction (this will require Warehouse to remember a value)
115140
- Somehow restricting to uploads coming from a given project's CI
116-
- ...? (ideas welcome)
117141

118142
Most of those were initially discussed in the `Warehouse tracker`__.
119143

@@ -132,10 +156,10 @@ Is this library a part of PyPI?
132156
It's being developper externally. The initiator of this project is a member of the
133157
Python Packaging Authority (PyPA) and PyPI moderator, but not an admin nor a committer.
134158

135-
As of today, the library is not part of the PyPA. Once this lib is done, it's planned
136-
to apply for this lib to be added to PyPA.
159+
There was an offer__ for this library to be adopted by PyPA, but it didn't gain any
160+
traction
137161

138-
.. _Macaroon recipe:
162+
.. __: https://discuss.python.org/t/pypitoken-a-library-for-generating-and-manipulating-pypi-tokens/7572
139163

140164
Why is there a noop restriction?
141165
--------------------------------
@@ -144,32 +168,20 @@ Good question. The author is not sure either. In the original discussions in War
144168
the idea was to have 2 types of tokens: "user" tokens and "projects" tokens. But even
145169
without restrictions, tokens are already scoped to a specific user, so adding a "user"
146170
restriction actually changes nothing, thus why it's implemented in ``pypitoken`` as a
147-
`NoopRestriction`.
171+
`LegacyNoopRestriction`.
148172

149-
Though it's not been tested yet, tokens without restrictions should work the same
150-
as tokens with a noop restriction (or, for what it's worth, token with multiple noop
151-
restrictions).
152-
153-
Should we have multiple restrictions in a single caveat?
154-
--------------------------------------------------------
173+
Tokens without restrictions work the same as tokens with a noop restriction
174+
(or, for what it's worth, token with multiple noop restrictions).
155175

156-
This is a real question we may encounter when we'll have multiple types of restrictions:
157-
when we want to apply a restriction to multiple aspects, should we implement all of them
158-
in a single caveat modelling all the aspects, or should each restriction be its own
159-
caveat?
160-
161-
It can be noted that "user" (noop) restrictions and "projects" restrictions are not
162-
compatible, so this would tend to indicate it was not planned for multiple restrictions
163-
to be grouped into a single caveat.
164-
165-
Having multiple restrictions in a single json payload makes it harder to check whether
166-
it's valid or not (at least, while the general format of the json payloads is not
167-
unified). No definitive answer is given at this time.
176+
Note that when the restrictions were re-worked in PyPI in Summer 2022, the
177+
"user" caveat was actually associated with a check that the request was
178+
originated by the corresponding user. This is mainly relevant around OpenID
179+
Connect use-cases.
168180

169181
What does "normalized name" mean?
170182
---------------------------------
171183

172-
Throughout the doc, the term "normalized name" for a project is regularily used.
184+
Throughout the doc, the term "normalized name" for a project is regularly used.
173185
This is because some characters are synonymous in a project name, so in order to match
174186
a project name, we need to put it to canonical form first.
175187

@@ -206,6 +218,8 @@ Adding restrictions yourself on existing tokens have consequences on those eleme
206218
This way, your PyPI account page will still be a good place to track all of your
207219
existing tokens, and you will be able to follow each of them easily.
208220

221+
.. _Macaroon recipe:
222+
209223
All this talking about Macaroons, I'm hungry now!
210224
-------------------------------------------------
211225

@@ -222,12 +236,12 @@ Ingredients:
222236
Steps:
223237

224238
1. Preheat oven to 50°C.
225-
2. Spread the gound almonds on a baking sheet, put in oven for 10 to 15 minutes.
239+
2. Spread the ground almonds on a baking sheet, put in oven for 10 to 15 minutes.
226240
3. Remove it from oven, let it cool and mix with sugar.
227241
4. Whip the egg whites stiff and add a few drops of bitter almond.
228242
5. Using a rubber spatula, fold the egg whites into the sugar & almond batter.
229243
6. Pour the batter into a piping bag with a ribbed nozzle.
230-
7. Form the macaroons on baking paper and leave them to rest for 2h at ambiant
244+
7. Form the macaroons on baking paper and leave them to rest for 2h at ambient
231245
temperature.
232246
8. Preheat oven to 190°C.
233247
9. Lower the oven to 180°C, and put the macaroons in for 3 minutes, then 15 minutes at

0 commit comments

Comments
 (0)