Compare commits

...

9 Commits

Author SHA1 Message Date
b4ef55db4e Minor improvement on the Postgres docs changing a warning to a tip 2025-07-15 21:45:31 -03:00
307b5d1f87 Merge pull request #4112 from Infisical/misc/re-added-est
misc: re-added EST to PKI templates
2025-07-15 17:00:24 -07:00
54087038c2 Merge pull request #4106 from Infisical/secret-change-status-badge
improvement(frontend): add merge/closed status badge to closed secret change request table
2025-07-15 14:03:23 -07:00
f835bf0ba8 Merge pull request #4111 from Infisical/fix/improvePostgresDocs
Add missing setting for postgres app connection
2025-07-15 16:58:13 -03:00
c79ea0631e misc: re-added EST 2025-07-16 03:12:49 +08:00
948799822f Minor wording improvement 2025-07-15 16:12:16 -03:00
c14a431177 Add missing setting for postgres app connection 2025-07-15 16:06:36 -03:00
c4e08b9811 improvement: change closed to rejected and address feedback 2025-07-14 19:15:52 -07:00
7784b8a81c improvement: add merge/closed status badge to closed secret change request table 2025-07-14 19:10:28 -07:00
8 changed files with 103 additions and 30 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 KiB

After

Width:  |  Height:  |  Size: 894 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

After

Width:  |  Height:  |  Size: 666 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 KiB

After

Width:  |  Height:  |  Size: 447 KiB

View File

@ -30,6 +30,14 @@ Infisical supports connecting to PostgreSQL using a database role.
-- enable permissions to alter login credentials -- enable permissions to alter login credentials
ALTER ROLE infisical_role WITH CREATEROLE; ALTER ROLE infisical_role WITH CREATEROLE;
``` ```
<Tip>
In some configurations, the role performing the rotation must be explicitly granted access to manage each user. To do this, grant the user's role to the rotation role with:
```SQL
-- grant each user role to admin user for password rotation
GRANT <secret_rotation_user> TO <infisical_role> WITH ADMIN OPTION;
```
Replace `<secret_rotation_user>` with each specific username whose credentials will be rotated, and `<infisical_role>` with the role that will perform the rotation.
</Tip>
</Tab> </Tab>
</Tabs> </Tabs>
</Step> </Step>

View File

@ -43,6 +43,7 @@ export type TSecretApprovalRequest = {
isReplicated?: boolean; isReplicated?: boolean;
slug: string; slug: string;
createdAt: string; createdAt: string;
updatedAt: string;
committerUserId: string; committerUserId: string;
reviewers: { reviewers: {
userId: string; userId: string;

View File

@ -76,6 +76,7 @@ export const CertificateTemplateEnrollmentModal = ({ popUp, handlePopUpToggle }:
useEffect(() => { useEffect(() => {
if (data) { if (data) {
reset({ reset({
method: EnrollmentMethod.EST,
caChain: data.caChain, caChain: data.caChain,
isEnabled: data.isEnabled, isEnabled: data.isEnabled,
disableBootstrapCertValidation: data.disableBootstrapCertValidation disableBootstrapCertValidation: data.disableBootstrapCertValidation

View File

@ -3,6 +3,7 @@ import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
faCertificate, faCertificate,
faCog,
faEllipsis, faEllipsis,
faPencil, faPencil,
faPlus, faPlus,
@ -12,6 +13,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns"; import { format } from "date-fns";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
@ -40,12 +42,14 @@ import {
import { import {
ProjectPermissionPkiTemplateActions, ProjectPermissionPkiTemplateActions,
ProjectPermissionSub, ProjectPermissionSub,
useSubscription,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useDeleteCertTemplateV2 } from "@app/hooks/api"; import { useDeleteCertTemplateV2 } from "@app/hooks/api";
import { useListCertificateTemplates } from "@app/hooks/api/certificateTemplates/queries"; import { useListCertificateTemplates } from "@app/hooks/api/certificateTemplates/queries";
import { CertificateTemplateEnrollmentModal } from "../CertificatesPage/components/CertificateTemplateEnrollmentModal";
import { PkiTemplateForm } from "./components/PkiTemplateForm"; import { PkiTemplateForm } from "./components/PkiTemplateForm";
const PER_PAGE_INIT = 25; const PER_PAGE_INIT = 25;
@ -56,9 +60,13 @@ export const PkiTemplateListPage = () => {
const [perPage, setPerPage] = useState(PER_PAGE_INIT); const [perPage, setPerPage] = useState(PER_PAGE_INIT);
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"certificateTemplate", "certificateTemplate",
"deleteTemplate" "deleteTemplate",
"enrollmentOptions",
"estUpgradePlan"
] as const); ] as const);
const { subscription } = useSubscription();
const { data, isPending } = useListCertificateTemplates({ const { data, isPending } = useListCertificateTemplates({
projectId: currentWorkspace.id, projectId: currentWorkspace.id,
offset: (page - 1) * perPage, offset: (page - 1) * perPage,
@ -92,7 +100,7 @@ export const PkiTemplateListPage = () => {
return ( return (
<> <>
<Helmet> <Helmet>
<title>{t("common.head-title", { title: "PKI Subscribers" })}</title> <title>{t("common.head-title", { title: "PKI Templates" })}</title>
</Helmet> </Helmet>
<div className="h-full bg-bunker-800"> <div className="h-full bg-bunker-800">
<div className="container mx-auto flex flex-col justify-between text-white"> <div className="container mx-auto flex flex-col justify-between text-white">
@ -177,7 +185,33 @@ export const PkiTemplateListPage = () => {
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionPkiTemplateActions.Edit}
a={ProjectPermissionSub.CertificateTemplates}
>
{(isAllowed) => (
<DropdownMenuItem
className={twMerge(
!isAllowed &&
"pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={(e) => {
e.stopPropagation();
if (!subscription.pkiEst) {
handlePopUpOpen("estUpgradePlan");
return;
}
handlePopUpOpen("enrollmentOptions", {
id: template.id
});
}}
disabled={!isAllowed}
icon={<FontAwesomeIcon icon={faCog} />}
>
Manage Enrollment
</DropdownMenuItem>
)}
</ProjectPermissionCan>
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionPkiTemplateActions.Delete} I={ProjectPermissionPkiTemplateActions.Delete}
a={ProjectPermissionSub.CertificateTemplates} a={ProjectPermissionSub.CertificateTemplates}
@ -251,7 +285,13 @@ export const PkiTemplateListPage = () => {
/> />
</ModalContent> </ModalContent>
</Modal> </Modal>
<CertificateTemplateEnrollmentModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
</div> </div>
<UpgradePlanModal
isOpen={popUp.estUpgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("estUpgradePlan", isOpen)}
text="You can only configure template enrollment methods if you switch to Infisical's Enterprise plan."
/>
</> </>
); );
}; };

View File

@ -6,16 +6,19 @@ import {
faCheckCircle, faCheckCircle,
faChevronDown, faChevronDown,
faCodeBranch, faCodeBranch,
faCodeMerge,
faMagnifyingGlass, faMagnifyingGlass,
faSearch faSearch,
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 { useSearch } from "@tanstack/react-router"; import { useSearch } from "@tanstack/react-router";
import { formatDistance } from "date-fns"; import { format, formatDistance } from "date-fns";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { import {
Badge,
Button, Button,
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@ -25,7 +28,8 @@ import {
EmptyState, EmptyState,
Input, Input,
Pagination, Pagination,
Skeleton Skeleton,
Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes"; import { ROUTE_PATHS } from "@app/const/routes";
import { import {
@ -308,7 +312,9 @@ export const SecretApprovalRequest = () => {
createdAt, createdAt,
reviewers, reviewers,
status, status,
committerUser committerUser,
hasMerged,
updatedAt
} = secretApproval; } = secretApproval;
const isReviewed = reviewers.some( const isReviewed = reviewers.some(
({ status: reviewStatus, userId }) => ({ status: reviewStatus, userId }) =>
@ -317,7 +323,7 @@ export const SecretApprovalRequest = () => {
return ( return (
<div <div
key={reqId} key={reqId}
className="flex flex-col border-b border-mineshaft-600 px-8 py-3 last:border-b-0 hover:bg-mineshaft-700" className="flex border-b border-mineshaft-600 px-8 py-3 last:border-b-0 hover:bg-mineshaft-700"
role="button" role="button"
tabIndex={0} tabIndex={0}
onClick={() => setSelectedApprovalId(secretApproval.id)} onClick={() => setSelectedApprovalId(secretApproval.id)}
@ -325,29 +331,46 @@ export const SecretApprovalRequest = () => {
if (evt.key === "Enter") setSelectedApprovalId(secretApproval.id); if (evt.key === "Enter") setSelectedApprovalId(secretApproval.id);
}} }}
> >
<div className="mb-1 text-sm"> <div className="flex flex-col">
<FontAwesomeIcon <div className="mb-1 text-sm">
icon={faCodeBranch} <FontAwesomeIcon
size="sm" icon={faCodeBranch}
className="mr-1.5 text-mineshaft-300" size="sm"
/> className="mr-1.5 text-mineshaft-300"
{secretApproval.isReplicated />
? `${commits.length} secret pending import` {secretApproval.isReplicated
: generateCommitText(commits)} ? `${commits.length} secret pending import`
<span className="text-xs text-bunker-300"> #{secretApproval.slug}</span> : generateCommitText(commits)}
<span className="text-xs text-bunker-300"> #{secretApproval.slug}</span>
</div>
<span className="text-xs leading-3 text-gray-500">
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "}
{committerUser ? (
<>
{committerUser?.firstName || ""} {committerUser?.lastName || ""} (
{committerUser?.email})
</>
) : (
<span className="text-gray-600">Deleted User</span>
)}
{!isReviewed && status === "open" && " - Review required"}
</span>
</div> </div>
<span className="text-xs leading-3 text-gray-500"> {status === "close" && (
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "} <Tooltip
{committerUser ? ( content={updatedAt ? format(new Date(updatedAt), "M/dd/yyyy h:mm a") : ""}
<> >
{committerUser?.firstName || ""} {committerUser?.lastName || ""} ( <div className="my-auto ml-auto">
{committerUser?.email}) <Badge
</> variant={hasMerged ? "success" : "danger"}
) : ( className="flex h-min items-center gap-1"
<span className="text-gray-600">Deleted User</span> >
)} <FontAwesomeIcon icon={hasMerged ? faCodeMerge : faXmark} />
{!isReviewed && status === "open" && " - Review required"} {hasMerged ? "Merged" : "Rejected"}
</span> </Badge>
</div>
</Tooltip>
)}
</div> </div>
); );
})} })}