Skip to content

fix(kessel): enhance checks with several workspaces#2654

Draft
josejulio wants to merge 1 commit into
RedHatInsights:masterfrom
josejulio:rework-group-query
Draft

fix(kessel): enhance checks with several workspaces#2654
josejulio wants to merge 1 commit into
RedHatInsights:masterfrom
josejulio:rework-group-query

Conversation

@josejulio
Copy link
Copy Markdown
Member

@josejulio josejulio commented Feb 18, 2026

Secure Coding Practices Checklist GitHub Link

Secure Coding Checklist

  • Input Validation
  • Output Encoding
  • Authentication and Password Management
  • Session Management
  • Access Control
  • Cryptographic Practices
  • Error Handling and Logging
  • Data Protection
  • Communication Security
  • System Configuration
  • Database Security
  • File Management
  • Memory Management
  • General Coding Practices

Summary by Sourcery

Optimize Kessel RBAC group-based system scoping for large group lists using a temporary-table approach and make the threshold configurable.

Enhancements:

  • Add a temporary-table based implementation for filtering systems by large RBAC group lists to reduce query planning overhead.
  • Introduce a configurable Kessel groups_temp_table_threshold with sensible defaults wired into application settings.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Feb 18, 2026

Reviewer's Guide

Implements an optimized RBAC group filtering path for Kessel by using a temporary table for large group lists, configurable via a new threshold setting, while preserving the existing behavior and adding a safe fallback path.

Sequence diagram for optimized Kessel RBAC group filtering in system policy

sequenceDiagram
  actor Request
  participant SystemPolicyScope as V2_SystemPolicy_Scope
  participant KesselRbac as KesselRbac
  participant ARScope as ActiveRecord_Scope
  participant DB as Database

  Request ->> SystemPolicyScope: resolve_regular
  SystemPolicyScope ->> SystemPolicyScope: validate org_id and groups
  alt groups equals Rbac_ANY
    SystemPolicyScope ->> ARScope: base_scope
    SystemPolicyScope -->> Request: base_scope
  else groups not Rbac_ANY
    SystemPolicyScope ->> ARScope: base_scope
    SystemPolicyScope ->> KesselRbac: enabled
    KesselRbac -->> SystemPolicyScope: boolean
    alt KesselRbac enabled and groups length > groups_temp_table_threshold
      SystemPolicyScope ->> KesselRbac: groups_temp_table_threshold
      KesselRbac -->> SystemPolicyScope: threshold
      SystemPolicyScope ->> SystemPolicyScope: with_groups_via_temp_table(base_scope, groups)
      SystemPolicyScope ->> DB: CREATE TEMP TABLE temp_user_groups_...
      SystemPolicyScope ->> DB: INSERT group ids into temp table
      SystemPolicyScope ->> DB: SELECT with EXISTS using temp table
      DB -->> SystemPolicyScope: filtered records
      SystemPolicyScope -->> Request: optimized filtered_scope
      note over SystemPolicyScope,DB: On ActiveRecord_StatementInvalid, log error and fallback to base_scope.with_groups(groups)
    else KesselRbac disabled or groups length <= threshold
      SystemPolicyScope ->> ARScope: base_scope.with_groups(groups)
      SystemPolicyScope -->> Request: filtered_scope
    end
  end
Loading

Updated class diagram for KesselRbac and system policy scope

classDiagram
  class V2_SystemPolicy_Scope {
    - user
    - scope
    + resolve_regular()
    + resolve_cert_auth()
    + base_scope()
    + with_groups_via_temp_table(filtered_scope, groups)
  }

  class KesselRbac {
    + enabled()
    + groups_temp_table_threshold()
    + client()
    + build_client()
  }

  class SettingsKessel {
    + enabled: boolean
    + principal_domain: string
    + oauth2_token_url: string
    + client_id: string
    + client_secret: string
    + oidc_issuer: string
    + insecure: boolean
    + groups_temp_table_threshold: integer
  }

  class KesselInitializer {
    + load_from_env()
    + set_defaults()
  }

  V2_SystemPolicy_Scope ..> KesselRbac : uses
  V2_SystemPolicy_Scope ..> SettingsKessel : reads
  KesselRbac ..> SettingsKessel : reads
  KesselInitializer ..> SettingsKessel : configures
Loading

File-Level Changes

Change Details Files
Optimize group-based system scoping when Kessel RBAC is enabled and many groups are involved by using a temporary table instead of a large JSONB @> filter list.
  • Adjust resolve_regular to short‑circuit when groups is ANY, otherwise optionally use a temp-table based filter when Kessel RBAC is enabled and the group count exceeds a configurable threshold
  • Introduce with_groups_via_temp_table to create a per-query temp table of group IDs, insert groups as JSONB, and filter the scope with an EXISTS clause against inventory.hosts.groups
  • Add error handling to log failures in the temp table path and fall back to the standard with_groups filtering
app/policies/v2/system_policy.rb
Introduce a configurable threshold for when to use the temp-table optimization for group filtering in Kessel RBAC.
  • Read KESSEL_GROUPS_TEMP_TABLE_THRESHOLD from ENV in the Kessel initializer, defaulting to 50 when not set
  • Expose groups_temp_table_threshold on KesselRbac so policy code can query it
  • Add groups_temp_table_threshold default value to settings.yml for non-ENV configuration paths
config/initializers/kessel.rb
app/services/kessel_rbac.rb
config/settings.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@josejulio josejulio force-pushed the rework-group-query branch 2 times, most recently from 77dfc19 to 5991abc Compare February 18, 2026 23:27
@lpichler
Copy link
Copy Markdown

@josejulio what is status of this PR ?

@josejulio
Copy link
Copy Markdown
Member Author

@josejulio what is status of this PR ?

It needs testing - the idea is there, the original query took long in the planning phase and too little on the execution phase. The hope is that this would alleviate the planning phase by using the temporal table.

If it succeeds it might need to port the raw queries to the framework it is being used (if relevant)

Comment thread app/policies/v2/system_policy.rb Outdated
Copy link
Copy Markdown
Contributor

@coderbydesign coderbydesign left a comment

Choose a reason for hiding this comment

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

Potential suggestion to the issue as reviewed by Claude. I personally don't have a strong opinion on this vs the proposed option (especially to test) and would defer to the service SMEs on preference, but wanted to drop it in here anyway:

Consider EXISTS + unnest instead of temp tables

The diagnosis here seems correct: large group lists cause query planning overhead with the current @> ANY(CAST('{...}' AS jsonb[])) pattern, and this can absolutely contribute to pod resource pressure under concurrent load. Nice catch.

The temp table approach may introduce more complexity than needed. A few notes:

Bug: find_in_temp_table arity mismatch

with_groups_via_temp_table calls find_in_temp_table(conn, filtered_scope, table_name) (3 args), but the method is defined as def find_in_temp_table(scope, table_name) (2 params). This will raise ArgumentError at runtime, meaning every request with 50+ groups hits the rescue and falls back to the unoptimized with_groups — making the optimization effectively dead code.

Simpler alternative: EXISTS + unnest

Instead of 3 DB round-trips (CREATE TEMP TABLE, INSERT, SELECT) and connection-level state management, the same result can be achieved in a single query:

WHERE EXISTS (
  SELECT 1 FROM unnest(ARRAY['uuid-1','uuid-2',...,'uuid-N']::text[]) AS g(val)
  WHERE inventory.hosts.groups @> jsonb_build_array(jsonb_build_object('id', g.val))
)

This gives you:

  • Single @> per row — GIN-index friendly (same benefit as the temp table approach)
  • 1 DB round-trip instead of 3
  • No connection-level state — no temp table lifecycle, no cleanup, no concurrency concerns
  • No rescue/fallback needed — it's just a WHERE clause
  • ~20 lines instead of ~50

Here's what the scope could look like on V2::System:

scope :with_groups_unnest, lambda { |groups, key = :id|
  flat = groups.flatten
  quoted_groups = flat.map { |g| connection.quote(g) }.join(',')
  quoted_key = connection.quote(key.to_s)

  exists_sql = <<~SQL.squish
    EXISTS (
      SELECT 1 FROM unnest(ARRAY[#{quoted_groups}]::text[]) AS g(val)
      WHERE #{quoted_table_name}.groups @> jsonb_build_array(jsonb_build_object(#{quoted_key}, g.val))
    )
  SQL

  ungrouped = arel_table[:groups].eq(AN::Quoted.new('[]'))
  where(groups.include?([]) ? Arel::Nodes::Grouping.new(Arel.sql(exists_sql)).or(ungrouped) : Arel.sql(exists_sql))
}

And the policy becomes:

def resolve_regular
  return scope.none if user.org_id.blank? || groups.blank?
  return base_scope if groups == Rbac::ANY
  return base_scope.with_groups_unnest(groups) if use_unnest?(groups)

  base_scope.with_groups(groups)
end

def use_unnest?(groups)
  KesselRbac.enabled? && groups.length > KesselRbac.groups_unnest_threshold
end

The config/threshold parts of the PR are fine as-is (just rename to groups_unnest_threshold or similar).

Summary

Temp table (current PR) EXISTS + unnest (proposed)
DB round-trips 3 (CREATE, INSERT, SELECT) 1
Connection-level state Yes None
GIN index friendly
Error handling needed Yes (rescue + fallback) No
Lines of code ~50 ~20
Bugs arity mismatch on find_in_temp_table N/A

@josejulio josejulio force-pushed the rework-group-query branch from 5991abc to 36c5fe5 Compare March 30, 2026 16:57
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 30, 2026

Important

Review skipped

Draft detected.

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: 9be433f4-39e7-4560-a4be-47c0ef4a687e

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

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.

3 participants