|
4 | 4 |
|
5 | 5 | import java.util.List; |
6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | +import org.openmetadata.schema.entity.policies.Policy; |
| 8 | +import org.openmetadata.schema.entity.policies.accessControl.Rule; |
| 9 | +import org.openmetadata.schema.type.Include; |
7 | 10 | import org.openmetadata.schema.type.MetadataOperation; |
| 11 | +import org.openmetadata.schema.utils.JsonUtils; |
| 12 | +import org.openmetadata.service.Entity; |
| 13 | +import org.openmetadata.service.exception.EntityNotFoundException; |
8 | 14 | import org.openmetadata.service.jdbi3.CollectionDAO; |
| 15 | +import org.openmetadata.service.jdbi3.PolicyRepository; |
9 | 16 |
|
10 | 17 | @Slf4j |
11 | 18 | public class MigrationUtil { |
12 | 19 |
|
13 | 20 | private MigrationUtil() {} |
14 | 21 |
|
15 | 22 | /** |
16 | | - * Retrofits default seeded policies that grant broad edit capability with the {@code Trigger} |
17 | | - * operation, keeping pre-existing customer behavior intact after {@code /trigger} starts |
18 | | - * enforcing authz (GH-27962). |
| 23 | + * Retrofits seeded bot policies that grant broad {@code EditAll} on {@code ["All"]} resources |
| 24 | + * with the {@code Trigger} operation. Pre-fix these identities could trigger pipelines because |
| 25 | + * {@code /trigger} skipped authz; the migration preserves that behavior under the new authz |
| 26 | + * enforcement (GH-27962). |
19 | 27 | * |
20 | | - * <p>Targets the rules whose resources are {@code "All"} and that already grant {@code EditAll} |
21 | | - * (the broad-bot allow rules) plus {@code DataStewardPolicy} which grants {@code EditOwners} — |
22 | | - * stewards can already reach trigger via the ownership-edit escalation path, so granting it |
23 | | - * explicitly aligns the policy with the effective capability and improves the audit trail. |
24 | | - * |
25 | | - * <p>Each call is idempotent via {@link |
| 28 | + * <p>Each entry is idempotent via {@link |
26 | 29 | * org.openmetadata.service.migration.utils.v160.MigrationUtil#addOperationsToPolicyRule}. |
27 | 30 | */ |
28 | | - public static void addTriggerOperationToDefaultPolicies(CollectionDAO collectionDAO) { |
| 31 | + public static void addTriggerOperationToDefaultBotPolicies(CollectionDAO collectionDAO) { |
29 | 32 | record PolicyRule(String policy, String rule) {} |
30 | 33 | List<PolicyRule> targets = |
31 | 34 | List.of( |
32 | 35 | new PolicyRule("IngestionBotPolicy", "IngestionBotRule-Allow"), |
33 | 36 | new PolicyRule("LineageBotPolicy", "LineageBotRule-Allow"), |
34 | 37 | new PolicyRule("ProfilerBotPolicy", "ProfilerBotBotRule-Allow"), |
35 | 38 | new PolicyRule("QualityBotPolicy", "QualityBotBotRule-Allow"), |
36 | | - new PolicyRule("UsageBotPolicy", "UsageBotRule-Allow-Usage"), |
37 | | - new PolicyRule("DataStewardPolicy", "DataStewardPolicy-EditRule")); |
| 39 | + new PolicyRule("UsageBotPolicy", "UsageBotRule-Allow-Usage")); |
38 | 40 | for (PolicyRule t : targets) { |
39 | 41 | addOperationsToPolicyRule( |
40 | 42 | t.policy(), t.rule(), List.of(MetadataOperation.TRIGGER), collectionDAO); |
41 | 43 | } |
42 | 44 | } |
| 45 | + |
| 46 | + /** |
| 47 | + * Adds a dedicated {@code DataStewardPolicy-TriggerRule} to the existing {@code |
| 48 | + * DataStewardPolicy} if not already present. Data stewards already have {@code EditOwners} on |
| 49 | + * all resources, so they could already reach trigger via an ownership rewrite; this rule makes |
| 50 | + * the capability explicit for audit clarity rather than burying it inside the existing edit |
| 51 | + * rule. |
| 52 | + * |
| 53 | + * <p>Mirrors the new-rule shape used by {@code |
| 54 | + * v180.MigrationUtil.addDenyDisplayNameRuleToBotPolicies}. Idempotent — skips when the rule |
| 55 | + * already exists. |
| 56 | + */ |
| 57 | + public static void addTriggerRuleToDataStewardPolicy(CollectionDAO collectionDAO) { |
| 58 | + PolicyRepository repository = (PolicyRepository) Entity.getEntityRepository(Entity.POLICY); |
| 59 | + try { |
| 60 | + Policy policy = repository.findByName("DataStewardPolicy", Include.NON_DELETED); |
| 61 | + boolean hasTriggerRule = |
| 62 | + policy.getRules().stream() |
| 63 | + .anyMatch( |
| 64 | + r -> |
| 65 | + "DataStewardPolicy-TriggerRule".equals(r.getName()) |
| 66 | + && r.getEffect() == Rule.Effect.ALLOW |
| 67 | + && r.getOperations() != null |
| 68 | + && r.getOperations().contains(MetadataOperation.TRIGGER)); |
| 69 | + if (!hasTriggerRule) { |
| 70 | + Rule triggerRule = |
| 71 | + new Rule() |
| 72 | + .withName("DataStewardPolicy-TriggerRule") |
| 73 | + .withDescription( |
| 74 | + "Allow data stewards to trigger ingestion pipelines. Stewards already have " |
| 75 | + + "EditOwners on all resources and can reach trigger via an ownership " |
| 76 | + + "rewrite; this rule makes the capability explicit for audit clarity.") |
| 77 | + .withResources(List.of("all")) |
| 78 | + .withOperations(List.of(MetadataOperation.TRIGGER)) |
| 79 | + .withEffect(Rule.Effect.ALLOW); |
| 80 | + policy.getRules().add(triggerRule); |
| 81 | + collectionDAO |
| 82 | + .policyDAO() |
| 83 | + .update(policy.getId(), policy.getFullyQualifiedName(), JsonUtils.pojoToJson(policy)); |
| 84 | + LOG.info("Added DataStewardPolicy-TriggerRule to DataStewardPolicy"); |
| 85 | + } else { |
| 86 | + LOG.debug("DataStewardPolicy already has TriggerRule, skipping"); |
| 87 | + } |
| 88 | + } catch (EntityNotFoundException ex) { |
| 89 | + LOG.warn("DataStewardPolicy not found, skipping TriggerRule addition"); |
| 90 | + } catch (Exception e) { |
| 91 | + LOG.error("Failed to add TriggerRule to DataStewardPolicy: {}", e.getMessage(), e); |
| 92 | + } |
| 93 | + } |
43 | 94 | } |
0 commit comments