Skip to content

fix(multi-tenancy): fix organization access validation across tenants (#300) - #8002

Open
Gaetan Santucci (GaetanSantucci) wants to merge 4 commits into
mainfrom
fix/300-security-cross-tenant-organization
Open

Gaetan Santucci (GaetanSantucci) wants to merge 4 commits into
mainfrom
fix/300-security-cross-tenant-organization

Conversation

@GaetanSantucci

Copy link
Copy Markdown
Member

Proposed changes

  • Improved cross-tenant security handling for organization-related access.
  • Ensured organization data is resolved within the correct tenant/request scope.
  • Added safeguards to prevent unauthorized access across tenant boundaries.

Testing Instructions

  1. Reproduce the organization access flow in a multi-tenant environment.
  2. Verify that access is allowed for the correct tenant.
  3. Verify that cross-tenant access is rejected as expected.
  4. Confirm existing organization functionality still works normally.

Related issues

  • Related OpenAEV-Platform/filigran-private#300
  • I consider the submitted work as finished
  • I tested the code for its functionality
  • For bug fix -> I implemented a test that covers the bug

Copilot AI lite review requested due to automatic review settings September 16, 2026 14:33
@github-actions

Copy link
Copy Markdown
Contributor

📖 Documentation check — ✅ Passed

3 functional file(s), 0 doc file(s) changed.

No documentation gaps detected.

Copilot AI left a comment

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.

Pull request overview

Improves organization tenant isolation by threading TxCtx through organization access paths and adding extensive HTTP isolation tests.

Changes:

  • Adds tenant-aware organization CRUD, search, options, and bulk operations.
  • Restricts related tags, teams, and injects by tenant.
  • Adds multi-tenant integration coverage.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
openaev-model/src/main/java/io/openaev/database/repository/OrganizationRepository.java Updated as part of this pull request.
openaev-api/src/test/java/io/openaev/rest/organization/OrganizationHttpIsolationTest.java Updated as part of this pull request.
openaev-api/src/main/java/io/openaev/service/organization/OrganizationService.java Updated as part of this pull request.
openaev-api/src/main/java/io/openaev/rest/organization/OrganizationApi.java Updated as part of this pull request.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +215 to +218
private Specification<Organization> inScope(TxCtx ctx) {
Set<String> tenantIds = TxCtxScopeUtils.tenantIdsFromHTTPCtx(ctx);
return (root, query, cb) ->
tenantIds.isEmpty() ? cb.disjunction() : root.get("tenant").get("id").in(tenantIds);
Comment on lines 93 to 94
return injectSearchService.getPageOfInjectResultsForOrganization(
organizationId, searchPaginationInput);
Comment on lines +468 to +472
@Test
@DisplayName("Tenant-prefixed collections and options contain only owned organizations")
void given_tenantPath_should_filterAllCollections() throws Exception {
// Arrange
tenantHelper.attachCurrentUserToTenant(tenantA.getId());
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestPropertySource(properties = "openaev.tenant.active-tables=tags")
@DisplayName("Organization HTTP tenant isolation")
class OrganizationHttpIsolationTest extends IntegrationTest {
@GaetanSantucci Gaetan Santucci (GaetanSantucci) changed the title fix in progress fix(multi-tenancy): fix organization access validation across tenants (#300) Sep 17, 2026
@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.02985% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.30%. Comparing base (99d8945) to head (8efac89).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
...naev/service/organization/OrganizationService.java 92.98% 1 Missing and 3 partials ⚠️

❌ Your project check has failed because the head coverage (11.57%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8002      +/-   ##
============================================
- Coverage     46.82%   46.30%   -0.52%     
- Complexity    12406    12482      +76     
============================================
  Files          3039     3020      -19     
  Lines        101275   101141     -134     
  Branches      16390    16281     -109     
============================================
- Hits          47420    46833     -587     
- Misses        50039    50567     +528     
+ Partials       3816     3741      -75     
Flag Coverage Δ
backend 46.30% <94.02%> (-0.52%) ⬇️
e2e 46.30% <94.02%> (-0.52%) ⬇️
frontend 46.30% <94.02%> (-0.52%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@GaetanSantucci
Gaetan Santucci (GaetanSantucci) force-pushed the fix/300-security-cross-tenant-organization branch from 8577344 to c231ab6 Compare September 18, 2026 13:31
private final EntityManager entityManager;

/** Lists organizations in the legacy tenant scope, bounded by the caller's authorized scope. */
@Transactional(readOnly = true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seeing as these functions are all called from the API layer where all methods are all already under a transactional, so it's not necessary to add it here. (same for following methods)

private final TagService tagService;
private final EntityManager entityManager;

/** Lists organizations in the legacy tenant scope, bounded by the caller's authorized scope. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Javadoc is incorrect (missing parameters, etc.)

/** Creates an organization in the tenant explicitly resolved by the API write-scope resolver. */
@Transactional(rollbackFor = Exception.class)
public Organization createOrganization(
TxCtx ctx, OrganizationCreateInput input, String tenantId) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ctx isn't useful here as the check was already done on the API layer

tenantIds.isEmpty() ? cb.disjunction() : root.get("tenant").get("id").in(tenantIds);
}

private Set<Tag> resolveTags(List<String> tagIds, String tenantId) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This function should not exist here. Instead, it whould call the tagSet function from TagService. But tagSet doesn't seem to support the tenant filter. So a new function doing this should be created in the tagService class.

Also, this class still fetches all tags requested by their ID and then filter them in memory instead of filtering directly in the select in the database which would be more efficient.

private Organization findAccessibleById(TxCtx ctx, String organizationId) {
Specification<Organization> specification =
inTenantScope(ctx).and((root, query, cb) -> cb.equal(root.get("id"), organizationId));
Session session = entityManager.unwrap(Session.class);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure why we need to disable the filtering here before making our query

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

Labels

filigran team Item from the Filigran team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants