feat(infisical-pg): auth injection completed and validation in password router

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:22 +05:30
parent fcfd6b3fb2
commit f3b37de3f3
196 changed files with 1871 additions and 624 deletions
@@ -42,7 +42,7 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
link = `${window.location.origin}/integrations/gcp-secret-manager/authorize`;
break;
case "azure-key-vault":
link = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/azure-key-vault/oauth2/callback&response_mode=query&scope=https://vault.azure.net/.default openid offline_access&state=${state}`;
link = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?clientid=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/azure-key-vault/oauth2/callback&response_mode=query&scope=https://vault.azure.net/.default openid offline_access&state=${state}`;
break;
case "aws-parameter-store":
link = `${window.location.origin}/integrations/aws-parameter-store/authorize`;
@@ -51,16 +51,16 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
link = `${window.location.origin}/integrations/aws-secret-manager/authorize`;
break;
case "heroku":
link = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`;
link = `https://id.heroku.com/oauth/authorize?clientid=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`;
break;
case "vercel":
link = `https://vercel.com/integrations/${integrationOption.clientSlug}/new?state=${state}`;
break;
case "netlify":
link = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`;
link = `https://app.netlify.com/authorize?clientid=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`;
break;
case "github":
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
link = `https://github.com/login/oauth/authorize?clientid=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
break;
case "gitlab":
link = `${window.location.origin}/integrations/gitlab/authorize`;
@@ -105,7 +105,7 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
link = `${window.location.origin}/integrations/cloudflare-workers/authorize`;
break;
case "bitbucket":
link = `https://bitbucket.org/site/oauth2/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/bitbucket/oauth2/callback&state=${state}`;
link = `https://bitbucket.org/site/oauth2/authorize?clientid=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/bitbucket/oauth2/callback&state=${state}`;
break;
case "codefresh":
link = `${window.location.origin}/integrations/codefresh/authorize`;
@@ -21,10 +21,7 @@ import { IntegrationAuth } from "@app/hooks/api/types";
import { CloudIntegrationSection } from "./components/CloudIntegrationSection";
import { FrameworkIntegrationSection } from "./components/FrameworkIntegrationSection";
import { IntegrationsSection } from "./components/IntegrationsSection";
import {
generateBotKey,
redirectForProviderAuth
} from "./IntegrationPage.utils";
import { generateBotKey, redirectForProviderAuth } from "./IntegrationPage.utils";
type Props = {
frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
@@ -36,7 +33,7 @@ export const IntegrationsPage = withProjectPermission(
const { createNotification } = useNotificationContext();
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
const environments = currentWorkspace?.environments || [];
const { data: latestWsKey } = useGetUserWsKey(workspaceId);
@@ -76,7 +73,7 @@ export const IntegrationsPage = withProjectPermission(
const { mutateAsync: updateBotActiveStatus, mutate: updateBotActiveStatusSync } =
useUpdateBotActiveStatus();
const { mutateAsync: deleteIntegration } = useDeleteIntegration();
const {
const {
mutateAsync: deleteIntegrationAuths,
isSuccess: isDeleteIntegrationAuthSuccess,
reset: resetDeleteIntegrationAuths
@@ -95,10 +92,10 @@ export const IntegrationsPage = withProjectPermission(
isIntegrationsAuthorizedEmpty &&
isIntegrationsEmpty
) {
if (bot?._id)
if (bot?.id)
updateBotActiveStatusSync({
isActive: false,
botId: bot._id,
botId: bot.id,
workspaceId
});
resetDeleteIntegrationAuths();
@@ -114,7 +111,7 @@ export const IntegrationsPage = withProjectPermission(
const handleProviderIntegration = async (provider: string) => {
const selectedCloudIntegration = cloudIntegrations?.find(({ slug }) => provider === slug);
if (!selectedCloudIntegration) return;
try {
if (bot && !bot.isActive) {
const botKey = generateBotKey(bot.publicKey, latestWsKey!);
@@ -122,7 +119,7 @@ export const IntegrationsPage = withProjectPermission(
workspaceId,
botKey,
isActive: true,
botId: bot._id
botId: bot.id
});
}
@@ -194,7 +191,7 @@ export const IntegrationsPage = withProjectPermission(
isLoading={isIntegrationLoading}
integrations={integrations}
environments={environments}
onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)}
onIntegrationDelete={({ id }, cb) => handleIntegrationDelete(id, cb)}
isBotActive={bot?.isActive}
workspaceId={workspaceId}
/>
@@ -81,7 +81,7 @@ export const IntegrationsSection = ({
{integrations?.map((integration) => (
<div
className="max-w-8xl flex justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6 pb-2"
key={`integration-${integration?._id.toString()}`}
key={`integration-${integration?.id.toString()}`}
>
<div className="flex">
<div>
+1 -1
View File
@@ -13,7 +13,7 @@ export const navigateUserToOrg = async (router: NextRouter, organizationId?: str
if (userOrgs.length > 0) {
// user is part of at least 1 org
const userOrg = userOrgs[0] && userOrgs[0]._id;
const userOrg = userOrgs[0] && userOrgs[0].id;
localStorage.setItem("orgData.id", userOrg);
router.push(`/org/${userOrg}/overview`);
} else {
@@ -80,11 +80,11 @@ export const IPAllowlistModal = ({
comment
}: FormData) => {
try {
if (!currentWorkspace?._id) return;
if (!currentWorkspace?.id) return;
if (popUp?.trustedIp?.data) {
await updateTrustedIp.mutateAsync({
workspaceId: currentWorkspace._id,
workspaceId: currentWorkspace.id,
trustedIpId: (popUp?.trustedIp?.data as { trustedIpId: string })?.trustedIpId,
ipAddress,
comment,
@@ -92,7 +92,7 @@ export const IPAllowlistModal = ({
});
} else {
await addTrustedIp.mutateAsync({
workspaceId: currentWorkspace._id,
workspaceId: currentWorkspace.id,
ipAddress,
comment,
isActive: true
@@ -30,10 +30,10 @@ export const IPAllowlistSection = () => {
const onDeleteTrustedIpSubmit = async (trustedIpId: string) => {
try {
if (!currentWorkspace?._id) return;
if (!currentWorkspace?.id) return;
await mutateAsync({
workspaceId: currentWorkspace._id,
workspaceId: currentWorkspace.id,
trustedIpId
});
@@ -42,7 +42,7 @@ type Props = {
export const IPAllowlistTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => {
const { subscription } = useSubscription();
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetTrustedIps(currentWorkspace?._id ?? "");
const { data, isLoading } = useGetTrustedIps(currentWorkspace?.id ?? "");
const formatType = (type: string, prefix?: number) => {
return `${type.slice(0, 2).toUpperCase() + type.slice(2)} ${
@@ -69,9 +69,9 @@ export const IPAllowlistTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
data?.length > 0 &&
data
.sort((a, b) => a.ipAddress.localeCompare(b.ipAddress))
.map(({ _id, ipAddress, comment, type, prefix, isActive }) => {
.map(({ id, ipAddress, comment, type, prefix, isActive }) => {
return (
<Tr key={`ip-access-range-${_id}`} className="h-10">
<Tr key={`ip-access-range-${id}`} className="h-10">
<Td>{`${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`}</Td>
<Td>{formatType(type, prefix)}</Td>
<Td>{comment}</Td>
@@ -95,7 +95,7 @@ export const IPAllowlistTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
onClick={() => {
if (subscription?.ipAllowlisting) {
handlePopUpOpen("trustedIp", {
trustedIpId: _id,
trustedIpId: id,
ipAddress,
comment,
prefix,
@@ -123,7 +123,7 @@ export const IPAllowlistTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
onClick={() => {
if (subscription?.ipAllowlisting) {
handlePopUpOpen("deleteTrustedIp", {
trustedIpId: _id
trustedIpId: id
});
} else {
handlePopUpOpen("upgradePlan");
@@ -103,7 +103,7 @@ export const AddServiceTokenModal = ({ popUp, handlePopUpToggle }: Props) => {
const [newToken, setToken] = useState("");
const [isTokenCopied, setIsTokenCopied] = useToggle(false);
const { data: latestFileKey } = useGetUserWsKey(currentWorkspace?._id ?? "");
const { data: latestFileKey } = useGetUserWsKey(currentWorkspace?.id ?? "");
const createServiceToken = useCreateServiceToken();
const hasServiceToken = Boolean(newToken);
@@ -123,7 +123,7 @@ export const AddServiceTokenModal = ({ popUp, handlePopUpToggle }: Props) => {
const onFormSubmit = async ({ name, scopes, expiresIn, permissions }: FormData) => {
try {
if (!currentWorkspace?._id) return;
if (!currentWorkspace?.id) return;
if (!latestFileKey) return;
const key = decryptAssymmetric({
@@ -147,7 +147,7 @@ export const AddServiceTokenModal = ({ popUp, handlePopUpToggle }: Props) => {
scopes,
expiresIn: Number(expiresIn),
name,
workspaceId: currentWorkspace._id,
workspaceId: currentWorkspace.id,
randomBytes,
permissions: Object.entries(permissions)
.filter(([, permissionsValue]) => permissionsValue)
@@ -34,7 +34,7 @@ type Props = {
export const ServiceTokenTable = ({ handlePopUpOpen }: Props) => {
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetUserWsServiceTokens({
workspaceID: currentWorkspace?._id || ""
workspaceID: currentWorkspace?.id || ""
});
return (
@@ -53,13 +53,13 @@ export const ServiceTokenTable = ({ handlePopUpOpen }: Props) => {
{!isLoading &&
data &&
data.map((row) => (
<Tr key={row._id}>
<Tr key={row.id}>
<Td>{row.name}</Td>
<Td>
<div className="mb-2 flex flex-col flex-wrap space-y-1">
{row?.scopes.map(({ secretPath, environment }) => (
<div
key={`${row._id}-${environment}-${secretPath}`}
key={`${row.id}-${environment}-${secretPath}`}
className="inline-flex items-center space-x-1 rounded-md border border-mineshaft-600 p-1 px-2"
>
<div className="mr-2 border-r border-mineshaft-600 pr-2">{environment}</div>
@@ -80,7 +80,7 @@ export const ServiceTokenTable = ({ handlePopUpOpen }: Props) => {
onClick={() =>
handlePopUpOpen("deleteAPITokenConfirmation", {
name: row.name,
id: row._id
id: row.id
})
}
colorSchema="danger"
@@ -0,0 +1,539 @@
import { useEffect, useState } from "react";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faCheck, faCopy,faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup";
import { motion } from "framer-motion";
import nacl from "tweetnacl";
import { encodeBase64 } from "tweetnacl-util";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import {
decryptAssymmetric,
encryptAssymmetric
} from "@app/components/utilities/cryptography/crypto";
import {
Button,
FormControl,
IconButton,
Input,
Modal,
ModalContent,
Select,
SelectItem,
Switch,
Tab,
TabList,
TabPanel,
Tabs,
UpgradePlanModal} from "@app/components/v2";
import {
useOrganization,
useSubscription,
useWorkspace
} from "@app/context";
import { useToggle } from "@app/hooks";
import {
useCreateServiceTokenV3,
useGetRoles,
useGetUserWsKey,
useUpdateServiceTokenV3} from "@app/hooks/api";
import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
enum TabSections {
General = "general",
Advanced = "advanced"
}
const expirations = [
{ label: "Never", value: "" },
{ label: "1 day", value: "86400" },
{ label: "7 days", value: "604800" },
{ label: "1 month", value: "2592000" },
{ label: "6 months", value: "15552000" },
{ label: "12 months", value: "31104000" }
];
const schema = yup.object({
name: yup.string().required("ST V3 name is required"),
expiresIn: yup.string(),
accessTokenTTL: yup
.string()
.test("is-positive-integer", "Access Token TTL must be a positive integer", (value) => {
if (typeof value === "undefined") {
return false;
}
const num = parseInt(value, 10);
return !Number.isNaN(num) && num > 0 && String(num) === value;
})
.required("Access Token TTL is required"),
role: yup.string().required("ST V3 role is required"),
trustedIps: yup
.array(
yup.object({
ipAddress: yup.string().max(50).required().label("IP Address")
})
)
.min(1)
.required()
.label("Trusted IP"),
isRefreshTokenRotationEnabled: yup.boolean().default(false)
}).required();
export type FormData = yup.InferType<typeof schema>;
type Props = {
popUp: UsePopUpState<["serviceTokenV3", "upgradePlan"]>;
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["serviceTokenV3", "upgradePlan"]>, state?: boolean) => void;
};
export const AddServiceTokenV3Modal = ({
popUp,
handlePopUpOpen,
handlePopUpToggle
}: Props) => {
const [newServiceTokenJSON, setNewServiceTokenJSON] = useState("");
const [isServiceTokenJSONCopied, setIsServiceTokenJSONCopied] = useToggle(false);
const { subscription } = useSubscription();
const { currentOrg } = useOrganization();
const { currentWorkspace } = useWorkspace();
const orgId = currentOrg?.id || "";
const workspaceId = currentWorkspace?.id || "";
const { data: roles } = useGetRoles({
orgId,
workspaceId
});
const { data: latestFileKey } = useGetUserWsKey(workspaceId);
const { mutateAsync: createMutateAsync } = useCreateServiceTokenV3();
const { mutateAsync: updateMutateAsync } = useUpdateServiceTokenV3();
const { createNotification } = useNotificationContext();
const {
control,
handleSubmit,
reset,
formState: { isSubmitting }
} = useForm<FormData>({
resolver: yupResolver(schema),
defaultValues: {
name: "",
accessTokenTTL: "7200",
trustedIps: [{
ipAddress: "0.0.0.0/0"
}]
}
});
useEffect(() => {
let timer: NodeJS.Timeout;
if (isServiceTokenJSONCopied) {
timer = setTimeout(() => setIsServiceTokenJSONCopied.off(), 2000);
}
return () => clearTimeout(timer);
}, [setIsServiceTokenJSONCopied]);
const copyTokenToClipboard = () => {
navigator.clipboard.writeText(newServiceTokenJSON);
setIsServiceTokenJSONCopied.on();
};
useEffect(() => {
const serviceTokenData = popUp?.serviceTokenV3?.data as {
serviceTokenDataId: string;
name: string;
role: string;
customRole: {
name: string;
slug: string;
};
trustedIps: ServiceTokenV3TrustedIp[];
accessTokenTTL: number;
isRefreshTokenRotationEnabled: boolean;
};
if (!roles?.length) return;
if (serviceTokenData) {
reset({
name: serviceTokenData.name,
role: serviceTokenData?.customRole?.slug ?? serviceTokenData.role,
trustedIps: serviceTokenData.trustedIps.map(({
ipAddress,
prefix
}: ServiceTokenV3TrustedIp) => {
return ({
ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`
});
}),
accessTokenTTL: String(serviceTokenData.accessTokenTTL),
isRefreshTokenRotationEnabled: serviceTokenData.isRefreshTokenRotationEnabled
});
} else {
reset({
name: "",
accessTokenTTL: "7200",
role: roles[0].slug,
trustedIps: [{
ipAddress: "0.0.0.0/0"
}]
});
}
}, [popUp?.serviceTokenV3?.data, roles]);
const { fields: tokenTrustedIps, append: appendTrustedIp, remove: removeTrustedIp } = useFieldArray({ control, name: "trustedIps" });
const onFormSubmit = async ({
name,
expiresIn,
accessTokenTTL,
role,
trustedIps,
isRefreshTokenRotationEnabled
}: FormData) => {
try {
const serviceTokenData = popUp?.serviceTokenV3?.data as {
serviceTokenDataId: string;
name: string;
role: string;
};
if (serviceTokenData) {
// update
await updateMutateAsync({
serviceTokenDataId: serviceTokenData.serviceTokenDataId,
name,
role,
trustedIps,
expiresIn: expiresIn === "" ? undefined : Number(expiresIn),
accessTokenTTL: Number(accessTokenTTL),
isRefreshTokenRotationEnabled
});
handlePopUpToggle("serviceTokenV3", false);
} else {
// create
if (!workspaceId) return;
if (!latestFileKey) return;
const pair = nacl.box.keyPair();
const secretKeyUint8Array = pair.secretKey;
const publicKeyUint8Array = pair.publicKey;
const privateKey = encodeBase64(secretKeyUint8Array);
const publicKey = encodeBase64(publicKeyUint8Array);
const key = decryptAssymmetric({
ciphertext: latestFileKey.encryptedKey,
nonce: latestFileKey.nonce,
publicKey: latestFileKey.sender.publicKey,
privateKey: localStorage.getItem("PRIVATE_KEY") as string
});
const { ciphertext, nonce } = encryptAssymmetric({
plaintext: key,
publicKey,
privateKey: localStorage.getItem("PRIVATE_KEY") as string
});
const { refreshToken } = await createMutateAsync({
name,
role,
workspaceId,
publicKey,
trustedIps,
expiresIn: expiresIn === "" ? undefined : Number(expiresIn),
accessTokenTTL: Number(accessTokenTTL),
encryptedKey: ciphertext,
nonce,
isRefreshTokenRotationEnabled
});
const downloadData = {
public_key: publicKey,
private_key: privateKey,
refresh_token: refreshToken
};
const serviceTokenJSON = JSON.stringify(downloadData, null, 2);
setNewServiceTokenJSON(serviceTokenJSON);
const blob = new Blob([serviceTokenJSON], { type: "application/json" });
const href = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = href;
link.download = `infisical_${name}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
createNotification({
text: `Successfully ${popUp?.serviceTokenV3?.data ? "updated" : "created"} ST V3`,
type: "success"
});
reset();
} catch (err) {
console.error(err);
createNotification({
text: `Failed to ${popUp?.serviceTokenV3?.data ? "updated" : "created"} ST V3`,
type: "error"
});
}
}
const hasServiceTokenJSON = Boolean(newServiceTokenJSON);
return (
<Modal
isOpen={popUp?.serviceTokenV3?.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("serviceTokenV3", isOpen);
reset();
setNewServiceTokenJSON("");
}}
>
<ModalContent title={`${popUp?.serviceTokenV3?.data ? "Update" : "Create"} Service Token V3`}>
{!hasServiceTokenJSON ? (
<form onSubmit={handleSubmit(onFormSubmit)}>
<Tabs defaultValue={TabSections.General}>
<TabList>
<div className="flex flex-row border-b border-mineshaft-600 w-full">
<Tab value={TabSections.General}>General</Tab>
<Tab value={TabSections.Advanced}>Advanced</Tab>
</div>
</TabList>
<TabPanel value={TabSections.General}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<Controller
control={control}
defaultValue=""
name="name"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Name"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
placeholder="My ST V3"
/>
</FormControl>
)}
/>
<Controller
control={control}
name="role"
defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label={`${popUp?.serviceTokenV3?.data ? "Update" : ""} Role`}
errorText={error?.message}
isError={Boolean(error)}
className="mt-4"
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{(roles || []).map(({ name, slug }) => (
<SelectItem value={slug} key={`st-role-${slug}`}>
{name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control}
name="expiresIn"
defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label={`${popUp?.serviceTokenV3?.data ? "Update" : ""} Refresh Token Expires In`}
errorText={error?.message}
isError={Boolean(error)}
className="mt-4"
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{expirations.map(({ label, value }) => (
<SelectItem value={String(value || "")} key={`api-key-expiration-${label}`}>
{label}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
</motion.div>
</TabPanel>
<TabPanel value={TabSections.Advanced}>
<div>
{tokenTrustedIps.map(({ id }, index) => (
<div className="flex items-end space-x-2 mb-3" key={id}>
<Controller
control={control}
name={`trustedIps.${index}.ipAddress`}
defaultValue="0.0.0.0/0"
render={({ field, fieldState: { error } }) => {
return (
<FormControl
className="mb-0 flex-grow"
label={index === 0 ? "Trusted IP" : undefined}
isError={Boolean(error)}
errorText={error?.message}
>
<Input
value={field.value}
onChange={(e) => {
if (subscription?.ipAllowlisting) {
field.onChange(e);
return;
}
handlePopUpOpen("upgradePlan");
}}
placeholder="123.456.789.0"
/>
</FormControl>
);
}}
/>
<IconButton
onClick={() => {
if (subscription?.ipAllowlisting) {
removeTrustedIp(index);
return;
}
handlePopUpOpen("upgradePlan");
}}
size="lg"
colorSchema="danger"
variant="plain"
ariaLabel="update"
className="p-3"
>
<FontAwesomeIcon icon={faXmark} />
</IconButton>
</div>
))}
<div className="my-4 ml-1">
<Button
variant="outline_bg"
onClick={() => {
if (subscription?.ipAllowlisting) {
appendTrustedIp({
ipAddress: "0.0.0.0/0"
})
return;
}
handlePopUpOpen("upgradePlan");
}}
leftIcon={<FontAwesomeIcon icon={faPlus} />}
size="xs"
>
Add IP Address
</Button>
</div>
<Controller
control={control}
defaultValue="7200"
name="accessTokenTTL"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token TTL (seconds)"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
placeholder="7200"
/>
</FormControl>
)}
/>
<div className="mt-8">
<Controller
control={control}
name="isRefreshTokenRotationEnabled"
render={({ field: { onChange, value } }) => (
<Switch
id="label-refresh-token-rotation"
onCheckedChange={(isChecked) => onChange(isChecked)}
isChecked={value}
>
Refresh Token Rotation
</Switch>
)}
/>
<p className="mt-4 text-sm font-normal text-mineshaft-400">When enabled, as a result of exchanging a refresh token, a new refresh token will be issued and the existing token will be invalidated.</p>
</div>
</div>
</TabPanel>
</Tabs>
<div className="flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting}
>
{popUp?.serviceTokenV3?.data ? "Update" : "Create"}
</Button>
<Button colorSchema="secondary" variant="plain">
Cancel
</Button>
</div>
</form>
) : (
<div className="mt-2 mb-3 mr-2 flex items-center justify-end rounded-md bg-white/[0.07] p-2 text-base text-gray-400">
<p className="mr-4 break-all">{newServiceTokenJSON}</p>
<IconButton
ariaLabel="copy icon"
colorSchema="secondary"
className="group relative"
onClick={copyTokenToClipboard}
>
<FontAwesomeIcon icon={isServiceTokenJSONCopied ? faCheck : faCopy} />
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">
Click to copy
</span>
</IconButton>
</div>
)}
<UpgradePlanModal
isOpen={popUp?.upgradePlan?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
/>
</ModalContent>
</Modal>
);
}
@@ -0,0 +1,218 @@
import { faKey, faPencil,faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
EmptyState,
IconButton,
Switch,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
THead,
Tr
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub , useWorkspace } from "@app/context";
import {
useGetWorkspaceServiceTokenDataV3,
useUpdateServiceTokenV3
} from "@app/hooks/api";
import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types"
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["deleteServiceTokenV3", "serviceTokenV3"]>,
data?: {
serviceTokenDataId?: string;
name?: string;
role?: string;
customRole?: {
name: string;
slug: string;
};
trustedIps?: ServiceTokenV3TrustedIp[];
accessTokenTTL?: number;
isRefreshTokenRotationEnabled?: boolean;
}
) => void;
};
export const ServiceTokenV3Table = ({
handlePopUpOpen
}: Props) => {
const { createNotification } = useNotificationContext();
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetWorkspaceServiceTokenDataV3(currentWorkspace?.id || "");
const { mutateAsync: updateMutateAsync } = useUpdateServiceTokenV3();
const handleToggleServiceTokenDataStatus = async ({
serviceTokenDataId,
isActive
}: {
serviceTokenDataId: string;
isActive: boolean;
}) => {
try {
await updateMutateAsync({
serviceTokenDataId,
isActive
});
createNotification({
text: `Successfully ${isActive ? "enabled" : "disabled"} service token v3`,
type: "success"
});
} catch (err) {
console.log(err);
createNotification({
text: `Failed to ${isActive ? "enable" : "disable"} service token v3`,
type: "error"
});
}
}
return (
<TableContainer>
<Table>
<THead>
<Tr>
<Th>Name</Th>
<Th>Status</Th>
<Th>Role</Th>
<Th>Trusted IPs</Th>
<Th>Access Token TTL</Th>
<Th>Created At</Th>
<Th>Valid Until</Th>
<Th className="w-5" />
</Tr>
</THead>
<TBody>
{isLoading && <TableSkeleton columns={7} innerKey="service-tokens" />}
{!isLoading &&
data &&
data.length > 0 &&
data.map(({
id,
name,
isActive,
role,
customRole,
trustedIps,
createdAt,
expiresAt,
accessTokenTTL,
isRefreshTokenRotationEnabled
}) => {
return (
<Tr className="h-10" key={`st-v3-${id}`}>
<Td>{name}</Td>
<Td>
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.ServiceTokens}
>
{(isAllowed) => (
<Switch
id={`enable-service-token-${id}`}
onCheckedChange={(value) => handleToggleServiceTokenDataStatus({
serviceTokenDataId: id,
isActive: value
})}
isChecked={isActive}
isDisabled={!isAllowed}
>
<p className="w-12 mr-4">{isActive ? "Active" : "Inactive"}</p>
</Switch>
)}
</ProjectPermissionCan>
</Td>
<Td>{customRole?.slug ?? role}</Td>
<Td>
{trustedIps.map(({
id: trustedIpId,
ipAddress,
prefix
}) => {
return (
<p key={`service-token-${id}-}-trusted-ip-${trustedIpId}`}>
{`${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`}
</p>
);
})}
</Td>
<Td>{accessTokenTTL}</Td>
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
<Td>{expiresAt ? format(new Date(expiresAt), "yyyy-MM-dd") : "-"}</Td>
<Td className="flex justify-end">
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.ServiceTokens}
>
{(isAllowed) => (
<IconButton
onClick={async () => {
handlePopUpOpen("serviceTokenV3", {
serviceTokenDataId: id,
name,
role,
customRole,
trustedIps,
accessTokenTTL,
isRefreshTokenRotationEnabled
});
}}
size="lg"
colorSchema="primary"
variant="plain"
ariaLabel="update"
isDisabled={!isAllowed}
>
<FontAwesomeIcon icon={faPencil} />
</IconButton>
)}
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.ServiceTokens}
>
{(isAllowed) => (
<IconButton
onClick={() => {
handlePopUpOpen("deleteServiceTokenV3", {
serviceTokenDataId: id,
name
});
}}
size="lg"
colorSchema="danger"
variant="plain"
ariaLabel="update"
className="ml-4"
isDisabled={!isAllowed}
>
<FontAwesomeIcon icon={faXmark} />
</IconButton>
)}
</ProjectPermissionCan>
</Td>
</Tr>
);
})}
{!isLoading && data && data?.length === 0 && (
<Tr>
<Td colSpan={7}>
<EmptyState title="No service token v3 on file" icon={faKey} />
</Td>
</Tr>
)}
</TBody>
</Table>
</TableContainer>
);
}
@@ -15,7 +15,7 @@ enum TabSection {
export const SecretApprovalPage = () => {
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
return (
<div className="container mx-auto bg-bunker-800 text-white w-full h-full max-w-7xl px-6">
@@ -59,7 +59,7 @@ export const SecretApprovalPolicyList = ({ workspaceId }: Props) => {
const { mutateAsync: deleteSecretApprovalPolicy } = useDeleteSecretApprovalPolicy();
const handleDeletePolicy = async () => {
const { _id: id } = popUp.deletePolicy.data as TSecretApprovalPolicy;
const { id: id } = popUp.deletePolicy.data as TSecretApprovalPolicy;
try {
await deleteSecretApprovalPolicy({
workspaceId,
@@ -138,7 +138,7 @@ export const SecretApprovalPolicyList = ({ workspaceId }: Props) => {
<SecretApprovalPolicyRow
workspaceId={workspaceId}
policy={policy}
key={policy._id}
key={policy.id}
members={members}
onEdit={() => handlePopUpOpen("secretPolicyForm", policy)}
onDelete={() => handlePopUpOpen("deletePolicy", policy)}
@@ -54,7 +54,7 @@ export const SecretApprovalRequestChangeItem = ({
// meaning request has changed
const isStale = (secretVersion?.version || 1) < presentSecretVersionNumber;
const itemConflict =
hasMerged && conflicts.find((el) => el.op === op && el.secretId === newVersion?._id);
hasMerged && conflicts.find((el) => el.op === op && el.secretId === newVersion?.id);
const hasConflict = Boolean(itemConflict);
return (
@@ -97,10 +97,10 @@ export const SecretApprovalRequestChangeItem = ({
</Td>
<Td>{secretVersion?.comment}</Td>
<Td>
{secretVersion?.tags?.map(({ name, _id: tagId, tagColor }) => (
{secretVersion?.tags?.map(({ name, id: tagId, tagColor }) => (
<Tag
className="flex items-center space-x-2 w-min"
key={`${secretVersion._id}-${tagId}`}
key={`${secretVersion.id}-${tagId}`}
>
<div
className="w-3 h-3 rounded-full"
@@ -119,10 +119,10 @@ export const SecretApprovalRequestChangeItem = ({
</Td>
<Td>{newVersion?.secretComment}</Td>
<Td>
{newVersion?.tags?.map(({ name, _id: tagId, tagColor }) => (
{newVersion?.tags?.map(({ name, id: tagId, tagColor }) => (
<Tag
className="flex items-center space-x-2 w-min"
key={`${newVersion._id}-${tagId}`}
key={`${newVersion.id}-${tagId}`}
>
<div
className="w-3 h-3 rounded-full"
@@ -151,11 +151,11 @@ export const SecretApprovalRequestChangeItem = ({
</Td>
<Td>
{(op === CommitType.CREATE ? newVersion?.tags : secretVersion?.tags)?.map(
({ name, _id: tagId, tagColor }) => (
({ name, id: tagId, tagColor }) => (
<Tag
className="flex items-center space-x-2 w-min"
key={`${
op === CommitType.CREATE ? newVersion?._id : secretVersion?._id
op === CommitType.CREATE ? newVersion?.id : secretVersion?.id
}-${tagId}`}
>
<div
@@ -63,7 +63,7 @@ export const SecretMainPage = () => {
// env slug
const environment = router.query.env as string;
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
const secretPath = (router.query.secretPath as string) || "/";
const canReadSecret = permission.can(
ProjectPermissionActions.Read,
@@ -163,7 +163,7 @@ export const ActionBar = ({
};
const handleSecretBulkDelete = async () => {
const bulkDeletedSecrets = secrets.filter(({ _id }) => Boolean(selectedSecrets?.[_id]));
const bulkDeletedSecrets = secrets.filter(({ id }) => Boolean(selectedSecrets?.[id]));
try {
await deleteBatchSecretV3({
secretPath,
@@ -233,14 +233,14 @@ export const ActionBar = ({
</DropdownSubMenuTrigger>
<DropdownSubMenuContent className="rounded-l-none">
<DropdownMenuLabel>Apply tags to filter secrets</DropdownMenuLabel>
{tags.map(({ _id, name, tagColor }) => (
{tags.map(({ id, name, tagColor }) => (
<DropdownMenuItem
onClick={(evt) => {
evt.preventDefault();
onToggleTagFilter(_id);
onToggleTagFilter(id);
}}
key={_id}
icon={filter?.tags[_id] && <FontAwesomeIcon icon={faCheckCircle} />}
key={id}
icon={filter?.tags[id] && <FontAwesomeIcon icon={faCheckCircle} />}
iconPos="right"
>
<div className="flex items-center">
@@ -41,26 +41,26 @@ export const PitDrawer = ({
<div className="flex flex-col space-y-2">
{secretSnaphots?.pages?.map((group, i) => (
<Fragment key={`snapshot-item-${i + 1}`}>
{group.map(({ _id, createdAt }, index) => (
{group.map(({ id, createdAt }, index) => (
<Button
key={_id}
key={id}
className="py-3 px-4 text-sm"
isFullWidth
colorSchema={
(i === 0 && index === 0 && snapshotId === null) || snapshotId === _id
(i === 0 && index === 0 && snapshotId === null) || snapshotId === id
? "primary"
: "secondary"
}
variant={
(i === 0 && index === 0 && snapshotId === null) || snapshotId === _id
(i === 0 && index === 0 && snapshotId === null) || snapshotId === id
? "selected"
: "star"
}
onClick={() => onSelectSnapshot(_id)}
onClick={() => onSelectSnapshot(id)}
>
<div className="flex w-full justify-between">
<div>{timeSince(new Date(createdAt))}</div>
<div>{getButtonLabel(i === 0 && index === 0, snapshotId === _id)}</div>
<div>{getButtonLabel(i === 0 && index === 0, snapshotId === id)}</div>
</div>
</Button>
))}
@@ -240,14 +240,14 @@ export const CopySecretsFromBoard = ({
{secrets
?.filter(({ key }) => key.toLowerCase().includes(searchFilter.toLowerCase()))
?.map(({ _id, key, value: secVal }) => (
?.map(({ id, key, value: secVal }) => (
<Controller
key={`pull-secret--${_id}`}
key={`pull-secret--${id}`}
control={control}
name={`secrets.${key}`}
render={({ field: { value, onChange } }) => (
<Checkbox
id={`pull-secret-${_id}`}
id={`pull-secret-${id}`}
isChecked={Boolean(value)}
onCheckedChange={(isChecked) => onChange(isChecked ? secVal : "")}
>
@@ -132,12 +132,12 @@ export const SecretImportListView = ({
const { environment: importEnv, secretPath: impSecPath } = popUp.deleteSecretImport
?.data as TDeleteSecretImport;
try {
if (secretImports?._id) {
if (secretImports?.id) {
await deleteSecretImport({
workspaceId,
environment,
directory: secretPath,
id: secretImports?._id,
id: secretImports?.id,
secretImportEnv: importEnv,
secretImportPath: impSecPath
});
@@ -166,7 +166,7 @@ export const SecretImportListView = ({
workspaceId,
environment,
directory: secretPath,
id: secretImports?._id || "",
id: secretImports?.id || "",
secretImports: newImportOrder
});
}
@@ -49,7 +49,7 @@ type Props = {
onDeleteSecret: () => void;
onSaveSecret: (
orgSec: DecryptedSecret,
modSec: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] },
modSec: Omit<DecryptedSecret, "tags"> & { tags: { id: string }[] },
cb?: () => void
) => Promise<void>;
tags: WsTag[];
@@ -98,7 +98,7 @@ export const SecretDetailSidebar = ({
});
const selectedTags = watch("tags", []);
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
(prev, curr) => ({ ...prev, [curr._id]: true }),
(prev, curr) => ({ ...prev, [curr.id]: true }),
{}
);
@@ -109,7 +109,7 @@ export const SecretDetailSidebar = ({
const { data: secretVersion } = useGetSecretVersion({
limit: 10,
offset: 0,
secretId: secret?._id,
secretId: secret?.id,
decryptFileKey
});
@@ -133,8 +133,8 @@ export const SecretDetailSidebar = ({
};
const handleTagSelect = (tag: WsTag) => {
if (selectedTagsGroupById?.[tag._id]) {
const tagPos = selectedTags.findIndex(({ _id }) => _id === tag._id);
if (selectedTagsGroupById?.[tag.id]) {
const tagPos = selectedTags.findIndex(({ id }) => id === tag.id);
if (tagPos !== -1) {
remove(tagPos);
}
@@ -227,7 +227,7 @@ export const SecretDetailSidebar = ({
)}
<FormControl label="Tags" className="">
<div className="overflow-hidden grid gap-2 grid-flow-col auto-cols-min pt-2">
{fields.map(({ tagColor, id: formId, name, _id }) => (
{fields.map(({ tagColor, id: formId, name, id }) => (
<Tag
className="flex items-center space-x-2 w-min"
key={formId}
@@ -236,7 +236,7 @@ export const SecretDetailSidebar = ({
createNotification({ type: "error", text: "Access denied" });
return;
}
const tag = tags?.find(({ _id: id }) => id === _id);
const tag = tags?.find(({ id: id }) => id === id);
if (tag) handleTagSelect(tag);
}}
>
@@ -269,7 +269,7 @@ export const SecretDetailSidebar = ({
<DropdownMenuContent align="end" className="z-[100]">
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
{tags.map((tag) => {
const { _id: tagId, name, tagColor } = tag;
const { id: tagId, name, tagColor } = tag;
const isSelected = selectedTagsGroupById?.[tagId];
return (
@@ -56,7 +56,7 @@ type Props = {
secret: DecryptedSecret;
onSaveSecret: (
orgSec: DecryptedSecret,
modSec: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] },
modSec: Omit<DecryptedSecret, "tags"> & { tags: { id: string }[] },
cb?: () => void
) => Promise<void>;
onDeleteSecret: (sec: DecryptedSecret) => void;
@@ -117,7 +117,7 @@ export const SecretItem = memo(
const selectedTags = watch("tags", []);
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
(prev, curr) => ({ ...prev, [curr._id]: true }),
(prev, curr) => ({ ...prev, [curr.id]: true }),
{}
);
const { fields, append, remove } = useFieldArray({
@@ -168,8 +168,8 @@ export const SecretItem = memo(
};
const handleTagSelect = (tag: WsTag) => {
if (selectedTagsGroupById?.[tag._id]) {
const tagPos = selectedTags.findIndex(({ _id }) => _id === tag._id);
if (selectedTagsGroupById?.[tag.id]) {
const tagPos = selectedTags.findIndex(({ id }) => id === tag.id);
if (tagPos !== -1) {
remove(tagPos);
}
@@ -217,9 +217,9 @@ export const SecretItem = memo(
)}
>
<Checkbox
id={`checkbox-${secret._id}`}
id={`checkbox-${secret.id}`}
isChecked={isSelected}
onCheckedChange={() => onToggleSecretSelect(secret._id)}
onCheckedChange={() => onToggleSecretSelect(secret.id)}
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
/>
<FontAwesomeIcon
@@ -319,7 +319,7 @@ export const SecretItem = memo(
<DropdownMenuContent align="end">
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
{tags.map((tag) => {
const { _id: tagId, name, tagColor } = tag;
const { id: tagId, name, tagColor } = tag;
const isTagSelected = selectedTagsGroupById?.[tagId];
return (
@@ -31,7 +31,7 @@ export const formSchema = z.object({
tags: z
.object({
_id: z.string(),
id: z.string(),
name: z.string(),
slug: z.string(),
tagColor: z.string().optional()
@@ -151,10 +151,10 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
<Td className="border-r border-mineshaft-600">Tags</Td>
{isModified && (
<Td className="border-r border-mineshaft-600">
{preSecret?.tags?.map(({ name, _id: tagId, tagColor }) => (
{preSecret?.tags?.map(({ name, id: tagId, tagColor }) => (
<Tag
className="flex items-center space-x-2 w-min"
key={`${preSecret._id}-${tagId}`}
key={`${preSecret.id}-${tagId}`}
>
<div
className="w-3 h-3 rounded-full"
@@ -166,10 +166,10 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
</Td>
)}
<Td>
{postSecret?.tags?.map(({ name, _id: tagId, tagColor }) => (
{postSecret?.tags?.map(({ name, id: tagId, tagColor }) => (
<Tag
className="flex items-center space-x-2 w-min"
key={`${postSecret._id}-${tagId}`}
key={`${postSecret.id}-${tagId}`}
>
<div
className="w-3 h-3 rounded-full"
@@ -93,12 +93,12 @@ export const SnapshotView = ({
const secretDiffView = useMemo(() => {
const secretGroupById = secrets.reduce<Record<string, DecryptedSecret>>(
(prev, curr) => ({ ...prev, [curr._id]: curr }),
(prev, curr) => ({ ...prev, [curr.id]: curr }),
{}
);
const diffView: Array<TDiffView<DecryptedSecret>> = [];
rollingSecrets.forEach((rollSecret) => {
const { _id: id } = rollSecret;
const { id: id } = rollSecret;
const doesExist = Boolean(secretGroupById?.[id]);
if (doesExist) {
diffView.push({
@@ -74,14 +74,14 @@ export const SecretOverviewPage = () => {
const { currentWorkspace, isLoading: isWorkspaceLoading } = useWorkspace();
const { currentOrg } = useOrganization();
const workspaceId = currentWorkspace?._id as string;
const workspaceId = currentWorkspace?.id as string;
const { data: latestFileKey } = useGetUserWsKey(workspaceId);
const [searchFilter, setSearchFilter] = useState("");
const secretPath = (router.query?.secretPath as string) || "/";
useEffect(() => {
if (!isWorkspaceLoading && !workspaceId && router.isReady) {
router.push(`/org/${currentOrg?._id}/overview`);
router.push(`/org/${currentOrg?.id}/overview`);
}
}, [isWorkspaceLoading, workspaceId, router.isReady]);
@@ -19,16 +19,16 @@ type Props = {
export const ProjectIndexSecretsSection = ({ decryptFileKey }: Props) => {
const { currentWorkspace } = useWorkspace();
const { data: isBlindIndexed, isLoading: isBlindIndexedLoading } = useGetWorkspaceIndexStatus(
currentWorkspace?._id ?? ""
currentWorkspace?.id ?? ""
);
const [isIndexing, setIsIndexing] = useToggle();
const nameWorkspaceSecrets = useNameWorkspaceSecrets();
const onEnableBlindIndices = async () => {
if (!currentWorkspace?._id) return;
if (!currentWorkspace?.id) return;
setIsIndexing.on();
try {
const encryptedSecrets = await fetchWorkspaceSecrets(currentWorkspace._id);
const encryptedSecrets = await fetchWorkspaceSecrets(currentWorkspace.id);
const key = decryptAssymmetric({
ciphertext: decryptFileKey.encryptedKey,
@@ -47,11 +47,11 @@ export const ProjectIndexSecretsSection = ({ decryptFileKey }: Props) => {
return {
secretName,
_id: encryptedSecret._id
id: encryptedSecret.id
};
});
await nameWorkspaceSecrets.mutateAsync({
workspaceId: currentWorkspace._id,
workspaceId: currentWorkspace.id,
secretsToUpdate
});
} catch (err) {
@@ -149,7 +149,7 @@ export const SecretOverviewTableRow = ({
isVisible={isSecretVisible}
secretName={secretKey}
defaultValue={secret?.value}
secretId={secret?._id}
secretId={secret?.id}
isCreatable={isCreatable}
onSecretDelete={onSecretDelete}
onSecretCreate={onSecretCreate}
@@ -70,7 +70,7 @@ export const SecretRotationPage = withProjectPermission(
"deleteRotation",
"upgradePlan"
] as const);
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
const canCreateRotation = permission.can(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRotation
@@ -145,11 +145,11 @@ export const SecretRotationPage = withProjectPermission(
const handleUserAcceptBotCondition = async () => {
const provider = popUp.activeBot?.data as TSecretRotationProvider;
try {
if (bot?._id) {
if (bot?.id) {
const botKey = generateBotKey(bot.publicKey, userWsKey!);
await updateBotActiveStatus({
isActive: true,
botId: bot._id,
botId: bot.id,
workspaceId,
botKey
});
@@ -232,16 +232,16 @@ export const SecretRotationPage = withProjectPermission(
secretPath,
outputs,
provider,
_id,
id,
lastRotatedAt,
status,
statusMessage
}) => {
const isDeleting = deleteSecretRotationVars?.id === _id && isDeletingRotation;
const isDeleting = deleteSecretRotationVars?.id === id && isDeletingRotation;
const isRestarting =
restartSecretRotationVar?.id === _id && isRestartingRotation;
restartSecretRotationVar?.id === id && isRestartingRotation;
return (
<Tr key={_id}>
<Tr key={id}>
<Td>
{outputs
.map(({ key }) => key)
@@ -291,7 +291,7 @@ export const SecretRotationPage = withProjectPermission(
colorSchema="danger"
ariaLabel="delete-rotation"
isDisabled={isDeleting || !isAllowed}
onClick={() => handleRestartRotation(_id)}
onClick={() => handleRestartRotation(id)}
>
{isRestarting ? (
<Spinner size="xs" />
@@ -313,7 +313,7 @@ export const SecretRotationPage = withProjectPermission(
colorSchema="danger"
ariaLabel="delete-rotation"
isDisabled={isDeleting || !isAllowed}
onClick={() => handlePopUpOpen("deleteRotation", { id: _id })}
onClick={() => handlePopUpOpen("deleteRotation", { id: id })}
>
{isDeleting ? (
<Spinner size="xs" />
@@ -23,7 +23,7 @@ type Props = {
export const RotationOutputForm = ({ onSubmit, onCancel, outputSchema = {} }: Props) => {
const { currentWorkspace } = useWorkspace();
const environments = currentWorkspace?.environments || [];
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
const {
control,
handleSubmit,
@@ -117,11 +117,11 @@ export const RotationOutputForm = ({ onSubmit, onCancel, outputSchema = {} }: Pr
{!isSecretsLoading &&
secrets
?.filter(
({ _id }) =>
value === _id || !Object.values(selectedSecrets || {}).includes(_id)
({ id }) =>
value === id || !Object.values(selectedSecrets || {}).includes(id)
)
?.map(({ key, _id }) => (
<SelectItem value={_id} key={_id}>
?.map(({ key, id }) => (
<SelectItem value={id} key={id}>
{key}
</SelectItem>
))}
@@ -83,7 +83,7 @@ export const SecretScanningLogsTable = () => {
</Td>
<Td>{risk.isResolved ? "Resolved" : "Needs Attention"}</Td>
<Td>
<RiskStatusSelection riskId={risk._id} currentSelection={risk.status} />
<RiskStatusSelection riskId={risk.id} currentSelection={risk.status} />
</Td>
</Tr>
);
@@ -17,7 +17,7 @@ import { useGetOrgPlanTable } from "@app/hooks/api";
export const CurrentPlanSection = () => {
const { currentOrg } = useOrganization();
const { data, isLoading } = useGetOrgPlanTable(currentOrg?._id ?? "");
const { data, isLoading } = useGetOrgPlanTable(currentOrg?.id ?? "");
const displayCell = (value: null | number | string | boolean) => {
if (value === null) return "-";
@@ -24,7 +24,7 @@ export const ManagePlansTable = ({ billingCycle }: Props) => {
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { data: tableData, isLoading: isTableDataLoading } = useGetOrgPlansTable({
organizationId: currentOrg?._id ?? "",
organizationId: currentOrg?.id ?? "",
billingCycle
});
const createCustomerPortalSession = useCreateCustomerPortalSession();
@@ -110,11 +110,11 @@ export const ManagePlansTable = ({ billingCycle }: Props) => {
<Td>
<Button
onClick={async () => {
if (!currentOrg?._id) return;
if (!currentOrg?.id) return;
if (tier !== 3) {
const { url } = await createCustomerPortalSession.mutateAsync(
currentOrg._id
currentOrg.id
);
window.location.href = url;
return;
@@ -22,7 +22,7 @@ import { ManagePlansModal } from "./ManagePlansModal";
export const PreviewSection = () => {
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { data, isLoading } = useGetOrgPlanBillingInfo(currentOrg?._id ?? "");
const { data, isLoading } = useGetOrgPlanBillingInfo(currentOrg?.id ?? "");
const getOrgTrialUrl = useGetOrgTrialUrl();
const createCustomerPortalSession = useCreateCustomerPortalSession();
@@ -58,7 +58,7 @@ export const PreviewSection = () => {
if (!subscription.has_used_trial) {
// direct user to start pro trial
const url = await getOrgTrialUrl.mutateAsync({
orgId: currentOrg._id,
orgId: currentOrg.id,
success_url: window.location.href
});
@@ -124,8 +124,8 @@ export const PreviewSection = () => {
<button
type="button"
onClick={async () => {
if (!currentOrg?._id) return;
const { url } = await createCustomerPortalSession.mutateAsync(currentOrg._id);
if (!currentOrg?.id) return;
const { url } = await createCustomerPortalSession.mutateAsync(currentOrg.id);
window.location.href = url;
}}
disabled={!isAllowed}
@@ -18,7 +18,7 @@ import {
export const LicensesSection = () => {
const { currentOrg } = useOrganization();
const { data, isLoading } = useGetOrgLicenses(currentOrg?._id ?? "");
const { data, isLoading } = useGetOrgLicenses(currentOrg?.id ?? "");
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
@@ -35,7 +35,7 @@ export const LicensesSection = () => {
</THead>
<TBody>
{!isLoading && data && data?.length > 0 && data.map(({
_id,
id,
licenseKey,
isActivated,
createdAt,
@@ -44,7 +44,7 @@ export const LicensesSection = () => {
const formattedCreatedAt = new Date(createdAt).toISOString().split("T")[0];
const formattedExpiresAt = new Date(expiresAt).toISOString().split("T")[0];
return (
<Tr key={`license-${_id}`} className="h-10">
<Tr key={`license-${id}`} className="h-10">
<Td>{licenseKey}</Td>
<Td>{isActivated ? "Active" : "Inactive"}</Td>
<Td>{formattedCreatedAt}</Td>
@@ -25,7 +25,7 @@ export const OrgSSOSection = (): JSX.Element => {
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { createNotification } = useNotificationContext();
const { data, isLoading } = useGetSSOConfig(currentOrg?._id ?? "");
const { data, isLoading } = useGetSSOConfig(currentOrg?.id ?? "");
const { mutateAsync } = useUpdateSSOConfig();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"upgradePlan",
@@ -36,10 +36,10 @@ export const OrgSSOSection = (): JSX.Element => {
const handleSamlSSOToggle = async (value: boolean) => {
try {
if (!currentOrg?._id) return;
if (!currentOrg?.id) return;
await mutateAsync({
organizationId: currentOrg?._id,
organizationId: currentOrg?.id,
isActive: value
});
@@ -63,7 +63,7 @@ export const OrgSSOSection = (): JSX.Element => {
// case: SAML SSO is not configured
// -> initialize empty SAML SSO configuration
await createMutateAsync({
organizationId: currentOrg._id,
organizationId: currentOrg.id,
authProvider: "okta-saml",
isActive: false,
entryPoint: "",
@@ -118,7 +118,7 @@ export const OrgSSOSection = (): JSX.Element => {
)}
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">SSO identifier</h3>
<p className="text-gray-400 text-md">{data && data._id !== "" ? data._id : "-"}</p>
<p className="text-gray-400 text-md">{data && data.id !== "" ? data.id : "-"}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Type</h3>
@@ -57,7 +57,7 @@ export const SSOModal = ({
const { createNotification } = useNotificationContext();
const { mutateAsync: createMutateAsync, isLoading: createIsLoading } = useCreateSSOConfig();
const { mutateAsync: updateMutateAsync, isLoading: updateIsLoading } = useUpdateSSOConfig();
const { data } = useGetSSOConfig(currentOrg?._id ?? "");
const { data } = useGetSSOConfig(currentOrg?.id ?? "");
const {
control,
@@ -93,7 +93,7 @@ export const SSOModal = ({
if (!data) {
await createMutateAsync({
organizationId: currentOrg._id,
organizationId: currentOrg.id,
authProvider,
isActive: false,
entryPoint,
@@ -102,7 +102,7 @@ export const SSOModal = ({
});
} else {
await updateMutateAsync({
organizationId: currentOrg._id,
organizationId: currentOrg.id,
authProvider,
isActive: false,
entryPoint,
@@ -209,7 +209,7 @@ export const SSOModal = ({
<>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">{renderLabels(authProvider).acsUrl}</h3>
<p className="text-gray-400 text-md break-all">{`${window.origin}/api/v1/sso/saml2/${data._id}`}</p>
<p className="text-gray-400 text-md break-all">{`${window.origin}/api/v1/sso/saml2/${data.id}`}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">{renderLabels(authProvider).entityId}</h3>
@@ -21,10 +21,10 @@ export const OrgDeleteSection = () => {
const handleDeleteOrgSubmit = async () => {
try {
if (!currentOrg?._id) return;
if (!currentOrg?.id) return;
await mutateAsync({
organizationId: currentOrg?._id
organizationId: currentOrg?.id
});
createNotification({
@@ -37,10 +37,10 @@ export const AddOrgIncidentContactModal = ({
const onFormSubmit = async ({ email }: TAddContactForm) => {
try {
if (!currentOrg?._id) return;
if (!currentOrg?.id) return;
await mutateAsync({
orgId: currentOrg._id,
orgId: currentOrg.id,
email
});
@@ -25,7 +25,7 @@ import { useDeleteIncidentContact, useGetOrgIncidentContact } from "@app/hooks/a
export const OrgIncidentContactsTable = () => {
const { createNotification } = useNotificationContext();
const { currentOrg } = useOrganization();
const { data: contacts, isLoading } = useGetOrgIncidentContact(currentOrg?._id ?? "");
const { data: contacts, isLoading } = useGetOrgIncidentContact(currentOrg?.id ?? "");
const [searchContact, setSearchContact] = useState("");
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"removeContact",
@@ -37,9 +37,9 @@ export const OrgIncidentContactsTable = () => {
try {
const incidentContactEmail = (popUp?.removeContact?.data as { email: string })?.email;
if (!currentOrg?._id) return;
if (!currentOrg?.id) return;
await mutateAsync({
orgId: currentOrg._id,
orgId: currentOrg.id,
email: incidentContactEmail
});
@@ -31,10 +31,10 @@ export const OrgNameChangeSection = (): JSX.Element => {
const onFormSubmit = async ({ name }: FormData) => {
try {
if (!currentOrg?._id) return;
if (!currentOrg?.id) return;
if (name === "") return;
await mutateAsync({ orgId: currentOrg?._id, newOrgName: name });
await mutateAsync({ orgId: currentOrg?.id, newOrgName: name });
createNotification({
text: "Successfully renamed organization",
type: "success"
@@ -50,13 +50,13 @@ export const APIKeyV2Table = ({
data &&
data.length > 0 &&
data.map(({
_id,
id,
name,
lastUsed,
createdAt
}) => {
return (
<Tr className="h-10" key={`api-key-v2-${_id}`}>
<Tr className="h-10" key={`api-key-v2-${id}`}>
<Td>{name}</Td>
<Td>{lastUsed ? format(new Date(lastUsed), "yyyy-MM-dd") : "-"}</Td>
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
@@ -64,7 +64,7 @@ export const APIKeyV2Table = ({
<IconButton
onClick={async () => {
handlePopUpOpen("apiKeyV2", {
apiKeyDataId: _id,
apiKeyDataId: id,
name
});
}}
@@ -78,7 +78,7 @@ export const APIKeyV2Table = ({
<IconButton
onClick={() => {
handlePopUpOpen("deleteAPIKeyV2", {
apiKeyDataId: _id
apiKeyDataId: id
});
}}
size="lg"
@@ -187,14 +187,14 @@ export const UserInfoSSOStep = ({
});
const userOrgs = await fetchOrganizations();
const orgId = userOrgs[0]?._id;
const orgId = userOrgs[0]?.id;
const project = await ProjectService.initProject({
organizationId: orgId,
projectName: "Example Project"
});
localStorage.setItem("orgData.id", orgId);
localStorage.setItem("projectData.id", project._id);
localStorage.setItem("projectData.id", project.id);
setStep(1);
} catch (error) {
@@ -22,8 +22,8 @@ export const AdminDashboardPage = () => {
useEffect(() => {
if (isNotAllowed && !isUserLoading) {
if (orgs?.length) {
localStorage.setItem("orgData.id", orgs?.[0]?._id);
router.push(`/org/${orgs?.[0]?._id}/overview`);
localStorage.setItem("orgData.id", orgs?.[0]?.id);
router.push(`/org/${orgs?.[0]?.id}/overview`);
}
}
}, [isNotAllowed, isUserLoading]);