feat(ql3): persist package secret approval plans

This commit is contained in:
whyour
2026-08-13 12:43:43 +08:00
parent e43fd607aa
commit 3a58e1508e
16 changed files with 1335 additions and 94 deletions
@@ -386,6 +386,72 @@ export const pluginPackageSecretBindings = ql3Schema.table(
],
);
export const pluginPackageSecretBindingApprovalPlans = ql3Schema.table(
'plugin_package_secret_binding_approval_plans',
{
actionRef: varchar('action_ref', { length: 255 }).primaryKey(),
approvalPlanDigest: char('approval_plan_digest', { length: 64 }).notNull(),
bindingPlanDigest: char('binding_plan_digest', { length: 64 }).notNull(),
generationDigest: char('generation_digest', { length: 64 }).notNull(),
projectId: varchar('project_id', { length: 128 }).notNull(),
packageName: varchar('package_name', { length: 63 }).notNull(),
installationId: varchar('installation_id', { length: 128 }).notNull(),
lockDigest: char('lock_digest', { length: 64 }).notNull(),
generation: integer('generation').notNull(),
manifestDigest: char('manifest_digest', { length: 64 }).notNull(),
requestedByType: varchar('requested_by_type', { length: 16 }).notNull(),
requestedById: varchar('requested_by_id', { length: 255 }).notNull(),
plannedAtMs: bigint('planned_at_ms', { mode: 'number' }).notNull(),
expiresAtMs: bigint('expires_at_ms', { mode: 'number' }).notNull(),
planJson: jsonb('plan_json').$type<Record<string, unknown>>().notNull(),
},
(table) => [
foreignKey({
name: 'ql3_plugin_package_secret_binding_approval_plan_project_fk',
columns: [table.projectId],
foreignColumns: [projects.id],
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
name: 'ql3_plugin_package_secret_binding_approval_plan_install_fk',
columns: [table.installationId],
foreignColumns: [pluginPackageInstalls.installationId],
})
.onDelete('restrict')
.onUpdate('restrict'),
check(
'ql3_plugin_package_secret_binding_approval_plan_identity_check',
sql`${table.actionRef} ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$' and ${table.projectId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.packageName} ~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$' and ${table.installationId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.generation} between 1 and 2147483647 and ${table.requestedByType} = 'user' and octet_length(${table.requestedById}) between 1 and 255 and ${table.requestedById} !~ '[[:cntrl:]]'`,
),
check(
'ql3_plugin_package_secret_binding_approval_plan_digest_check',
sql`${table.approvalPlanDigest} ~ '^[0-9a-f]{64}$' and ${table.bindingPlanDigest} ~ '^[0-9a-f]{64}$' and ${table.generationDigest} ~ '^[0-9a-f]{64}$' and ${table.lockDigest} ~ '^[0-9a-f]{64}$' and ${table.manifestDigest} ~ '^[0-9a-f]{64}$'`,
),
check(
'ql3_plugin_package_secret_binding_approval_plan_time_check',
sql`${table.plannedAtMs} >= 0 and ${table.expiresAtMs} > ${table.plannedAtMs} and ${table.expiresAtMs} - ${table.plannedAtMs} <= 900000`,
),
check(
'ql3_plugin_package_secret_binding_approval_plan_json_check',
sql`jsonb_typeof(${table.planJson}) = 'object' and octet_length(${table.planJson}::text) between 2 and 98304 and ${table.planJson} @> jsonb_build_object('schema', 'qinglong/plugin-package-secret-binding-approval-plan@v1', 'actionRef', ${table.actionRef}, 'approvalPlanDigest', ${table.approvalPlanDigest}, 'requestedBy', jsonb_build_object('type', ${table.requestedByType}, 'id', ${table.requestedById}), 'expiresAtMs', ${table.expiresAtMs}, 'bindingPlan', jsonb_build_object('schema', 'qinglong/plugin-package-secret-binding-plan@v1', 'planDigest', ${table.bindingPlanDigest}, 'plannedAtMs', ${table.plannedAtMs}, 'target', jsonb_build_object('generationDigest', ${table.generationDigest}, 'projectId', ${table.projectId}, 'packageName', ${table.packageName}, 'installationId', ${table.installationId}, 'lockDigest', ${table.lockDigest}, 'generation', ${table.generation}, 'manifestDigest', ${table.manifestDigest}))) and jsonb_typeof(${table.planJson} #> '{bindingPlan,entries}') = 'array' and jsonb_array_length(${table.planJson} #> '{bindingPlan,entries}') between 1 and 64`,
),
uniqueIndex(
'ql3_plugin_package_secret_binding_approval_plan_digest_uidx',
).on(table.approvalPlanDigest),
index('ql3_plugin_package_secret_binding_approval_plan_target_idx').on(
table.projectId,
table.packageName,
table.generation,
table.actionRef,
),
index('ql3_plugin_package_secret_binding_approval_plan_expiry_idx').on(
table.expiresAtMs,
table.actionRef,
),
],
);
export const projectToolDefinitionSnapshots = ql3Schema.table(
'project_tool_definition_snapshots',
{
@@ -5873,6 +5939,7 @@ export const ql3PostgresTables = [
pluginPackageInstallMutations,
pluginPackageMaterializedRevisions,
pluginPackageSecretBindings,
pluginPackageSecretBindingApprovalPlans,
projectToolDefinitionSnapshots,
projectToolDefinitionSnapshotSources,
pluginPackageQuarantineEvents,
@@ -15,8 +15,8 @@ export interface PostgresSchemaContractFunction {
export interface PostgresSchemaContract {
readonly schema: 'ql3';
readonly contractName: 'control-core';
readonly contractVersion: 59;
readonly migrationId: 'pg-0060-plugin-package-secret-materialization-guard';
readonly contractVersion: 60;
readonly migrationId: 'pg-0061-plugin-package-secret-binding-approval-plans';
readonly minimumServerMajor: 16;
readonly maximumServerMajor: 18;
readonly capabilities: Readonly<{
@@ -56,6 +56,7 @@ export interface PostgresSchemaContract {
plugin_package_management_quota: 1;
plugin_package_materialized_revision: 1;
plugin_package_secret_binding: 1;
plugin_package_secret_binding_approval_plan: 1;
plugin_package_secret_materialization: 1;
plugin_package_proposal: 1;
plugin_package_publisher_provenance: 1;
@@ -106,8 +107,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
Object.freeze({
schema: 'ql3',
contractName: 'control-core',
contractVersion: 59,
migrationId: 'pg-0060-plugin-package-secret-materialization-guard',
contractVersion: 60,
migrationId: 'pg-0061-plugin-package-secret-binding-approval-plans',
minimumServerMajor: 16,
maximumServerMajor: 18,
capabilities: Object.freeze({
@@ -140,6 +141,7 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
plugin_package_management_quota: 1,
plugin_package_materialized_revision: 1,
plugin_package_secret_binding: 1,
plugin_package_secret_binding_approval_plan: 1,
plugin_package_secret_materialization: 1,
plugin_package_proposal: 1,
plugin_package_publisher_provenance: 1,
@@ -260,6 +262,23 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'binding_digest',
'binding_json',
]),
table('plugin_package_secret_binding_approval_plans', [
'action_ref',
'approval_plan_digest',
'binding_plan_digest',
'generation_digest',
'project_id',
'package_name',
'installation_id',
'lock_digest',
'generation',
'manifest_digest',
'requested_by_type',
'requested_by_id',
'planned_at_ms',
'expires_at_ms',
'plan_json',
]),
table('project_tool_definition_snapshots', [
'project_id',
'active_vector_digest',
@@ -1439,6 +1458,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_plugin_package_secret_binding_generation_uidx',
'ql3_plugin_package_secret_binding_digest_uidx',
'ql3_plugin_package_secret_binding_install_idx',
'plugin_package_secret_binding_approval_plans_pkey',
'ql3_plugin_package_secret_binding_approval_plan_digest_uidx',
'ql3_plugin_package_secret_binding_approval_plan_target_idx',
'ql3_plugin_package_secret_binding_approval_plan_expiry_idx',
'project_tool_definition_snapshots_pkey',
'ql3_project_tool_snapshot_withdrawal_key',
'ql3_project_tool_definition_snapshot_digest_uidx',
@@ -1733,6 +1756,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_plugin_package_secret_binding_identity_check',
'ql3_plugin_package_secret_binding_digest_check',
'ql3_plugin_package_secret_binding_json_check',
'ql3_plugin_package_secret_binding_approval_plan_identity_check',
'ql3_plugin_package_secret_binding_approval_plan_digest_check',
'ql3_plugin_package_secret_binding_approval_plan_time_check',
'ql3_plugin_package_secret_binding_approval_plan_json_check',
'ql3_plugin_package_quarantine_identity_check',
'ql3_plugin_package_quarantine_state_check',
'ql3_plugin_package_quarantine_subject_check',
@@ -2188,6 +2215,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_plugin_package_materialized_revision_project_fk',
'ql3_plugin_package_secret_binding_project_fk',
'ql3_plugin_package_secret_binding_install_fk',
'ql3_plugin_package_secret_binding_approval_plan_project_fk',
'ql3_plugin_package_secret_binding_approval_plan_install_fk',
'ql3_project_tool_definition_snapshot_project_fk',
'ql3_project_tool_definition_snapshot_source_snapshot_fk',
'ql3_project_tool_definition_snapshot_source_install_fk',
@@ -2338,6 +2367,23 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_run_retry_policies_run_fk',
]),
functions: Object.freeze([
Object.freeze({
name: 'plugin_package_secret_binding_planning_snapshot',
identityArguments:
'p_project_id character varying, p_package_name character varying',
owner: 'ql3_migration',
securityDefiner: true,
volatility: 'volatile',
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
}),
Object.freeze({
name: 'create_plugin_package_secret_binding_approval_plan',
identityArguments: 'p_plan_json jsonb',
owner: 'ql3_migration',
securityDefiner: true,
volatility: 'volatile',
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
}),
Object.freeze({
name: 'enforce_plugin_package_secret_materialization',
identityArguments: '',
@@ -173,6 +173,12 @@ const REQUIRED_RUNTIME_PRIVILEGES = Object.freeze({
update: false,
delete: false,
}),
plugin_package_secret_binding_approval_plans: Object.freeze({
select: false,
insert: false,
update: false,
delete: false,
}),
project_tool_definition_snapshots: Object.freeze({
select: false,
insert: false,
@@ -692,6 +698,12 @@ const REQUIRED_ADMIN_PRIVILEGES = Object.freeze({
update: false,
delete: false,
}),
plugin_package_secret_binding_approval_plans: Object.freeze({
select: false,
insert: false,
update: false,
delete: false,
}),
project_tool_definition_snapshots: Object.freeze({
select: false,
insert: false,
@@ -1193,6 +1205,7 @@ const REQUIRED_PACKAGE_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze(
name === 'projects' ||
name === 'project_role_bindings' ||
name === 'plugin_package_lifecycle_plans' ||
name === 'plugin_package_secret_binding_approval_plans' ||
name === 'plugin_package_automation_publications' ||
name === 'plugin_package_automation_publication_heads' ||
name === 'plugin_package_publisher_trust_transition_receipts'
@@ -1235,6 +1248,7 @@ const REQUIRED_PACKAGE_EXECUTOR_PRIVILEGES: RequiredPrivileges = Object.freeze(
name === 'projects' ||
name === 'project_role_bindings' ||
name === 'plugin_package_install_proposals' ||
name === 'plugin_package_secret_binding_approval_plans' ||
name === 'plugin_package_publisher_revocation_proposals' ||
name === 'plugin_package_publisher_trust_transition_proposals'
? { ...NO_TABLE_PRIVILEGES, select: true }
@@ -1504,6 +1518,7 @@ const REQUIRED_ADMIN_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
create_plugin_package_secret_binding_approval_plan: false,
commit_plugin_package_lifecycle: false,
commit_plugin_package_quarantine: false,
commit_plugin_package_task_reconciliation: false,
@@ -1517,12 +1532,14 @@ const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
plugin_package_workflow_task_attempt_snapshot: true,
plugin_package_lifecycle_blocking_runs: false,
plugin_package_run_start_allowed: true,
plugin_package_secret_binding_planning_snapshot: false,
plugin_package_tool_start_allowed: true,
register_plugin_package_automation_disposition_event: false,
});
const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
create_plugin_package_secret_binding_approval_plan: true,
commit_plugin_package_lifecycle: false,
commit_plugin_package_quarantine: false,
commit_plugin_package_task_reconciliation: false,
@@ -1536,12 +1553,14 @@ const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
plugin_package_workflow_task_attempt_snapshot: false,
plugin_package_lifecycle_blocking_runs: false,
plugin_package_run_start_allowed: false,
plugin_package_secret_binding_planning_snapshot: true,
plugin_package_tool_start_allowed: false,
register_plugin_package_automation_disposition_event: false,
});
const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
create_plugin_package_secret_binding_approval_plan: false,
commit_plugin_package_lifecycle: true,
commit_plugin_package_quarantine: true,
commit_plugin_package_task_reconciliation: true,
@@ -1555,6 +1574,7 @@ const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges
plugin_package_workflow_task_attempt_snapshot: false,
plugin_package_lifecycle_blocking_runs: true,
plugin_package_run_start_allowed: false,
plugin_package_secret_binding_planning_snapshot: false,
plugin_package_tool_start_allowed: false,
register_plugin_package_automation_disposition_event: false,
});