Skip to content

Conversation

@Swiddis
Copy link
Collaborator

@Swiddis Swiddis commented Dec 30, 2025

Description

Adds some tests to validate various scenarios for document-level security (DLS), since there was an internal scare for a bit on if DLS is working with background IO. Turns out we're protected implicitly by ContextPreservingRunnable. Just pushing some work from that to keep verifying this in the future.

Related Issues

N/A

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Summary by CodeRabbit

  • Tests

    • Added comprehensive integration tests for fine‑grained access control covering index-, field-, and row‑level security across large datasets
    • Introduced shared security test infrastructure with helpers for role/user setup, authenticated queries, data bulk-loads, and DLS/FLS scenarios
    • Updated existing permission tests to use the new security test base
  • Chores

    • Added junit‑jupiter‑params test dependency

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds SecurityTestBase utilities for test-auth, a new FGACIndexScanningIT integration test covering index/field/row-level FGAC with background scanning, refactors PPLPermissionsIT to use the new base class, and updates test dependencies to include JUnit parameterized support.

Changes

Cohort / File(s) Summary
Test Infrastructure Configuration
integ-test/build.gradle
Adds junit-jupiter-params:5.9.3 to testImplementation; includes FGACIndexScanningIT in the integTestWithSecurity test filter.
New Security Test Base Class
integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java
Adds abstract test base with helpers for creating roles (index/DLS/FLS/permissions), creating users and role-mappings, executing authenticated PPL queries (Basic Auth), bulk insert builders/performer, and index-with-mapping helper plus BulkDocumentBuilder.
New FGAC Integration Test
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
New integration test validating FGAC across index-level, field-level (FLS), and row-level (DLS) scenarios using large datasets to trigger background scanning; defines users/roles, index setup, bulk data population, and multiple test methods (index/column/row-level checks, V2/V3 engine paths).
Refactored Permissions Test
integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
Changes superclass from PPLIntegTestCase to SecurityTestBase; removes in-class user/role/password boilerplate and replaces local helpers with SecurityTestBase utilities (createRoleWithIndexAccess, createRoleWithPermissions, etc.).

Sequence Diagram(s)

sequenceDiagram
    actor Test
    participant Base as SecurityTestBase
    participant OpenSearch
    participant Auth as Auth/Authorization

    rect rgb(230,240,255)
        Note over Test,Base: Setup Phase
        Test->>Base: createRoleWithIndexAccess(role, indexPattern)
        Base->>OpenSearch: HTTP role creation (index/DLS/FLS/permissions)
        OpenSearch-->>Base: 200 OK
        Test->>Base: createUser(username, role)
        Base->>OpenSearch: HTTP user creation & role-mapping
        OpenSearch-->>Base: 200 OK
    end

    rect rgb(240,255,230)
        Note over Test,Base: Bulk Load Phase
        Test->>Base: performBulkInsert(bulkBody)
        Base->>OpenSearch: Bulk API request (with refresh)
        OpenSearch-->>Base: Bulk response
    end

    rect rgb(255,240,230)
        Note over Test,Base: Authenticated Query Phase
        Test->>Base: executeQueryAsUser(query, username)
        Base->>Base: createBasicAuthHeader(username, password)
        Base->>OpenSearch: PPL query + Basic Auth header
        OpenSearch->>Auth: Validate credentials & evaluate roles
        Auth-->>OpenSearch: Authorization decision
        OpenSearch-->>Base: Query results (filtered by FGAC)
        Base-->>Test: JSONObject response
    end

    rect rgb(245,235,255)
        Note over Test: Verification
        Test->>Test: Assert results match expected FGAC behavior
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.54% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Integ test cases for field-level security' accurately describes the main changes, which include adding new integration tests for security features including field-level security (FLS) and document-level security (DLS).
Description check ✅ Passed The description is directly related to the changeset, explaining that tests are being added to validate document-level security (DLS) scenarios with background I/O, which aligns with the new test classes and security base utilities introduced.
✨ Finishing touches
  • 📝 Generate docstrings

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

Signed-off-by: Simeon Widdis <[email protected]>
@Swiddis Swiddis force-pushed the fix/background-io-thread-context branch from 1b1ca7b to ded19e9 Compare December 30, 2025 19:00
@Swiddis Swiddis added the maintenance Improves code quality, but not the product label Dec 30, 2025
@Swiddis
Copy link
Collaborator Author

Swiddis commented Dec 30, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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: 1

🧹 Nitpick comments (4)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (2)

53-64: Static initialization flag may cause issues with test isolation.

The static boolean initialized flag combined with @BeforeEach could cause issues if tests run in parallel or if the test class is instantiated multiple times. Consider using @BeforeAll with a static method instead, which is the idiomatic JUnit 5 pattern for one-time setup.

🔎 Proposed refactor to use @BeforeAll
- private static boolean initialized = false;
-
- @SneakyThrows
- @BeforeEach
- public void initialize() {
-   if (!initialized) {
-     setUpIndices(); // Initialize client if needed
-     setupTestIndices();
-     createSecurityRolesAndUsers();
-     initialized = true;
-   }
- }
+ @SneakyThrows
+ @BeforeAll
+ public static void initializeOnce() {
+   // Note: This requires making the setup methods static or 
+   // using a different initialization approach
+ }

Alternatively, if @BeforeAll isn't compatible with the parent class, document why the static flag pattern is necessary.


74-85: Consider adding test cleanup for created indices.

The test creates multiple indices (public_logs_fgac, sensitive_logs_fgac, secure_logs_fgac, employee_records_fgac) but there's no @AfterAll or @AfterEach cleanup method. While using unique _fgac suffix provides isolation, these indices will persist after tests complete.

As per coding guidelines, integration tests should validate that tests clean up resources after execution.

🔎 Consider adding cleanup
@AfterAll
public static void cleanup() throws IOException {
  // Delete test indices
  String[] indices = {PUBLIC_LOGS, SENSITIVE_LOGS, SECURE_LOGS, EMPLOYEE_RECORDS};
  for (String index : indices) {
    try {
      client().performRequest(new Request("DELETE", "/" + index));
    } catch (Exception e) {
      // Index may not exist, ignore
    }
  }
}
integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java (2)

56-98: Consider using JSONArray for permission arrays to avoid manual string building.

The manual StringBuilder approach for building JSON arrays works but is error-prone. Using JSONArray would be cleaner and safer.

🔎 Optional refactor using JSONArray
import org.json.JSONArray;

// In createRoleWithPermissions:
JSONArray clusterPermsArray = new JSONArray(clusterPermissions);
JSONArray indexPermsArray = new JSONArray(indexPermissions);

// Then use clusterPermsArray.toString() and indexPermsArray.toString() in the template

341-353: Remove unused BulkDocumentBuilder helper class or document its intended purpose.

The BulkDocumentBuilder is defined but never instantiated or used anywhere in the codebase. Either remove it if not needed for this PR, or add a comment documenting its intended usage in future tests.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08be6f9 and ded19e9.

📒 Files selected for processing (4)
  • integ-test/build.gradle
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

**/*.java: Use PascalCase for class names (e.g., QueryExecutor)
Use camelCase for method and variable names (e.g., executeQuery)
Use UPPER_SNAKE_CASE for constants (e.g., MAX_RETRY_COUNT)
Keep methods under 20 lines with single responsibility
All public classes and methods must have proper JavaDoc
Use specific exception types with meaningful messages for error handling
Prefer Optional<T> for nullable returns in Java
Avoid unnecessary object creation in loops
Use StringBuilder for string concatenation in loops
Validate all user inputs, especially queries
Sanitize data before logging to prevent injection attacks
Use try-with-resources for proper resource cleanup in Java
Maintain Java 11 compatibility when possible for OpenSearch 2.x
Document Calcite-specific workarounds in code

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java

⚙️ CodeRabbit configuration file

**/*.java: - Flag methods >50 lines as potentially too complex - suggest refactoring

  • Flag classes >500 lines as needing organization review
  • Check for dead code, unused imports, and unused variables
  • Identify code reuse opportunities across similar implementations
  • Assess holistic maintainability - is code easy to understand and modify?
  • Flag code that appears AI-generated without sufficient human review
  • Verify Java naming conventions (PascalCase for classes, camelCase for methods/variables)
  • Check for proper JavaDoc on public classes and methods
  • Flag redundant comments that restate obvious code
  • Ensure proper error handling with specific exception types
  • Check for Optional usage instead of null returns
  • Validate proper use of try-with-resources for resource management

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java
integ-test/**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

End-to-end scenarios need integration tests in integ-test/ module

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

integ-test/**/*IT.java: - Integration tests MUST use valid test data from resources

  • Verify test data files exist in integ-test/src/test/resources/
  • Check test assertions are meaningful and specific
  • Validate tests clean up resources after execution
  • Ensure tests are independent and can run in any order
  • Flag tests that reference non-existent indices (e.g., EMP)
  • Verify integration tests are in correct module (integ-test/)
  • Check tests can be run with ./gradlew :integ-test:integTest
  • Ensure proper test data setup and teardown
  • Validate end-to-end scenario coverage

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

Name integration tests with *IT.java suffix in OpenSearch SQL

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/test/**/*.java

⚙️ CodeRabbit configuration file

**/test/**/*.java: - Verify NULL input tests for all new functions

  • Check boundary condition tests (min/max values, empty inputs)
  • Validate error condition tests (invalid inputs, exceptions)
  • Ensure multi-document tests for per-document operations
  • Flag smoke tests without meaningful assertions
  • Check test naming follows pattern: test
  • Verify test data is realistic and covers edge cases
  • Verify test coverage for new business logic
  • Ensure tests are independent and don't rely on execution order
  • Validate meaningful test data that reflects real-world scenarios
  • Check for proper cleanup of test resources

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to **/*IT.java : Name integration tests with `*IT.java` suffix in OpenSearch SQL
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Verify changes with `./gradlew :integ-test:integTest` before merge
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Verify changes with `./gradlew :integ-test:integTest` before merge

Applied to files:

  • integ-test/build.gradle
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to integ-test/**/*IT.java : End-to-end scenarios need integration tests in `integ-test/` module

Applied to files:

  • integ-test/build.gradle
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Use `./gradlew :integ-test:integTest` for integration testing in OpenSearch SQL

Applied to files:

  • integ-test/build.gradle
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to **/*IT.java : Name integration tests with `*IT.java` suffix in OpenSearch SQL

Applied to files:

  • integ-test/build.gradle
  • integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
  • integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Development requires JDK 21 for the OpenSearch SQL project

Applied to files:

  • integ-test/build.gradle
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to **/*Test.java : Name unit tests with `*Test.java` suffix in OpenSearch SQL

Applied to files:

  • integ-test/build.gradle
  • integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Test SQL generation and optimization paths for Calcite integration changes

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧬 Code graph analysis (1)
integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java (1)
integ-test/src/test/java/org/opensearch/sql/ppl/PPLIntegTestCase.java (1)
  • PPLIntegTestCase (36-415)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build-linux (21, integration)
  • GitHub Check: build-linux (25, unit)
  • GitHub Check: build-linux (25, integration)
  • GitHub Check: build-linux (21, unit)
  • GitHub Check: build-windows-macos (macos-14, 25, integration)
  • GitHub Check: build-windows-macos (macos-14, 25, unit)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (macos-14, 21, integration)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, unit)
  • GitHub Check: build-windows-macos (macos-14, 21, unit)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, unit)
  • GitHub Check: security-it-windows-macos (windows-latest, 25)
  • GitHub Check: CodeQL-Scan (java)
🔇 Additional comments (12)
integ-test/build.gradle (2)

211-212: LGTM - Dependency addition for parameterized tests.

The junit-jupiter-params dependency aligns with the existing JUnit 5 version (5.9.3) used in the project.


449-453: LGTM - Test filter inclusion follows existing pattern.

The FGACIndexScanningIT test is correctly added to the security test filter alongside existing security integration tests. Based on learnings, the *IT.java naming convention is properly followed.

integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (4)

197-223: LGTM - Bulk insert implementation follows best practices.

The method correctly uses StringBuilder for string concatenation in loops (as per coding guidelines), sets the proper Content-Type: application/x-ndjson header, and verifies the response status code.


363-405: LGTM - Index-level security test is comprehensive.

The test properly validates both positive cases (user can access their index) and negative cases (user cannot access unauthorized index) with meaningful assertions. The exception handling verifies appropriate error messages.


407-499: LGTM - Column-level security test covers FLS comprehensively.

The test validates:

  1. HR user can see all fields including sensitive ssn
  2. Manager user cannot see ssn field
  3. Manager cannot even query ssn field
  4. FLS is enforced even with large datasets (background scanning)

Error messages clearly indicate security violations with actionable context.


501-628: LGTM - Row-level security tests cover both V2 and V3 engines.

The tests appropriately:

  • Test V2 (legacy) engine by explicitly calling disableCalcite()
  • Test V3 (Calcite) engine using the default enabled state
  • Verify DLS filtering by counting documents per security level
  • Include detailed error messages explaining ThreadContext implications
integ-test/src/test/java/org/opensearch/sql/security/SecurityTestBase.java (3)

19-26: LGTM - Well-structured base class with clear documentation.

The abstract class properly inherits from PPLIntegTestCase and provides reusable utilities for security integration tests, reducing code duplication across test classes.


198-248: LGTM - User creation with role mapping is convenient for test setup.

The method appropriately combines user creation and role mapping, which is a common pattern for test utilities. Both operations verify success via status codes.


250-277: LGTM - Clean implementation for authenticated query execution.

The method properly constructs the request with Basic Auth headers and validates the response status code before parsing.

integ-test/src/test/java/org/opensearch/sql/security/PPLPermissionsIT.java (3)

31-31: LGTM - Good refactoring to extend SecurityTestBase.

The class now properly inherits from SecurityTestBase, eliminating duplicate code for role/user creation and authenticated query execution.


83-102: LGTM - Clean usage of inherited SecurityTestBase methods.

The refactored setup properly delegates to the base class methods for creating roles and users, following DRY principles.


104-194: LGTM - Comprehensive negative test setup with inherited utilities.

The helper methods properly use createRoleWithPermissions from SecurityTestBase to create roles with specific missing permissions, enabling thorough negative testing of permission enforcement.

Signed-off-by: Simeon Widdis <[email protected]>
Copy link
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: 1

🧹 Nitpick comments (3)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (3)

74-195: Consider adding test cleanup to remove indices, roles, and users.

Integration tests should clean up resources after execution. Currently, there's no @AfterEach or @AfterAll method to delete the created indices (public_logs_fgac, sensitive_logs_fgac, secure_logs_fgac, employee_records_fgac) and security artifacts (roles/users).

🔎 Example cleanup approach
@AfterAll
public static void cleanup() throws IOException {
  // Delete test indices
  deleteIndexIfExists(PUBLIC_LOGS);
  deleteIndexIfExists(SENSITIVE_LOGS);
  deleteIndexIfExists(SECURE_LOGS);
  deleteIndexIfExists(EMPLOYEE_RECORDS);
  
  // Delete test users and roles
  deleteUser(PUBLIC_USER);
  deleteUser(SENSITIVE_USER);
  deleteUser(LIMITED_USER);
  deleteUser(MANAGER_USER);
  deleteUser(HR_USER);
  
  deleteRole(PUBLIC_ROLE);
  deleteRole(SENSITIVE_ROLE);
  deleteRole(LIMITED_ROLE);
  deleteRole(MANAGER_ROLE);
  deleteRole(HR_ROLE);
}

Check if SecurityTestBase provides cleanup utilities.

As per coding guidelines, integration tests should ensure proper cleanup of test resources.


404-496: Consider splitting this complex test into smaller, focused test methods.

This method is 92 lines and tests 4 distinct scenarios. Per coding guidelines, methods should be under 50 lines with single responsibility.

🔎 Suggested refactoring

Split into focused test methods:

@Test
public void testHRUserCanAccessAllFieldsIncludingSSN() throws IOException {
  // Current Test 1 logic (lines 408-429)
}

@Test
public void testManagerUserCannotAccessSSNField() throws IOException {
  // Current Test 2 logic (lines 431-458)
}

@Test
public void testManagerUserCannotQuerySSNField() throws IOException {
  // Current Test 3 logic (lines 460-474)
}

@Test
public void testFLSEnforcedWithBackgroundScanning() throws IOException {
  // Current Test 4 logic (lines 476-495)
}

This improves readability and makes test failures easier to diagnose.

As per coding guidelines, flag methods >50 lines as potentially too complex and suggest refactoring.


498-625: Significant code duplication between testRowLevelSecurityV2 and testRowLevelSecurity.

These two methods have nearly identical logic (lines 498-559 vs 561-625), differing only in:

  • V2 calls disableCalcite() while V3 uses Calcite by default
  • Error message prefixes ("[V2]" vs "[V3]")

Both methods exceed 50 lines. Consider extracting the common validation logic to reduce duplication and improve maintainability.

🔎 Suggested refactoring
@Test
public void testRowLevelSecurityV2() throws IOException {
  disableCalcite();
  verifyRowLevelSecurityEnforcement("V2");
}

@Test
public void testRowLevelSecurity() throws IOException {
  // Calcite enabled by default in init()
  verifyRowLevelSecurityEnforcement("V3");
}

private void verifyRowLevelSecurityEnforcement(String engineVersion) throws IOException {
  String query = String.format(
      "search source=%s | fields security_level, message | stats count() by security_level",
      SECURE_LOGS);
  JSONObject result = executeQueryAsUser(query, LIMITED_USER);
  
  var datarows = result.getJSONArray("datarows");
  int totalDocs = 0;
  boolean sawConfidential = false;
  boolean sawInternal = false;
  int publicDocs = 0;
  
  for (int i = 0; i < datarows.length(); i++) {
    var row = datarows.getJSONArray(i);
    int count = row.getInt(0);
    String securityLevel = row.getString(1);
    totalDocs += count;
    
    if ("confidential".equals(securityLevel)) sawConfidential = true;
    else if ("internal".equals(securityLevel)) sawInternal = true;
    else if ("public".equals(securityLevel)) publicDocs = count;
  }
  
  assertFalse(
      String.format("[%s] SECURITY VIOLATION: limited_user should NOT see 'confidential' documents...", engineVersion),
      sawConfidential);
  assertFalse(
      String.format("[%s] SECURITY VIOLATION: limited_user should NOT see 'internal' documents...", engineVersion),
      sawInternal);
  assertEquals(
      String.format("[%s] limited_user should ONLY see 'public' documents (~1000)...", engineVersion),
      1000,
      publicDocs);
  assertEquals(
      String.format("[%s] Total visible documents should be ~1000 (only public)...", engineVersion),
      1000,
      totalDocs);
}

As per coding guidelines, identify code reuse opportunities across similar implementations and flag methods >50 lines as potentially too complex.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ded19e9 and a07f47a.

📒 Files selected for processing (1)
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

**/*.java: Use PascalCase for class names (e.g., QueryExecutor)
Use camelCase for method and variable names (e.g., executeQuery)
Use UPPER_SNAKE_CASE for constants (e.g., MAX_RETRY_COUNT)
Keep methods under 20 lines with single responsibility
All public classes and methods must have proper JavaDoc
Use specific exception types with meaningful messages for error handling
Prefer Optional<T> for nullable returns in Java
Avoid unnecessary object creation in loops
Use StringBuilder for string concatenation in loops
Validate all user inputs, especially queries
Sanitize data before logging to prevent injection attacks
Use try-with-resources for proper resource cleanup in Java
Maintain Java 11 compatibility when possible for OpenSearch 2.x
Document Calcite-specific workarounds in code

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

**/*.java: - Flag methods >50 lines as potentially too complex - suggest refactoring

  • Flag classes >500 lines as needing organization review
  • Check for dead code, unused imports, and unused variables
  • Identify code reuse opportunities across similar implementations
  • Assess holistic maintainability - is code easy to understand and modify?
  • Flag code that appears AI-generated without sufficient human review
  • Verify Java naming conventions (PascalCase for classes, camelCase for methods/variables)
  • Check for proper JavaDoc on public classes and methods
  • Flag redundant comments that restate obvious code
  • Ensure proper error handling with specific exception types
  • Check for Optional usage instead of null returns
  • Validate proper use of try-with-resources for resource management

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
integ-test/**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

End-to-end scenarios need integration tests in integ-test/ module

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

integ-test/**/*IT.java: - Integration tests MUST use valid test data from resources

  • Verify test data files exist in integ-test/src/test/resources/
  • Check test assertions are meaningful and specific
  • Validate tests clean up resources after execution
  • Ensure tests are independent and can run in any order
  • Flag tests that reference non-existent indices (e.g., EMP)
  • Verify integration tests are in correct module (integ-test/)
  • Check tests can be run with ./gradlew :integ-test:integTest
  • Ensure proper test data setup and teardown
  • Validate end-to-end scenario coverage

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

Name integration tests with *IT.java suffix in OpenSearch SQL

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/test/**/*.java

⚙️ CodeRabbit configuration file

**/test/**/*.java: - Verify NULL input tests for all new functions

  • Check boundary condition tests (min/max values, empty inputs)
  • Validate error condition tests (invalid inputs, exceptions)
  • Ensure multi-document tests for per-document operations
  • Flag smoke tests without meaningful assertions
  • Check test naming follows pattern: test
  • Verify test data is realistic and covers edge cases
  • Verify test coverage for new business logic
  • Ensure tests are independent and don't rely on execution order
  • Validate meaningful test data that reflects real-world scenarios
  • Check for proper cleanup of test resources

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧠 Learnings (3)
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to **/*IT.java : Name integration tests with `*IT.java` suffix in OpenSearch SQL

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to integ-test/**/*IT.java : End-to-end scenarios need integration tests in `integ-test/` module

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Test SQL generation and optimization paths for Calcite integration changes

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: build-linux (21, integration)
  • GitHub Check: build-linux (21, unit)
  • GitHub Check: build-linux (25, doc)
  • GitHub Check: build-linux (25, unit)
  • GitHub Check: build-linux (21, doc)
  • GitHub Check: bwc-tests-full-restart (21)
  • GitHub Check: build-linux (25, integration)
  • GitHub Check: bwc-tests-rolling-upgrade (21)
  • GitHub Check: bwc-tests-rolling-upgrade (25)
  • GitHub Check: bwc-tests-full-restart (25)
  • GitHub Check: security-it-linux (21)
  • GitHub Check: security-it-linux (25)
  • GitHub Check: build-windows-macos (macos-14, 25, doc)
  • GitHub Check: build-windows-macos (macos-14, 21, doc)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, unit)
  • GitHub Check: build-windows-macos (macos-14, 25, integration)
  • GitHub Check: build-windows-macos (macos-14, 21, unit)
  • GitHub Check: build-windows-macos (macos-14, 25, unit)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, unit)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (macos-14, 21, integration)
  • GitHub Check: security-it-windows-macos (windows-latest, 25)
  • GitHub Check: security-it-windows-macos (windows-latest, 21)
  • GitHub Check: security-it-windows-macos (macos-14, 21)
  • GitHub Check: security-it-windows-macos (macos-14, 25)
  • GitHub Check: test-sql-cli-integration (21)
  • GitHub Check: CodeQL-Scan (java)
🔇 Additional comments (1)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (1)

331-334: Previous review concern appears resolved.

The past review flagged redundant role creation for LIMITED_ROLE, but the current code only creates the role once via createRoleWithDocumentLevelSecurity() at line 333. The issue has been addressed.

Copy link
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: 0

🧹 Nitpick comments (4)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (4)

198-198: Remove redundant class name qualification.

Within the class, constants can be referenced directly without the FGACIndexScanningIT. prefix. This applies to LARGE_DATASET_SIZE, EMPLOYEE_RECORDS, and SECURE_LOGS at lines 198, 253, 277, 290, and 303.

🔎 Proposed simplification
-    for (int i = 0; i < FGACIndexScanningIT.LARGE_DATASET_SIZE; i++) {
+    for (int i = 0; i < LARGE_DATASET_SIZE; i++) {

Apply similar changes at lines 253, 277, 290, and 303 to remove the FGACIndexScanningIT. prefix.

Also applies to: 253-253, 277-277, 290-290, 303-303


402-494: Consider splitting this test method for better clarity.

At 92 lines, testColumnLevelSecurity contains four distinct test scenarios that could be separated into individual test methods: (1) HR user sees all fields, (2) Manager user sees restricted fields, (3) Manager cannot query SSN directly, and (4) FLS enforcement with large datasets. Splitting would improve test isolation and make failures easier to diagnose.

💡 Suggested approach

Consider creating separate test methods:

  • testHRUserSeesAllFields()
  • testManagerUserSensitiveFieldsHidden()
  • testManagerUserCannotQuerySensitiveField()
  • testFieldLevelSecurityWithLargeDataset()

This would make each test focused on a single concern and improve maintainability.

As per coding guidelines, methods over 50 lines should be reviewed for complexity.


496-623: Consider refactoring duplicated row-level security test logic.

The methods testRowLevelSecurityV2 and testRowLevelSecurity contain nearly identical logic (validation code at lines 514-556 and 579-622), differing only in the engine configuration. This duplication could be reduced using JUnit 5's parameterized tests or a shared helper method.

💡 Suggested approach using parameterized tests
@ParameterizedTest
@ValueSource(booleans = {false, true}) // false = V2, true = V3/Calcite
public void testRowLevelSecurity(boolean useCalcite) throws IOException {
  if (!useCalcite) {
    disableCalcite();
  }
  
  String engineLabel = useCalcite ? "V3" : "V2";
  
  // Execute query as limited_user
  String query = String.format(
      "search source=%s | fields security_level, message | stats count() by security_level",
      SECURE_LOGS);
  JSONObject result = executeQueryAsUser(query, LIMITED_USER);
  
  // Extract and validate datarows (shared logic)
  var datarows = result.getJSONArray("datarows");
  int totalDocs = 0;
  boolean sawConfidential = false;
  boolean sawInternal = false;
  int publicDocs = 0;
  
  for (int i = 0; i < datarows.length(); i++) {
    var row = datarows.getJSONArray(i);
    int count = row.getInt(0);
    String securityLevel = row.getString(1);
    totalDocs += count;
    
    if ("confidential".equals(securityLevel)) sawConfidential = true;
    else if ("internal".equals(securityLevel)) sawInternal = true;
    else if ("public".equals(securityLevel)) publicDocs = count;
  }
  
  assertFalse(
      String.format("[%s] SECURITY VIOLATION: limited_user should NOT see 'confidential' documents...", engineLabel),
      sawConfidential);
  assertFalse(
      String.format("[%s] SECURITY VIOLATION: limited_user should NOT see 'internal' documents...", engineLabel),
      sawInternal);
  assertEquals(1000, publicDocs, 
      String.format("[%s] limited_user should ONLY see 'public' documents (~1000)...", engineLabel));
  assertEquals(1000, totalDocs,
      String.format("[%s] Total visible documents should be ~1000 (only public)...", engineLabel));
}

Note: The test dependencies already include junit-jupiter-params (from build.gradle changes in this PR).


56-62: Consider adding explicit test resource cleanup.

While the test indices use the _fgac suffix for isolation, consider adding an @AfterAll method to explicitly clean up test indices, roles, and users. This ensures a clean state even if tests fail and improves test isolation.

💡 Suggested cleanup approach
@SneakyThrows
@AfterAll
public void cleanup() {
  // Delete test indices
  deleteIndex(PUBLIC_LOGS);
  deleteIndex(SENSITIVE_LOGS);
  deleteIndex(SECURE_LOGS);
  deleteIndex(EMPLOYEE_RECORDS);
  
  // Delete test users and roles (if helper methods exist in SecurityTestBase)
  deleteUser(PUBLIC_USER);
  deleteUser(SENSITIVE_USER);
  deleteUser(LIMITED_USER);
  deleteUser(MANAGER_USER);
  deleteUser(HR_USER);
  
  deleteRole(PUBLIC_ROLE);
  deleteRole(SENSITIVE_ROLE);
  deleteRole(LIMITED_ROLE);
  deleteRole(MANAGER_ROLE);
  deleteRole(HR_ROLE);
}

As per coding guidelines, integration tests should properly clean up resources after execution.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a07f47a and 22564a6.

📒 Files selected for processing (1)
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

**/*.java: Use PascalCase for class names (e.g., QueryExecutor)
Use camelCase for method and variable names (e.g., executeQuery)
Use UPPER_SNAKE_CASE for constants (e.g., MAX_RETRY_COUNT)
Keep methods under 20 lines with single responsibility
All public classes and methods must have proper JavaDoc
Use specific exception types with meaningful messages for error handling
Prefer Optional<T> for nullable returns in Java
Avoid unnecessary object creation in loops
Use StringBuilder for string concatenation in loops
Validate all user inputs, especially queries
Sanitize data before logging to prevent injection attacks
Use try-with-resources for proper resource cleanup in Java
Maintain Java 11 compatibility when possible for OpenSearch 2.x
Document Calcite-specific workarounds in code

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

**/*.java: - Flag methods >50 lines as potentially too complex - suggest refactoring

  • Flag classes >500 lines as needing organization review
  • Check for dead code, unused imports, and unused variables
  • Identify code reuse opportunities across similar implementations
  • Assess holistic maintainability - is code easy to understand and modify?
  • Flag code that appears AI-generated without sufficient human review
  • Verify Java naming conventions (PascalCase for classes, camelCase for methods/variables)
  • Check for proper JavaDoc on public classes and methods
  • Flag redundant comments that restate obvious code
  • Ensure proper error handling with specific exception types
  • Check for Optional usage instead of null returns
  • Validate proper use of try-with-resources for resource management

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
integ-test/**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

End-to-end scenarios need integration tests in integ-test/ module

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

integ-test/**/*IT.java: - Integration tests MUST use valid test data from resources

  • Verify test data files exist in integ-test/src/test/resources/
  • Check test assertions are meaningful and specific
  • Validate tests clean up resources after execution
  • Ensure tests are independent and can run in any order
  • Flag tests that reference non-existent indices (e.g., EMP)
  • Verify integration tests are in correct module (integ-test/)
  • Check tests can be run with ./gradlew :integ-test:integTest
  • Ensure proper test data setup and teardown
  • Validate end-to-end scenario coverage

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

Name integration tests with *IT.java suffix in OpenSearch SQL

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/test/**/*.java

⚙️ CodeRabbit configuration file

**/test/**/*.java: - Verify NULL input tests for all new functions

  • Check boundary condition tests (min/max values, empty inputs)
  • Validate error condition tests (invalid inputs, exceptions)
  • Ensure multi-document tests for per-document operations
  • Flag smoke tests without meaningful assertions
  • Check test naming follows pattern: test
  • Verify test data is realistic and covers edge cases
  • Verify test coverage for new business logic
  • Ensure tests are independent and don't rely on execution order
  • Validate meaningful test data that reflects real-world scenarios
  • Check for proper cleanup of test resources

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧠 Learnings (4)
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to **/*IT.java : Name integration tests with `*IT.java` suffix in OpenSearch SQL

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to integ-test/**/*IT.java : End-to-end scenarios need integration tests in `integ-test/` module

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Test SQL generation and optimization paths for Calcite integration changes

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Verify changes with `./gradlew :integ-test:integTest` before merge

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: build-linux (25, unit)
  • GitHub Check: build-linux (21, doc)
  • GitHub Check: build-linux (21, integration)
  • GitHub Check: build-linux (25, doc)
  • GitHub Check: build-linux (25, integration)
  • GitHub Check: build-linux (21, unit)
  • GitHub Check: bwc-tests-rolling-upgrade (21)
  • GitHub Check: bwc-tests-rolling-upgrade (25)
  • GitHub Check: bwc-tests-full-restart (25)
  • GitHub Check: bwc-tests-full-restart (21)
  • GitHub Check: security-it-linux (21)
  • GitHub Check: security-it-linux (25)
  • GitHub Check: build-windows-macos (macos-14, 21, integration)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, unit)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, unit)
  • GitHub Check: build-windows-macos (macos-14, 21, doc)
  • GitHub Check: build-windows-macos (macos-14, 25, unit)
  • GitHub Check: build-windows-macos (macos-14, 25, doc)
  • GitHub Check: build-windows-macos (macos-14, 25, integration)
  • GitHub Check: build-windows-macos (macos-14, 21, unit)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, integration)
  • GitHub Check: security-it-windows-macos (windows-latest, 21)
  • GitHub Check: security-it-windows-macos (macos-14, 21)
  • GitHub Check: security-it-windows-macos (windows-latest, 25)
  • GitHub Check: security-it-windows-macos (macos-14, 25)
  • GitHub Check: test-sql-cli-integration (21)
  • GitHub Check: CodeQL-Scan (java)
🔇 Additional comments (3)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (3)

25-55: LGTM! Class structure and constants are well-organized.

The use of @TestInstance(Lifecycle.PER_CLASS) with @BeforeAll properly addresses the previous concern about static mutable test state. Constants follow proper naming conventions and are clearly documented.


56-83: LGTM! Initialization is properly structured.

The test setup correctly uses @BeforeAll with instance-level lifecycle management, enabling proper one-time initialization without static state issues.


319-356: LGTM! Security roles and users are correctly configured.

The setup properly creates roles with appropriate access controls for testing index-level, field-level, and document-level security. The previous issue with redundant role creation has been resolved.

Signed-off-by: Simeon Widdis <[email protected]>
Copy link
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: 0

🧹 Nitpick comments (4)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (4)

56-62: Consider adding cleanup to ensure test isolation.

Integration tests should clean up created resources (indices, users, roles) after execution to maintain test independence and avoid side effects in CI environments.

💡 Consider adding an @afterall cleanup method
+ @SneakyThrows
+ @AfterAll
+ public void cleanup() {
+   // Delete test indices
+   deleteIndex(PUBLIC_LOGS);
+   deleteIndex(SENSITIVE_LOGS);
+   deleteIndex(SECURE_LOGS);
+   deleteIndex(EMPLOYEE_RECORDS);
+   
+   // Delete test users and roles
+   deleteUser(PUBLIC_USER);
+   deleteUser(SENSITIVE_USER);
+   deleteUser(LIMITED_USER);
+   deleteUser(MANAGER_USER);
+   deleteUser(HR_USER);
+   
+   deleteRole(PUBLIC_ROLE);
+   deleteRole(SENSITIVE_ROLE);
+   deleteRole(LIMITED_ROLE);
+   deleteRole(MANAGER_ROLE);
+   deleteRole(HR_ROLE);
+ }

As per coding guidelines, integration tests should clean up resources after execution and be independent.


254-307: Consider refactoring to reduce method length and repetition.

This method exceeds 50 lines and contains three similar loops that differ only by security level, count range, and message prefix. Extracting a helper method would improve maintainability.

💡 Proposed refactoring to reduce duplication
+ private void appendSecurityLevelDocs(StringBuilder bulk, String level, int start, int end) {
+   for (int i = start; i < end; i++) {
+     bulk.append(
+         String.format(
+             Locale.ROOT,
+             """
+             { "index": { "_index": "%s" } }
+             { "message": "%s message %d", "security_level": "%s", "timestamp": "2025-01-01T00:00:00Z" }
+             """,
+             SECURE_LOGS,
+             level,
+             i,
+             level));
+   }
+ }
+
  private void bulkInsertDocsWithSecurityLevel() throws IOException {
    StringBuilder bulk = new StringBuilder();
-
-   // 1000 public documents
-   for (int i = 0; i < 1000; i++) {
-     bulk.append(...);
-   }
-
-   // 500 internal documents
-   for (int i = 1000; i < 1500; i++) {
-     bulk.append(...);
-   }
-
-   // 500 confidential documents
-   for (int i = 1500; i < 2000; i++) {
-     bulk.append(...);
-   }
+   appendSecurityLevelDocs(bulk, "public", 0, 1000);
+   appendSecurityLevelDocs(bulk, "internal", 1000, 1500);
+   appendSecurityLevelDocs(bulk, "confidential", 1500, 2000);

    Request request = new Request("POST", "/_bulk");
    // ... rest of method
  }

As per coding guidelines, methods over 50 lines should be reviewed for complexity.


499-625: Eliminate duplication with parameterized tests.

Both row-level security test methods (testRowLevelSecurityV2 and testRowLevelSecurity) exceed 50 lines and share nearly identical logic, differing only in the engine toggle and assertion message prefixes. Since junit-jupiter-params was added to dependencies, consider using @ParameterizedTest to eliminate this duplication.

💡 Proposed parameterized test approach
+ @ParameterizedTest
+ @CsvSource({
+   "V2, true",
+   "V3, false"
+ })
+ public void testRowLevelSecurity(String engineVersion, boolean shouldDisableCalcite) throws IOException {
+   if (shouldDisableCalcite) {
+     disableCalcite();
+   }
+
+   String query =
+       String.format(
+           "search source=%s | fields security_level, message | stats count() by security_level",
+           SECURE_LOGS);
+   JSONObject result = executeQueryAsUser(query, LIMITED_USER);
+
+   var datarows = result.getJSONArray("datarows");
+   int totalDocs = 0;
+   boolean sawConfidential = false;
+   boolean sawInternal = false;
+   int publicDocs = 0;
+
+   for (int i = 0; i < datarows.length(); i++) {
+     var row = datarows.getJSONArray(i);
+     int count = row.getInt(0);
+     String securityLevel = row.getString(1);
+     totalDocs += count;
+
+     if ("confidential".equals(securityLevel)) {
+       sawConfidential = true;
+     } else if ("internal".equals(securityLevel)) {
+       sawInternal = true;
+     } else if ("public".equals(securityLevel)) {
+       publicDocs = count;
+     }
+   }
+
+   assertFalse(
+       String.format("[%s] SECURITY VIOLATION: limited_user should NOT see 'confidential' documents. "
+           + "This indicates ThreadContext is not being properly copied to async worker threads, "
+           + "causing queries to run with admin permissions and bypass row-level security.", engineVersion),
+       sawConfidential);
+
+   assertFalse(
+       String.format("[%s] SECURITY VIOLATION: limited_user should NOT see 'internal' documents. "
+           + "This indicates ThreadContext is not being properly copied to async worker threads, "
+           + "causing queries to run with admin permissions and bypass row-level security.", engineVersion),
+       sawInternal);
+
+   assertEquals(
+       1000,
+       publicDocs,
+       String.format("[%s] limited_user should ONLY see 'public' documents (~1000). "
+           + "Seeing more indicates row-level security is being bypassed.", engineVersion));
+
+   assertEquals(
+       1000,
+       totalDocs,
+       String.format("[%s] Total visible documents should be ~1000 (only public). "
+           + "Seeing 2000 documents indicates row-level security is completely bypassed.", engineVersion));
+ }
-
- @Test
- public void testRowLevelSecurityV2() throws IOException {
-   // ... 60 lines of duplicated code
- }
-
- @Test
- public void testRowLevelSecurity() throws IOException {
-   // ... 63 lines of duplicated code
- }

As per coding guidelines, methods over 50 lines should be reviewed for complexity, and code reuse opportunities should be identified.


400-496: Consider extracting field-checking logic into a helper method.

Multiple test methods use the same pattern to check for field presence in the schema (iterating through schema array and setting boolean flags). Extracting this into a reusable helper would reduce repetition.

💡 Example helper method
private Set<String> extractFieldNamesFromSchema(JSONObject result) {
  Set<String> fieldNames = new HashSet<>();
  var schema = result.getJSONArray("schema");
  for (int i = 0; i < schema.length(); i++) {
    fieldNames.add(schema.getJSONObject(i).getString("name"));
  }
  return fieldNames;
}

Usage:

Set<String> fields = extractFieldNamesFromSchema(hrResult);
assertTrue("hr_user should see 'name' field", fields.contains("name"));
assertTrue("hr_user should see 'ssn' field", fields.contains("ssn"));
// etc.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22564a6 and 764dfef.

📒 Files selected for processing (1)
  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

**/*.java: Use PascalCase for class names (e.g., QueryExecutor)
Use camelCase for method and variable names (e.g., executeQuery)
Use UPPER_SNAKE_CASE for constants (e.g., MAX_RETRY_COUNT)
Keep methods under 20 lines with single responsibility
All public classes and methods must have proper JavaDoc
Use specific exception types with meaningful messages for error handling
Prefer Optional<T> for nullable returns in Java
Avoid unnecessary object creation in loops
Use StringBuilder for string concatenation in loops
Validate all user inputs, especially queries
Sanitize data before logging to prevent injection attacks
Use try-with-resources for proper resource cleanup in Java
Maintain Java 11 compatibility when possible for OpenSearch 2.x
Document Calcite-specific workarounds in code

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

**/*.java: - Flag methods >50 lines as potentially too complex - suggest refactoring

  • Flag classes >500 lines as needing organization review
  • Check for dead code, unused imports, and unused variables
  • Identify code reuse opportunities across similar implementations
  • Assess holistic maintainability - is code easy to understand and modify?
  • Flag code that appears AI-generated without sufficient human review
  • Verify Java naming conventions (PascalCase for classes, camelCase for methods/variables)
  • Check for proper JavaDoc on public classes and methods
  • Flag redundant comments that restate obvious code
  • Ensure proper error handling with specific exception types
  • Check for Optional usage instead of null returns
  • Validate proper use of try-with-resources for resource management

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
integ-test/**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

End-to-end scenarios need integration tests in integ-test/ module

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java

⚙️ CodeRabbit configuration file

integ-test/**/*IT.java: - Integration tests MUST use valid test data from resources

  • Verify test data files exist in integ-test/src/test/resources/
  • Check test assertions are meaningful and specific
  • Validate tests clean up resources after execution
  • Ensure tests are independent and can run in any order
  • Flag tests that reference non-existent indices (e.g., EMP)
  • Verify integration tests are in correct module (integ-test/)
  • Check tests can be run with ./gradlew :integ-test:integTest
  • Ensure proper test data setup and teardown
  • Validate end-to-end scenario coverage

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/*IT.java

📄 CodeRabbit inference engine (.rules/REVIEW_GUIDELINES.md)

Name integration tests with *IT.java suffix in OpenSearch SQL

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
**/test/**/*.java

⚙️ CodeRabbit configuration file

**/test/**/*.java: - Verify NULL input tests for all new functions

  • Check boundary condition tests (min/max values, empty inputs)
  • Validate error condition tests (invalid inputs, exceptions)
  • Ensure multi-document tests for per-document operations
  • Flag smoke tests without meaningful assertions
  • Check test naming follows pattern: test
  • Verify test data is realistic and covers edge cases
  • Verify test coverage for new business logic
  • Ensure tests are independent and don't rely on execution order
  • Validate meaningful test data that reflects real-world scenarios
  • Check for proper cleanup of test resources

Files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
🧠 Learnings (4)
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to **/*IT.java : Name integration tests with `*IT.java` suffix in OpenSearch SQL

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Applies to integ-test/**/*IT.java : End-to-end scenarios need integration tests in `integ-test/` module

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Test SQL generation and optimization paths for Calcite integration changes

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
📚 Learning: 2025-12-02T17:27:55.938Z
Learnt from: CR
Repo: opensearch-project/sql PR: 0
File: .rules/REVIEW_GUIDELINES.md:0-0
Timestamp: 2025-12-02T17:27:55.938Z
Learning: Verify changes with `./gradlew :integ-test:integTest` before merge

Applied to files:

  • integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: build-linux (25, integration)
  • GitHub Check: build-linux (21, doc)
  • GitHub Check: build-linux (21, integration)
  • GitHub Check: build-linux (25, doc)
  • GitHub Check: build-linux (21, unit)
  • GitHub Check: build-linux (25, unit)
  • GitHub Check: bwc-tests-full-restart (21)
  • GitHub Check: bwc-tests-full-restart (25)
  • GitHub Check: bwc-tests-rolling-upgrade (25)
  • GitHub Check: bwc-tests-rolling-upgrade (21)
  • GitHub Check: security-it-linux (21)
  • GitHub Check: security-it-linux (25)
  • GitHub Check: security-it-windows-macos (windows-latest, 25)
  • GitHub Check: security-it-windows-macos (macos-14, 25)
  • GitHub Check: security-it-windows-macos (macos-14, 21)
  • GitHub Check: security-it-windows-macos (windows-latest, 21)
  • GitHub Check: build-windows-macos (macos-14, 21, unit)
  • GitHub Check: build-windows-macos (macos-14, 25, integration)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, unit)
  • GitHub Check: build-windows-macos (macos-14, 21, doc)
  • GitHub Check: build-windows-macos (windows-latest, 21, -PbuildPlatform=windows, integration)
  • GitHub Check: build-windows-macos (macos-14, 25, unit)
  • GitHub Check: build-windows-macos (macos-14, 25, doc)
  • GitHub Check: build-windows-macos (macos-14, 21, integration)
  • GitHub Check: build-windows-macos (windows-latest, 25, -PbuildPlatform=windows, unit)
  • GitHub Check: test-sql-cli-integration (21)
  • GitHub Check: CodeQL-Scan (java)
🔇 Additional comments (4)
integ-test/src/test/java/org/opensearch/sql/security/FGACIndexScanningIT.java (4)

25-55: Well-structured test class with clear security testing scope.

The class-level JavaDoc clearly documents the three levels of FGAC being tested (index, field, row), and constants are properly defined. The use of @TestInstance(Lifecycle.PER_CLASS) appropriately supports the @BeforeAll setup pattern.


56-62: Good setup pattern with @BeforeAll.

The initialization properly leverages @TestInstance(Lifecycle.PER_CLASS) and the previous issue with the static initialized flag has been resolved.


348-397: Index-level security tests are well-structured.

These tests properly validate index-level access control with clear assertions and appropriate error handling for forbidden access scenarios. Test naming follows best practices.


309-332: Security role setup is clean and properly structured.

The role and user creation logic is well-organized. The previous issue with redundant LIMITED_ROLE creation has been successfully resolved.

@Swiddis Swiddis mentioned this pull request Dec 31, 2025
8 tasks
}

@Test
public void testRowLevelSecurity() throws IOException {
Copy link
Collaborator

Choose a reason for hiding this comment

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

  • testRowLevelSecurity and testRowLevelSecurityV2 can be merge to testRowLevelSecurity
  • all tests should run with calcite enable and disable.

/** Bulk inserts documents to trigger background scanning. */
private void bulkInsertDocs(String indexName, String prefix) throws IOException {
StringBuilder bulk = new StringBuilder();
for (int i = 0; i < FGACIndexScanningIT.LARGE_DATASET_SIZE; i++) {
Copy link
Collaborator

@dai-chen dai-chen Jan 5, 2026

Choose a reason for hiding this comment

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

Why we need large test dataset to verify the issue? Is it an overhead for both CI and troubleshoot?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance Improves code quality, but not the product

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants