misc: added handling for case enforcement

This commit is contained in:
Sheen Capadngan
2025-01-16 19:10:00 +08:00
parent 6ee446e574
commit 440f93f392
11 changed files with 111 additions and 19 deletions
@@ -0,0 +1,33 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasEnforceCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "enforceCapitalization");
const hasAutoCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "autoCapitalization");
await knex.schema.alterTable(TableName.Project, (t) => {
if (!hasEnforceCapitalizationCol) {
t.boolean("enforceCapitalization").defaultTo(false).notNullable();
}
if (hasAutoCapitalizationCol) {
t.boolean("autoCapitalization").defaultTo(false).alter();
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasEnforceCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "enforceCapitalization");
const hasAutoCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "autoCapitalization");
await knex.schema.alterTable(TableName.Project, (t) => {
if (hasEnforceCapitalizationCol) {
t.dropColumn("enforceCapitalization");
}
if (hasAutoCapitalizationCol) {
t.boolean("autoCapitalization").defaultTo(true).alter();
}
});
}
+3 -2
View File
@@ -13,7 +13,7 @@ export const ProjectsSchema = z.object({
id: z.string(), id: z.string(),
name: z.string(), name: z.string(),
slug: z.string(), slug: z.string(),
autoCapitalization: z.boolean().default(true).nullable().optional(), autoCapitalization: z.boolean().default(false).nullable().optional(),
orgId: z.string().uuid(), orgId: z.string().uuid(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
@@ -25,7 +25,8 @@ export const ProjectsSchema = z.object({
kmsSecretManagerKeyId: z.string().uuid().nullable().optional(), kmsSecretManagerKeyId: z.string().uuid().nullable().optional(),
kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(), kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(),
description: z.string().nullable().optional(), description: z.string().nullable().optional(),
type: z.string() type: z.string(),
enforceCapitalization: z.boolean().default(false)
}); });
export type TProjects = z.infer<typeof ProjectsSchema>; export type TProjects = z.infer<typeof ProjectsSchema>;
@@ -1267,9 +1267,10 @@ export const secretApprovalRequestServiceFactory = ({
type: SecretType.Shared type: SecretType.Shared
})) }))
); );
if (secrets.length)
if (secrets.length !== secretsWithNewName.length)
throw new NotFoundError({ throw new NotFoundError({
message: `Secret does not exist: ${secretsToUpdateStoredInDB.map((el) => el.key).join(",")}` message: `Secret does not exist: ${secrets.map((el) => el.key).join(",")}`
}); });
} }
@@ -544,8 +544,10 @@ export const projectServiceFactory = ({
const updatedProject = await projectDAL.updateById(project.id, { const updatedProject = await projectDAL.updateById(project.id, {
name: update.name, name: update.name,
description: update.description, description: update.description,
autoCapitalization: update.autoCapitalization autoCapitalization: update.autoCapitalization,
enforceCapitalization: update.autoCapitalization
}); });
return updatedProject; return updatedProject;
}; };
@@ -567,7 +569,11 @@ export const projectServiceFactory = ({
}); });
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
const updatedProject = await projectDAL.updateById(projectId, { autoCapitalization }); const updatedProject = await projectDAL.updateById(projectId, {
autoCapitalization,
enforceCapitalization: autoCapitalization
});
return updatedProject; return updatedProject;
}; };
+52 -1
View File
@@ -88,7 +88,7 @@ type TSecretServiceFactoryDep = {
secretDAL: TSecretDALFactory; secretDAL: TSecretDALFactory;
secretTagDAL: TSecretTagDALFactory; secretTagDAL: TSecretTagDALFactory;
secretVersionDAL: TSecretVersionDALFactory; secretVersionDAL: TSecretVersionDALFactory;
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug">; projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug" | "findById">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">; projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
folderDAL: Pick< folderDAL: Pick<
TSecretFolderDALFactory, TSecretFolderDALFactory,
@@ -1466,6 +1466,16 @@ export const secretServiceFactory = ({
secretMetadata secretMetadata
}: TCreateSecretRawDTO) => { }: TCreateSecretRawDTO) => {
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId); const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
const project = await projectDAL.findById(projectId);
if (project.enforceCapitalization) {
if (secretName !== secretName.toUpperCase()) {
throw new BadRequestError({
message:
"Secret names must be in UPPERCASE per project requirements. You can disable this requirement in project settings."
});
}
}
const policy = const policy =
actor === ActorType.USER && type === SecretType.Shared actor === ActorType.USER && type === SecretType.Shared
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath) ? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
@@ -1609,6 +1619,16 @@ export const secretServiceFactory = ({
secretMetadata secretMetadata
}: TUpdateSecretRawDTO) => { }: TUpdateSecretRawDTO) => {
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId); const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
const project = await projectDAL.findById(projectId);
if (project.enforceCapitalization) {
if (newSecretName && newSecretName !== newSecretName.toUpperCase()) {
throw new BadRequestError({
message:
"Secret names must be in UPPERCASE per project requirements. You can disable this requirement in project settings."
});
}
}
const policy = const policy =
actor === ActorType.USER && type === SecretType.Shared actor === ActorType.USER && type === SecretType.Shared
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath) ? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
@@ -1858,7 +1878,23 @@ export const secretServiceFactory = ({
actor === ActorType.USER actor === ActorType.USER
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath) ? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
: undefined; : undefined;
if (shouldUseSecretV2Bridge) { if (shouldUseSecretV2Bridge) {
const project = await projectDAL.findById(projectId);
if (project.enforceCapitalization) {
const caseViolatingSecretKeys = inputSecrets
.filter((sec) => sec.secretKey !== sec.secretKey.toUpperCase())
.map((sec) => sec.secretKey);
if (caseViolatingSecretKeys.length) {
throw new BadRequestError({
message: `Secret names must be in UPPERCASE per project requirements: ${caseViolatingSecretKeys.join(
", "
)}. You can disable this requirement in project settings`
});
}
}
if (policy) { if (policy) {
const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({ const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({
policy, policy,
@@ -1987,6 +2023,21 @@ export const secretServiceFactory = ({
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath) ? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
: undefined; : undefined;
if (shouldUseSecretV2Bridge) { if (shouldUseSecretV2Bridge) {
const project = await projectDAL.findById(projectId);
if (project.enforceCapitalization) {
const caseViolatingSecretKeys = inputSecrets
.filter((sec) => sec.newSecretName && sec.newSecretName !== sec.newSecretName.toUpperCase())
.map((sec) => sec.newSecretName);
if (caseViolatingSecretKeys.length) {
throw new BadRequestError({
message: `Secret names must be in UPPERCASE per project requirements: ${caseViolatingSecretKeys.join(
", "
)}. You can disable this requirement in project settings`
});
}
}
if (policy) { if (policy) {
const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({ const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({
policy, policy,
+2 -2
View File
@@ -301,8 +301,8 @@
"project-id-description2": "For more guidance, including code snipets for various languages and frameworks, see ", "project-id-description2": "For more guidance, including code snipets for various languages and frameworks, see ",
"auto-generated": "This is your project's auto-generated unique identifier. It can't be changed.", "auto-generated": "This is your project's auto-generated unique identifier. It can't be changed.",
"docs": "Infisical Docs", "docs": "Infisical Docs",
"auto-capitalization": "Auto Capitalization", "enforce-capitalization": "Enforce Capitalization",
"auto-capitalization-description": "According to standards, Infisical will automatically capitalize your keys. If you want to disable this feature, you can do so here." "enforce-capitalization-description": "According to standards, Infisical enforces uppercase secret keys. If you want to disable this feature, you can do so here."
} }
}, },
"signup": { "signup": {
+2 -2
View File
@@ -289,8 +289,8 @@
"project-id-description2": "Para más guías, incluyendo ejemplos de código en diferentes lenguajes y frameworks, visita ", "project-id-description2": "Para más guías, incluyendo ejemplos de código en diferentes lenguajes y frameworks, visita ",
"auto-generated": "Este es el ID único y autogenerado de proyecto. No se puede modificar.", "auto-generated": "Este es el ID único y autogenerado de proyecto. No se puede modificar.",
"docs": "Documentación de Infisical", "docs": "Documentación de Infisical",
"auto-capitalization": "Mayúsculas automáticas", "enforce-capitalization": "Hacer cumplir la capitalización",
"auto-capitalization-description": "De acuerdo con los estándares, Infisical pondrá en mayúsculas tus claves. Si quieres desactivar esta funcionalidad, lo puedes hacer aquí." "enforce-capitalization-description": "Según los estándares, Infisical aplica claves secretas en mayúsculas. Si desea desactivar esta función, puede hacerlo aquí."
} }
}, },
"signup": { "signup": {
@@ -266,8 +266,8 @@
"project-id-description2": "Para obter mais orientações, incluindo trechos de código para várias linguagens e frameworks, consulte ", "project-id-description2": "Para obter mais orientações, incluindo trechos de código para várias linguagens e frameworks, consulte ",
"auto-generated": "Este é o identificador exclusivo - gerado automaticamente - do seu projeto. Não pode ser alterado.", "auto-generated": "Este é o identificador exclusivo - gerado automaticamente - do seu projeto. Não pode ser alterado.",
"docs": "Documentação do Infisical", "docs": "Documentação do Infisical",
"auto-capitalization": "Converter em caixa alta automaticamente", "enforce-capitalization": "Aplicar capitalização",
"auto-capitalization-description": "Por padrão, Infisical converte automaticamente as chaves em caixa alta. Se você quiser desativar essa funcionalidade, pode fazê-lo aqui." "enforce-capitalization-description": "De acordo com os padrões, o Infisical impõe chaves secretas em letras maiúsculas. Se quiser desabilitar esse recurso, você pode fazer isso aqui."
} }
}, },
"signup": { "signup": {
@@ -169,8 +169,8 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => {
if (!secretKey || isWholeKeyHighlighted) { if (!secretKey || isWholeKeyHighlighted) {
e.preventDefault(); e.preventDefault();
const keyStr = currentWorkspace.autoCapitalization ? key.toUpperCase() : key;
setValue("key", key); setValue("key", keyStr);
if (value) { if (value) {
setValue("value", value); setValue("value", value);
} }
@@ -125,8 +125,8 @@ export const CreateSecretForm = ({
if (!secretKey || isWholeKeyHighlighted) { if (!secretKey || isWholeKeyHighlighted) {
e.preventDefault(); e.preventDefault();
const keyStr = autoCapitalize ? key.toUpperCase() : key;
setValue("key", key); setValue("key", keyStr);
if (value) { if (value) {
setValue("value", value); setValue("value", value);
} }
@@ -37,7 +37,7 @@ export const AutoCapitalizationSection = () => {
return ( return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<p className="mb-3 text-xl font-semibold">{t("settings.project.auto-capitalization")}</p> <p className="mb-3 text-xl font-semibold">{t("settings.project.enforce-capitalization")}</p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}> <ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => ( {(isAllowed) => (
<div className="w-max"> <div className="w-max">
@@ -50,7 +50,7 @@ export const AutoCapitalizationSection = () => {
handleToggleCapitalizationToggle(state as boolean); handleToggleCapitalizationToggle(state as boolean);
}} }}
> >
{t("settings.project.auto-capitalization-description")} {t("settings.project.enforce-capitalization-description")}
</Checkbox> </Checkbox>
</div> </div>
)} )}