Authz Redaction Service for Knowledge Graph

What does this MR do and why?

This MR introduces Authz::RedactionService, a new service that performs batch authorization checks for resources.

This service is the foundation for Layer 3: Final Redaction Layer in the Knowledge Graph Security Architecture.

This code isn't used anywhere yet, it is a prerequisite before introducing the endpoint.

References

Problem this is solving

The Knowledge Graph service queries ClickHouse for GitLab data (issues, merge requests, vulnerabilities, etc.) that has been pre-filtered using traversal IDs. However, traversal ID filtering provides only coarse-grained authorization at the group/project level. It cannot account for:

  • Confidential issues - Only visible to project members and issue participants
  • Runtime checks - SAML group links, IP restrictions
  • Custom roles - Fine-grained permissions beyond Reporter+ access
  • Other resource-specific visibility - Feature access levels, banned/blocked users

Rails is the authoritative source for all authorization decisions via Ability.allowed?. Any service that returns search or discovery results must perform final redaction using Rails authorization.

Solution

Authz::RedactionService provides a generic, reusable service for batch authorization checks that:

  1. Accepts a user and a hash of resources grouped by type (e.g., { 'issues' => [1, 2, 3], 'merge_requests' => [4, 5] })
  2. Batch loads resources with association preloading to prevent N+1 queries
  3. Performs authorization checks using Ability.allowed? with DeclarativePolicy.user_scope optimization
  4. Returns authorization results as a hash mapping resource IDs to boolean values

Important Notes

  1. Does not include all items: This MR will not include every item we plan to index, only a subset. Future items will be added in subsequent MRs.
  2. Assumes authenticated user: The service does NOT validate user state (blocked, deactivated, etc.). The caller (API endpoint) is responsible for authentication and user validation. Marks it clearly as so.
  3. Follows SearchService pattern: Uses the same visible_result? pattern as SearchService to ensure consistent authorization behavior.
  4. Accepts optional logger: For audit logging of redacted results (to be used by the API endpoint).

How to set up and validate locally

There are many ways that this service could filter out data but here is one example

  • Create a confidential issue
  • Create a non-confidential issue
  • Add a guest user to the issue's parent project (guests cannot read confidental issues)
  • Call the service for the guest user
service = Authz::RedactionService.new(
  user: guest_user,
  resources_by_type: {
    'issues' => [$CONFIDENTIAL_ISSUE_ID, $NON_CONFIDENTIAL_ISSUE_ID]
  }
)
result = service.execute
# => {
#      'issues' => { CONFIDENTIAL_ISSUE_ID => false, NON_CONFIDENTIAL_ISSUE_ID => true }
#    }
Edited by Michael Angelo Rivera

Merge request reports

Loading