mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 19:29:13 +08:00
feat(ql3): resolve secret action recovery manually
This commit is contained in:
@@ -1027,6 +1027,80 @@ export const approvedActionExecutions = ql3Schema.table(
|
||||
],
|
||||
);
|
||||
|
||||
export const approvedActionManualRecoveryResolutions = ql3Schema.table(
|
||||
'approved_action_manual_recovery_resolutions',
|
||||
{
|
||||
dispatchId: varchar('dispatch_id', { length: 128 }).primaryKey(),
|
||||
dispatchDigest: char('dispatch_digest', { length: 64 }).notNull(),
|
||||
projectId: varchar('project_id', { length: 128 }).notNull(),
|
||||
actionType: varchar('action_type', { length: 128 }).notNull(),
|
||||
actionDigest: char('action_digest', { length: 64 }).notNull(),
|
||||
executionVersion: integer('execution_version').notNull(),
|
||||
executionDigest: char('execution_digest', { length: 64 }).notNull(),
|
||||
mutationId: varchar('mutation_id', { length: 128 }).notNull(),
|
||||
decision: varchar('decision', { length: 32 }).notNull(),
|
||||
evidenceDigest: char('evidence_digest', { length: 64 }).notNull(),
|
||||
reasonCode: varchar('reason_code', { length: 64 }).notNull(),
|
||||
resolvedByType: varchar('resolved_by_type', { length: 16 }).notNull(),
|
||||
resolvedById: varchar('resolved_by_id', { length: 255 }).notNull(),
|
||||
authenticationId: varchar('authentication_id', { length: 128 }).notNull(),
|
||||
assurance: varchar('assurance', { length: 32 }).notNull(),
|
||||
authenticatedAtMs: bigint('authenticated_at_ms', { mode: 'number' }).notNull(),
|
||||
projectVersion: integer('project_version').notNull(),
|
||||
bindingVersion: integer('binding_version').notNull(),
|
||||
auditEventId: uuid('audit_event_id').notNull(),
|
||||
resolvedAtMs: bigint('resolved_at_ms', { mode: 'number' }).notNull(),
|
||||
resolutionJson: jsonb('resolution_json')
|
||||
.$type<Record<string, unknown>>()
|
||||
.notNull(),
|
||||
resolutionDigest: char('resolution_digest', { length: 64 }).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
foreignKey({
|
||||
name: 'ql3_approved_action_manual_recovery_dispatch_fk',
|
||||
columns: [table.dispatchId],
|
||||
foreignColumns: [approvedActionDispatches.dispatchId],
|
||||
}).onDelete('restrict'),
|
||||
foreignKey({
|
||||
name: 'ql3_approved_action_manual_recovery_project_fk',
|
||||
columns: [table.projectId],
|
||||
foreignColumns: [projects.id],
|
||||
}).onDelete('restrict'),
|
||||
foreignKey({
|
||||
name: 'ql3_approved_action_manual_recovery_audit_fk',
|
||||
columns: [table.auditEventId],
|
||||
foreignColumns: [securityAuditEvents.eventId],
|
||||
}).onDelete('restrict'),
|
||||
check(
|
||||
'ql3_approved_action_manual_recovery_identity_check',
|
||||
sql`${table.dispatchId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.projectId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.actionType} in ('plugin_package.secret_binding.bind','plugin_package.secret_binding.transition') and ${table.executionVersion} between 1 and 2147483647 and ${table.mutationId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.decision} in ('confirm_failed','abandon_unknown') and ${table.reasonCode} ~ '^[a-z][a-z0-9_]{0,63}$' and ${table.resolvedByType} = 'user' and octet_length(${table.resolvedById}) between 1 and 255 and ${table.resolvedById} !~ '[[:cntrl:]]' and ${table.authenticationId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.assurance} in ('multi_factor','hardware') and ${table.projectVersion} >= 1 and ${table.bindingVersion} >= 1`,
|
||||
),
|
||||
check(
|
||||
'ql3_approved_action_manual_recovery_digest_check',
|
||||
sql`${table.dispatchDigest} ~ '^[0-9a-f]{64}$' and ${table.actionDigest} ~ '^[0-9a-f]{64}$' and ${table.executionDigest} ~ '^[0-9a-f]{64}$' and ${table.evidenceDigest} ~ '^[0-9a-f]{64}$' and ${table.resolutionDigest} ~ '^[0-9a-f]{64}$'`,
|
||||
),
|
||||
check(
|
||||
'ql3_approved_action_manual_recovery_time_check',
|
||||
sql`${table.authenticatedAtMs} >= 0 and ${table.resolvedAtMs} >= ${table.authenticatedAtMs} and ${table.resolvedAtMs} - ${table.authenticatedAtMs} <= 300000`,
|
||||
),
|
||||
check(
|
||||
'ql3_approved_action_manual_recovery_json_check',
|
||||
sql`jsonb_typeof(${table.resolutionJson}) = 'object' and octet_length(${table.resolutionJson}::text) between 2 and 65536 and ${table.resolutionJson} @> jsonb_build_object('schema', 'qinglong/approved-action-manual-recovery@v1', 'dispatchId', ${table.dispatchId}, 'dispatchDigest', ${table.dispatchDigest}, 'projectId', ${table.projectId}, 'actionType', ${table.actionType}, 'actionDigest', ${table.actionDigest}, 'executionVersion', ${table.executionVersion}, 'executionDigest', ${table.executionDigest}, 'mutationId', ${table.mutationId}, 'decision', ${table.decision}, 'evidenceDigest', ${table.evidenceDigest}, 'reasonCode', ${table.reasonCode}, 'resolvedBy', jsonb_build_object('type', ${table.resolvedByType}, 'id', ${table.resolvedById}), 'authenticationId', ${table.authenticationId}, 'assurance', ${table.assurance}, 'authenticatedAtMs', ${table.authenticatedAtMs}, 'authorizationFence', jsonb_build_object('projectVersion', ${table.projectVersion}, 'bindingVersion', ${table.bindingVersion}), 'auditEventId', ${table.auditEventId}, 'resolvedAtMs', ${table.resolvedAtMs}, 'resolutionDigest', ${table.resolutionDigest})`,
|
||||
),
|
||||
uniqueIndex('ql3_approved_action_manual_recovery_mutation_uidx').on(
|
||||
table.mutationId,
|
||||
),
|
||||
uniqueIndex('ql3_approved_action_manual_recovery_digest_uidx').on(
|
||||
table.resolutionDigest,
|
||||
),
|
||||
index('ql3_approved_action_manual_recovery_project_idx').on(
|
||||
table.projectId,
|
||||
table.resolvedAtMs,
|
||||
table.dispatchId,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
export const pluginPackageInstallProposals = ql3Schema.table(
|
||||
'plugin_package_install_proposals',
|
||||
{
|
||||
@@ -6103,6 +6177,7 @@ export const ql3PostgresTables = [
|
||||
approvalRequests,
|
||||
approvedActionDispatches,
|
||||
approvedActionExecutions,
|
||||
approvedActionManualRecoveryResolutions,
|
||||
pluginPackageInstallProposals,
|
||||
pluginPackageManagementQuotaBuckets,
|
||||
workerCredentialManagementQuotaBuckets,
|
||||
|
||||
@@ -21,8 +21,8 @@ export interface PostgresSchemaContractTrigger {
|
||||
export interface PostgresSchemaContract {
|
||||
readonly schema: 'ql3';
|
||||
readonly contractName: 'control-core';
|
||||
readonly contractVersion: 63;
|
||||
readonly migrationId: 'pg-0064-plugin-package-secret-binding-transition-approval-plans';
|
||||
readonly contractVersion: 64;
|
||||
readonly migrationId: 'pg-0065-approved-action-manual-recovery';
|
||||
readonly minimumServerMajor: 16;
|
||||
readonly maximumServerMajor: 18;
|
||||
readonly capabilities: Readonly<{
|
||||
@@ -38,6 +38,7 @@ export interface PostgresSchemaContract {
|
||||
api_credential_pepper_binding: 1;
|
||||
approved_action: 1;
|
||||
approved_action_execution: 1;
|
||||
approved_action_manual_recovery: 1;
|
||||
approval_management_boundary: 1;
|
||||
automation_management_boundary: 1;
|
||||
automation_management_identity_keyset_ledger: 1;
|
||||
@@ -117,8 +118,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
Object.freeze({
|
||||
schema: 'ql3',
|
||||
contractName: 'control-core',
|
||||
contractVersion: 63,
|
||||
migrationId: 'pg-0064-plugin-package-secret-binding-transition-approval-plans',
|
||||
contractVersion: 64,
|
||||
migrationId: 'pg-0065-approved-action-manual-recovery',
|
||||
minimumServerMajor: 16,
|
||||
maximumServerMajor: 18,
|
||||
capabilities: Object.freeze({
|
||||
@@ -127,6 +128,7 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
api_credential_pepper_binding: 1,
|
||||
approved_action: 1,
|
||||
approved_action_execution: 1,
|
||||
approved_action_manual_recovery: 1,
|
||||
approval_management_boundary: 1,
|
||||
automation_management_boundary: 1,
|
||||
automation_management_identity_keyset_ledger: 1,
|
||||
@@ -770,6 +772,30 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'execution_json',
|
||||
'execution_digest',
|
||||
]),
|
||||
table('approved_action_manual_recovery_resolutions', [
|
||||
'dispatch_id',
|
||||
'dispatch_digest',
|
||||
'project_id',
|
||||
'action_type',
|
||||
'action_digest',
|
||||
'execution_version',
|
||||
'execution_digest',
|
||||
'mutation_id',
|
||||
'decision',
|
||||
'evidence_digest',
|
||||
'reason_code',
|
||||
'resolved_by_type',
|
||||
'resolved_by_id',
|
||||
'authentication_id',
|
||||
'assurance',
|
||||
'authenticated_at_ms',
|
||||
'project_version',
|
||||
'binding_version',
|
||||
'audit_event_id',
|
||||
'resolved_at_ms',
|
||||
'resolution_json',
|
||||
'resolution_digest',
|
||||
]),
|
||||
table('plugin_package_install_proposals', [
|
||||
'action_ref',
|
||||
'project_id',
|
||||
@@ -1624,6 +1650,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_approved_action_execution_due_idx',
|
||||
'ql3_approved_action_execution_recovery_idx',
|
||||
'ql3_approved_action_execution_project_idx',
|
||||
'approved_action_manual_recovery_resolutions_pkey',
|
||||
'ql3_approved_action_manual_recovery_mutation_uidx',
|
||||
'ql3_approved_action_manual_recovery_digest_uidx',
|
||||
'ql3_approved_action_manual_recovery_project_idx',
|
||||
'plugin_package_install_proposals_pkey',
|
||||
'ql3_plugin_package_proposal_project_idx',
|
||||
'plugin_package_management_quota_buckets_pkey',
|
||||
@@ -1942,6 +1972,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_approved_action_execution_digest_check',
|
||||
'ql3_approved_action_execution_json_check',
|
||||
'ql3_approved_action_execution_time_check',
|
||||
'ql3_approved_action_manual_recovery_identity_check',
|
||||
'ql3_approved_action_manual_recovery_digest_check',
|
||||
'ql3_approved_action_manual_recovery_time_check',
|
||||
'ql3_approved_action_manual_recovery_json_check',
|
||||
'ql3_plugin_package_proposal_identity_check',
|
||||
'ql3_plugin_package_proposal_digest_check',
|
||||
'ql3_plugin_package_proposal_json_check',
|
||||
@@ -2347,6 +2381,9 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_approved_action_dispatch_request_fk',
|
||||
'ql3_approved_action_dispatch_project_fk',
|
||||
'ql3_approved_action_execution_dispatch_fk',
|
||||
'ql3_approved_action_manual_recovery_dispatch_fk',
|
||||
'ql3_approved_action_manual_recovery_project_fk',
|
||||
'ql3_approved_action_manual_recovery_audit_fk',
|
||||
'ql3_approved_action_execution_project_fk',
|
||||
'ql3_plugin_package_proposal_project_fk',
|
||||
'ql3_plugin_package_management_quota_project_fk',
|
||||
@@ -2434,6 +2471,15 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_run_retry_policies_run_fk',
|
||||
]),
|
||||
functions: Object.freeze([
|
||||
Object.freeze({
|
||||
name: 'resolve_approved_action_manual_recovery',
|
||||
identityArguments:
|
||||
'p_resolution_json jsonb, p_next_execution_json jsonb, p_audit_json jsonb',
|
||||
owner: 'ql3_migration',
|
||||
securityDefiner: true,
|
||||
volatility: 'volatile',
|
||||
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
|
||||
}),
|
||||
Object.freeze({
|
||||
name: 'plugin_package_secret_binding_transition_snapshot',
|
||||
identityArguments:
|
||||
|
||||
@@ -384,6 +384,12 @@ const REQUIRED_RUNTIME_PRIVILEGES = Object.freeze({
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
approved_action_manual_recovery_resolutions: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
plugin_package_install_proposals: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
@@ -921,6 +927,12 @@ const REQUIRED_ADMIN_PRIVILEGES = Object.freeze({
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
approved_action_manual_recovery_resolutions: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
plugin_package_install_proposals: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
@@ -1421,7 +1433,10 @@ const REQUIRED_APPROVAL_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze(
|
||||
name === 'schema_capabilities' ||
|
||||
name === 'projects' ||
|
||||
name === 'project_role_bindings' ||
|
||||
name === 'tool_invocation_preview_artifacts'
|
||||
name === 'tool_invocation_preview_artifacts' ||
|
||||
name === 'approved_action_dispatches' ||
|
||||
name === 'approved_action_executions' ||
|
||||
name === 'approved_action_manual_recovery_resolutions'
|
||||
? { ...NO_TABLE_PRIVILEGES, select: true }
|
||||
: name === 'security_audit_events'
|
||||
? { ...NO_TABLE_PRIVILEGES, select: true, insert: true }
|
||||
@@ -1573,6 +1588,7 @@ const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
|
||||
plugin_package_secret_binding_transition_snapshot: false,
|
||||
plugin_package_tool_start_allowed: true,
|
||||
register_plugin_package_automation_disposition_event: false,
|
||||
resolve_approved_action_manual_recovery: false,
|
||||
});
|
||||
|
||||
const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
|
||||
@@ -1598,6 +1614,7 @@ const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
|
||||
plugin_package_secret_binding_transition_snapshot: true,
|
||||
plugin_package_tool_start_allowed: false,
|
||||
register_plugin_package_automation_disposition_event: false,
|
||||
resolve_approved_action_manual_recovery: false,
|
||||
});
|
||||
|
||||
const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
|
||||
@@ -1623,6 +1640,7 @@ const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges
|
||||
plugin_package_secret_binding_transition_snapshot: false,
|
||||
plugin_package_tool_start_allowed: false,
|
||||
register_plugin_package_automation_disposition_event: false,
|
||||
resolve_approved_action_manual_recovery: false,
|
||||
});
|
||||
|
||||
const REQUIRED_WORKER_CREDENTIAL_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
|
||||
@@ -1635,6 +1653,7 @@ const REQUIRED_APPROVAL_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges
|
||||
Object.freeze({
|
||||
...NO_FUNCTION_PRIVILEGES,
|
||||
lock_approval_policy_fence: true,
|
||||
resolve_approved_action_manual_recovery: true,
|
||||
});
|
||||
|
||||
const REQUIRED_RUN_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
|
||||
|
||||
Reference in New Issue
Block a user