Repository: Kong/public-ui-components
Pull request #2084: feat(vault): add conjur vault provider [KM-1191]
Author: login: 2eha0
Created: 2025-04-17T06:16:12Z | Merged: 2025-04-17T06:37:56Z

# Summary

[KM-1191](https://konghq.atlassian.net/browse/KM-1191)
<img width="1240" alt="image" src="https://github.com/user-attachments/assets/8522e3a7-c8f8-4c19-81a1-d8671c1d2b09" />


[KM-1191]: https://konghq.atlassian.net/browse/KM-1191?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/packages/entities/entities-vaults/sandbox/pages/VaultFormPage.vue b/packages/entities/entities-vaults/sandbox/pages/VaultFormPage.vue
index c9ac85a648..84fc512f0a 100644
--- a/packages/entities/entities-vaults/sandbox/pages/VaultFormPage.vue
+++ b/packages/entities/entities-vaults/sandbox/pages/VaultFormPage.vue
@@ -39,6 +39,7 @@ const konnectConfig = ref<KonnectVaultFormConfig>({
   ttl: true,
   hcvAppRoleMethodAvailable: true,
   konnectConfigStoreAvailable: true,
+  conjurVaultProviderAvailable: true,
 })
 
 const kongManagerConfig = ref<KongManagerVaultFormConfig>({
@@ -55,6 +56,7 @@ const kongManagerConfig = ref<KongManagerVaultFormConfig>({
   ttl: true,
   hcvAppRoleMethodAvailable: true,
   awsStsEndpointUrlAvailable: true,
+  conjurVaultProviderAvailable: true,
 })
 
 const onError = (error: AxiosError) => {
diff --git a/packages/entities/entities-vaults/src/components/VaultForm.vue b/packages/entities/entities-vaults/src/components/VaultForm.vue
index 7ae784e99b..08aec1ea72 100644
--- a/packages/entities/entities-vaults/src/components/VaultForm.vue
+++ b/packages/entities/entities-vaults/src/components/VaultForm.vue
@@ -342,7 +342,7 @@
           <!-- Azure fields -->
           <div
             v-if="vaultProvider === VaultProviders.AZURE"
-            :key="`${VaultProviders.HCV}-vault-config-fields`"
+            :key="`${VaultProviders.AZURE}-vault-config-fields`"
             class="vault-form-config-fields-container"
           >
             <KInput
@@ -400,6 +400,38 @@
             />
           </div>
 
+          <!-- Conjur fields -->
+          <div
+            v-if="vaultProvider === VaultProviders.CONJUR"
+            :key="`${VaultProviders.CONJUR}-vault-config-fields`"
+            class="vault-form-config-fields-container"
+          >
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].endpoint_url"
+              autocomplete="off"
+              data-testid="vault-form-config-conjur-endpoint_url"
+              :is-readonly="form.isReadonly"
+              :label="t('form.config.conjur.fields.endpoint_url.label')"
+              type="text"
+            />
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].login"
+              autocomplete="off"
+              data-testid="vault-form-config-conjur-login"
+              :is-readonly="form.isReadonly"
+              :label="t('form.config.conjur.fields.login.label')"
+              type="text"
+            />
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].api_token"
+              autocomplete="off"
+              data-testid="vault-form-config-conjur-api_token"
+              :is-readonly="form.isReadonly"
+              :label="t('form.config.conjur.fields.api_token.label')"
+              type="text"
+            />
+          </div>
+
           <div v-if="config.ttl">
             <KCollapse
               v-if="isAvailableTTLConfig"
@@ -524,6 +556,7 @@ import type {
   KongManagerVaultFormConfig,
   KonnectVaultFormConfig,
   VaultPayload,
+  ConjurVaultConfig,
 } from '../types'
 import {
   VaultProviders,
@@ -549,6 +582,7 @@ interface ConfigFields {
   [VaultProviders.HCV]: HCVVaultConfig
   [VaultProviders.AZURE]: AzureVaultConfig
   [VaultProviders.KONNECT]: ConfigStoreConfig
+  [VaultProviders.CONJUR]: ConjurVaultConfig
 }
 
 // Component props - This structure must exist in ALL entity components, with the exclusion of unneeded action props (e.g. if you don't need `canDelete`, just exclude it)
@@ -605,7 +639,7 @@ const originalVaultProvider = ref<VaultProviders | null>(null)
 const configStoreId = ref<string>()
 
 const isAvailableTTLConfig = computed(() => {
-  return [VaultProviders.AWS, VaultProviders.GCP, VaultProviders.HCV, VaultProviders.AZURE].includes(vaultProvider.value)
+  return [VaultProviders.AWS, VaultProviders.GCP, VaultProviders.HCV, VaultProviders.AZURE, VaultProviders.CONJUR].includes(vaultProvider.value)
 })
 
 const providers = computed<Array<{ label: string, value: VaultProviders }>>(() => {
@@ -646,6 +680,15 @@ const providers = computed<Array<{ label: string, value: VaultProviders }>>(() =
         }]
         : []
     ),
+    ...(
+      props.config.conjurVaultProviderAvailable
+        ? [{
+          label: t('form.config.conjur.label'),
+          value: VaultProviders.CONJUR,
+          disabled: !isOtherProvidersSupported.value,
+        }]
+        : []
+    ),
   ]
 })
 
@@ -689,6 +732,7 @@ const configFields = reactive<ConfigFields>({
     client_id: '',
     tenant_id: '',
   } as AzureVaultConfig,
+  [VaultProviders.CONJUR]: {},
 })
 
 const originalConfigFields = reactive<ConfigFields>({
@@ -731,6 +775,7 @@ const originalConfigFields = reactive<ConfigFields>({
     client_id: '',
     tenant_id: '',
   } as AzureVaultConfig,
+  [VaultProviders.CONJUR]: {},
 })
 
 const awsRegions = [
@@ -807,6 +852,8 @@ const getProviderIcon = (providerName: VaultProviders) => {
       return HashicorpIcon
     case VaultProviders.AZURE:
       return AzureIcon
+    case VaultProviders.CONJUR:
+      return KongIcon // todo(zehao): waiting for new icon be added
   }
 }
 
@@ -824,6 +871,8 @@ const getProviderDescription = (providerName: VaultProviders) => {
       return t('form.config.hcv.description')
     case VaultProviders.AZURE:
       return t('form.config.azure.description')
+    case VaultProviders.CONJUR:
+      return t('form.config.conjur.description')
   }
 }
 
diff --git a/packages/entities/entities-vaults/src/locales/en.json b/packages/entities/entities-vaults/src/locales/en.json
index b9e8de1e67..2b709cd3b6 100644
--- a/packages/entities/entities-vaults/src/locales/en.json
+++ b/packages/entities/entities-vaults/src/locales/en.json
@@ -329,6 +329,21 @@
           }
         }
       },
+      "conjur": {
+        "label": "Conjur Vault",
+        "description": "Access secrets stored in Conjur Key Vault for Kong Gateway configuration.",
+        "fields": {
+          "endpoint_url": {
+            "label": "Endpoint URL"
+          },
+          "login": {
+            "label": "Login"
+          },
+          "api_token": {
+            "label": "API Token"
+          }
+        }
+      },
       "advancedFields": {
         "title": "View Advanced Fields",
         "ttl": "Time-to-Live",
diff --git a/packages/entities/entities-vaults/src/types/vault-form.ts b/packages/entities/entities-vaults/src/types/vault-form.ts
index 4cd9a0d2eb..2aa913c9a3 100644
--- a/packages/entities/entities-vaults/src/types/vault-form.ts
+++ b/packages/entities/entities-vaults/src/types/vault-form.ts
@@ -27,6 +27,12 @@ export interface BaseVaultFormConfig extends Omit<BaseFormConfig, 'cancelRoute'>
  * Show/hide AWS StsEndpointUrl field
  */
   awsStsEndpointUrlAvailable?: boolean
+
+  /**
+   * Show/hide Conjur option - mostly for Konnect to pass feature flag value
+   * TODO: remove when support for Conjur is added
+   */
+  conjurVaultProviderAvailable?: boolean
 }
 
 /** Konnect Vault form config */
@@ -42,6 +48,7 @@ export enum VaultProviders {
   ENV = 'env',
   AZURE = 'azure',
   KONNECT = 'konnect',
+  CONJUR = 'conjur',
 }
 
 export enum VaultAuthMethods {
@@ -108,6 +115,16 @@ export interface AzureVaultConfig {
   resurrect_ttl?: number
 }
 
+export interface ConjurVaultConfig { // todo(zehao): which fields are required?
+  endpoint_url?: string
+  login?: string
+  account?: string
+  api_token?: string
+  ttl?: number
+  neg_ttl?: number
+  resurrect_ttl?: number
+}
+
 // allow for nullish values in payload because Kong Admin API treats null as an empty value
 // in case it's an empty string, it will be treated as a value and must have length > 0
 export interface HCVVaultConfigPayload extends Omit<HCVVaultConfig, 'namespace' | 'token'> {


Reviews:
user: login: jw-foss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T06:28:29Z
user: login: jw-foss
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T06:34:02Z

Review comments:
diff_hunk: @@ -400,6 +400,38 @@
             />
           </div>
 
+          <!-- Conjur fields -->
+          <div
+            v-if="vaultProvider === VaultProviders.CONJUR"
+            :key="`${VaultProviders.CONJUR}-vault-config-fields`"
+            class="vault-form-config-fields-container"
+          >
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].endpoint_url"
+              autocomplete="off"
+              data-testid="vault-form-config-azure-location"
+              :is-readonly="form.isReadonly"
+              :label="t('form.config.conjur.fields.endpoint_url.label')"
+              type="text"
+            />
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].login"
+              autocomplete="off"
+              data-testid="vault-form-config-azure-location"
path: packages/entities/entities-vaults/src/components/VaultForm.vue
user: login: jw-foss
body: **q** is this correct? Shouldn't it be `conjure` instead of `azure`?
created_at: 2025-04-17T06:27:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -400,6 +400,38 @@
             />
           </div>
 
+          <!-- Conjur fields -->
+          <div
+            v-if="vaultProvider === VaultProviders.CONJUR"
+            :key="`${VaultProviders.CONJUR}-vault-config-fields`"
+            class="vault-form-config-fields-container"
+          >
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].endpoint_url"
+              autocomplete="off"
+              data-testid="vault-form-config-azure-location"
+              :is-readonly="form.isReadonly"
+              :label="t('form.config.conjur.fields.endpoint_url.label')"
+              type="text"
+            />
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].login"
+              autocomplete="off"
+              data-testid="vault-form-config-azure-location"
+              :is-readonly="form.isReadonly"
+              :label="t('form.config.conjur.fields.login.label')"
+              type="text"
+            />
+            <KInput
+              v-model.trim="configFields[VaultProviders.CONJUR].api_token"
+              autocomplete="off"
+              data-testid="vault-form-config-azure-location"
path: packages/entities/entities-vaults/src/components/VaultForm.vue
user: login: jw-foss
body: Same here
created_at: 2025-04-17T06:27:46Z
author_association: CONTRIBUTOR

PR comments: