Skip to content

Add per-user Cargo token authentication - #40

Merged
dralley merged 1 commit into
pulp:mainfrom
aKlimau:add-auth
Jul 29, 2026
Merged

Add per-user Cargo token authentication#40
dralley merged 1 commit into
pulp:mainfrom
aKlimau:add-auth

Conversation

@aKlimau

@aKlimau aKlimau commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Replaces the hardcoded stub token with per-user Cargo token authentication. Users create tokens via the REST API (POST /api/v3/cargo/tokens/), then use them with cargo login / cargo publish / cargo yank as normal. Tokens are stored as SHA-256 hashes and scoped to the requesting user.

closes: #24

@aKlimau
aKlimau marked this pull request as draft July 8, 2026 15:40
@aKlimau
aKlimau force-pushed the add-auth branch 5 times, most recently from f23fca6 to 5b349bf Compare July 15, 2026 14:34
@aKlimau
aKlimau marked this pull request as ready for review July 15, 2026 14:35
model = models.RustCargoToken
fields = core_serializers.ModelSerializer.Meta.fields + (
"name",
"token",

@dralley dralley Jul 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This isn't displaying the token values to any API user, right? Even the user that created it should probably not be able to see it again after initial creation.

It also doesn't map directly to any field name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, in this implementation there is no way to retrieve token value. It's shown just once, right after creation in the response. So this field exists only for this one response. Any reads on this field will return None.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So you're saying that the token value is filled out on creation, but afterwards (list, detail, etc.) it shows as None?

Do you think it would be better to use two serializers so that the field is ommitted entirely in the post-creation lookup cases, rather than set null?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah I guess you are right, that makes more sense

Comment thread pulp_rust/app/views.py
# Authentication disabled for now
authentication_classes = []
permission_classes = []
authentication_classes = [CargoTokenAuthentication]

@dralley dralley Jul 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this prevent users without tokens from downloading packages, yes? If so, I feel like that needs to at least be configurable behavior, normally there's no requirement to pass a token just to download packages from a registry, though the use case is permitted

@aKlimau aKlimau Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is a get_permission() override in that class, that just returns [] for GET requests so unauthenticated users are still able to download crates.

assert response.status_code == 403
errors = response.json()["errors"]
assert any("authorization token" in e["detail"] for e in errors)
unyank_url = urljoin(base, "api/v1/crates/fake/0.0.1/unyank")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should probably create a helper for the yank API, or else use the bindings


response = cargo_api_request("GET", urljoin(base, "me"))
assert response.status_code == 403
response = cargo_api_request("GET", urljoin(base, "me"), headers={"Authorization": token.token})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same with this API

@dralley

dralley commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

At minimum this section of the docs needs updating. But probably others do as well. https://github.com/pulp/pulp_rust/blob/main/docs/user/guides/private-registry.md#authentication

edit: also

https://github.com/pulp/pulp_rust/blob/main/docs/index.md#features

And maybe authorization should have it's own separate document to be referenced in the other docs. I'm not sure, probably depends on how much detail we need.

It should be clear what auth Pulp supports and how it differs from crates.io, e.g. no package ownership at the moment.

@aKlimau
aKlimau force-pushed the add-auth branch 3 times, most recently from ceede98 to fce4315 Compare July 29, 2026 10:54
@github-actions github-actions Bot removed the no-issue label Jul 29, 2026
@dralley

dralley commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Please make changes in temporary commits so that it's easier to review what changed, it can be squashed at the end.

closes: pulp#24

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread pulp_rust/app/serializers.py Outdated

class Meta:
model = models.RustCargoToken
fields = core_serializers.ModelSerializer.Meta.fields + ("name", "last_used")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The name and last_used fields don't seem to be declared? I recall them being here, maybe they got lost in the shuffle?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I actually do not recall them being there, it's possible that claude removed them while rolling back some of the changes. If they actually were there, it must have been for the help text

Comment thread pulp_rust/app/viewsets.py Outdated
],
}

def get_serializer_class(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this wouldn't be necessary?

@dralley
dralley merged commit 5e4e272 into pulp:main Jul 29, 2026
20 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Cargo Authentication / Authorization protocols

2 participants