From 91e0e2c7e0f5b3fa6db51a7fbed3c2467889577f Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 7 Jan 2026 20:24:20 -0500 Subject: [PATCH] reduce code reusability --- .../hc-vault/hc-vault-connection-fns.ts | 84 ++-- .../external-migration-service.ts | 372 +++--------------- 2 files changed, 79 insertions(+), 377 deletions(-) diff --git a/backend/src/services/app-connection/hc-vault/hc-vault-connection-fns.ts b/backend/src/services/app-connection/hc-vault/hc-vault-connection-fns.ts index 3e35285f7c..169fb3c4a3 100644 --- a/backend/src/services/app-connection/hc-vault/hc-vault-connection-fns.ts +++ b/backend/src/services/app-connection/hc-vault/hc-vault-connection-fns.ts @@ -49,6 +49,23 @@ export const convertVaultValueToString = (value: JsonValue): string => { // Concurrency limit for HC Vault API requests to avoid rate limiting const HC_VAULT_CONCURRENCY_LIMIT = 20; +// Helper to check if error is a 404 +const isVault404Error = (error: unknown): boolean => { + if (error && typeof error === "object" && "response" in error) { + const axiosError = error as { response?: { status?: number } }; + return axiosError.response?.status === 404; + } + return false; +}; + +// Helper to extract error message from Vault API errors +const getVaultErrorMessage = (error: unknown, fallback: string): string => { + if (error instanceof AxiosError) { + return (error.response?.data as { errors?: string[] })?.errors?.[0] || error.message || fallback; + } + return fallback; +}; + /** * Creates a concurrency limiter that restricts the number of concurrent async operations * @param limit - Maximum number of concurrent operations @@ -866,14 +883,7 @@ export const getHCVaultKubernetesRoles = async ( ); roleNames = roleListResponse.data.keys || []; } catch (error) { - // Vault returns 404 when no roles are configured yet - if (error && typeof error === "object" && "response" in error) { - const axiosError = error as { response?: { status?: number } }; - if (axiosError.response?.status === 404) { - return []; - } - } - + if (isVault404Error(error)) return []; throw error; } @@ -910,7 +920,6 @@ export const getHCVaultKubernetesRoles = async ( } }); - // 4. Merge the role with the config return { ...roleResponse.data, name: roleName, @@ -920,22 +929,11 @@ export const getHCVaultKubernetesRoles = async ( }) ); - const roles = await Promise.all(roleDetailsPromises); - - return roles; + return Promise.all(roleDetailsPromises); } catch (error: unknown) { logger.error(error, "Unable to list HC Vault Kubernetes secrets engine roles"); - - if (error instanceof AxiosError) { - const errorMessage = - (error.response?.data as { errors?: string[] })?.errors?.[0] || error.message || "Unknown error"; - throw new BadRequestError({ - message: `Failed to list Kubernetes secrets engine roles: ${errorMessage}` - }); - } - throw new BadRequestError({ - message: "Unable to list Kubernetes secrets engine roles from HashiCorp Vault" + message: `Failed to list Kubernetes secrets engine roles: ${getVaultErrorMessage(error, "Unknown error")}` }); } }; @@ -970,13 +968,7 @@ export const getHCVaultDatabaseRoles = async ( ); connectionNames = connectionListResponse.data.keys || []; } catch (error) { - // Vault returns 404 when no connections are configured yet - if (error && typeof error === "object" && "response" in error) { - const axiosError = error as { response?: { status?: number } }; - if (axiosError.response?.status === 404) { - return []; - } - } + if (isVault404Error(error)) return []; throw error; } @@ -1023,13 +1015,7 @@ export const getHCVaultDatabaseRoles = async ( ); roleNames = roleListResponse.data.keys || []; } catch (error) { - // Vault returns 404 when no roles are configured yet - if (error && typeof error === "object" && "response" in error) { - const axiosError = error as { response?: { status?: number } }; - if (axiosError.response?.status === 404) { - return []; - } - } + if (isVault404Error(error)) return []; throw error; } @@ -1047,13 +1033,7 @@ export const getHCVaultDatabaseRoles = async ( max_ttl?: number; creation_statements?: string[]; revocation_statements?: string[]; - rollback_statements?: string[]; renew_statements?: string[]; - rotation_statements?: string[]; - credential_type?: string; - credential_config?: { - password_policy?: string; - }; }; }>(connection, gatewayService, { url: `${instanceUrl}/v1/${cleanMountPath}/roles/${roleName}`, @@ -1064,12 +1044,9 @@ export const getHCVaultDatabaseRoles = async ( } }); - // Get the connection config for this role's db_name const dbConfig = connectionConfigs.get(roleResponse.data.db_name) || { plugin_name: "", - connection_details: { - connection_url: "" - } + connection_details: { connection_url: "" } }; return { @@ -1086,22 +1063,11 @@ export const getHCVaultDatabaseRoles = async ( }) ); - const roles = await Promise.all(roleDetailsPromises); - - return roles; + return Promise.all(roleDetailsPromises); } catch (error: unknown) { logger.error(error, "Unable to list HC Vault database secrets engine roles"); - - if (error instanceof AxiosError) { - const errorMessage = - (error.response?.data as { errors?: string[] })?.errors?.[0] || error.message || "Unknown error"; - throw new BadRequestError({ - message: `Failed to list database secrets engine roles: ${errorMessage}` - }); - } - throw new BadRequestError({ - message: "Unable to list database secrets engine roles from HashiCorp Vault" + message: `Failed to list database secrets engine roles: ${getVaultErrorMessage(error, "Unknown error")}` }); } }; diff --git a/backend/src/services/external-migration/external-migration-service.ts b/backend/src/services/external-migration/external-migration-service.ts index 39e9321326..00240fe992 100644 --- a/backend/src/services/external-migration/external-migration-service.ts +++ b/backend/src/services/external-migration/external-migration-service.ts @@ -80,6 +80,47 @@ export const externalMigrationServiceFactory = ({ vaultExternalMigrationConfigDAL, kmsService }: TExternalMigrationServiceFactoryDep) => { + // Helper to verify admin permissions and get vault connection + const getVaultConnectionForNamespace = async (actor: OrgServiceActor, namespace: string, action: string) => { + const { hasRole } = await permissionService.getOrgPermission({ + scope: OrganizationActionScope.Any, + actor: actor.type, + actorId: actor.id, + orgId: actor.orgId, + actorAuthMethod: actor.authMethod, + actorOrgId: actor.orgId + }); + + if (!hasRole(OrgMembershipRole.Admin)) { + throw new ForbiddenRequestError({ message: `Only admins can ${action}` }); + } + + const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ + orgId: actor.orgId, + namespace + }); + + if (!vaultConfig) { + throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); + } + + if (!vaultConfig.connection) { + throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); + } + + const credentials = await decryptAppConnectionCredentials({ + orgId: vaultConfig.orgId, + encryptedCredentials: vaultConfig.connection.encryptedCredentials, + kmsService, + projectId: null + }); + + return { + ...vaultConfig.connection, + credentials + } as THCVaultConnection; + }; + const importEnvKeyData = async ({ decryptionKey, encryptedJson, @@ -390,89 +431,13 @@ export const externalMigrationServiceFactory = ({ }; const getVaultPolicies = async ({ actor, namespace }: { actor: OrgServiceActor; namespace: string }) => { - const { hasRole } = await permissionService.getOrgPermission({ - actorId: actor.id, - actor: actor.type, - orgId: actor.orgId, - actorOrgId: actor.orgId, - actorAuthMethod: actor.authMethod, - scope: OrganizationActionScope.Any - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can view vault policies" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - - const policies = await listHCVaultPolicies(namespace, connection, gatewayService); - return policies; + const connection = await getVaultConnectionForNamespace(actor, namespace, "view vault policies"); + return listHCVaultPolicies(namespace, connection, gatewayService); }; const getVaultMounts = async ({ actor, namespace }: { actor: OrgServiceActor; namespace: string }) => { - const { hasRole } = await permissionService.getOrgPermission({ - actorId: actor.id, - actor: actor.type, - orgId: actor.orgId, - actorOrgId: actor.orgId, - actorAuthMethod: actor.authMethod, - scope: OrganizationActionScope.Any - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can view vault mounts" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - - const mounts = await listHCVaultMounts(connection, gatewayService, namespace); - return mounts; + const connection = await getVaultConnectionForNamespace(actor, namespace, "view vault mounts"); + return listHCVaultMounts(connection, gatewayService, namespace); }; const getVaultSecretPaths = async ({ @@ -484,47 +449,8 @@ export const externalMigrationServiceFactory = ({ namespace: string; mountPath: string; }) => { - const { hasRole } = await permissionService.getOrgPermission({ - actorId: actor.id, - actor: actor.type, - orgId: actor.orgId, - actorOrgId: actor.orgId, - actorAuthMethod: actor.authMethod, - scope: OrganizationActionScope.Any - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can view vault secret paths" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - - const secretPaths = await listHCVaultSecretPaths(namespace, connection, gatewayService, mountPath); - - return secretPaths; + const connection = await getVaultConnectionForNamespace(actor, namespace, "view vault secret paths"); + return listHCVaultSecretPaths(namespace, connection, gatewayService, mountPath); }; const importVaultSecrets = async ({ @@ -544,44 +470,7 @@ export const externalMigrationServiceFactory = ({ vaultSecretPath: string; auditLogInfo: AuditLogInfo; }) => { - const { hasRole } = await permissionService.getOrgPermission({ - actorId: actor.id, - actor: actor.type, - orgId: actor.orgId, - actorOrgId: actor.orgId, - actorAuthMethod: actor.authMethod, - scope: OrganizationActionScope.Any - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can import vault secrets" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace: vaultNamespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - + const connection = await getVaultConnectionForNamespace(actor, vaultNamespace, "import vault secrets"); const vaultSecrets = await getHCVaultSecretsForPath(vaultNamespace, vaultSecretPath, connection, gatewayService); try { @@ -668,47 +557,8 @@ export const externalMigrationServiceFactory = ({ namespace: string; authType?: string; }) => { - const { hasRole } = await permissionService.getOrgPermission({ - actorId: actor.id, - actor: actor.type, - orgId: actor.orgId, - actorOrgId: actor.orgId, - actorAuthMethod: actor.authMethod, - scope: OrganizationActionScope.Any - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can view vault auth mounts" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - - const authMounts = await getHCVaultAuthMounts(namespace, authType as HCVaultAuthType, connection, gatewayService); - - return authMounts; + const connection = await getVaultConnectionForNamespace(actor, namespace, "view vault auth mounts"); + return getHCVaultAuthMounts(namespace, authType as HCVaultAuthType, connection, gatewayService); }; const getVaultKubernetesAuthRoles = async ({ @@ -720,48 +570,8 @@ export const externalMigrationServiceFactory = ({ namespace: string; mountPath: string; }) => { - const { hasRole } = await permissionService.getOrgPermission({ - actorId: actor.id, - actor: actor.type, - orgId: actor.orgId, - actorOrgId: actor.orgId, - actorAuthMethod: actor.authMethod, - scope: OrganizationActionScope.Any - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can view vault Kubernetes auth roles" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - - // Get roles for the specified mount path only - const roles = await getHCVaultKubernetesAuthRoles(namespace, mountPath, connection, gatewayService); - - return roles; + const connection = await getVaultConnectionForNamespace(actor, namespace, "view vault Kubernetes auth roles"); + return getHCVaultKubernetesAuthRoles(namespace, mountPath, connection, gatewayService); }; const getVaultKubernetesRoles = async ({ @@ -773,44 +583,7 @@ export const externalMigrationServiceFactory = ({ namespace: string; mountPath: string; }) => { - const { hasRole } = await permissionService.getOrgPermission({ - scope: OrganizationActionScope.Any, - actor: actor.type, - actorId: actor.id, - orgId: actor.orgId, - actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can get Kubernetes roles" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - + const connection = await getVaultConnectionForNamespace(actor, namespace, "get Kubernetes roles"); return getHCVaultKubernetesRoles(namespace, mountPath, connection, gatewayService); }; @@ -823,44 +596,7 @@ export const externalMigrationServiceFactory = ({ namespace: string; mountPath: string; }) => { - const { hasRole } = await permissionService.getOrgPermission({ - scope: OrganizationActionScope.Any, - actor: actor.type, - actorId: actor.id, - orgId: actor.orgId, - actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId - }); - - if (!hasRole(OrgMembershipRole.Admin)) { - throw new ForbiddenRequestError({ message: "Only admins can get database roles" }); - } - - const vaultConfig = await vaultExternalMigrationConfigDAL.findOne({ - orgId: actor.orgId, - namespace - }); - - if (!vaultConfig) { - throw new NotFoundError({ message: "Vault migration config not found for this namespace" }); - } - - if (!vaultConfig.connection) { - throw new BadRequestError({ message: "Vault migration connection is not configured for this namespace" }); - } - - const credentials = await decryptAppConnectionCredentials({ - orgId: vaultConfig.orgId, - encryptedCredentials: vaultConfig.connection.encryptedCredentials, - kmsService, - projectId: null - }); - - const connection = { - ...vaultConfig.connection, - credentials - } as THCVaultConnection; - + const connection = await getVaultConnectionForNamespace(actor, namespace, "get database roles"); return getHCVaultDatabaseRoles(namespace, mountPath, connection, gatewayService); };