Skip to content

fix(server): scope personal access token deletion to the caller's tenant - #375

Open
yuvrajjsingh0 wants to merge 1 commit into
mainfrom
fix/s7-scope-token-deletion
Open

fix(server): scope personal access token deletion to the caller's tenant#375
yuvrajjsingh0 wants to merge 1 commit into
mainfrom
fix/s7-scope-token-deletion

Conversation

@yuvrajjsingh0

@yuvrajjsingh0 yuvrajjsingh0 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Problem

delete_token resolved the caller's organisation and application, then threw both away:

let (_organisation, _application) = require_org_and_app(...)?;
...
diesel::delete(user_credentials_table.filter(uid.eq(*client_id)))

The delete predicate was the client id alone. A client id is an identifier, not a secret — it is returned by the create and list APIs and stored by clients. So the authorisation check ran against one tenant while the write landed on any tenant: an authorised admin or owner who learned another organisation's client id could delete that organisation's credential.

list_tokens already filtered on username + organisation + application, so the two paths disagreed about what a caller may touch.

Audit reference: S7.

Change

  • Narrow the delete predicate to the organisation and application whose authorisation was actually checked.
  • Return 404 when no row matched. The response is identical whether the token does not exist or belongs to another tenant, so the endpoint cannot be used to probe for live client ids.
  • Extract the predicate into scoped_token_delete so the scoping is directly testable without a database.

Deliberately not scoped by username

The audit suggested optionally also scoping by owning username. I did not do that: this endpoint is gated to org owner/admin and app admin, and revoking a compromised token that belongs to another user in your own tenant is a legitimate administrative capability. Narrowing to username would remove it. The cross-tenant hole is fully closed by the org/app scoping. Happy to add the username filter if you'd rather tokens be strictly self-service — it is a one-line change in scoped_token_delete.

Testing

Two unit tests assert on the generated SQL via diesel::debug_query, so they need no database:

test token::tests::delete_predicate_is_scoped_to_tenant ... ok
test token::tests::delete_predicate_conjoins_its_filters ... ok

Both were verified non-vacuous — restoring the original unscoped predicate fails them with the vulnerable SQL printed:

delete predicate is missing `organisation`, so it is not tenant-scoped:
DELETE FROM "hyperotaserver"."user_credentials" WHERE ("hyperotaserver"."user_credentials"."client_id" = $1)

The second test guards the filters staying AND-joined, so a later edit cannot widen the predicate back out with an OR.

cargo fmt --check and cargo clippy --all-targets -- -Dwarnings pass.

Notes

This operation is not in the Smithy model and the docs only refer to token revocation generically, so no documentation or API-reference regeneration applies.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Personal access token deletion is now limited to the selected organization and application.
    • Attempts to delete nonexistent or out-of-scope tokens now return a “Token not found” response.
    • Tokens belonging to another tenant can no longer be deleted accidentally.

@semanticdiff-com

semanticdiff-com Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  airborne_server/src/token.rs  33% smaller

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1543e04f-3fa7-4855-a428-d84a68e39ac2

📥 Commits

Reviewing files that changed from the base of the PR and between a706e7f and 3fe3bf4.

📒 Files selected for processing (1)
  • airborne_server/src/token.rs

Walkthrough

Token deletion now resolves the organisation and application scope, deletes only matching personal access tokens, and returns NotFound when no token is deleted.

Changes

Tenant-scoped token deletion

Layer / File(s) Summary
Scoped deletion and missing-token handling
airborne_server/src/token.rs
The delete query filters by client, organisation, and application; the handler resolves the tenant scope and returns NotFound("Token not found") when no row matches.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit checks the tenant gate,
Then trims one token from its state.
If none are found beneath the sun,
“Not found!” says Bunny—the job is done.
Scoped and tidy, hop-hop-hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: tenant-scoped personal access token deletion.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/s7-scope-token-deletion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yuvrajjsingh0
yuvrajjsingh0 force-pushed the fix/s7-scope-token-deletion branch from 62e3b0c to 3fe3bf4 Compare July 29, 2026 13:23
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.

1 participant