Files
yidaima/RuoYi-Vue/docs/superpowers/specs/2026-07-11-plugin-permission-target-executor-design.md

5.8 KiB

Plugin Permission Target Executor Design

Context

P2-D8e2d can route each contribution to a frozen Executor and persist approval evidence, but only sql-jdbc:v1 changes a real target. Permission contributions still use dry-run:v1; configuring the reserved permission route to any other code fails because no adapter is installed.

P2-D8e2e introduces permission-jdbc:v1 for RuoYi-compatible target databases. It consumes a narrow structured document, creates one Plugin-owned button permission, grants it to named roles, records target ownership, and safely compensates only the resources it owns.

Target And Isolation

The adapter reuses the existing isolated JDBC target configuration and connection factory:

  • factory.plugin-execution.sql-target supplies environment, catalog, connection, and lease settings;
  • factory_plugin_target_receipt remains the durable per-step lease and idempotency ledger;
  • the target must be separate from the Factory control database;
  • sys_menu, sys_role, and sys_role_menu must be RuoYi-compatible;
  • permission ownership tables must be installed by the updated target bootstrap script.

The adapter never executes SQL supplied by a Plugin. All statements are fixed prepared statements owned by the platform.

Permission Document

Each PERMISSION + backend contribution contains exactly one JSON document:

{
  "schemaVersion": "1.0",
  "permissionCode": "generator:delivery:verify",
  "displayName": "交付验证",
  "parentPermissionCode": "generator:plugin:list",
  "roleKeys": ["common"]
}

Validation is strict:

  • only the five published fields are accepted;
  • schemaVersion must be 1.0;
  • permissionCode must equal the immutable contribution key and use a three-part lowercase permission identifier;
  • wildcard permission identifiers are rejected;
  • parentPermissionCode must identify a different existing active menu;
  • display name is non-blank and at most 50 characters;
  • one to 20 unique lowercase role keys are allowed;
  • the document is bounded to 64 KiB.

Role keys are sorted before computing a semantic document fingerprint. Apply and rollback resources may differ in whitespace or role ordering, but must describe the same semantic permission identity.

Apply Semantics

After claiming the generic target receipt lease, the adapter runs one database transaction:

  1. Resolve exactly one active parent menu by parentPermissionCode.
  2. Resolve exactly one active sys_role row for every role key.
  3. Lock the ownership row for permissionCode.
  4. Reject an existing unowned sys_menu.perms collision.
  5. Create a button (menu_type = 'F') with a generated target-local menu_id.
  6. Persist the Plugin owner, contribution key, semantic fingerprint, parent identity, generated menu ID, and ACTIVE status.
  7. Insert only the declared sys_role_menu grants and matching ownership rows.

An interrupted attempt can be reclaimed through the existing lease. If the ownership row is already ACTIVE, every immutable identity, menu field, owned role grant, and role ID must still match before the attempt can complete. Drift is rejected rather than overwritten.

Compensation Semantics

Compensation locks and validates the ownership row before deletion. It refuses to continue when:

  • the permission is not owned by the same Plugin and contribution key;
  • the semantic document fingerprint differs;
  • the target menu or an owned role grant has drifted;
  • another role was granted the Plugin-owned menu outside the ownership ledger;
  • the button unexpectedly has child menus.

When validation succeeds, one transaction deletes the owned role grants and button menu, removes active grant rows, and marks the permission ownership row REMOVED. The tombstone makes an interrupted compensation retry idempotent and preserves audit identity. A later apply by the same Plugin/contribution key may reactivate the permission with a new target menu ID after the prior version has been compensated.

Receipts And Failures

The external receipt is deterministic: permission:<environment>:<step-idempotency-key>. Duplicate SUCCEEDED delivery returns the stored receipt without touching target authorization rows. Active leases cannot be stolen; failed or expired attempts can be reclaimed.

All target errors are sanitized. JDBC URLs, credentials, SQL details, role IDs, and driver exceptions are not returned through control-side execution history.

First Delivery Plugin

delivery.preview-permission@1.0.0 publishes the reviewed generator:delivery:verify permission document and its compensation resource. The rehearsal target supplies an existing generator:plugin:list parent and common role, then proves publish-plan resolution, apply, duplicate delivery, role authorization, compensation, and duplicate compensation against H2 in MySQL mode.

Probe And UI

When permission-jdbc:v1 is selected by the default or route map, the existing manual JDBC target probe additionally checks both permission ownership tables. A missing permission schema has its own non-secret state and blocks a READY result. The Plugin page recognizes that state and treats both SQL and permission JDBC adapters as requiring the same isolated target readiness.

Compatibility And Scope

  • Default routing stays dry-run:v1.
  • SQL-only installations do not require permission ownership tables unless permission-jdbc:v1 is configured.
  • Existing SQL receipts and SQL Executor behavior are unchanged.
  • This stage does not create directory/menu pages, backend endpoints, users, roles, or arbitrary permission hierarchies.
  • It does not adopt pre-existing permission rows or delete unmanaged role grants.
  • Multi-person approval, external tickets, permission cache invalidation across a running target cluster, and the real admin-menu adapter remain later work.
  • Plaintext development credentials and TLS verification remain deferred by explicit user decision.