Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use pypitoken to generate, check, introspect tokens #9264

Closed
wants to merge 3 commits into from

Conversation

ewjoachim
Copy link
Contributor

@ewjoachim ewjoachim commented Mar 19, 2021

Fixes #9184
Fixes #9018

Ah, a PR where we remove code without removing features, I love those :D

So, normally after this PR, the warehouse codebase won't know about the format of the caveats anymore, and we should be able to add new restriction types with much more ease :)

Comment on lines -72 to -100
def test_find_userid_no_macaroon(self, macaroon_service):
assert macaroon_service.find_userid(None) is None
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MacaroonService.find_userid method was never called with None. I'm not sure it makes sense to leave this codepath.

Comment on lines 152 to 188
def test_deserialize_raw_macaroon_when_none(self, macaroon_service):
raw_macaroon = pretend.stub()
macaroon_service._extract_raw_macaroon = pretend.call_recorder(lambda a: None)

with pytest.raises(services.InvalidMacaroon):
macaroon_service._deserialize_raw_macaroon(raw_macaroon)

assert macaroon_service._extract_raw_macaroon.calls == [
pretend.call(raw_macaroon),
]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_extract_raw_macaroon doesn't exist anymore, and the load function will raise when no token is extracted, and test_deserialize_raw_macaroon tests that.

Comment on lines 163 to 210
@pytest.mark.parametrize(
"exception",
[
IndexError,
TypeError,
UnicodeDecodeError,
ValueError,
binascii.Error,
struct.error,
MacaroonDeserializationException,
Exception, # https://github.com/ecordell/pymacaroons/issues/50
],
)
def test_deserialize_raw_macaroon(self, monkeypatch, macaroon_service, exception):
raw_macaroon = pretend.stub()
macaroon_service._extract_raw_macaroon = pretend.call_recorder(
lambda a: raw_macaroon
)
monkeypatch.setattr(
pymacaroons.Macaroon, "deserialize", pretend.raiser(exception)
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -431,6 +431,7 @@ def test_validate_token_scope_valid_user(self):
)

assert form.validate()
assert form.validated_restrictions == {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't tested, I added a test, it's free :)

Comment on lines +112 to +127
identifier = uuid.uuid4()
key = _generate_key()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous flow was

  1. we generate the caveats object
  2. we create the database macaroon, letting the DB choose the identifier and have the key computed by a python function set as model default
  3. we read the id and key from the object after the DB flush
  4. we generate the pymacaroons object from those

Because we want pypitoken to generate the caveats object and pypitoken needs the key and id, the new flow is:

  1. we generate a random key and id
  2. we generate the pypitoken object
  3. we extract the caveats
  4. we create the database macaroon with the already computer key, id and caveats

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above the model field should be updated (it discusses having the default factory in database vs python).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(done)

raise InvalidMacaroon("malformed macaroon")
return pypitoken.Token.load(raw_macaroon)
except pypitoken.LoaderError as exc:
raise InvalidMacaroon(str(exc))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's one of the promises of the pypitoken API that the exception message is displayable to the user (same when calling check())

@ewjoachim
Copy link
Contributor Author

PR updated following the merge of the token scanning feature :)

@@ -496,7 +496,7 @@ <h3 id="apitoken">{{ apitoken() }}</h3>
Where you edit or add these values will depend on your individual use case. For example, some users may need to edit <a href="{{ pypirc_href }}" title="{{ title }}" target="_blank" rel="noopener">their <code>.pypirc</code> file</a>, while others may need to update their CI configuration file (e.g. <a href="{{ travis_href }}" title="{{ title }}" target="_blank" rel="noopener"><code>.travis.yml</code> if you are using Travis</a>).
{% endtrans %}
</p>
<p>{% trans %}Advanced users may wish to inspect their token by decoding it with base64, and checking the output against the unique identifier displayed on PyPI.{% endtrans %}</p>
<p>{% trans trimmed pypitoken_href='https://pypitoken.readthedocs.io/', title=gettext('External link') %}Advanced users may wish to inspect their token by decoding it with <a href="{{ pypitoken_href }}" title="{{ title }}" target="_blank" rel="noopener">the <code>pypitoken</code> library</a>, and checking the identifier against the unique identifier displayed on PyPI. Please make sure to keep your token identifier private, though. Knowing just the identifier of a token is not enough to impersonate the user, but it's sufficient to have a token be disabled.{% endtrans %}</p>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a little bit more info than strictly required but I thought it was interesting :)

@ewjoachim
Copy link
Contributor Author

Rebased but blocked by #10230

@ewjoachim ewjoachim force-pushed the pypitoken branch 3 times, most recently from 735423f to 9ee2782 Compare October 26, 2021 21:28
@miketheman miketheman added the tokens Issues relating to API tokens label Jan 18, 2024
@ewjoachim ewjoachim closed this Feb 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tokens Issues relating to API tokens
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Managing API tokens with pypitoken Improve "invalid macaroon signature" error
3 participants