Skip to content

update: store API keys as a user type in store metadata#1628

Merged
nikhilsinhaparseable merged 5 commits intoparseablehq:mainfrom
nikhilsinhaparseable:key-as-user
Apr 26, 2026
Merged

update: store API keys as a user type in store metadata#1628
nikhilsinhaparseable merged 5 commits intoparseablehq:mainfrom
nikhilsinhaparseable:key-as-user

Conversation

@nikhilsinhaparseable
Copy link
Copy Markdown
Contributor

@nikhilsinhaparseable nikhilsinhaparseable commented Apr 23, 2026

Summary by CodeRabbit

  • Refactor
    • API keys now authenticate by creating short-lived sessions so they follow the same permission checks as regular sessions.
    • API-key-backed users are represented distinctly (shown as method "apikey"), excluded from standard user listings and onboarding counts.
    • API key creation now accepts a set of roles for RBAC-driven permissions.
    • API key persistence and related metastore/storage hooks were removed from the metadata layer; error reporting and HTTP status mapping for API-key flows were refined.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Walkthrough

API-key persistence and metastore APIs were removed; API-key auth now resolves x-api-key into a tenant-scoped UserType::ApiKey, derives permissions from roles, creates a 5-minute ephemeral session inserted into request extensions, and API-key users are excluded from user listings and related counts.

Changes

Cohort / File(s) Summary
API Key Domain Removed
src/apikeys.rs
Removed in-module ApiKey storage/models and metastore-backed CRUD/validation; CreateApiKeyRequest now carries roles: HashSet<String>; ApiKeyError now uses Storage/Rbac variants and renames AnyhowErrorAnyhow.
HTTP Middleware → Session Integration
src/handlers/http/middleware.rs
Replaced direct API-key short-circuit with session-based flow: find_api_key_user resolves ApiKey user, derive permissions from roles, create 5-minute ephemeral session, insert SessionKey in request extensions, and use ApiKeySessionGuard to remove session on drop.
RBAC: User model, serialization & utils
src/rbac/user.rs, src/rbac/utils.rs, src/rbac/mod.rs
Added UserType::ApiKey(Box<ApiKeyUser>), ApiKeyUser model, User::new_api_key, is_api_key/as_api_key, updated serde ordering and prism conversion (method: "apikey"), and adjusted user collection to exclude API-key users.
RBAC: Authorization/role changes
src/rbac/map.rs, src/rbac/role.rs
Expanded global permission matches in Sessions::check_auth (more Actions allowed when resource_type is None); DefaultPrivilege::Writer.resource changed to Option, conditional resource attachment in conversion.
Handlers / RBAC endpoints & flows
src/handlers/http/rbac.rs, src/handlers/http/modal/query/querier_rbac.rs
/users Prism listing now filters API-key users; user creation collision logic excludes OAuth/API-key for userid match; delete_user handles UserType::ApiKey when removing users from groups.
Metastore & Storage API removals
src/metastore/metastore_traits.rs, src/metastore/metastores/object_store_metastore.rs, src/storage/mod.rs, src/storage/object_storage.rs
Removed metastore API-key trait methods and object-store API-key constants/helpers (APIKEYS_ROOT_DIRECTORY, apikey_json_path) and their implementations.
Prism / Home UI count adjustment
src/prism/home/mod.rs
Home checklist user_added computation updated to exclude API-key users from counted users.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/Request
    participant Middleware as HTTP Middleware
    participant KeyLookup as API Key Lookup
    participant RBAC as RBAC Engine
    participant SessionMgr as Session Manager
    participant Handler as Request Handler

    Client->>Middleware: Request + x-api-key header
    Middleware->>KeyLookup: find_api_key_user(tenant, key)
    KeyLookup-->>Middleware: ApiKeyUser (UserType::ApiKey)
    Middleware->>RBAC: Build permissions from roles
    RBAC-->>Middleware: Permission set / RoleBuilder
    Middleware->>SessionMgr: Create ephemeral session (5m)
    SessionMgr-->>Middleware: SessionKey
    Middleware->>Middleware: Insert SessionKey into request extensions
    Middleware->>Handler: Forward request with session
    Handler->>RBAC: Authorize using session permissions
    RBAC-->>Handler: Authorization result
    Handler-->>Client: Response
    Middleware->>SessionMgr: Drop guard removes session
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

for next release

Suggested reviewers

  • nitisht
  • parmesant

Poem

🐇 I hopped through code, a tiny sleuth,
Keys once stored now fade from truth.
Roles whisper rights, a session's song,
Five minutes' breath — then hopped along.
Hooray, the rabbit left the proof!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is entirely empty, missing all required template sections including the description of goals, changes, and testing/documentation checklists. Add a comprehensive description explaining the refactoring goals, key architectural changes (metastore removal, session-based auth, user type integration), and confirm testing and documentation updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 46.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main refactoring: API keys are being migrated from a separate store mechanism to being represented as a user type in the metadata system.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/apikeys.rs (1)

89-103: ⚠️ Potential issue | 🟠 Major

Delegate to RBACError's status code instead of forcing 500.

ApiKeyError::Rbac(_) is currently grouped with storage and anyhow failures, forcing all RBAC errors to return INTERNAL_SERVER_ERROR. However, RBACError's own ResponseError implementation returns appropriate status codes: BAD_REQUEST for validation failures (UserExists, RoleValidationError, etc.), NOT_FOUND for UserDoesNotExist, and only INTERNAL_SERVER_ERROR for actual storage/network faults. This wrapper masks those client-side errors as 500s.

Suggested fix
 impl actix_web::ResponseError for ApiKeyError {
     fn status_code(&self) -> actix_web::http::StatusCode {
         match self {
             ApiKeyError::KeyNotFound(_) => actix_web::http::StatusCode::NOT_FOUND,
             ApiKeyError::DuplicateKeyName(_) => actix_web::http::StatusCode::CONFLICT,
             ApiKeyError::Unauthorized(_) => actix_web::http::StatusCode::FORBIDDEN,
-            ApiKeyError::Storage(_) | ApiKeyError::Rbac(_) | ApiKeyError::Anyhow(_) => {
+            ApiKeyError::Storage(_) | ApiKeyError::Anyhow(_) => {
                 actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
             }
+            ApiKeyError::Rbac(err) => actix_web::ResponseError::status_code(err),
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/apikeys.rs` around lines 89 - 103, ApiKeyError currently maps
ApiKeyError::Rbac(_) to INTERNAL_SERVER_ERROR, masking RBACError's proper HTTP
status; update the ResponseError impl for ApiKeyError so the match arm delegates
to the wrapped RBACError's status_code (e.g., match ApiKeyError::Rbac(inner) =>
inner.status_code()), leaving other arms unchanged and importing/using the
RBACError type if necessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/apikeys.rs`:
- Around line 89-103: ApiKeyError currently maps ApiKeyError::Rbac(_) to
INTERNAL_SERVER_ERROR, masking RBACError's proper HTTP status; update the
ResponseError impl for ApiKeyError so the match arm delegates to the wrapped
RBACError's status_code (e.g., match ApiKeyError::Rbac(inner) =>
inner.status_code()), leaving other arms unchanged and importing/using the
RBACError type if necessary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 318c6373-123f-474b-95d8-b796f4042e9c

📥 Commits

Reviewing files that changed from the base of the PR and between 76ff210 and 03e5367.

📒 Files selected for processing (12)
  • src/apikeys.rs
  • src/handlers/http/middleware.rs
  • src/handlers/http/modal/query/querier_rbac.rs
  • src/handlers/http/rbac.rs
  • src/metastore/metastore_traits.rs
  • src/metastore/metastores/object_store_metastore.rs
  • src/prism/home/mod.rs
  • src/rbac/mod.rs
  • src/rbac/user.rs
  • src/rbac/utils.rs
  • src/storage/mod.rs
  • src/storage/object_storage.rs
💤 Files with no reviewable changes (3)
  • src/storage/object_storage.rs
  • src/metastore/metastore_traits.rs
  • src/storage/mod.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 23, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 26, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/handlers/http/rbac.rs (1)

48-48: Keep UPDATE_LOCK visibility narrow unless external crates must access it.

If this lock is only consumed internally, prefer pub(crate) to avoid exposing a global sync primitive as part of a broader public surface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/handlers/http/rbac.rs` at line 48, The global Mutex named UPDATE_LOCK is
currently declared as public (pub) but should have narrowed visibility; change
its declaration from pub to pub(crate) (i.e., make UPDATE_LOCK pub(crate):
Mutex<()> = Mutex::const_new(()) in rbac.rs) so the synchronization primitive is
only exposed within the crate unless you explicitly need external access; update
any internal references if needed to compile.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/handlers/http/rbac.rs`:
- Line 48: The global Mutex named UPDATE_LOCK is currently declared as public
(pub) but should have narrowed visibility; change its declaration from pub to
pub(crate) (i.e., make UPDATE_LOCK pub(crate): Mutex<()> = Mutex::const_new(())
in rbac.rs) so the synchronization primitive is only exposed within the crate
unless you explicitly need external access; update any internal references if
needed to compile.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0d02ae5b-ee2c-4d35-8a19-f3201c6f61f8

📥 Commits

Reviewing files that changed from the base of the PR and between 906dd06 and 70a483a.

📒 Files selected for processing (1)
  • src/handlers/http/rbac.rs

@nikhilsinhaparseable nikhilsinhaparseable merged commit bd31b0d into parseablehq:main Apr 26, 2026
12 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.

2 participants