misc: addressed comments

This commit is contained in:
Sheen Capadngan
2025-10-17 21:30:55 +08:00
parent a95cb63682
commit ee2ab9f4ba
7 changed files with 78 additions and 48 deletions
@@ -8,7 +8,8 @@ import {
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { DatabaseErrorCode } from "@app/lib/error-codes";
import { BadRequestError, DatabaseError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
import { AppConnection } from "../app-connection/app-connection-enums"; import { AppConnection } from "../app-connection/app-connection-enums";
@@ -231,8 +232,13 @@ export const externalMigrationServiceFactory = ({
try { try {
await listHCVaultPolicies(namespace, connection, gatewayService); await listHCVaultPolicies(namespace, connection, gatewayService);
await listHCVaultSecretPaths(namespace, connection, gatewayService); await getHCVaultAuthMounts(namespace, HCVaultAuthType.Kubernetes, connection, gatewayService);
await listHCVaultMounts(connection, gatewayService);
const mounts = await listHCVaultMounts(connection, gatewayService);
const sampleKvMount = mounts.find((mount) => mount.type === "kv");
if (sampleKvMount) {
await listHCVaultSecretPaths(namespace, connection, gatewayService, sampleKvMount.path);
}
} catch (error) { } catch (error) {
throw new BadRequestError({ throw new BadRequestError({
message: `Failed to establish namespace confiugration. ${error instanceof Error ? error.message : "Unknown error"}` message: `Failed to establish namespace confiugration. ${error instanceof Error ? error.message : "Unknown error"}`
@@ -264,13 +270,26 @@ export const externalMigrationServiceFactory = ({
namespace namespace
}); });
const config = await vaultExternalMigrationConfigDAL.create({ try {
namespace, const config = await vaultExternalMigrationConfigDAL.create({
connectionId, namespace,
orgId: actor.orgId connectionId,
}); orgId: actor.orgId
});
return config; return config;
} catch (error) {
if (
error instanceof DatabaseError &&
(error.error as { code: string })?.code === DatabaseErrorCode.UniqueViolation
) {
throw new BadRequestError({
message: `Vault external migration already exists for this namespace`
});
}
throw error;
}
}; };
const updateVaultExternalMigration = async ({ const updateVaultExternalMigration = async ({
@@ -124,7 +124,7 @@ export enum ExternalMigrationProviders {
export enum VaultImportStatus { export enum VaultImportStatus {
Imported = "imported", Imported = "imported",
ApprovalRequired = "approval_required" ApprovalRequired = "approval-required"
} }
export type TCreateVaultExternalMigrationDTO = { export type TCreateVaultExternalMigrationDTO = {
@@ -33,7 +33,7 @@ This migration approach lets you set up a connection to your Vault instance once
In your Vault instance, create a policy that allows Infisical to read your secrets, policies, and authentication configurations. This policy grants read-only access and doesn't allow Infisical to modify anything in Vault. In your Vault instance, create a policy that allows Infisical to read your secrets, policies, and authentication configurations. This policy grants read-only access and doesn't allow Infisical to modify anything in Vault.
<Accordion title="View the complete policy"> <Accordion title="View the complete policy">
```python ```hcl
# System endpoints - for listing namespaces, policies, mounts, and auth methods # System endpoints - for listing namespaces, policies, mounts, and auth methods
path "sys/namespaces" { path "sys/namespaces" {
capabilities = ["list"] capabilities = ["list"]
@@ -162,7 +162,17 @@ The authentication settings (service accounts, TTL, policies, etc.) will be auto
#### Import and Translate Access Control Policies #### Import and Translate Access Control Policies
When configuring project role-based access control, you can import Vault HCL policies and automatically translate them to Infisical permissions: When configuring project role-based access control, you can import Vault HCL policies and automatically translate them to Infisical permissions.
<Note>
Policy translation is best-effort and provides a starting point based on your
Vault configuration. The translated permissions should be reviewed and
adjusted as needed since Vault and Infisical have different access control
models. Infisical will analyze path patterns and capabilities to suggest
equivalent permissions.
</Note>
**To import and translate a policy:**
1. Navigate to your project, then go to **Access Control > Roles** and create or edit a role 1. Navigate to your project, then go to **Access Control > Roles** and create or edit a role
2. In the policy configuration, click **"Add from HashiCorp Vault"** 2. In the policy configuration, click **"Add from HashiCorp Vault"**
@@ -177,12 +187,16 @@ When configuring project role-based access control, you can import Vault HCL pol
5. Review the automatically translated Infisical permissions 5. Review the automatically translated Infisical permissions
6. Make any adjustments and save 6. Make any adjustments and save
**How policy translation works:** <Tip>
**How policy translation works:**
- Vault path patterns are analyzed to identify KV secret engines and environments
- Vault capabilities (`read`, `list`, `create`, etc.) are mapped to Infisical permissions - Vault path patterns are analyzed to identify KV secret engines and environments
- Wildcards in paths are converted to glob patterns - Vault capabilities (`read`,`list`, `create`, etc.) are mapped to Infisical permissions
- Secret paths are preserved for granular access control - Wildcards in paths are converted to glob patterns
- Secret paths are preserved for granular access control
Always review the translated permissions carefully, as Vault's capability-based model may not map 1:1 with Infisical's permission structure.
</Tip>
--- ---
@@ -219,7 +233,7 @@ Before starting the bulk import, you need to decide how your Vault structure wil
In your Vault instance, create a policy that allows Infisical to read all secrets and metadata. This policy grants read-only access. In your Vault instance, create a policy that allows Infisical to read all secrets and metadata. This policy grants read-only access.
<Accordion title="View the bulk import policy"> <Accordion title="View the bulk import policy">
```python ```hcl
# Allow listing secret engines/mounts # Allow listing secret engines/mounts
path "sys/mounts" { path "sys/mounts" {
capabilities = ["read", "list"] capabilities = ["read", "list"]
+1 -1
View File
@@ -5,7 +5,7 @@ export enum ExternalMigrationProviders {
export enum VaultImportStatus { export enum VaultImportStatus {
Imported = "imported", Imported = "imported",
ApprovalRequired = "approval_required" ApprovalRequired = "approval-required"
} }
export type TVaultExternalMigrationConfig = { export type TVaultExternalMigrationConfig = {
@@ -1,6 +1,7 @@
import { useState } from "react"; import { useState } from "react";
import { faEdit, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faEdit, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link } from "@tanstack/react-router";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { import {
@@ -162,7 +163,14 @@ export const VaultConnectionSection = () => {
<p className="mt-4 text-xs text-mineshaft-400"> <p className="mt-4 text-xs text-mineshaft-400">
Configure namespace-specific connections to enable in-platform migration features. Manage Configure namespace-specific connections to enable in-platform migration features. Manage
connections in the App Connections section. connections in the{" "}
<Link
to="/organization/app-connections"
className="text-primary underline hover:text-primary-300"
>
App Connections
</Link>{" "}
section.
</p> </p>
<VaultNamespaceConfigModal <VaultNamespaceConfigModal
@@ -21,7 +21,9 @@ import {
import { TVaultExternalMigrationConfig } from "@app/hooks/api/migration/types"; import { TVaultExternalMigrationConfig } from "@app/hooks/api/migration/types";
const schema = z.object({ const schema = z.object({
namespace: z.string().min(1, "Namespace is required"), namespace: z
.string()
.min(1, "Namespace is required. If you intend to use the root namespace, use root or /."),
connectionId: z.string().min(1, "Connection is required") connectionId: z.string().min(1, "Connection is required")
}); });
@@ -94,38 +94,25 @@ const Content = ({ onClose }: ContentProps) => {
} }
// Apply the parsed permissions to the form // Apply the parsed permissions to the form
Object.entries(parsedPermissions).forEach(([subject, value]) => { (Object.keys(parsedPermissions) as ProjectPermissionSub[]).forEach((subjectKey) => {
const value = parsedPermissions[subjectKey];
if (!value) return; if (!value) return;
const subjectKey = subject as ProjectPermissionSub; const existingValue = rootForm.getValues(`permissions.${subjectKey}`) as unknown[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const existingValue = rootForm.getValues(`permissions.${subjectKey}`) as any;
if (Array.isArray(existingValue) && existingValue.length > 0) { if (Array.isArray(existingValue) && existingValue.length > 0) {
// Merge with existing permissions // Merge with existing permissions
rootForm.setValue( rootForm.setValue(`permissions.${subjectKey}`, [...existingValue, ...value] as never, {
`permissions.${subjectKey}`, shouldDirty: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment shouldTouch: true,
// @ts-ignore-error shouldValidate: true
[...existingValue, ...value], });
{
shouldDirty: true,
shouldTouch: true,
shouldValidate: true
}
);
} else { } else {
rootForm.setValue( rootForm.setValue(`permissions.${subjectKey}`, value as never, {
`permissions.${subjectKey}`, shouldDirty: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment shouldTouch: true,
// @ts-ignore-error shouldValidate: true
value, });
{
shouldDirty: true,
shouldTouch: true,
shouldValidate: true
}
);
} }
}); });