Skip to content

feat(authz): ABAC for default_config, context and experiment#910

Merged
ayushjain17 merged 4 commits intomainfrom
auth_z_abac
Mar 17, 2026
Merged

feat(authz): ABAC for default_config, context and experiment#910
ayushjain17 merged 4 commits intomainfrom
auth_z_abac

Conversation

@ayushjain17
Copy link
Copy Markdown
Collaborator

@ayushjain17 ayushjain17 commented Mar 11, 2026

Change log

Added ABAC support for all write APIs for default config, context and experiment - essentially for all those resources which have config as a resource in them

Summary by CodeRabbit

  • New Features

    • Authorization enforcement integrated into context and experiment operations including create, update, delete, move, and bulk management.
    • Reorganized authorization management API with dedicated modules for policy, role, and action group operations.
  • Improvements

    • Enhanced authorization middleware with persistent context tracking and action-level permission validation.

@ayushjain17 ayushjain17 requested a review from a team as a code owner March 11, 2026 10:51
@semanticdiff-com
Copy link
Copy Markdown

semanticdiff-com bot commented Mar 11, 2026

Review changes with  SemanticDiff

Changed Files
File Status
  crates/frontend/src/pages/experiment.rs  97% smaller
  crates/experimentation_platform/src/api/experiment_groups/helpers.rs  64% smaller
  crates/frontend/src/api.rs  60% smaller
  crates/service_utils/src/middlewares/auth_z.rs  28% smaller
  crates/experimentation_platform/src/api/experiments/handlers.rs  19% smaller
  crates/frontend/src/components/authz.rs  14% smaller
  crates/frontend/src/utils.rs  4% smaller
  crates/context_aware_config/src/api/context/handlers.rs  1% smaller
  crates/superposition_types/src/api/authz/casbin.rs  1% smaller
  crates/context_aware_config/src/api/context/helpers.rs  0% smaller
  crates/context_aware_config/src/api/context/operations.rs  0% smaller
  crates/context_aware_config/src/api/default_config/handlers.rs  0% smaller
  crates/experimentation_client/src/lib.rs  0% smaller
  crates/experimentation_platform/src/api/experiments/helpers.rs  0% smaller
  crates/service_utils/src/middlewares/auth_z/authorization.rs  0% smaller
  crates/service_utils/src/middlewares/auth_z/casbin.rs Unsupported file format
  crates/service_utils/src/middlewares/auth_z/casbin/handlers.rs Unsupported file format
  crates/service_utils/src/middlewares/auth_z/no_auth.rs  0% smaller
  crates/superposition_core/src/experiment.rs  0% smaller
  crates/superposition_types/src/database/models/experimentation.rs  0% smaller
  crates/superposition_types/src/lib.rs  0% smaller

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 32752dfe-3ad2-4386-8b25-4e00352bbaee

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Authorization system integration across context-aware config and experimentation handlers. Added authorization checks via AuthZ to create, update, move, delete, and bulk operations. Restructured casbin API into nested modules (policy, role, action_group). Enhanced AuthZ middleware with persistent state and authorization methods. Updated Authorizer trait signature for string reference handling. Added Deref derives to wrapper types.

Changes

Cohort / File(s) Summary
Context-Aware Config Authorization
crates/context_aware_config/src/api/context/handlers.rs, crates/context_aware_config/src/api/context/helpers.rs, crates/context_aware_config/src/api/context/operations.rs
Added authorization helpers (authorize_create, authorize_update, authorize_action, authorize_delete, authorize_bulk) and wired them into handlers. Implemented changed_keys utility for computing differences between override maps. Added get_overrides_from_ctx_id and get_overrides_from_identifier for fetching overrides by context identifiers.
Experimentation Authorization
crates/experimentation_platform/src/api/experiments/handlers.rs, crates/experimentation_platform/src/api/experiments/helpers.rs
Introduced authorize_create and authorize_action helpers for variant and experiment-level authorization checks. Integrated authorization into create, conclude, discard, ramp, pause, resume, and update handlers. Added get_control_overrides_from_exp_id to fetch control variant overrides for authorization validation.
Default Config Authorization
crates/default_config/handlers.rs
Added authorization checks using dereferenced keys in create, update, and delete handlers prior to database operations.
Casbin API Restructuring
crates/frontend/src/api.rs
Reorganized casbin API into nested modules (policy, role, action_group) with domain-aware and non-domain-aware variants. Consolidated related operations under submodules for improved API ergonomics and discoverability.
Frontend Component Updates
crates/frontend/src/components/authz.rs
Updated all casbin API calls to use new nested module paths (list_policies → policy::list, add_policy → policy::add, etc.), maintaining existing data flows and error handling.
AuthZ Middleware Enhancement
crates/service_utils/src/middlewares/auth_z.rs
Added persistent authorization state fields (authz_handler, domain, user, internal_user) to AuthZ struct. Implemented authorize_action and authorize methods for runtime authorization checks. Updated FromRequest to perform authorization validation with internal_user flag handling.
Authorizer Trait Updates
crates/service_utils/src/middlewares/auth_z/authorization.rs, crates/service_utils/src/middlewares/auth_z/casbin.rs, crates/service_utils/src/middlewares/auth_z/no_auth.rs
Changed is_allowed method signature across Authorizer trait and implementations from Option<&[&str]> to Option<&[&String]> for attributes parameter.
Type Definition Enhancement
crates/superposition_types/src/lib.rs
Added Deref and DerefMut trait derives to Cac and Exp generic wrapper types for direct dereferencing support.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Handler
    participant AuthZ
    participant Authorizer
    participant Database
    
    Client->>Handler: POST /create (with overrides)
    Handler->>AuthZ: authorize_create(overrides)
    AuthZ->>Authorizer: is_allowed(resource, action, attributes)
    Authorizer-->>AuthZ: allowed/denied
    AuthZ-->>Handler: Ok() / Forbidden
    alt Authorization Success
        Handler->>Database: Insert new config
        Database-->>Handler: Success
        Handler-->>Client: 200 OK
    else Authorization Failure
        Handler-->>Client: 403 Forbidden
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

enhancement, P1

Suggested reviewers

  • Datron
  • sauraww
  • mahatoankitkumar

Poem

🐰 Authorization gates spring to life,
Keys and overrides in structured strife,
Casbin modules nest so tight,
AuthZ flows authorize what's right!
The rabbit hops through access control, ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding ABAC (Attribute-Based Access Control) authorization support to three resource modules (default_config, context, and experiment). This directly matches the PR objective and the substantial code changes across all affected modules.
Docstring Coverage ✅ Passed Docstring coverage is 81.97% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auth_z_abac
📝 Coding Plan
  • Generate coding plan for human review comments

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 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.

Actionable comments posted: 5

🧹 Nitpick comments (1)
crates/experimentation_platform/src/api/experiments/handlers.rs (1)

136-146: Consider using slice parameter type.

The function works correctly. Minor style suggestion: prefer &[Variant] over &Vec<Variant> for the parameter type, which is more idiomatic in Rust and allows passing slices directly.

♻️ Optional idiomatic fix
 async fn authorize_create(
     auth_z: AuthZ<AuthZActionCreate>,
-    variants: &Vec<Variant>,
+    variants: &[Variant],
 ) -> superposition::Result<()> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/experimentation_platform/src/api/experiments/handlers.rs` around lines
136 - 146, Change the authorize_create signature to accept a slice instead of a
Vec reference (i.e., replace &Vec<Variant> with &[Variant]) and update any
callers to pass slices or vec.as_slice() as needed; inside the function keep all
logic the same (use variants.iter()...) so no other internal changes are
required—this makes the API more idiomatic and allows callers to pass either Vec
or slice directly while still referencing the existing function name
authorize_create and its use of Variant and VariantType.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/context_aware_config/src/api/context/handlers.rs`:
- Around line 193-209: authorize_update reads current overrides
(operations::get_overrides_from_identifier), calls auth_z.authorize_action with
changed_keys and then returns, creating a race where the overrides can change
before the write; change the flow so the read and authorization are bound to the
same locked write path: either perform a SELECT ... FOR UPDATE (or start the
write transaction/lock) before calling auth_z.authorize_action inside
authorize_update, or record the row version/override_id during the locked read
and re-check it immediately before committing (and reject the write if it
changed); apply the same pattern to the other authorization helpers referenced
in the review (the blocks around lines 226-264, 303-316, 619-632, 718-772) so
authorization is always based on the locked/transactional state or fails if the
version changed.

In `@crates/context_aware_config/src/api/context/helpers.rs`:
- Around line 495-516: The changed_keys function uses sentinel Value::String
markers which conflates actual string values with absence; replace the sentinel
logic by comparing Option<&Value> directly so None stays distinct from Some(..).
In changed_keys, collect the union of keys as before, but in the filter use
old_values.get(key) and new_values.get(key) (i.e., Option<&Value>) and keep keys
where those Option<&Value> differ (old_values.get(*key) !=
new_values.get(*key)); remove the unwrap_or sentinel branches so
authorize_update receives real None vs Some distinctions.

In `@crates/context_aware_config/src/api/context/operations.rs`:
- Around line 111-123: The match in get_overrides_from_identifier returns a
reference to a temporary String created by &hash(...) which will be dropped,
causing E0716; fix it by producing an owned String and then taking a reference
to it for the call to get_overrides_from_ctx_id: change the local variable
context_id to an owned String (e.g., let context_id = match ... {
Identifier::Context(context) => hash(...), Identifier::Id(id) => id.clone() })
and then call get_overrides_from_ctx_id(&context_id, schema_name, conn) so no
reference points into a temporary; reference the functions/variants
get_overrides_from_identifier, Identifier::Context, hash, and
get_overrides_from_ctx_id when making the change.

In `@crates/frontend/src/api.rs`:
- Around line 140-147: The code uses matches!(scope, AuthzScope::Admin) which
moves the non-Copy AuthzScope and makes scope unavailable for subsequent calls;
in the functions list_domain, add_domain, and delete_domain replace
matches!(scope, AuthzScope::Admin) with a borrow comparison scope ==
AuthzScope::Admin so scope is not moved and can be passed into
casbin_url_and_headers and other callers.

In `@crates/superposition_types/src/lib.rs`:
- Around line 135-144: The types Cac<T> and Exp<T> should not expose DerefMut
because that allows mutation of the inner value after validation; remove
DerefMut from the derive attributes (and any use/import of DerefMut) for both
Cac<T> and Exp<T> so they only implement immutable Deref, keeping validation
guarantees enforced by preventing mutable access to the wrapped value.

---

Nitpick comments:
In `@crates/experimentation_platform/src/api/experiments/handlers.rs`:
- Around line 136-146: Change the authorize_create signature to accept a slice
instead of a Vec reference (i.e., replace &Vec<Variant> with &[Variant]) and
update any callers to pass slices or vec.as_slice() as needed; inside the
function keep all logic the same (use variants.iter()...) so no other internal
changes are required—this makes the API more idiomatic and allows callers to
pass either Vec or slice directly while still referencing the existing function
name authorize_create and its use of Variant and VariantType.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74f4375e-1a01-4a7f-9717-b306b13d84e9

📥 Commits

Reviewing files that changed from the base of the PR and between 29dcef7 and ba25281.

📒 Files selected for processing (13)
  • crates/context_aware_config/src/api/context/handlers.rs
  • crates/context_aware_config/src/api/context/helpers.rs
  • crates/context_aware_config/src/api/context/operations.rs
  • crates/context_aware_config/src/api/default_config/handlers.rs
  • crates/experimentation_platform/src/api/experiments/handlers.rs
  • crates/experimentation_platform/src/api/experiments/helpers.rs
  • crates/frontend/src/api.rs
  • crates/frontend/src/components/authz.rs
  • crates/service_utils/src/middlewares/auth_z.rs
  • crates/service_utils/src/middlewares/auth_z/authorization.rs
  • crates/service_utils/src/middlewares/auth_z/casbin.rs
  • crates/service_utils/src/middlewares/auth_z/no_auth.rs
  • crates/superposition_types/src/lib.rs

Comment on lines +193 to +209
async fn authorize_update<A: AuthZAction>(
auth_z: &AuthZ<A>,
context: &Identifier,
new_overrides: &Cac<Overrides>,
schema_name: &SchemaName,
conn: &mut DBConnection,
) -> superposition::Result<()> {
let overrides =
operations::get_overrides_from_identifier(context, schema_name, conn)?;

auth_z
.authorize_action(
&AuthZActionUpdate::get(),
&changed_keys(&overrides, &new_overrides.deref()),
)
.await
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 11, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don't authorize against stale overrides.

These helpers read the current overrides, await AuthZ, and only then start the write transaction. Another request can update or delete the same context in between, so ABAC approves one key set while the mutation applies to another. The same race exists in bulk because the entire batch is authorized before the transaction begins.

Please bind the read + authorization decision to the same locked write path, or reject the write if the row's override_id / version changed after authorization.

Also applies to: 226-264, 303-316, 619-632, 718-772

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

In `@crates/context_aware_config/src/api/context/handlers.rs` around lines 193 -
209, authorize_update reads current overrides
(operations::get_overrides_from_identifier), calls auth_z.authorize_action with
changed_keys and then returns, creating a race where the overrides can change
before the write; change the flow so the read and authorization are bound to the
same locked write path: either perform a SELECT ... FOR UPDATE (or start the
write transaction/lock) before calling auth_z.authorize_action inside
authorize_update, or record the row version/override_id during the locked read
and re-check it immediately before committing (and reject the write if it
changed); apply the same pattern to the other authorization helpers referenced
in the review (the blocks around lines 226-264, 303-316, 619-632, 718-772) so
authorization is always based on the locked/transactional state or fails if the
version changed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

suggest a fix

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.

@ayushjain17, here's a concrete fix using an optimistic-lock / version-token pattern — the only approach that cleanly works when async authorization must happen outside a sync Diesel transaction closure.


The pattern

Each authorize_* helper returns the snapshot version (last_modified_at) it read when fetching overrides. Inside the write transaction, before mutating, a SELECT … FOR UPDATE re-fetches the row and rejects the write if the version changed since authorization.


Step 1 — operations.rs: add a version-aware reader

/// Returns overrides *and* the last_modified_at snapshot used to detect races.
pub fn get_overrides_and_version_from_ctx_id(
    ctx_id: &str,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> result::Result<(Overrides, chrono::DateTime<chrono::Utc>)> {
    use superposition_types::database::schema::contexts::dsl::*;
    let ctx: Context = contexts
        .filter(id.eq(ctx_id))
        .schema_name(schema_name)
        .get_result(conn)?;
    Ok((
        serde_json::from_value(ctx.override_.clone()).map_err(|e| unexpected_error!(e))?,
        ctx.last_modified_at,
    ))
}

pub fn get_overrides_and_version_from_identifier(
    identifier: &Identifier,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> result::Result<(Overrides, chrono::DateTime<chrono::Utc>)> {
    let ctx_id = match identifier {
        Identifier::Context(ctx) => hash(&Value::Object(ctx.clone().into_inner().into())),
        Identifier::Id(id) => id.clone(),
    };
    get_overrides_and_version_from_ctx_id(&ctx_id, schema_name, conn)
}

Step 2 — handlers.rs: return the snapshot from each auth helper

async fn authorize_update<A: AuthZAction>(
    auth_z: &AuthZ<A>,
    context: &Identifier,
    new_overrides: &Cac<Overrides>,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> superposition::Result<chrono::DateTime<chrono::Utc>> {  // <-- returns snapshot
    let (overrides, snapshot_version) =
        operations::get_overrides_and_version_from_identifier(context, schema_name, conn)?;

    auth_z
        .authorize_action(
            &AuthZActionUpdate::get(),
            &changed_keys(&overrides, new_overrides.deref()),
        )
        .await?;

    Ok(snapshot_version)
}

async fn authorize_action<A: AuthZAction>(  // used by move_handler
    auth_z: &AuthZ<A>,
    ctx_id: &str,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> superposition::Result<chrono::DateTime<chrono::Utc>> {
    let (overrides, snapshot_version) =
        operations::get_overrides_and_version_from_ctx_id(ctx_id, schema_name, conn)?;

    auth_z
        .authorize_action(
            &AuthZActionMove::get(),
            &overrides.keys().collect::<Vec<_>>(),
        )
        .await?;

    Ok(snapshot_version)
}

async fn authorize_delete<A: AuthZAction>(
    auth_z: &AuthZ<A>,
    ctx_id: &str,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> superposition::Result<chrono::DateTime<chrono::Utc>> {
    let (overrides, snapshot_version) =
        operations::get_overrides_and_version_from_ctx_id(ctx_id, schema_name, conn)?;

    auth_z
        .authorize_action(
            &AuthZActionDelete::get(),
            &overrides.keys().collect::<Vec<_>>(),
        )
        .await?;

    Ok(snapshot_version)
}

For bulk, collect a map of ctx_id → snapshot_version:

async fn authorize_bulk<A: AuthZAction>(
    auth_z: &AuthZ<A>,
    ops: &Vec<ContextAction>,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> superposition::Result<std::collections::HashMap<String, chrono::DateTime<chrono::Utc>>> {
    let mut snapshots = std::collections::HashMap::new();
    for op in ops {
        match op {
            ContextAction::Put(put_req) => {
                authorize_create(auth_z, &put_req.r#override).await?;
                // No version needed for new inserts (no existing row).
            }
            ContextAction::Replace(update_req) => {
                let ctx_id = match &update_req.context {
                    Identifier::Context(ctx) => {
                        superposition_core::helpers::hash(
                            &serde_json::Value::Object(ctx.clone().into_inner().into())
                        )
                    }
                    Identifier::Id(id) => id.clone(),
                };
                let v = authorize_update(
                    auth_z,
                    &update_req.context,
                    &update_req.override_,
                    schema_name,
                    conn,
                ).await?;
                snapshots.insert(ctx_id, v);
            }
            ContextAction::Delete(ctx_id) => {
                let v = authorize_delete(auth_z, ctx_id, schema_name, conn).await?;
                snapshots.insert(ctx_id.clone(), v);
            }
            ContextAction::Move { id: ctx_id, .. } => {
                let v = authorize_action(auth_z, ctx_id, schema_name, conn).await?;
                snapshots.insert(ctx_id.clone(), v);
            }
        }
    }
    Ok(snapshots)
}

Step 3 — Add a version-guard helper (called inside transactions)

/// Called inside a Diesel transaction. Locks the row with SELECT FOR UPDATE
/// and rejects if `last_modified_at` changed since authorization.
fn assert_context_version_unchanged(
    ctx_id: &str,
    authorized_version: chrono::DateTime<chrono::Utc>,
    schema_name: &SchemaName,
    conn: &mut DBConnection,
) -> superposition::Result<()> {
    use superposition_types::database::schema::contexts::dsl::*;
    use diesel::dsl::for_update;

    let current: chrono::DateTime<chrono::Utc> = contexts
        .filter(id.eq(ctx_id))
        .schema_name(schema_name)
        .for_update()          // SELECT … FOR UPDATE — locks the row
        .select(last_modified_at)
        .get_result(conn)?;

    if current != authorized_version {
        return Err(superposition_macros::unexpected_error!(
            "Context {} was concurrently modified; authorization may be stale. \
             Please retry the request.",
            ctx_id
        ));
    }
    Ok(())
}

Note: for_update() requires Diesel's locking_clause feature; if not already enabled, add diesel = { features = ["postgres", "with-deprecated"] } or enable for-update-locking in your Diesel feature flags.


Step 4 — Thread the snapshot into each handler's transaction

update_handler:

let authorized_version = authorize_update(
    &_auth_z,
    &req.context,
    &req.override_,
    &workspace_context.schema_name,
    &mut db_conn,
).await?;

let ctx_id = /* resolve identifier to string, same logic as operations.rs */;

let (override_resp, version_id) = db_conn
    .transaction::<_, superposition::AppError, _>(|transaction_conn| {
        assert_context_version_unchanged(
            &ctx_id, authorized_version,
            &workspace_context.schema_name, transaction_conn
        )?;
        // ... existing update logic
    })?;

move_handler and delete_handler: same pattern — pass authorized_version into the transaction and call assert_context_version_unchanged as the first statement.

bulk_operations_handler: pass the snapshots map in; for each Replace, Delete, and Move action call assert_context_version_unchanged using the matching entry in the map before the mutation.


Why this is correct

Concern How it's addressed
Async auth can't run inside sync Diesel closure Auth runs outside; only a sync SELECT FOR UPDATE guard runs inside
Row mutated between auth and write FOR UPDATE lock + version comparison detects and rejects the stale write
Put (new insert) has no version to check Not vulnerable — there is no pre-existing row to be concurrently modified
Bulk batch authorized before transaction Each item's snapshot is verified under lock at the start of its mutation step

🧠 Learnings used
Learnt from: ayushjain17
Repo: juspay/superposition PR: 543
File: crates/service_utils/src/middlewares/auth_z.rs:141-152
Timestamp: 2026-01-02T20:59:01.233Z
Learning: In crates/service_utils/src/middlewares/auth_z.rs, the AuthZHandler::init function is intentionally designed to panic on startup if AUTH_Z_PROVIDER environment variable is missing or set to an unknown value. This fail-fast behavior is expected and preferred for this critical authorization configuration.

Learnt from: ayushjain17
Repo: juspay/superposition PR: 908
File: crates/context_aware_config/src/api/secrets/handlers.rs:283-283
Timestamp: 2026-03-09T12:19:57.206Z
Learning: In crates/context_aware_config/src/api/secrets/handlers.rs (and similar handler files in the juspay/superposition repo), `declare_resource!(ResourceName)` is a file-level macro that sets the default resource for all `#[authorized]` handlers that do NOT explicitly specify a resource. When a handler uses `#[authorized(resource = SomeResource)]` with an explicit resource argument, no separate `declare_resource!(SomeResource)` is needed — the inline resource specification is sufficient for AuthZ registry registration. Do not flag missing `declare_resource!` for resources already specified inline via `#[authorized(resource = ...)]`.

Learnt from: ayushjain17
Repo: juspay/superposition PR: 910
File: crates/frontend/src/api.rs:159-166
Timestamp: 2026-03-12T06:30:24.697Z
Learning: In `crates/frontend/src/api.rs`, using `matches!(scope, AuthzScope::Admin)` where `scope: AuthzScope` (non-Copy enum with `Clone, PartialEq, Eq`) followed by passing `scope` to subsequent calls (e.g., `casbin_url_and_headers`) is valid and compiles correctly. Do not flag `matches!` on a non-Copy enum value as a move issue when the matched variant (`Admin`) has no fields and the code path that uses `scope` afterward is only reached when the condition is false. This pattern is accepted by the Rust compiler in the `superposition` codebase.

Learnt from: ayushjain17
Repo: juspay/superposition PR: 827
File: crates/superposition_core/src/config.rs:154-154
Timestamp: 2026-01-07T20:38:53.153Z
Learning: Deprecate jsonlogic-based context conditions and migrate to simple key-value pair map conditions across the codebase. Replace jsonlogic::apply usage with superposition_types::apply for condition evaluation. Update all relevant modules (notably Rust files under crates) to use the new approach, remove jsonlogic dependencies where applicable, and run full compilation and tests to catch regressions. Ensure context evaluation logic consistently uses key-value maps and that the architectural change is reflected in unit/integration tests.

@ayushjain17 ayushjain17 force-pushed the auth_z_abac branch 4 times, most recently from 020c0af to 388342e Compare March 12, 2026 16:16
@Datron Datron added the P0 label Mar 16, 2026
@ayushjain17 ayushjain17 force-pushed the auth_z_abac branch 2 times, most recently from b211a5f to 83c7c24 Compare March 16, 2026 12:05
Copy link
Copy Markdown
Collaborator

@Datron Datron left a comment

Choose a reason for hiding this comment

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

Mostly nitpicks, please take a look

Ok(HttpResponse::Ok().body("Policy already exists"))
}
Ok(Json(ActionResponse {
success: added,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm guessing this says either true or false, wouldn't the 200 OK response mean success? Do we need to send this back?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah, because casbin does not give me an error when it is trying to delete something which does not exist

Copy link
Copy Markdown
Collaborator

@Datron Datron Mar 17, 2026

Choose a reason for hiding this comment

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

Can we check the success boolean in code and throw the error in the handler? Return 200 and saying the request failed is bad API semantics

@ayushjain17 ayushjain17 added this pull request to the merge queue Mar 17, 2026
Merged via the queue into main with commit 453266d Mar 17, 2026
27 of 34 checks passed
@ayushjain17 ayushjain17 deleted the auth_z_abac branch March 17, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants