mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-19 16:01:44 +00:00
feature: add suport for project scoped app connections
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { ProjectType } from "@app/db/schemas";
|
||||
import { TAppConnections } from "@app/db/schemas/app-connections";
|
||||
import {
|
||||
getOCIConnectionListItem,
|
||||
@@ -8,6 +9,8 @@ import { getOracleDBConnectionListItem, OracleDBConnectionMethod } from "@app/ee
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { SECRET_ROTATION_CONNECTION_MAP } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-maps";
|
||||
import { SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-maps";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { APP_CONNECTION_NAME_MAP, APP_CONNECTION_PLAN_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||
@@ -16,6 +19,7 @@ import {
|
||||
validateSqlConnectionCredentials
|
||||
} from "@app/services/app-connection/shared/sql";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
import { SECRET_SYNC_CONNECTION_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
|
||||
import {
|
||||
getOnePassConnectionListItem,
|
||||
@@ -133,7 +137,19 @@ import {
|
||||
} from "./windmill";
|
||||
import { getZabbixConnectionListItem, validateZabbixConnectionCredentials, ZabbixConnectionMethod } from "./zabbix";
|
||||
|
||||
export const listAppConnectionOptions = () => {
|
||||
const SECRET_SYNC_APP_CONNECTION_MAP = Object.fromEntries(
|
||||
Object.entries(SECRET_SYNC_CONNECTION_MAP).map(([key, value]) => [value, key])
|
||||
);
|
||||
|
||||
const SECRET_ROTATION_APP_CONNECTION_MAP = Object.fromEntries(
|
||||
Object.entries(SECRET_ROTATION_CONNECTION_MAP).map(([key, value]) => [value, key])
|
||||
);
|
||||
|
||||
const SECRET_SCANNING_APP_CONNECTION_MAP = Object.fromEntries(
|
||||
Object.entries(SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP).map(([key, value]) => [value, key])
|
||||
);
|
||||
|
||||
export const listAppConnectionOptions = (projectType?: ProjectType) => {
|
||||
return [
|
||||
getAwsConnectionListItem(),
|
||||
getGitHubConnectionListItem(),
|
||||
@@ -173,22 +189,55 @@ export const listAppConnectionOptions = () => {
|
||||
getDigitalOceanConnectionListItem(),
|
||||
getNetlifyConnectionListItem(),
|
||||
getOktaConnectionListItem()
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
]
|
||||
.filter((option) => {
|
||||
switch (projectType) {
|
||||
case ProjectType.SecretManager:
|
||||
return (
|
||||
Boolean(SECRET_SYNC_APP_CONNECTION_MAP[option.app]) ||
|
||||
Boolean(SECRET_ROTATION_APP_CONNECTION_MAP[option.app])
|
||||
);
|
||||
case ProjectType.SecretScanning:
|
||||
return Boolean(SECRET_SCANNING_APP_CONNECTION_MAP[option.app]);
|
||||
case ProjectType.CertificateManager:
|
||||
return (
|
||||
option.app === AppConnection.AWS ||
|
||||
option.app === AppConnection.Cloudflare ||
|
||||
option.app === AppConnection.AzureADCS
|
||||
);
|
||||
case ProjectType.KMS:
|
||||
return false;
|
||||
case ProjectType.SSH:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
export const encryptAppConnectionCredentials = async ({
|
||||
orgId,
|
||||
credentials,
|
||||
kmsService
|
||||
kmsService,
|
||||
projectId
|
||||
}: {
|
||||
orgId: string;
|
||||
credentials: TAppConnection["credentials"];
|
||||
kmsService: TAppConnectionServiceFactoryDep["kmsService"];
|
||||
projectId: string | null | undefined;
|
||||
}) => {
|
||||
const { encryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
});
|
||||
const { encryptor } = await kmsService.createCipherPairWithDataKey(
|
||||
projectId
|
||||
? {
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
}
|
||||
: {
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
}
|
||||
);
|
||||
|
||||
const { cipherTextBlob: encryptedCredentialsBlob } = encryptor({
|
||||
plainText: Buffer.from(JSON.stringify(credentials))
|
||||
@@ -200,16 +249,22 @@ export const encryptAppConnectionCredentials = async ({
|
||||
export const decryptAppConnectionCredentials = async ({
|
||||
orgId,
|
||||
encryptedCredentials,
|
||||
kmsService
|
||||
kmsService,
|
||||
projectId
|
||||
}: {
|
||||
orgId: string;
|
||||
encryptedCredentials: Buffer;
|
||||
kmsService: TAppConnectionServiceFactoryDep["kmsService"];
|
||||
projectId: string | null | undefined;
|
||||
}) => {
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
});
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey(
|
||||
projectId
|
||||
? { type: KmsDataKey.SecretManager, projectId }
|
||||
: {
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
}
|
||||
);
|
||||
|
||||
const decryptedPlainTextBlob = decryptor({
|
||||
cipherTextBlob: encryptedCredentials
|
||||
@@ -343,6 +398,7 @@ export const decryptAppConnection = async (
|
||||
credentials: await decryptAppConnectionCredentials({
|
||||
encryptedCredentials: appConnection.encryptedCredentials,
|
||||
orgId: appConnection.orgId,
|
||||
projectId: appConnection.projectId,
|
||||
kmsService
|
||||
}),
|
||||
credentialsHash: crypto.nativeCrypto.createHash("sha256").update(appConnection.encryptedCredentials).digest("hex")
|
||||
@@ -413,3 +469,73 @@ export const enterpriseAppCheck = async (
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
type Resource = {
|
||||
name: string;
|
||||
id: string;
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
projectSlug: string;
|
||||
projectType: string;
|
||||
};
|
||||
|
||||
type UsageData = {
|
||||
secretSyncs: Resource[];
|
||||
secretRotations: Resource[];
|
||||
dataSources: Resource[];
|
||||
externalCas: Resource[];
|
||||
};
|
||||
|
||||
type ResourceSummary = {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
type ProjectWithResources = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
type: ProjectType;
|
||||
resources: {
|
||||
secretSyncs: ResourceSummary[];
|
||||
secretRotations: ResourceSummary[];
|
||||
dataSources: ResourceSummary[];
|
||||
externalCas: (ResourceSummary & { appConnectionId?: string; dnsAppConnectionId?: string })[];
|
||||
};
|
||||
};
|
||||
|
||||
export const transformUsageToProjects = (data: UsageData): ProjectWithResources[] => {
|
||||
const projectMap = new Map<string, ProjectWithResources>();
|
||||
|
||||
Object.entries(data).forEach(([resourceType, resources]) => {
|
||||
resources.forEach((resource) => {
|
||||
const { projectId, projectName, projectSlug, projectType, name, id, ...rest } = resource;
|
||||
|
||||
const projectKey = projectId;
|
||||
|
||||
if (!projectMap.has(projectKey)) {
|
||||
projectMap.set(projectKey, {
|
||||
id: projectId,
|
||||
name: projectName,
|
||||
slug: projectSlug,
|
||||
type: projectType as ProjectType,
|
||||
resources: {
|
||||
secretSyncs: [],
|
||||
secretRotations: [],
|
||||
dataSources: [],
|
||||
externalCas: []
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const project = projectMap.get(projectKey)!;
|
||||
project.resources[resourceType as keyof ProjectWithResources["resources"]].push({
|
||||
name,
|
||||
id,
|
||||
...rest
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return Array.from(projectMap.values());
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user