fix(composite-key): use composite key on java side#6523
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates connector entities (Collector/Injector/Executor) and their repositories to use the database’s composite primary key (id, tenant_id) on the Java side, reducing cross-tenant ambiguity and removing the need for tenant @ManyToOne relations on those connector entities.
Changes:
- Switch connector entities to composite identifiers via
@IdClass(ConnectorCompositeId.class)and introduceTenantIdBase+TenantIdBaseListener. - Update Spring Data repositories to use
ConnectorCompositeIdas the ID type and adjust service/API/test code to set/usetenantIddirectly. - Simplify builtin injector update logic to use standard
save()now that updates are correctly tenant-scoped by the composite key.
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| openaev-model/src/main/java/io/openaev/database/repository/InjectorRepository.java | Switch repository ID type to ConnectorCompositeId; remove workaround native update. |
| openaev-model/src/main/java/io/openaev/database/repository/ExecutorRepository.java | Switch repository ID type to ConnectorCompositeId. |
| openaev-model/src/main/java/io/openaev/database/repository/CollectorRepository.java | Switch repository ID type to ConnectorCompositeId; remove unused finder. |
| openaev-model/src/main/java/io/openaev/database/model/TenantIdBase.java | New interface for entities storing raw tenantId (no Tenant relation). |
| openaev-model/src/main/java/io/openaev/database/model/Injector.java | Convert to composite key; replace Tenant relation with tenantId; update join table columns. |
| openaev-model/src/main/java/io/openaev/database/model/Executor.java | Convert to composite key; replace Tenant relation with tenantId. |
| openaev-model/src/main/java/io/openaev/database/model/ConnectorCompositeId.java | New composite ID type (id, tenantId) for connector PKs. |
| openaev-model/src/main/java/io/openaev/database/model/Collector.java | Convert to composite key; replace Tenant relation with tenantId. |
| openaev-model/src/main/java/io/openaev/database/model/BaseConnectorEntity.java | Remove Persistable/new-entity tracking (but needs cleanup per comments). |
| openaev-model/src/main/java/io/openaev/database/audit/TenantIdBaseListener.java | New listener to set tenantId from TenantContext on persist. |
| openaev-api/src/test/java/io/openaev/utils/fixtures/composers/CollectorComposer.java | Remove “force merge” logic now that PK is composite. |
| openaev-api/src/test/java/io/openaev/service/EndpointServiceTest.java | Update test setup to set executor.tenantId. |
| openaev-api/src/test/java/io/openaev/rest/InjectorApiTest.java | Assert against injector.tenantId instead of injector.tenant.id. |
| openaev-api/src/test/java/io/openaev/rest/inject_expectation/InjectExpectationServiceTest.java | Remove connector new-entity flag usage. |
| openaev-api/src/test/java/io/openaev/executors/tanium/service/TaniumExecutorServiceTest.java | Update executor setup to set tenantId. |
| openaev-api/src/test/java/io/openaev/executors/sentinelone/service/SentinelOneExecutorServiceTest.java | Update executor setup to set tenantId. |
| openaev-api/src/test/java/io/openaev/executors/paloaltocortex/service/PaloAltoCortexExecutorServiceTest.java | Update executor setup to set tenantId. |
| openaev-api/src/test/java/io/openaev/executors/ExecutorServiceTest.java | Update assertions to validate tenantId directly. |
| openaev-api/src/test/java/io/openaev/executors/crowdstrike/service/CrowdstrikeExecutorServiceTest.java | Update executor setup to set tenantId. |
| openaev-api/src/test/java/io/openaev/executors/caldera/service/CalderaExecutorServiceTest.java | Update executor setup to set tenantId. |
| openaev-api/src/main/java/io/openaev/service/InjectorService.java | Update injector creation/update paths to set/use tenantId; update ID-based lookup to composite IDs. |
| openaev-api/src/main/java/io/openaev/service/EndpointService.java | Set endpoint/agent tenants from executor tenantId via id-only Tenant references. |
| openaev-api/src/main/java/io/openaev/rest/injector/InjectorApi.java | Pass current tenant to composite-ID lookup. |
| openaev-api/src/main/java/io/openaev/rest/injector_contract/InjectorContractService.java | Replace injector.getTenant() usage with new Tenant(injector.getTenantId()) and tenantId-based queries. |
| openaev-api/src/main/java/io/openaev/rest/collector/service/CollectorService.java | Set collector.tenantId on registration. |
| openaev-api/src/main/java/io/openaev/rest/collector/CollectorApi.java | Fix lookup by using findByIdAndTenantId instead of findById. |
| openaev-api/src/main/java/io/openaev/importer/V1_DataImporter.java | Set contract tenant from dummy injector tenantId. |
| openaev-api/src/main/java/io/openaev/executors/tanium/service/TaniumExecutorService.java | Use executor.tenantId for tenant-scoped operations. |
| openaev-api/src/main/java/io/openaev/executors/sentinelone/service/SentinelOneExecutorService.java | Use executor.tenantId for tenant-scoped operations and entity creation. |
| openaev-api/src/main/java/io/openaev/executors/paloaltocortex/service/PaloAltoCortexExecutorService.java | Use executor.tenantId for tenant-scoped operations and entity creation. |
| openaev-api/src/main/java/io/openaev/executors/ExecutorService.java | Simplify registration to set only tenantId (no Tenant relation on executor). |
| openaev-api/src/main/java/io/openaev/executors/crowdstrike/service/CrowdStrikeExecutorService.java | Use executor.tenantId for tenant-scoped operations and entity creation. |
| openaev-api/src/main/java/io/openaev/executors/caldera/service/CalderaExecutorService.java | Use executor.tenantId for tenant-scoped operations and entity creation. |
| openaev-api/src/main/java/io/openaev/api/chaining/InjectExecutionStep.java | Set injector tenantId when creating injects from chaining steps. |
| @@ -19,29 +15,9 @@ | |||
| * correctly uses {@code persist()} for new entities and {@code merge()} for existing ones. | |||
| @JoinColumn(name = "injector_id", referencedColumnName = "injector_id"), | ||
| @JoinColumn(name = "tenant_id", referencedColumnName = "tenant_id") | ||
| }) | ||
| }, | ||
| inverseJoinColumns = | ||
| @JoinColumn(name = "injector_contract_id", referencedColumnName = "injector_contract_id")) |
94fd7ea to
2631f9a
Compare
| private String tenantId; | ||
|
|
||
| @NotFound(action = NotFoundAction.IGNORE) | ||
| @ManyToOne(fetch = FetchType.EAGER) |
| import java.io.Serializable; | ||
| import java.util.Objects; | ||
|
|
||
| /** Composite identifier for the {@link InjectorInjectorContract} join entity. */ | ||
| public class InjectorInjectorContractId implements Serializable { | ||
|
|
||
| private String injectorId; | ||
| private String injectorContractId; | ||
| private String tenantId; | ||
|
|
||
| public InjectorInjectorContractId() {} |
Codecov Report❌ Patch coverage is ❌ Your project check has failed because the head coverage (2.89%) 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 #6523 +/- ##
============================================
- Coverage 44.47% 44.42% -0.05%
+ Complexity 7538 7536 -2
============================================
Files 2316 2316
Lines 64296 64311 +15
Branches 8552 8555 +3
============================================
- Hits 28595 28573 -22
- Misses 33874 33912 +38
+ Partials 1827 1826 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Known follow-ups (deliberately deferred)These items were found while finishing this PR and intentionally NOT fixed here, to keep the PR regression-free. They are latent (no live path today) and better decided at the design level than patched under pressure. 1.
|
What
Completes the composite-key migration for connector entities (Injector, Collector, Executor) and the injector-contract relationship, so the Hibernate model matches the composite primary key
(id, tenant_id)that already ships in the schema.Why
Built-in connector ids are shared across tenants, so
idalone cannot be the primary key. The database already uses a composite(id, tenant_id)key, but the JPA mapping was still single@Id, with native-query workarounds for the resulting multi-tenant bugs (a cross-tenant contract leak, and an UPDATE that matched every tenant). This PR moves the model to composite keys and removes those workarounds, fixing the bugs at the mapping level.Key changes
Injector,Collector,Executoruse a composite@IdClass(ConnectorCompositeId)(id, tenant_id);InjectorContractuses@EmbeddedId.@ManyToManyto a dedicated join entityInjectorInjectorContract. A@ManyToManycannot express the sharedtenant_idcolumn in both join groups; the join entity can, and it makes cross-tenant links structurally impossible at the database level.Inject.injectoruses@NotFound(IGNORE)so an uninstalled connector degrades to null instead of failing audit serialization on every inject update.InjectorInjectorContractconstructor, the single point where every link is created.Testing
openaev-apisuite: 3688 tests, no regressions attributable to these changes.Follow-ups (documented separately)
Three latent, design-level items were deliberately deferred rather than patched here, to avoid regression risk on working code:
equals/hashCodealignment with the composite key,@NotFoundassociations,@NotFoundon the join entity.