fix(pelican): require authentication on the federation routes - #264
Merged
Conversation
Every route under /pelican was mounted with no authentication and no authorization, so any caller reaching the port could browse a namespace, stream files out of the federation and attach resources to a package. PELICAN_ENABLED only decided whether the router was mounted, so the routes were open on every deployment that had Pelican switched on. Gate the router with the read-tier dependency and give /import-metadata the write-tier one used by the rest of the registration routes. The read gate lives on the router rather than on each route so a Pelican route added later cannot ship open the same way. Closes #261
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #261.
What was wrong
The five routes under
/pelicanwere mounted with no authentication and no authorization. The module never importedDepends, the router carried nodependencies, and the only middlewares on the application are correlation IDs and CORS, so nothing gated them anywhere along the path.Verified against the real application rather than by reading the code — with
PELICAN_ENABLED=trueand noAuthorizationheader,GET /pelican/federationsreturned200and the federation list. The same request with the router unmounted returns404, which is what rules out the response coming from an unmatched path.Anyone able to reach the port could enumerate a namespace, stream files out of the configured federation via
/download, and attach resources to an existing package via/import-metadata, which writes.PELICAN_ENABLEDhid the problem rather than solving it: it decides whether the router is mounted, so the routes were closed only on deployments that had Pelican switched off entirely.The change
dependencies=[Depends(get_user_for_read_operation)], covering all five routes./import-metadataadditionally takesget_user_for_write_operation, the dependency used by every other route that writes to the catalog.The read gate is declared on the router rather than repeated on each route deliberately. This bug happened because five routes were added and the authorization was missed on all five; a router-level dependency means a sixth route inherits it instead of shipping open.
No new mechanism was introduced — both dependencies already existed in
api/services/auth_services/authorization_service.py. The docstring ofget_user_for_read_operationalready described itself as available for exactly this case.Verification
Against the real app (
api.main):PELICAN_ENABLED=false, no tokenPELICAN_ENABLED=true, no token, before this changePELICAN_ENABLED=true, no token, after this changePELICAN_ENABLED=true, valid tokenSeven new cases in
tests/test_pelican_routes.py: anonymous caller rejected, authenticated user with no role tier rejected, viewer allowed to read, viewer rejected on/import-metadata, writer allowed. On/browseand/downloadthe tests also assert the Pelican service is never called, confirming the request is cut before it reaches the federation.Local gate:
blackandflake8clean, 1273 tests pass. Three failures intests/repositories/test_catalog_settings.pyandtests/test_publi_env.pypredate this branch — they reproduce on a cleanmainand are caused by a local.envbeing picked up by the settings tests, which does not exist in CI.Backwards compatibility
Callers of
/pelican/*must now send a bearer token and hold a viewer, writer or admin role on the endpoint — writer or admin for/import-metadata. An anonymous request that used to succeed returns 401, and an authenticated user without a role tier gets 403.No web UI code calls these routes, so the admin console is unaffected. Scripts and notebooks that reached them without a token need updating. Deployments running with
PELICAN_ENABLEDunset are unaffected.