feat(managed-secrets): add centrally managed secrets with GITOPS_ prefix pushed to multiple targets

Introduce managed secrets owned by GitOps that can be defined once and
pushed to any combination of projects/groups across connections. Values
are stored in OS keychain, secrets appear on targets as GITOPS_{key}.
This commit is contained in:
2026-02-28 23:43:32 +00:00
parent 78247c1d41
commit 75d35405dc
17 changed files with 1302 additions and 4 deletions

View File

@@ -0,0 +1,158 @@
import * as plugins from '../../plugins.ts';
import type { OpsServer } from '../classes.opsserver.ts';
import * as interfaces from '../../../ts_interfaces/index.ts';
import { requireValidIdentity } from '../helpers/guards.ts';
export class ManagedSecretsHandler {
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(private opsServerRef: OpsServer) {
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
this.registerHandlers();
}
private get actionLog() {
return this.opsServerRef.gitopsAppRef.actionLog;
}
private get manager() {
return this.opsServerRef.gitopsAppRef.managedSecretsManager;
}
private registerHandlers(): void {
// List all managed secrets
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetManagedSecrets>(
'getManagedSecrets',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const managedSecrets = await this.manager.getAll();
return { managedSecrets };
},
),
);
// Get single managed secret
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetManagedSecret>(
'getManagedSecret',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const managedSecret = await this.manager.getById(dataArg.managedSecretId);
if (!managedSecret) throw new Error(`Managed secret not found: ${dataArg.managedSecretId}`);
return { managedSecret };
},
),
);
// Create managed secret
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateManagedSecret>(
'createManagedSecret',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const result = await this.manager.create(
dataArg.key,
dataArg.value,
dataArg.description,
dataArg.targets,
);
this.actionLog.append({
actionType: 'create',
entityType: 'managed-secret',
entityId: result.managedSecret.id,
entityName: `GITOPS_${dataArg.key}`,
details: `Created managed secret "${dataArg.key}" with ${dataArg.targets.length} target(s)`,
username: dataArg.identity.username,
});
return result;
},
),
);
// Update managed secret
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateManagedSecret>(
'updateManagedSecret',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const result = await this.manager.update(dataArg.managedSecretId, {
value: dataArg.value,
description: dataArg.description,
targets: dataArg.targets,
});
this.actionLog.append({
actionType: 'update',
entityType: 'managed-secret',
entityId: dataArg.managedSecretId,
entityName: `GITOPS_${result.managedSecret.key}`,
details: `Updated managed secret "${result.managedSecret.key}"`,
username: dataArg.identity.username,
});
return result;
},
),
);
// Delete managed secret
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteManagedSecret>(
'deleteManagedSecret',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const secret = await this.manager.getById(dataArg.managedSecretId);
const result = await this.manager.delete(dataArg.managedSecretId);
this.actionLog.append({
actionType: 'delete',
entityType: 'managed-secret',
entityId: dataArg.managedSecretId,
entityName: secret ? `GITOPS_${secret.key}` : dataArg.managedSecretId,
details: `Deleted managed secret${secret ? ` "${secret.key}"` : ''}`,
username: dataArg.identity.username,
});
return result;
},
),
);
// Push single managed secret
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushManagedSecret>(
'pushManagedSecret',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const result = await this.manager.pushOne(dataArg.managedSecretId);
this.actionLog.append({
actionType: 'push',
entityType: 'managed-secret',
entityId: dataArg.managedSecretId,
entityName: `GITOPS_${result.managedSecret.key}`,
details: `Pushed managed secret "${result.managedSecret.key}" to ${result.pushResults.length} target(s)`,
username: dataArg.identity.username,
});
return result;
},
),
);
// Push all managed secrets
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushAllManagedSecrets>(
'pushAllManagedSecrets',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const results = await this.manager.pushAll();
this.actionLog.append({
actionType: 'push',
entityType: 'managed-secret',
entityId: 'all',
entityName: 'All managed secrets',
details: `Pushed ${results.length} managed secret(s) to their targets`,
username: dataArg.identity.username,
});
return { results };
},
),
);
}
}