Merge remote-tracking branch 'origin' into k8s-auth

This commit is contained in:
Tuan Dang
2024-05-12 16:16:44 -07:00
7 changed files with 245 additions and 142 deletions
@@ -5,8 +5,13 @@ import { getConfig } from "@app/lib/config/env";
export const maintenanceMode = fp(async (fastify) => { export const maintenanceMode = fp(async (fastify) => {
fastify.addHook("onRequest", async (req) => { fastify.addHook("onRequest", async (req) => {
const serverEnvs = getConfig(); const serverEnvs = getConfig();
if (req.url !== "/api/v1/auth/checkAuth" && req.method !== "GET" && serverEnvs.MAINTENANCE_MODE) { if (serverEnvs.MAINTENANCE_MODE) {
// skip if its universal auth login or renew
if (req.url === "/api/v1/auth/universal-auth/login" && req.method === "POST") return;
if (req.url === "/api/v1/auth/token/renew" && req.method === "POST") return;
if (req.url !== "/api/v1/auth/checkAuth" && req.method !== "GET") {
throw new Error("Infisical is in maintenance mode. Please try again later."); throw new Error("Infisical is in maintenance mode. Please try again later.");
} }
}
}); });
}); });
@@ -143,8 +143,8 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
integrationId: z.string().trim().describe(INTEGRATION.UPDATE.integrationId) integrationId: z.string().trim().describe(INTEGRATION.UPDATE.integrationId)
}), }),
body: z.object({ body: z.object({
app: z.string().trim().describe(INTEGRATION.UPDATE.app), app: z.string().trim().optional().describe(INTEGRATION.UPDATE.app),
appId: z.string().trim().describe(INTEGRATION.UPDATE.appId), appId: z.string().trim().optional().describe(INTEGRATION.UPDATE.appId),
isActive: z.boolean().describe(INTEGRATION.UPDATE.isActive), isActive: z.boolean().describe(INTEGRATION.UPDATE.isActive),
secretPath: z secretPath: z
.string() .string()
@@ -154,7 +154,33 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
.describe(INTEGRATION.UPDATE.secretPath), .describe(INTEGRATION.UPDATE.secretPath),
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment), targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner), owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment) environment: z.string().trim().describe(INTEGRATION.UPDATE.environment),
metadata: z
.object({
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
secretGCPLabel: z
.object({
labelName: z.string(),
labelValue: z.string()
})
.optional()
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
secretAWSTag: z
.array(
z.object({
key: z.string(),
value: z.string()
})
)
.optional()
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete)
})
.optional()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -9,9 +9,12 @@
import { import {
CreateSecretCommand, CreateSecretCommand,
DescribeSecretCommand,
GetSecretValueCommand, GetSecretValueCommand,
ResourceNotFoundException, ResourceNotFoundException,
SecretsManagerClient, SecretsManagerClient,
TagResourceCommand,
UntagResourceCommand,
UpdateSecretCommand UpdateSecretCommand
} from "@aws-sdk/client-secrets-manager"; } from "@aws-sdk/client-secrets-manager";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
@@ -574,6 +577,7 @@ const syncSecretsAWSSecretManager = async ({
if (awsSecretManagerSecret?.SecretString) { if (awsSecretManagerSecret?.SecretString) {
awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString); awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString);
} }
if (!isEqual(awsSecretManagerSecretObj, secKeyVal)) { if (!isEqual(awsSecretManagerSecretObj, secKeyVal)) {
await secretsManager.send( await secretsManager.send(
new UpdateSecretCommand({ new UpdateSecretCommand({
@@ -582,7 +586,88 @@ const syncSecretsAWSSecretManager = async ({
}) })
); );
} }
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
if (secretAWSTag && secretAWSTag.length) {
const describedSecret = await secretsManager.send(
// requires secretsmanager:DescribeSecret policy
new DescribeSecretCommand({
SecretId: integration.app as string
})
);
if (!describedSecret.Tags) return;
const integrationTagObj = secretAWSTag.reduce(
(acc, item) => {
acc[item.key] = item.value;
return acc;
},
{} as Record<string, string>
);
const awsTagObj = (describedSecret.Tags || []).reduce(
(acc, item) => {
if (item.Key && item.Value) {
acc[item.Key] = item.Value;
}
return acc;
},
{} as Record<string, string>
);
const tagsToUpdate: { Key: string; Value: string }[] = [];
const tagsToDelete: { Key: string; Value: string }[] = [];
describedSecret.Tags?.forEach((tag) => {
if (tag.Key && tag.Value) {
if (!(tag.Key in integrationTagObj)) {
// delete tag from AWS secret manager
tagsToDelete.push({
Key: tag.Key,
Value: tag.Value
});
} else if (tag.Value !== integrationTagObj[tag.Key]) {
// update tag in AWS secret manager
tagsToUpdate.push({
Key: tag.Key,
Value: integrationTagObj[tag.Key]
});
}
}
});
secretAWSTag?.forEach((tag) => {
if (!(tag.key in awsTagObj)) {
// create tag in AWS secret manager
tagsToUpdate.push({
Key: tag.key,
Value: tag.value
});
}
});
if (tagsToUpdate.length) {
await secretsManager.send(
new TagResourceCommand({
SecretId: integration.app as string,
Tags: tagsToUpdate
})
);
}
if (tagsToDelete.length) {
await secretsManager.send(
new UntagResourceCommand({
SecretId: integration.app as string,
TagKeys: tagsToDelete.map((tag) => tag.Key)
})
);
}
}
} catch (err) { } catch (err) {
// case when AWS manager can't find the specified secret
if (err instanceof ResourceNotFoundException && secretsManager) { if (err instanceof ResourceNotFoundException && secretsManager) {
await secretsManager.send( await secretsManager.send(
new CreateSecretCommand({ new CreateSecretCommand({
@@ -103,7 +103,8 @@ export const integrationServiceFactory = ({
owner, owner,
isActive, isActive,
environment, environment,
secretPath secretPath,
metadata
}: TUpdateIntegrationDTO) => { }: TUpdateIntegrationDTO) => {
const integration = await integrationDAL.findById(id); const integration = await integrationDAL.findById(id);
if (!integration) throw new BadRequestError({ message: "Integration auth not found" }); if (!integration) throw new BadRequestError({ message: "Integration auth not found" });
@@ -127,7 +128,17 @@ export const integrationServiceFactory = ({
appId, appId,
targetEnvironment, targetEnvironment,
owner, owner,
secretPath secretPath,
metadata: {
...(integration.metadata as object),
...metadata
}
});
await secretQueueService.syncIntegrations({
environment: folder.environment.slug,
secretPath,
projectId: folder.projectId
}); });
return updatedIntegration; return updatedIntegration;
@@ -33,13 +33,27 @@ export type TCreateIntegrationDTO = {
export type TUpdateIntegrationDTO = { export type TUpdateIntegrationDTO = {
id: string; id: string;
app: string; app?: string;
appId: string; appId?: string;
isActive?: boolean; isActive?: boolean;
secretPath: string; secretPath: string;
targetEnvironment: string; targetEnvironment: string;
owner: string; owner: string;
environment: string; environment: string;
metadata?: {
secretPrefix?: string;
secretSuffix?: string;
secretGCPLabel?: {
labelName: string;
labelValue: string;
};
secretAWSTag?: {
key: string;
value: string;
}[];
kmsKeyId?: string;
shouldDisableDelete?: boolean;
};
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TDeleteIntegrationDTO = { export type TDeleteIntegrationDTO = {
@@ -29,7 +29,9 @@ Prerequisites:
"secretsmanager:GetSecretValue", "secretsmanager:GetSecretValue",
"secretsmanager:CreateSecret", "secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret", "secretsmanager:UpdateSecret",
"secretsmanager:DescribeSecret", // if you need to add tags to secrets
"secretsmanager:TagResource", // if you need to add tags to secrets "secretsmanager:TagResource", // if you need to add tags to secrets
"secretsmanager:UntagResource", // if you need to add tags to secrets
"kms:ListKeys", // if you need to specify the KMS key "kms:ListKeys", // if you need to specify the KMS key
"kms:ListAliases", // if you need to specify the KMS key "kms:ListAliases", // if you need to specify the KMS key
"kms:Encrypt", // if you need to specify the KMS key "kms:Encrypt", // if you need to specify the KMS key
+7 -47
View File
@@ -21,9 +21,7 @@ import {
faNetworkWired, faNetworkWired,
faPlug, faPlug,
faPlus, faPlus,
faUserPlus, faUserPlus
faWarning,
faXmark
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
@@ -56,7 +54,6 @@ import {
fetchOrgUsers, fetchOrgUsers,
useAddUserToWsNonE2EE, useAddUserToWsNonE2EE,
useCreateWorkspace, useCreateWorkspace,
useGetUserAction,
useRegisterUserAction useRegisterUserAction
} from "@app/hooks/api"; } from "@app/hooks/api";
// import { fetchUserWsKey } from "@app/hooks/api/keys/queries"; // import { fetchUserWsKey } from "@app/hooks/api/keys/queries";
@@ -312,8 +309,7 @@ const LearningItem = ({
href={link} href={link}
> >
<div <div
className={`${ className={`${complete ? "bg-gradient-to-r from-primary-500/70 p-[0.07rem]" : ""
complete ? "bg-gradient-to-r from-primary-500/70 p-[0.07rem]" : ""
} mb-3 rounded-md`} } mb-3 rounded-md`}
> >
<div <div
@@ -325,8 +321,7 @@ const LearningItem = ({
await registerUserAction.mutateAsync(userAction); await registerUserAction.mutateAsync(userAction);
} }
}} }}
className={`group relative flex h-[5.5rem] w-full items-center justify-between overflow-hidden rounded-md border ${ className={`group relative flex h-[5.5rem] w-full items-center justify-between overflow-hidden rounded-md border ${complete
complete
? "cursor-default border-mineshaft-900 bg-gradient-to-r from-[#0e1f01] to-mineshaft-700" ? "cursor-default border-mineshaft-900 bg-gradient-to-r from-[#0e1f01] to-mineshaft-700"
: "cursor-pointer border-mineshaft-600 bg-mineshaft-800 shadow-xl hover:bg-mineshaft-700" : "cursor-pointer border-mineshaft-600 bg-mineshaft-800 shadow-xl hover:bg-mineshaft-700"
} text-mineshaft-100 duration-200`} } text-mineshaft-100 duration-200`}
@@ -407,8 +402,7 @@ const LearningItemSquare = ({
href={link} href={link}
> >
<div <div
className={`${ className={`${complete ? "bg-gradient-to-r from-primary-500/70 p-[0.07rem]" : ""
complete ? "bg-gradient-to-r from-primary-500/70 p-[0.07rem]" : ""
} w-full rounded-md`} } w-full rounded-md`}
> >
<div <div
@@ -420,8 +414,7 @@ const LearningItemSquare = ({
await registerUserAction.mutateAsync(userAction); await registerUserAction.mutateAsync(userAction);
} }
}} }}
className={`group relative flex w-full items-center justify-between overflow-hidden rounded-md border ${ className={`group relative flex w-full items-center justify-between overflow-hidden rounded-md border ${complete
complete
? "cursor-default border-mineshaft-900 bg-gradient-to-r from-[#0e1f01] to-mineshaft-700" ? "cursor-default border-mineshaft-900 bg-gradient-to-r from-[#0e1f01] to-mineshaft-700"
: "cursor-pointer border-mineshaft-600 bg-mineshaft-800 shadow-xl hover:bg-mineshaft-700" : "cursor-pointer border-mineshaft-600 bg-mineshaft-800 shadow-xl hover:bg-mineshaft-700"
} text-mineshaft-100 duration-200`} } text-mineshaft-100 duration-200`}
@@ -438,8 +431,7 @@ const LearningItemSquare = ({
</div> </div>
)} )}
<div <div
className={`text-right text-sm font-normal text-mineshaft-300 ${ className={`text-right text-sm font-normal text-mineshaft-300 ${complete ? "font-semibold text-primary" : ""
complete ? "font-semibold text-primary" : ""
}`} }`}
> >
{complete ? "Complete!" : `About ${time}`} {complete ? "Complete!" : `About ${time}`}
@@ -483,12 +475,6 @@ const OrganizationPage = withPermission(
const addUsersToProject = useAddUserToWsNonE2EE(); const addUsersToProject = useAddUserToWsNonE2EE();
const { data: updateClosed } = useGetUserAction("april_13_2024_db_update_closed");
const registerUserAction = useRegisterUserAction();
const closeUpdate = async () => {
await registerUserAction.mutateAsync("april_13_2024_db_update_closed");
};
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"addNewWs", "addNewWs",
"upgradePlan" "upgradePlan"
@@ -594,31 +580,6 @@ const OrganizationPage = withPermission(
</div> </div>
)} )}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl"> <div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
{(window.location.origin.includes("https://app.infisical.com") || window.location.origin.includes("http://localhost:8080")) && (
<div
className={`${
!updateClosed ? "block" : "hidden"
} mb-4 flex w-full flex-row items-center rounded-md border border-primary-600 bg-primary/10 p-2 text-base text-white`}
>
<FontAwesomeIcon icon={faWarning} className="p-6 text-4xl text-primary" />
<div className="text-sm">
<span className="text-lg font-semibold">Scheduled maintenance on May 11th 2024 </span>{" "}
<br />
Infisical will undergo scheduled maintenance for approximately 2 hour on Saturday, May 11th, 11am EST. During these hours, read
operations to Infisical will continue to function normally but no resources will be editable.
No action is required on your end — your applications will continue to fetch secrets.
<br />
</div>
<button
type="button"
onClick={() => closeUpdate()}
aria-label="close"
className="flex h-full items-start text-mineshaft-100 duration-200 hover:text-red-400"
>
<FontAwesomeIcon icon={faXmark} />
</button>
</div>)}
<p className="mr-4 font-semibold text-white">Projects</p> <p className="mr-4 font-semibold text-white">Projects</p>
<div className="mt-6 flex w-full flex-row"> <div className="mt-6 flex w-full flex-row">
<Input <Input
@@ -814,8 +775,7 @@ const OrganizationPage = withPermission(
</div> </div>
</div> </div>
<div <div
className={`w-28 pr-4 text-right text-sm font-semibold ${ className={`w-28 pr-4 text-right text-sm font-semibold ${false && "text-green"
false && "text-green"
}`} }`}
> >
About 2 min About 2 min