Skip to content

feat(feed): per-entity CreateTask/EditTask authorization for Feed task threads#29166

Open
yan-3005 wants to merge 6 commits into
mainfrom
dar-edit-tasks-op
Open

feat(feed): per-entity CreateTask/EditTask authorization for Feed task threads#29166
yan-3005 wants to merge 6 commits into
mainfrom
dar-edit-tasks-op

Conversation

@yan-3005

@yan-3005 yan-3005 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds two new `MetadataOperation` verbs — `CreateTask` and `EditTask` — and wires them into the Feed API so task threads (`POST /v1/feed` with `type=Task` and `PATCH /v1/feed/{id}`) are authorized against the target entity referenced by `about`, not against the abstract `task` resource.
  • This unblocks per-entity conditional rules (e.g. `isOwner()`, `matchAnyTag()`) for task filing — previously impossible because the resource-level `task.Create` check has no entity in scope to evaluate the condition against.
  • Default `DataConsumerPolicy` keeps the legacy `task.Create` grant for UI compatibility AND adds an `all`-scoped `CreateTask`/`EditTask` allow rule, so default UX is unchanged. Admins restrict by overriding the new rule with conditions.

Background

When a user files a Data Access Request (or any task) against table T, today's authorization checks operation `Create` on the global `task` resource. There is no entity in scope, so a rule like `isOwner()` can never be meaningfully evaluated — the rule's condition treats the would-be task itself as the subject, but the task doesn't exist yet and has no owner. Result: `CONDITIONAL_ALLOW` at resource level, UI converts to `false`, button hidden.

The fix is structural: the verb that gates task creation must live on the target entity's resource so the condition evaluates against table T (which has owners, tags, etc.). Existing parallel pattern: `CreateTests` / `EditTests` on table for test-case workflows.

Changes

File Change
`openmetadata-spec/.../resourceDescriptor.json` Add `CreateTask` and `EditTask` enum values
`openmetadata-service/.../ResourceRegistry.java` Add both verbs to `COMMON_OPERATIONS` so every entity exposes them
`openmetadata-service/.../resources/feeds/FeedResource.java` Authorize `CreateTask` on `about` entity at `POST /v1/feed` (Task type); authorize `EditTask` on `about` entity at `PATCH /v1/feed/{id}` (Task type)
`openmetadata-service/.../policy/DataConsumerPolicy.json` Keep legacy `task.Create` grant (UI compat) + add `all`-scoped `CreateTask`/`EditTask` grant (preserves default UX)

`EditTask` is auto-granted by anyone holding `EditAll` on the entity via the existing `Edit*` subsumption logic in `CompiledRule`. `CreateTask` is not subsumed — must be granted explicitly (matches `CreateTests` precedent).

Tests

  • Unit (`ResourceRegistryTaskOperationsTest`): asserts both verbs appear on every entity descriptor built via `ResourceRegistry.addResource`.
  • Integration (`FeedTaskAuthzIT`):
    • admin POST/PATCH task happy paths
    • non-admin user with explicit `DENY CreateTask` on `All` → POST blocked
    • non-admin user with explicit `DENY EditTask` on `All` → PATCH blocked

Local run: `Tests run: 4, Failures: 0, Errors: 0, Skipped: 0`.

Test plan

  • CI green
  • Verify upgrade path: existing tenants retain task-filing ability after migration (default seed rules cover this; existing custom policies are unaffected — they would need to grant `CreateTask`/`EditTask` to restrict)
  • Verify admin override path: write an `isOwner`-conditional rule on `CreateTask` for resource `table` and confirm non-owners get 403 on POST while owners succeed

Notes for downstream UI work (not in this PR)

The current UI gate (`getResourcePermission(TASK).Create`) continues to work because the legacy `task.Create` seed rule is preserved. A follow-up UI change can switch to entity-level `getEntityPermissionByFqn(aboutType, aboutFqn).CreateTask` to also hide the button for non-owners — without that change, non-owners see the button and get a 403 toast on submit.

🤖 Generated with Claude Code

Greptile Summary

This PR adds two new MetadataOperation verbs — CreateTask and EditTask — and wires them into the Feed API so task thread creation and editing are authorized against the target entity referenced by about, not the abstract task resource. A matching migration step and seed-policy update ensure that existing deployments preserve default UX while admins can now apply per-entity conditional rules like isOwner().

  • New authorization path: POST /v1/feed (Task type) and PATCH /v1/feed/{id} (Task type) call authorizeAgainstAbout, which builds an entity-scoped ResourceContext and OperationContext and runs them through the standard Authorizer, enabling conditions that inspect actual entity attributes.
  • Migration: addTaskRuleToDataConsumerPolicy in v200/MigrationUtil ensures upgraded tenants receive the new DataConsumerPolicy-TaskRule; the seed JSON receives the same rule for fresh installs.
  • Spec + UI types: CreateTask / EditTask enum values are added to resourceDescriptor.json and five TypeScript-generated files, completing the type surface across stack layers.

Confidence Score: 5/5

Safe to merge — the new authorization gates follow established patterns, the migration is idempotent, and the default policy preserves existing UX for all non-admin users.

The core authorization change is narrowly scoped: two new helper methods in FeedResource gate only Task-type threads, leaving all other thread types untouched. The migration step is idempotent (name-check before insert) and covered by both MySQL and Postgres paths. The only cosmetic concern is that the seed JSON renames the legacy task.Create rule entry while the existing migration constant still uses the old name, which can create a redundant rule entry depending on startup order — no functional impact, no security implication.

openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v200/MigrationUtil.java — the CREATE_TASK_RULE_NAME constant is out of sync with the renamed seed entry.

Important Files Changed

Filename Overview
openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java New authorizeThreadCreate / authorizeThreadEdit / authorizeAgainstAbout helpers cleanly gate POST and PATCH for Task-type threads; placement before entity construction is correct and follows the CreateTests/EditTests pattern.
openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v200/MigrationUtil.java addTaskRuleToDataConsumerPolicy is idempotent and follows the addCreateTaskRuleToDataConsumerPolicy pattern; the CREATE_TASK_RULE_NAME constant is now stale relative to the renamed seed entry, risking a duplicate rule on installations where the seed runs before the migration name-check fires.
openmetadata-service/src/main/java/org/openmetadata/service/ResourceRegistry.java CREATE_TASK and EDIT_TASK added to COMMON_OPERATIONS, correctly exposing both verbs on every entity descriptor.
openmetadata-service/src/main/resources/json/data/policy/DataConsumerPolicy.json Legacy task.Create rule preserved under a new name for UI compat; new DataConsumerPolicy-TaskRule grants CreateTask/EditTask on all resources for fresh installs.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FeedTaskAuthzIT.java Admin happy-path tests and two deny-rule tests (CreateTask / EditTask) cover the new authorization paths; test isolation via per-test namespace and UUID-prefixed resources is solid.

Comments Outside Diff (1)

  1. openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java, line 297-337 (link)

    P1 EditTask gate bypassed by resolve/close endpoints

    PATCH /v1/feed/{id} now enforces EditTask, but the task-specific PUT /tasks/{id}/resolve and PUT /tasks/{id}/close endpoints both call dao.checkPermissionsForResolveTask(), which checks admin, owner, assignee, and creator roles — it has no awareness of the new EditTask operation. A user explicitly denied EditTask on a target entity can still mutate task state (resolve or close) through these endpoints, bypassing the intent of the new per-entity authorization model.

Reviews (6): Last reviewed commit: "Merge branch 'main' into dar-edit-tasks-..." | Re-trigger Greptile

…eed thread tasks

Adds two new MetadataOperation verbs and wires them into the Feed API so that
filing or editing a Task thread is now authorized against the target entity
(the entity referenced by `about`) instead of the abstract `task` resource.

This lets policy authors write conditional rules like "only owners of this
table can file tasks against it" using the standard `isOwner()` /
`matchAnyTag()` expressions, which previously could not be evaluated at the
resource-level `task.Create` check because no entity instance was in scope.

Changes:
- `resourceDescriptor.json`: add `CreateTask` and `EditTask` to the
  MetadataOperation enum.
- `ResourceRegistry`: expose both verbs on every entity via COMMON_OPERATIONS,
  so policies can target table/topic/dashboard/etc. `EditTask` is auto-granted
  by `EditAll` via existing Edit* subsumption.
- `FeedResource.createThread` / `updateThread`: when the thread type is `Task`,
  resolve the `about` entity link and authorize `CreateTask` (POST) or
  `EditTask` (PATCH) against the target entity. Other thread types (Conversation)
  are unaffected.
- `DataConsumerPolicy.json`: preserve current UX by keeping the legacy
  `task.Create` rule (so the existing UI resource-level gate keeps returning
  true) and adding a new `all`-scoped rule granting `CreateTask` and `EditTask`
  to every authenticated user by default. Admins restrict by overriding the
  new rule with conditions.

Tests:
- `ResourceRegistryTaskOperationsTest`: unit test asserting both verbs appear
  on every entity descriptor.
- `FeedTaskAuthzIT`: integration tests covering admin happy paths (POST + PATCH)
  and DENY-policy enforcement for both verbs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 18, 2026 08:22
@yan-3005 yan-3005 added the safe to test Add this label to run secure Github workflows on PRs label Jun 18, 2026

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@yan-3005 yan-3005 self-assigned this Jun 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ TypeScript Types Auto-Updated

The generated TypeScript types have been automatically updated based on JSON schema changes in this PR.

@github-actions github-actions Bot requested a review from a team as a code owner June 18, 2026 08:27
…sumerPolicy

Seed policies are create-if-not-exists in initializeEntity, so the new
DataConsumerPolicy-TaskRule added in the previous commit only takes effect on
fresh installations. Existing tenants would silently lose task-filing for
non-admin users on upgrade because FeedResource.authorizeThreadCreate /
authorizeThreadEdit now require CreateTask / EditTask grants that aren't
present in the persisted DataConsumerPolicy row.

Adds addTaskRuleToDataConsumerPolicy() to v1130 MigrationUtil mirroring the
v200 addCreateTaskRuleToDataConsumerPolicy() precedent. The migration:
- locates the system DataConsumerPolicy
- checks for an existing DataConsumerPolicy-TaskRule
- if missing, appends an allow rule for CreateTask + EditTask on resources=["all"]
- swallows EntityNotFoundException (fresh installs may not have it yet) and
  logs other failures rather than failing the migration

Wired into mysql and postgres v1130 Migration.runDataMigration().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 18, 2026 08:41

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

yan-3005 and others added 2 commits June 18, 2026 14:21
…v200

The development version is now 2.0.0-SNAPSHOT, so the migration belongs in
v200 alongside the existing addCreateTaskRuleToDataConsumerPolicy backfill it
follows. Mirrors the v200 precedent: locate DataConsumerPolicy by name, append
DataConsumerPolicy-TaskRule (CreateTask + EditTask on resources=["all"]) if
absent, idempotent existence check, swallow EntityNotFoundException for fresh
installs, log other failures without aborting the migration. Wired into
runDataMigration() in mysql/v200/Migration and postgres/v200/Migration.

Removes the v1130 placement and the unused imports it pulled into
v1130/MigrationUtil.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 18, 2026 08:57

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
62.28% (66607/106947) 44.04% (37261/84607) 45.38% (11189/24653)

@gitar-bot

gitar-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Enables per-entity authorization for Feed tasks by adding CreateTask and EditTask operations, successfully migrating existing policies to maintain backward compatibility. The implementation secures task creation and editing workflows while allowing for granular conditional rules.

✅ 2 resolved
Bug: Upgrade regression: existing tenants lose task-filing without policy migration

📄 openmetadata-service/src/main/resources/json/data/policy/DataConsumerPolicy.json:24-30 📄 openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java:431-445
This PR makes POST /v1/feed (Task) and PATCH /v1/feed/{id} (Task) newly authorize CreateTask/EditTask against the target entity (FeedResource.authorizeThreadCreate / authorizeThreadEdit). Previously these endpoints performed no such authorization. The new grant that preserves default UX is added only to the seed file DataConsumerPolicy.json (the new DataConsumerPolicy-TaskRule).

However, seed policies are create-if-not-exists, not upsert. EntityRepository.initializeEntity (openmetadata-service/.../jdbi3/EntityRepository.java:~1449) returns early when the entity already exists, and the reseed path (migration/utils/.../MigrationUtil.reseedPolicies) only seeds missing policies. Therefore, on upgrade, an existing deployment's DataConsumerPolicy row in the database will NOT receive the new CreateTask/EditTask rule.

Result: after upgrade, non-admin users authorized solely by the default DataConsumerPolicy can no longer file tasks (data access requests, description/tag suggestions) or patch task threads — they will get 403. This is a silent functional regression for every existing tenant, contradicting the PR's stated upgrade-safety goal ("existing tenants retain task-filing ability after migration"): no migration is included in this PR.

Fix: add a data migration (mysql/postgres vXXX) that patches the existing DataConsumerPolicy (and any other affected default policies) to append the CreateTask/EditTask allow rule, matching the precedent used for prior policy-rule additions. Without it, the only path to restore task filing is manual admin policy editing on every upgraded instance.

Edge Case: Backfill failure is silently swallowed, regression can persist

📄 openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java:789-794
addTaskRuleToDataConsumerPolicy is the migration whose entire purpose is to prevent upgraded tenants from losing non-admin task-filing ability. However, the generic catch (Exception ex) block only logs an error and does not rethrow (MigrationUtil.java:791-794). If the policy update fails for a transient reason (DB contention, serialization issue, etc.), the migration completes 'successfully' and the DataConsumerPolicy-TaskRule is never written — silently reintroducing the exact 403 regression this commit is meant to fix, with only an ERROR line in the logs to indicate it.

This matches the broad error-handling style used elsewhere in this file (so it is intentional defensiveness to avoid blocking startup), hence minor. Consider at least surfacing the failure more prominently, or letting it propagate so the migration framework can retry/flag it, given the security/UX impact of a missing grant.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants