From 75e90708172c4d4ba98f3178dc6bc54e7e2e01b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20TR=C3=89BEL=20=28Perso=29?= Date: Wed, 7 Jan 2026 11:47:50 +0100 Subject: [PATCH 1/3] chore(plugins): update formatting --- plugins/vault/src/class.ts | 91 ++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/plugins/vault/src/class.ts b/plugins/vault/src/class.ts index 785dafb28..e0ee5fbf8 100644 --- a/plugins/vault/src/class.ts +++ b/plugins/vault/src/class.ts @@ -3,7 +3,11 @@ import axios from 'axios' import type { ProjectLite } from '@cpn-console/hooks' import { PluginApi } from '@cpn-console/hooks' import getConfig from './config.js' -import { generateKVConfigUpdate, getAuthMethod, isAppRoleEnabled } from './utils.js' +import { + generateKVConfigUpdate, + getAuthMethod, + isAppRoleEnabled, +} from './utils.js' interface ReadOptions { throwIfNoEntry: boolean @@ -31,15 +35,14 @@ export class VaultApi extends PluginApi { protected async getToken() { if (!this.token) { - this.token = (await this.axios.post('/v1/auth/token/create')) - .data.auth.client_token as string + this.token = (await this.axios.post('/v1/auth/token/create')).data.auth + .client_token as string } return this.token } protected async destroy(path: string, kvName: string) { - if (path.startsWith('/')) - path = path.slice(1) + if (path.startsWith('/')) path = path.slice(1) return await this.axios({ method: 'delete', url: `/v1/${kvName}/metadata/${path}`, @@ -73,7 +76,8 @@ export class VaultApi extends PluginApi { }, }, }) - } else { // means 200 status + } else { + // means 200 status const configUpdate = generateKVConfigUpdate(kvRes.data) if (configUpdate) { await this.axios({ @@ -124,7 +128,10 @@ export class VaultApi extends PluginApi { Role = { upsert: async (roleName: string, policies: string[]) => { - const appRoleEnabled = await isAppRoleEnabled(this.axios, await this.getToken()) + const appRoleEnabled = await isAppRoleEnabled( + this.axios, + await this.getToken(), + ) if (!appRoleEnabled) return await this.axios({ @@ -143,12 +150,9 @@ export class VaultApi extends PluginApi { }) }, delete: async (roleName: string) => { - await this.axios.delete( - `/v1/auth/approle/role/${roleName}`, - { - headers: { 'X-Vault-Token': await this.getToken() }, - }, - ) + await this.axios.delete(`/v1/auth/approle/role/${roleName}`, { + headers: { 'X-Vault-Token': await this.getToken() }, + }) }, getCredentials: async (roleName: string) => { const { data: dataRole } = await this.axios.get( @@ -205,8 +209,7 @@ export class VaultProjectApi extends VaultApi { } public async list(path: string = '/'): Promise { - if (!path.startsWith('/')) - path = `/${path}` + if (!path.startsWith('/')) path = `/${path}` const listSecretPath: string[] = [] const response = await this.axios({ @@ -221,7 +224,9 @@ export class VaultProjectApi extends VaultApi { if (response.status === 404) return listSecretPath for (const key of response.data.data.keys) { if (key.endsWith('/')) { - const subSecrets = await this.list(`${path.substring(this.basePath.length)}/${key}`) + const subSecrets = await this.list( + `${path.substring(this.basePath.length)}/${key}`, + ) subSecrets.forEach((secret) => { listSecretPath.push(`${key}${secret}`) }) @@ -232,22 +237,24 @@ export class VaultProjectApi extends VaultApi { return listSecretPath.flat() } - public async read(path: string = '/', options: ReadOptions = { throwIfNoEntry: true }) { - if (path.startsWith('/')) - path = path.slice(1) + public async read( + path: string = '/', + options: ReadOptions = { throwIfNoEntry: true }, + ) { + if (path.startsWith('/')) path = path.slice(1) const response = await this.axios.get( `/v1/${this.coreKvName}/data/${this.projectRootDir}/${this.basePath}/${path}`, { headers: { 'X-Vault-Token': await this.getToken() }, - validateStatus: status => (options.throwIfNoEntry ? [200] : [200, 404]).includes(status), + validateStatus: status => + (options.throwIfNoEntry ? [200] : [200, 404]).includes(status), }, ) return response.data.data } public async write(body: object, path: string = '/') { - if (path.startsWith('/')) - path = path.slice(1) + if (path.startsWith('/')) path = path.slice(1) const response = await this.axios.post( `/v1/${this.coreKvName}/data/${this.projectRootDir}/${this.basePath}/${path}`, { @@ -259,9 +266,11 @@ export class VaultProjectApi extends VaultApi { } public async destroy(path: string = '/') { - if (path.startsWith('/')) - path = path.slice(1) - return super.destroy(`${this.projectRootDir}/${this.basePath}/${path}`, this.coreKvName) + if (path.startsWith('/')) path = path.slice(1) + return super.destroy( + `${this.projectRootDir}/${this.basePath}/${path}`, + this.coreKvName, + ) } Project = { @@ -276,7 +285,10 @@ export class VaultProjectApi extends VaultApi { `path "${this.coreKvName}/data/${this.projectRootDir}/${this.basePath}/REGISTRY/ro-robot" { capabilities = ["read"] }`, ) await this.Group.upsert() - await this.Role.upsert(this.roleName, [this.policyName.techRO, this.policyName.appFull]) + await this.Role.upsert(this.roleName, [ + this.policyName.techRO, + this.policyName.appFull, + ]) }, delete: async () => { await this.Kv.delete(this.projectKvName) @@ -286,7 +298,10 @@ export class VaultProjectApi extends VaultApi { await this.Role.delete(this.roleName) }, getCredentials: async () => { - const appRoleEnabled = await isAppRoleEnabled(this.axios, await this.getToken()) + const appRoleEnabled = await isAppRoleEnabled( + this.axios, + await this.getToken(), + ) if (!appRoleEnabled) return this.defaultAppRoleCredentials const creds = await this.Role.getCredentials(this.roleName) return { @@ -357,7 +372,10 @@ export class VaultZoneApi extends VaultApi { public async upsert() { await this.Kv.upsert(this.kvName) - await this.Policy.upsert(this.policyName, `path "${this.kvName}/*" { capabilities = ["read"] }`) + await this.Policy.upsert( + this.policyName, + `path "${this.kvName}/*" { capabilities = ["read"] }`, + ) await this.Role.upsert(this.roleName, [this.policyName]) } @@ -368,21 +386,16 @@ export class VaultZoneApi extends VaultApi { } public async write(body: object, path: string = '/') { - if (path.startsWith('/')) - path = path.slice(1) - const response = await this.axios.post( - `/v1/${this.kvName}/data/${path}`, - { - headers: { 'X-Vault-Token': await this.getToken() }, - data: body, - }, - ) + if (path.startsWith('/')) path = path.slice(1) + const response = await this.axios.post(`/v1/${this.kvName}/data/${path}`, { + headers: { 'X-Vault-Token': await this.getToken() }, + data: body, + }) return await response.data } public async destroy(path: string = '/') { - if (path.startsWith('/')) - path = path.slice(1) + if (path.startsWith('/')) path = path.slice(1) return super.destroy(path, this.kvName) } From fe3d5a9ddc030b6b3b47f1fc5874d710b59263a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20TR=C3=89BEL=20=28Perso=29?= Date: Wed, 7 Jan 2026 11:49:33 +0100 Subject: [PATCH 2/3] fix(vault/argocd): rework how Vault values are passed to ArgoCD --- plugins/argocd/src/utils.ts | 2 +- plugins/vault/src/class.ts | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/plugins/argocd/src/utils.ts b/plugins/argocd/src/utils.ts index 21cb4dcd6..dff473b7e 100644 --- a/plugins/argocd/src/utils.ts +++ b/plugins/argocd/src/utils.ts @@ -65,7 +65,7 @@ export function getCustomK8sApi(): CustomObjectsApi { export async function updateZoneValues(zone: ZoneObject, apis: HookPayloadApis | HookPayloadApis) { const { gitlab, vault } = apis const values = { - vault: await vault.getCredentials(), + vault: await vault.getValues(), clusters: zone.clusterNames, } const zoneRepo = await gitlab.getOrCreateInfraProject(zone.slug) diff --git a/plugins/vault/src/class.ts b/plugins/vault/src/class.ts index e0ee5fbf8..f31297eed 100644 --- a/plugins/vault/src/class.ts +++ b/plugins/vault/src/class.ts @@ -359,6 +359,22 @@ export class VaultProjectApi extends VaultApi { } } +interface VaultValuesWithoutCredentials { + projectsRootDir: string +} +interface VaultCredentialsWithoutRole { + url: string + kvName: string +} +interface VaultCredentialsWithRole { + url: string + kvName: string + roleId: string + secretId: string +} +type VaultCredentials = VaultCredentialsWithRole | VaultCredentialsWithoutRole +type VaultValues = VaultCredentials & VaultValuesWithoutCredentials + export class VaultZoneApi extends VaultApi { private readonly kvName: string private readonly policyName: string @@ -399,19 +415,28 @@ export class VaultZoneApi extends VaultApi { return super.destroy(path, this.kvName) } - public async getCredentials() { - const appRoleEnabled = await isAppRoleEnabled(this.axios, await this.getToken()) - if (!appRoleEnabled) { + public async getCredentials(): Promise { + const appRoleEnabled = await isAppRoleEnabled( + this.axios, + await this.getToken(), + ) + if (appRoleEnabled) { return { url: getConfig().publicUrl, kvName: this.kvName, - } + ...(await this.Role.getCredentials(this.roleName)), + } as VaultCredentialsWithRole } - const creds = await this.Role.getCredentials(this.roleName) return { url: getConfig().publicUrl, kvName: this.kvName, - ...creds, + } as VaultCredentialsWithoutRole + } + + public async getValues(): Promise { + return { + projectsRootDir: getConfig().projectsRootDir, + ...(await this.getCredentials()), } } } From 76da0cd64165a33e4b54d96a5c0c598b17afc100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20TR=C3=89BEL=20=28Perso=29?= Date: Thu, 8 Jan 2026 09:08:47 +0100 Subject: [PATCH 3/3] chore(vault): explicit that VaultApi base class is abstract and must not be exported --- plugins/vault/src/class.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/vault/src/class.ts b/plugins/vault/src/class.ts index f31297eed..2ca0555d9 100644 --- a/plugins/vault/src/class.ts +++ b/plugins/vault/src/class.ts @@ -19,7 +19,7 @@ export interface AppRoleCredentials { secretId: string } -export class VaultApi extends PluginApi { +abstract class VaultApi extends PluginApi { protected readonly axios: AxiosInstance private token: string | undefined = undefined @@ -360,6 +360,7 @@ export class VaultProjectApi extends VaultApi { } interface VaultValuesWithoutCredentials { + /** Slash-separated directory (root node of all Gitlab projects) */ projectsRootDir: string } interface VaultCredentialsWithoutRole {