mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 13:27:46 +00:00
improvements: address feedback
This commit is contained in:
@@ -13,7 +13,7 @@ export const getDefaultProjectTemplate = (orgId: string, type: ProjectType) => (
|
|||||||
name: InfisicalProjectTemplate.Default,
|
name: InfisicalProjectTemplate.Default,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
description: `Infisical's ${type ? `"${type}"` : ""} default project template`,
|
description: `Infisical's ${type} default project template`,
|
||||||
environments: type === ProjectType.SecretManager ? ProjectTemplateDefaultEnvironments : null,
|
environments: type === ProjectType.SecretManager ? ProjectTemplateDefaultEnvironments : null,
|
||||||
roles: [...getPredefinedRoles({ projectId: "project-template", projectType: type })].map(
|
roles: [...getPredefinedRoles({ projectId: "project-template", projectType: type })].map(
|
||||||
({ name, slug, permissions }) => ({
|
({ name, slug, permissions }) => ({
|
||||||
|
|||||||
@@ -160,6 +160,17 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
|
if (environments && type !== ProjectType.SecretManager) {
|
||||||
|
throw new BadRequestError({ message: "Cannot configure environments for non-SecretManager project templates" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
message: `Failed to create project template due to environment count exceeding your current limit of ${plan.environmentLimit}. Contact Infisical to increase limit.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const isConflictingName = Boolean(
|
const isConflictingName = Boolean(
|
||||||
await projectTemplateDAL.findOne({
|
await projectTemplateDAL.findOne({
|
||||||
name: params.name,
|
name: params.name,
|
||||||
@@ -216,6 +227,13 @@ export const projectTemplateServiceFactory = ({
|
|||||||
if (projectTemplate.type === ProjectType.SecretManager && environments === null)
|
if (projectTemplate.type === ProjectType.SecretManager && environments === null)
|
||||||
throw new BadRequestError({ message: "Environments cannot be removed for SecretManager project templates" });
|
throw new BadRequestError({ message: "Environments cannot be removed for SecretManager project templates" });
|
||||||
|
|
||||||
|
if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
message: `Failed to update project template due to environment count exceeding your current limit of ${plan.environmentLimit}. Contact Infisical to increase limit.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (params.name && projectTemplate.name !== params.name) {
|
if (params.name && projectTemplate.name !== params.name) {
|
||||||
const isConflictingName = Boolean(
|
const isConflictingName = Boolean(
|
||||||
await projectTemplateDAL.findOne({
|
await projectTemplateDAL.findOne({
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectType } from "@app/db/schemas";
|
import { ProjectMembershipRole, ProjectType } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
cryptographicOperatorPermissions,
|
cryptographicOperatorPermissions,
|
||||||
@@ -12,7 +14,7 @@ import { TGetPredefinedRolesDTO } from "@app/services/project-role/project-role-
|
|||||||
export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetPredefinedRolesDTO) => {
|
export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetPredefinedRolesDTO) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // dummy userid
|
id: uuidv4(),
|
||||||
projectId,
|
projectId,
|
||||||
name: "Admin",
|
name: "Admin",
|
||||||
slug: ProjectMembershipRole.Admin,
|
slug: ProjectMembershipRole.Admin,
|
||||||
@@ -22,7 +24,7 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
|
|||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c70", // dummy user for zod validation in response
|
id: uuidv4(),
|
||||||
projectId,
|
projectId,
|
||||||
name: "Developer",
|
name: "Developer",
|
||||||
slug: ProjectMembershipRole.Member,
|
slug: ProjectMembershipRole.Member,
|
||||||
@@ -32,7 +34,7 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
|
|||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c73", // dummy user for zod validation in response
|
id: uuidv4(),
|
||||||
projectId,
|
projectId,
|
||||||
name: "SSH Host Bootstrapper",
|
name: "SSH Host Bootstrapper",
|
||||||
slug: ProjectMembershipRole.SshHostBootstrapper,
|
slug: ProjectMembershipRole.SshHostBootstrapper,
|
||||||
@@ -43,7 +45,7 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
|
|||||||
type: ProjectType.SSH
|
type: ProjectType.SSH
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c74", // dummy user for zod validation in response
|
id: uuidv4(),
|
||||||
projectId,
|
projectId,
|
||||||
name: "Cryptographic Operator",
|
name: "Cryptographic Operator",
|
||||||
slug: ProjectMembershipRole.KmsCryptographicOperator,
|
slug: ProjectMembershipRole.KmsCryptographicOperator,
|
||||||
@@ -54,7 +56,7 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
|
|||||||
type: ProjectType.KMS
|
type: ProjectType.KMS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c71", // dummy user for zod validation in response
|
id: uuidv4(),
|
||||||
projectId,
|
projectId,
|
||||||
name: "Viewer",
|
name: "Viewer",
|
||||||
slug: ProjectMembershipRole.Viewer,
|
slug: ProjectMembershipRole.Viewer,
|
||||||
@@ -64,7 +66,7 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
|
|||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c72", // dummy user for zod validation in response
|
id: uuidv4(),
|
||||||
projectId,
|
projectId,
|
||||||
name: "No Access",
|
name: "No Access",
|
||||||
slug: ProjectMembershipRole.NoAccess,
|
slug: ProjectMembershipRole.NoAccess,
|
||||||
@@ -73,5 +75,5 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
}
|
}
|
||||||
].filter(({ slug, type }) => (type ? type === projectType : true) && (!roleFilter || roleFilter.includes(slug)));
|
].filter(({ slug, type }) => (type ? type === projectType : true) && (!roleFilter || roleFilter === slug));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -117,11 +117,14 @@ export const projectRoleServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||||
if (roleSlug !== "custom" && Object.values(ProjectMembershipRole).includes(roleSlug as ProjectMembershipRole)) {
|
if (roleSlug !== "custom" && Object.values(ProjectMembershipRole).includes(roleSlug as ProjectMembershipRole)) {
|
||||||
const predefinedRole = getPredefinedRoles({
|
const [predefinedRole] = getPredefinedRoles({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
projectType: project.type as ProjectType,
|
projectType: project.type as ProjectType,
|
||||||
roleFilter: roleSlug as ProjectMembershipRole
|
roleFilter: roleSlug as ProjectMembershipRole
|
||||||
})[0];
|
});
|
||||||
|
|
||||||
|
if (!predefinedRole) throw new NotFoundError({ message: `Default role with slug '${roleSlug}' not found` });
|
||||||
|
|
||||||
return { ...predefinedRole, permissions: UnpackedPermissionSchema.array().parse(predefinedRole.permissions) };
|
return { ...predefinedRole, permissions: UnpackedPermissionSchema.array().parse(predefinedRole.permissions) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ In the following steps, we'll explore how to set up a project template.
|
|||||||
<Tab title="Infisical UI">
|
<Tab title="Infisical UI">
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Creating a Project Template">
|
<Step title="Creating a Project Template">
|
||||||
Navigate to the Project Templates tab on the Feature Settings page for the project type you want to create a template for and tap on the **Add Template** button.
|
Navigate to the **Project Templates** tab on the Feature Settings page for the project type you want to create a template for and tap on the **Add Template** button.
|
||||||

|

|
||||||
|
|
||||||
Specify your template details. Here's some guidance on each field:
|
Specify your template details. Here's some guidance on each field:
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ we will register a remote host with Infisical through a [machine identity](/docu
|
|||||||
This role grants the ability to **Create** and **Issue Host Certificates** on the **SSH Host** resource; this will enable the linked machine identity to bootstrap a remote host with Infisical
|
This role grants the ability to **Create** and **Issue Host Certificates** on the **SSH Host** resource; this will enable the linked machine identity to bootstrap a remote host with Infisical
|
||||||
and establish the necessary configuration on it.
|
and establish the necessary configuration on it.
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
If you plan to use a custom role to bootstrap SSH hosts, ensure the role has the **Create** and **Issue Host Certificates** on the **SSH Host** resource.
|
||||||
|
</Tip>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 656 KiB After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
+9
-2
@@ -19,7 +19,7 @@ import {
|
|||||||
THead,
|
THead,
|
||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
|
||||||
import { TProjectTemplate, useUpdateProjectTemplate } from "@app/hooks/api/projectTemplates";
|
import { TProjectTemplate, useUpdateProjectTemplate } from "@app/hooks/api/projectTemplates";
|
||||||
import { slugSchema } from "@app/lib/schemas";
|
import { slugSchema } from "@app/lib/schemas";
|
||||||
|
|
||||||
@@ -56,6 +56,8 @@ export const ProjectTemplateEnvironmentsForm = ({
|
|||||||
resolver: zodResolver(formSchema)
|
resolver: zodResolver(formSchema)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
fields: environments,
|
fields: environments,
|
||||||
move,
|
move,
|
||||||
@@ -90,6 +92,9 @@ export const ProjectTemplateEnvironmentsForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isEnvironmentLimitExceeded =
|
||||||
|
Boolean(subscription.environmentLimit) && environments.length >= subscription.environmentLimit;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
@@ -139,6 +144,8 @@ export const ProjectTemplateEnvironmentsForm = ({
|
|||||||
<OrgPermissionCan
|
<OrgPermissionCan
|
||||||
I={OrgPermissionActions.Edit}
|
I={OrgPermissionActions.Edit}
|
||||||
a={OrgPermissionSubjects.ProjectTemplates}
|
a={OrgPermissionSubjects.ProjectTemplates}
|
||||||
|
renderTooltip={isEnvironmentLimitExceeded ? true : undefined}
|
||||||
|
allowedLabel={`Plan environment limit of ${subscription.environmentLimit} exceeded. Contact Infisical to increase limit.`}
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
@@ -148,7 +155,7 @@ export const ProjectTemplateEnvironmentsForm = ({
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
size="xs"
|
size="xs"
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed || isEnvironmentLimitExceeded}
|
||||||
>
|
>
|
||||||
Add Environment
|
Add Environment
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
+7
-1
@@ -57,7 +57,13 @@ const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onFormSubmit = async (data: FormData) => {
|
const onFormSubmit = async (data: FormData) => {
|
||||||
if (!projectType) return;
|
if (!projectType) {
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to determine project type",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const mutation = projectTemplate
|
const mutation = projectTemplate
|
||||||
? updateProjectTemplate.mutateAsync({ templateId: projectTemplate.id, ...data })
|
? updateProjectTemplate.mutateAsync({ templateId: projectTemplate.id, ...data })
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const CertManagerSettingsPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Cert Managment Settings</title>
|
<title>Cert Management Settings</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div className="flex w-full justify-center bg-bunker-800 text-white">
|
<div className="flex w-full justify-center bg-bunker-800 text-white">
|
||||||
<div className="w-full max-w-7xl">
|
<div className="w-full max-w-7xl">
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const NewPermissionRule = ({ onClose }: Props) => {
|
|||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="type"
|
name="type"
|
||||||
defaultValue={ProjectPermissionSub.Secrets}
|
defaultValue={ProjectPermissionSub.Project}
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
<FormControl label="Subject" errorText={error?.message} isError={Boolean(error)}>
|
<FormControl label="Subject" errorText={error?.message} isError={Boolean(error)}>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
Reference in New Issue
Block a user