fix(ingestion-pipeline): inherit owners from service and authorize trigger#28109
fix(ingestion-pipeline): inherit owners from service and authorize trigger#28109manerow wants to merge 6 commits into
Conversation
a76c6e2 to
a3d7299
Compare
a3d7299 to
057d7d0
Compare
🔴 Playwright Results — 26 failure(s), 32 flaky✅ 3915 passed · ❌ 26 failed · 🟡 32 flaky · ⏭️ 199 skipped
Genuine Failures (failed on all attempts)❌
|
996dade to
5372307
Compare
… of mixing into EditRule
…n per code review
5372307 to
c75d5f8
Compare
Code Review ✅ Approved 5 resolved / 5 findingsInherits ingestion pipeline owners from parent services and mandates ✅ 5 resolved✅ Performance: batchFetchServices filter misses non-service parents
✅ Quality: Integration test uses legacy Joda-Time instead of java.time
✅ Quality: PR description contradicts code: LineageBot DOES get Trigger
✅ Quality: Generic catch swallows migration failures silently
✅ Quality: DataStewardPolicy.json missing trailing newline
OptionsDisplay: compact → Showing less information. Comment with these commands to change:
Was this helpful? React with 👍 / 👎 | Gitar |
|



Fixes #27962
Summary
Two coordinated platform fixes to make
isOwner()-based authorization work correctly onIngestionPipelineand to close a long-standing gap on the trigger endpoint:IngestionPipeline.ownersnow inherits from the parent referenced byservice(Service / TestSuite / App). Inherited owners are taggedinherited: true, indexed for search, and visible to the policy evaluator. Test-suite pipelines inherit transitively from the underlying Table viaTestSuiteRepository.setInheritedFields.POST /v1/services/ingestionPipelines/trigger/{id}previously skippedauthorizer.authorize(...)entirely, so any authenticated user could trigger any pipeline. It now authorizes againstMetadataOperation.TRIGGER(the existing enum value already declared in the method forlimits.enforceLimits— we just wire it to the authorizer).A coordinated set of seeded-policy updates + idempotent upgrade migration preserves pre-fix behavior for default roles that previously had broad capability, so no customer is surprised by the new authz check on the trigger endpoint.
What's changing
1.
IngestionPipelineRepository.setInheritedFieldsoverrideTableRepository.setInheritedFields:311-321,TestSuiteRepository.setInheritedFields, etc.).inheritOwners/inheritDomainshelpers (EntityRepository.java:4510-4525), which already null-guard and tag refs withinherited=true.try/catch EntityNotFoundExceptionprotects GET / list endpoints from 500s when a service is soft-deleted with orphan pipelines.setFieldsInBulkalready routes throughsuper.setFieldsInBulk→ basesetInheritedFields(List)→ per-entity call, so list endpoints get inheritance for free. No bulk override needed.2.
IngestionPipelineResource.triggerPipelineInternalauthorize callAdds the missing
authorizer.authorize(...)call to the existingOperationContext(entityType, MetadataOperation.TRIGGER)that was already built in the method:pipelineServiceClient == nullearly-return so authz fires even in environments without an Airflow client (tests, dev, customer instances without pipeline service).TRIGGERoperation value rather thanEDIT_ALL, consistent with upstream AI #200 - Add TRIGGER permission to application bots #25113 (AI #200 - Add TRIGGER permission to application bots) which already establishedTriggeras a discrete, explicitly-granted operation.getResourceContextById(id)builds aResourceContextthat loadsownersfor authz, so the inherited owners populated by Change 1 are visible toisOwner()evaluation.3. Seeded policies updated for
Trigger+ upgrade migrationEditAlldoes not implyTrigger(perCompiledRule.matchOperation:221-224— onlyEdit*-prefixed ops are covered byEditAll). To preserve pre-fix behavior for default roles that previously had broad capability, six seeded policies receiveTriggervia an idempotent migration:IngestionBotPolicyIngestionBotRule-AllowLineageBotPolicyLineageBotRule-Allow["All"]resourcesProfilerBotPolicyProfilerBotBotRule-Allow["All"]resourcesQualityBotPolicyQualityBotBotRule-Allow["All"]resourcesUsageBotPolicyUsageBotRule-Allow-Usage["All"]resourcesDataStewardPolicyDataStewardPolicy-EditRuleEditOwnerson all resources — stewards could already reach trigger via an ownership-rewrite escalation (verified empirically). GrantingTriggerdirectly aligns the policy with the effective capability and improves audit clarity.Migration (
migration/utils/v1129/MigrationUtil.addTriggerOperationToDefaultPolicies) iterates the six (policy, rule) targets and calls the existing idempotentv160.MigrationUtil.addOperationsToPolicyRulehelper. Wired into:migration/{mysql,postgres}/v1129/Migration.java— runs on upgrade to 1.12.9 (companion empty SQL placeholders inbootstrap/sql/migrations/native/1.12.9/{mysql,postgres}/).migration/{mysql,postgres}/v1130/Migration.java— runs again on upgrade to 1.13.0 as an idempotent safety net (existingrunDataMigrationbody extended with the call).Deliberately out of scope:
AutoClassificationBotPolicy—EditAllis scoped toTable/Containeronly;Triggerdoesn't apply to those resources, so adding it would be a no-op.DataConsumerPolicy— noEditAll, noEditOwners; Data Consumers genuinely shouldn't trigger pipelines.OrganizationPolicy-NoOwner-Rule— addingTriggerhere would re-introduce trivial-trigger for any unowned entity vianoOwner().OrganizationPolicy-Owner-Rulealready grants[All](which includesTrigger) toisOwner(), so no change needed.CompiledRule.matchOperation— no platform-wide semantic change.EditAll → Triggerwould have been a hard rule on the operator. We modify shipped policies, not the operator's behavior, so customer-builtEditAllpolicies remain unchanged and customers retain control over which roles trigger.4. Tests
New file:
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineOwnerInheritanceIT.java. Two tests in the new SDK-client framework (the oldIngestionPipelineResourceTest.javawas removed in #26204):testInheritedOwners_fromService— service owner appears on pipeline GET withinherited: true.testIsOwnerPolicy_appliesToEditAndTrigger— full Policy → Role → User chain withOperations: [EditAll, Trigger], Condition: isOwner(). Owner can PATCHdisplayName+ POST/trigger; non-owner gets precisepermissionNotAllowed(USER2, [EditDisplayName])/permissionNotAllowed(USER2, [Trigger])403s.Behavior change to call out
/triggernow requiresTrigger. Three groups of identities are affected:Can trigger after the fix:
All).OrganizationPolicy-Owner-Rule. Inheritance fix makes this fire automatically.DataStewardrole (via the migration above).EditAll:Ingestion,Application,Lineage,Profiler,Quality,Usage.Triggerexplicitly in a custom policy.Cannot trigger after the fix (intentional tightening):
DataConsumerrole.AutoClassificationBot(itsEditAllscope doesn't coveringestionPipeline).DefaultBot/ScimBot(no broad edit grant).Triggergrant — closes the pre-fix trivial-trigger hole.Customers should audit (the narrow regression surface):
/triggerusing a non-admin / non-default-bot identity needs an explicitTriggergrant.EditAlloningestionPipeline(not the defaultDataSteward, which is covered by the migration) needTriggeradded to keep their pre-fix trigger capability.Unaffected:
GET /pipelineStatusandPUT /pipelineStatus, neither of which calls/trigger.Why this shape
EditAllis the "broad edit capability" operator andTriggeris an "action" operation (alongsideDeploy,Kill). Conflating them would weaken the operation model and was rejected in favor of explicit seeded-policy grants.Triggerdirectly rather than relying on the ownership-edit escalation. The capability is functionally already there for stewards; making it explicit removes the indirection and gives a cleaner audit trail (oneTriggerevent vs. anEditOwnersPATCH followed by aTrigger).EditAllpolicies retain customer-controlled semantics. Customers who want broader trigger access addTriggerto their own roles; customers who want stricter trigger gates (like Orsted) get exactly what theirisOwner()policy already expressed.Test plan
IngestionPipelineOwnerInheritanceIT#testInheritedOwners_fromServicepasses.IngestionPipelineOwnerInheritanceIT#testIsOwnerPolicy_appliesToEditAndTriggerpasses.mvn -pl openmetadata-integration-tests -am clean install -DskipTestsbuilds cleanly.mvn spotless:applyclean.Triggerpresent inIngestionBotPolicy/LineageBotPolicy/ProfilerBotPolicy/QualityBotPolicy/UsageBotPolicy/DataStewardPolicypost-migration viaGET /api/v1/policies/name/{name}?fields=rules.ingestion_pipeline_search_indexafter deploy so the UI owner filter sees inherited owners.