diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index 3d42943a6..778589de8 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -572,14 +572,14 @@ export const integrationAuthServiceFactory = ({ const response = keys .Keys!.map((key) => { const keyAlias = aliases.Aliases!.find((alias) => key.KeyId === alias.TargetKeyId); - if (!keyAlias?.AliasName?.includes("alias/aws/") || keyAlias?.AliasName?.includes("alias/aws/secretsmanager")) { + if (!keyAlias?.AliasName?.includes("alias/aws/")) { return { id: String(key.KeyId), alias: String(keyAlias?.AliasName || key.KeyId) }; } return { id: "null", alias: "null" }; }) .filter((elem) => elem.id !== "null"); - return response; + return [...response, { id: "null", alias: "default" }]; }; const getQoveryProjects = async ({ diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 5fc11c038..de576dc99 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -477,24 +477,29 @@ const syncSecretsAWSParameterStore = async ({ }), {} as Record ); - // Identify secrets to create await Promise.all( Object.keys(secrets).map(async (key) => { if (!(key in awsParameterStoreSecretsObj)) { // case: secret does not exist in AWS parameter store // -> create secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - // Overwrite: true, - Tags: metadata.secretAWSTag - ? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value })) - : [] - }) - .promise(); + if (secrets[key].value) { + await ssm + .putParameter({ + Name: `${integration.path}${key}`, + Type: "SecureString", + Value: secrets[key].value, + KeyId: metadata.kmsKeyId ? metadata.kmsKeyId : undefined, + // Overwrite: true, + Tags: metadata.secretAWSTag + ? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ + Key: tag.key, + Value: tag.value + })) + : [] + }) + .promise(); + } // case: secret exists in AWS parameter store } else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) { // case: secret value doesn't match one in AWS parameter store diff --git a/docs/images/integrations/aws/integrations-aws-parameter-store-auth.png b/docs/images/integrations/aws/integrations-aws-parameter-store-auth.png index 7891e636b..14d54c9a6 100644 Binary files a/docs/images/integrations/aws/integrations-aws-parameter-store-auth.png and b/docs/images/integrations/aws/integrations-aws-parameter-store-auth.png differ diff --git a/docs/images/integrations/aws/integrations-aws-parameter-store-create.png b/docs/images/integrations/aws/integrations-aws-parameter-store-create.png index 7c51eb2b2..a168925d6 100644 Binary files a/docs/images/integrations/aws/integrations-aws-parameter-store-create.png and b/docs/images/integrations/aws/integrations-aws-parameter-store-create.png differ diff --git a/frontend/src/pages/integrations/aws-parameter-store/create.tsx b/frontend/src/pages/integrations/aws-parameter-store/create.tsx index 92e4662a9..9f52347ce 100644 --- a/frontend/src/pages/integrations/aws-parameter-store/create.tsx +++ b/frontend/src/pages/integrations/aws-parameter-store/create.tsx @@ -14,6 +14,7 @@ import { motion } from "framer-motion"; import queryString from "query-string"; import { useCreateIntegration } from "@app/hooks/api"; +import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries"; import { Button, @@ -90,6 +91,7 @@ export default function AWSParameterStoreCreateIntegrationPage() { const [shouldTag, setShouldTag] = useState(false); const [tagKey, setTagKey] = useState(""); const [tagValue, setTagValue] = useState(""); + const [kmsKeyId, setKmsKeyId] = useState(""); useEffect(() => { if (workspace) { @@ -98,6 +100,19 @@ export default function AWSParameterStoreCreateIntegrationPage() { } }, [workspace]); + + const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } = + useGetIntegrationAuthAwsKmsKeys({ + integrationAuthId: String(integrationAuthId), + region: selectedAWSRegion + }); + + useEffect(() => { + if (integrationAuthAwsKmsKeys) { + setKmsKeyId(String(integrationAuthAwsKmsKeys?.filter(key => key.alias === "default")[0]?.id)) + } + }, [integrationAuthAwsKmsKeys]) + const isValidAWSParameterStorePath = (awsStorePath: string) => { const pattern = /^\/([\w-]+\/)*[\w-]+\/$/; return pattern.test(awsStorePath) && awsStorePath.length <= 2048; @@ -133,7 +148,11 @@ export default function AWSParameterStoreCreateIntegrationPage() { value: tagValue }] } - : {}) + : {}), + ...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ? + { + kmsKeyId + }: {}) } }); @@ -146,7 +165,7 @@ export default function AWSParameterStoreCreateIntegrationPage() { } }; - return integrationAuth && workspace && selectedSourceEnvironment ? ( + return (integrationAuth && workspace && selectedSourceEnvironment && !isIntegrationAuthAwsKmsKeysLoading) ? (
Set Up AWS Parameter Integration @@ -286,6 +305,31 @@ export default function AWSParameterStoreCreateIntegrationPage() {
)} + + + @@ -318,7 +362,7 @@ export default function AWSParameterStoreCreateIntegrationPage() { Set Up AWS Parameter Store Integration - {isintegrationAuthLoading ? ( + {(isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading) ? ( key.id === kmsKeyId)[0]?.alias !== "alias/aws/secretsmanager") ? + ...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ? { kmsKeyId }: {}) diff --git a/frontend/src/views/IntegrationsPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx b/frontend/src/views/IntegrationsPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx index f81f8befe..164470289 100644 --- a/frontend/src/views/IntegrationsPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx +++ b/frontend/src/views/IntegrationsPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx @@ -109,7 +109,7 @@ export const CloudIntegrationSection = ({ {cloudIntegration.isAvailable && Boolean(integrationAuths?.[cloudIntegration.slug]) && ( -
+
diff --git a/frontend/src/views/IntegrationsPage/components/IntegrationsSection/IntegrationsSection.tsx b/frontend/src/views/IntegrationsPage/components/IntegrationsSection/IntegrationsSection.tsx index 2944203d0..da3d50c66 100644 --- a/frontend/src/views/IntegrationsPage/components/IntegrationsSection/IntegrationsSection.tsx +++ b/frontend/src/views/IntegrationsPage/components/IntegrationsSection/IntegrationsSection.tsx @@ -217,11 +217,12 @@ export const IntegrationsSection = ({ isOpen={popUp.deleteConfirmation.isOpen} title={`Are you sure want to remove ${ (popUp?.deleteConfirmation.data as TIntegration)?.integration || " " - } integration for ${(popUp?.deleteConfirmation.data as TIntegration)?.app || " "}?`} + } integration for ${(popUp?.deleteConfirmation.data as TIntegration)?.app || "this project"}?`} onChange={(isOpen) => handlePopUpToggle("deleteConfirmation", isOpen)} deleteKey={ (popUp?.deleteConfirmation?.data as TIntegration)?.app || (popUp?.deleteConfirmation?.data as TIntegration)?.owner || + (popUp?.deleteConfirmation?.data as TIntegration)?.path || "" } onDeleteApproved={async () =>