chore: resolve merge conflicts

This commit is contained in:
Chukwunonso Frank
2023-07-26 20:39:51 +01:00
135 changed files with 3253 additions and 311 deletions
@@ -35,7 +35,7 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
// generate CSRF token for OAuth2 code-token exchange integrations
const state = crypto.randomBytes(16).toString("hex");
localStorage.setItem("latestCSRFToken", state);
let link = "";
switch (integrationOption.slug) {
case "azure-key-vault":
@@ -86,6 +86,9 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
case "railway":
link = `${window.location.origin}/integrations/railway/authorize`;
break;
case "terraform-cloud":
link = `${window.location.origin}/integrations/terraform-cloud/authorize`;
break;
case "hashicorp-vault":
link = `${window.location.origin}/integrations/hashicorp-vault/authorize`;
break;
@@ -98,6 +101,12 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
case "codefresh":
link = `${window.location.origin}/integrations/codefresh/authorize`;
break;
case "digital-ocean-app-platform":
link = `${window.location.origin}/integrations/digital-ocean-app-platform/authorize`;
break;
case "cloud-66":
link = `${window.location.origin}/integrations/cloud-66/authorize`;
break;
case "northflank":
link = `${window.location.origin}/integrations/northflank/authorize`;
break;
@@ -32,7 +32,7 @@ export const CloudIntegrationSection = ({
const isEmpty = !isLoading && !cloudIntegrations?.length;
const sortedCloudIntegrations = cloudIntegrations.sort((a, b) => a.name.localeCompare(b.name));
return (
<div>
<div className="m-4 mt-7 flex max-w-5xl flex-col items-start justify-between px-2 text-xl">
@@ -67,18 +67,9 @@ export const CloudIntegrationSection = ({
width={70}
alt="integration logo"
/>
{cloudIntegration.name.split(" ").length > 2 ? (
<div className="ml-4 max-w-xs text-3xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
<div>{cloudIntegration.name.split(" ")[0]}</div>
<div className="text-base">
{cloudIntegration.name.split(" ")[1]} {cloudIntegration.name.split(" ")[2]}
</div>
</div>
) : (
<div className="ml-4 max-w-xs text-xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
{cloudIntegration.name}
</div>
)}
<div className="ml-4 max-w-xs text-xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
{cloudIntegration.name}
</div>
{cloudIntegration.isAvailable &&
Boolean(integrationAuths?.[cloudIntegration.slug]) && (
<div className="absolute top-0 right-0 z-40 h-full">
+3 -1
View File
@@ -18,6 +18,8 @@ export const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const queryParams = new URLSearchParams(window.location.search)
useEffect(() => {
// TODO(akhilmhdh): workspace will be controlled by a workspace context
const redirectToDashboard = async () => {
@@ -30,7 +32,6 @@ export const Login = () => {
const userDetails = await fetchUserDetails()
// send details back to client
const queryParams = new URLSearchParams(window.location.search)
if (queryParams && queryParams.get("callback_port")) {
const callbackPort = queryParams.get("callback_port")
@@ -67,6 +68,7 @@ export const Login = () => {
email={email}
password={password}
providerAuthToken={undefined}
callbackPort={queryParams.get("callback_port")}
/>
);
case 2:
+4 -8
View File
@@ -1,5 +1,4 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/router"
import jwt_decode from "jwt-decode";
import {
@@ -14,25 +13,20 @@ type Props = {
export const LoginSSO = ({ providerAuthToken }: Props) => {
const [step, setStep] = useState(0);
const [password, setPassword] = useState("");
const router = useRouter();
const {
email,
isUserCompleted,
callbackPort
} = jwt_decode(providerAuthToken) as any;
useEffect(() => {
if (!isUserCompleted) {
router.push(`/signup/sso?token=${encodeURIComponent(providerAuthToken)}`);
}
if (isUserCompleted) {
setStep(1);
}
}, []);
const renderView = () => {
// TODO: consider adding a complete account step here that's uniquely for SSO
switch (step) {
case 0:
return (
@@ -42,6 +36,7 @@ export const LoginSSO = ({ providerAuthToken }: Props) => {
return (
<PasswordStep
providerAuthToken={providerAuthToken}
callbackPort={callbackPort}
email={email}
password={password}
setPassword={setPassword}
@@ -52,6 +47,7 @@ export const LoginSSO = ({ providerAuthToken }: Props) => {
return (
<MFAStep
providerAuthToken={providerAuthToken}
callbackPort={callbackPort}
email={email}
password={password}
/>
@@ -33,6 +33,9 @@ export const InitialStep = ({
const { t } = useTranslation();
const [isLoading, setIsLoading] = useState(false);
const [loginError, setLoginError] = useState(false);
const [loginEmailChosen, setLoginEmailChosen] = useState(false);
const queryParams = new URLSearchParams(window.location.search);
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
@@ -42,7 +45,6 @@ export const InitialStep = ({
}
setIsLoading(true);
const queryParams = new URLSearchParams(window.location.search)
if (queryParams && queryParams.get("callback_port")) {
const callbackPort = queryParams.get("callback_port")
@@ -115,34 +117,49 @@ export const InitialStep = ({
return (
<form onSubmit={handleLogin} className='flex flex-col mx-auto w-full justify-center items-center'>
<h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >Login to Infisical</h1>
<div className="relative md:px-1.5 flex items-center justify-center lg:w-1/6 w-1/4 min-w-[21.3rem] md:min-w-[22rem] mx-auto rounded-lg max-h-24 md:max-h-28">
<div className="flex items-center justify-center w-full md:px-2 md:py-1 rounded-lg max-h-24 md:max-h-28">
<Input
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
placeholder="Enter your email..."
isRequired
autoComplete="username"
className="h-12"
/>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Button
colorSchema="primary"
variant={loginEmailChosen ? "outline_bg" : "solid"}
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(`/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-1" />}
className="h-12 w-full mx-0"
>
{t("login.continue-with-google")}
</Button>
</div>
<div className="relative pt-2 md:pt-0 md:px-1.5 flex items-center justify-center w-1/4 lg:w-1/6 min-w-[21.3rem] md:min-w-[22rem] mx-auto rounded-lg max-h-24 md:max-h-28">
<div className="flex items-center justify-center w-full md:p-2 rounded-lg max-h-24 md:max-h-28">
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
type="password"
placeholder="Enter your password..."
isRequired
autoComplete="current-password"
id="current-password"
className="h-12 select:-webkit-autofill:focus"
/>
</div>
{loginEmailChosen && <>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
<div className='w-full border-t border-mineshaft-500' />
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md'>
<Input
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
placeholder="Enter your email..."
isRequired
autoComplete="username"
className="h-12"
/>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
type="password"
placeholder="Enter your password..."
isRequired
autoComplete="current-password"
id="current-password"
className="h-12 select:-webkit-autofill:focus"
/>
</div>
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Button
type="submit"
@@ -152,28 +169,25 @@ export const InitialStep = ({
colorSchema="primary"
variant="solid"
isLoading={isLoading}
> Login </Button>
> Continue with Email </Button>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
<div className='w-1/2 border-t border-mineshaft-500' />
<span className='px-4 text-sm text-bunker-400'>or</span>
<div className='w-1/2 border-t border-mineshaft-500' />
</div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md'>
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center mt-4 py-2'>
<div className='w-full border-t border-mineshaft-500' />
</div></>}
{!loginEmailChosen && <div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Button
colorSchema="primary"
variant="solid"
onClick={() => {
window.open("/api/v1/sso/redirect/google");
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-1" />}
className="h-14 w-full mx-0"
>
{t("login.continue-with-google")}
</Button>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'>
setLoginEmailChosen(true);
}}
size="sm"
isFullWidth
className='h-12'
colorSchema="primary"
variant="outline_bg"
> Continue with Email </Button>
</div>}
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Button
colorSchema="primary"
variant="outline_bg"
@@ -181,7 +195,7 @@ export const InitialStep = ({
setStep(2);
}}
isFullWidth
className="h-14 w-full mx-0"
className="h-12 w-full mx-0"
>
Continue with SAML SSO
</Button>
@@ -36,6 +36,7 @@ type Props = {
email: string;
password: string;
providerAuthToken?: string;
callbackPort?: string | null;
}
interface VerifyMfaTokenError {
@@ -53,7 +54,8 @@ interface VerifyMfaTokenError {
export const MFAStep = ({
email,
password,
providerAuthToken
providerAuthToken,
callbackPort
}: Props) => {
const { createNotification } = useNotificationContext();
const router = useRouter();
@@ -77,9 +79,7 @@ export const MFAStep = ({
}
setIsLoading(true);
const queryParams = new URLSearchParams(window.location.search)
if (queryParams && queryParams.get("callback_port")){
const callbackPort = queryParams.get("callback_port")
if (callbackPort){
// attemptCliLogin
const isCliLoginSuccessful = await attemptCliLoginMfa({
@@ -2,14 +2,17 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import Link from "next/link";
import { useRouter } from "next/router"
import axios from "axios"
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
import attemptLogin from "@app/components/utilities/attemptLogin";
import { Button, Input } from "@app/components/v2";
import getOrganizations from "@app/pages/api/organization/getOrgs";
type Props = {
providerAuthToken: string;
callbackPort?: string;
email: string;
password: string;
setPassword: (password: string) => void;
@@ -18,6 +21,7 @@ type Props = {
export const PasswordStep = ({
providerAuthToken,
callbackPort,
email,
password,
setPassword,
@@ -31,34 +35,64 @@ export const PasswordStep = ({
const handleLogin = async () => {
try {
setIsLoading(true);
const loginAttempt = await attemptLogin({
email,
password,
providerAuthToken,
});
if (callbackPort) {
// attemptCliLogin
const isCliLoginSuccessful = await attemptCliLogin({
email,
password,
providerAuthToken
})
if (loginAttempt && loginAttempt.success) {
// case: login was successful
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
if (loginAttempt.mfaEnabled) {
// TODO: deal with MFA
// case: login requires MFA step
setIsLoading(false);
setStep(2);
return;
if (isCliLoginSuccessful.mfaEnabled) {
// case: login requires MFA step
setStep(2);
setIsLoading(false);
return;
}
// case: login was successful
const cliUrl = `http://localhost:${callbackPort}`
// send request to server endpoint
const instance = axios.create()
await instance.post(cliUrl, { ...isCliLoginSuccessful.loginResponse })
// cli page
router.push("/cli-redirect");
// on success, router.push to cli Login Successful page
}
// case: login does not require MFA step
const userOrgs = await getOrganizations();
const userOrg = userOrgs[0]._id;
setIsLoading(false);
createNotification({
text: "Successfully logged in",
type: "success"
} else {
const loginAttempt = await attemptLogin({
email,
password,
providerAuthToken,
});
router.push(`/org/${userOrg?._id}/overview`);
}
if (loginAttempt && loginAttempt.success) {
// case: login was successful
if (loginAttempt.mfaEnabled) {
// TODO: deal with MFA
// case: login requires MFA step
setIsLoading(false);
setStep(2);
return;
}
// case: login does not require MFA step
const userOrgs = await getOrganizations();
const userOrg = userOrgs[0]._id;
setIsLoading(false);
createNotification({
text: "Successfully logged in",
type: "success"
});
router.push(`/org/${userOrg?._id}/overview`);
}
}
} catch (err) {
setIsLoading(false);
createNotification({
@@ -13,6 +13,8 @@ export const SAMLSSOStep = ({
const [ssoIdentifier, setSSOIdentifier] = useState("");
const { t } = useTranslation();
const queryParams = new URLSearchParams(window.location.search);
return (
<div className="mx-auto w-full max-w-md md:px-6">
<p className="mx-auto mb-6 flex w-max justify-center text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8">
@@ -37,7 +39,8 @@ export const SAMLSSOStep = ({
colorSchema="primary"
variant="outline_bg"
onClick={() => {
window.open(`/api/v1/sso/redirect/saml2/${ssoIdentifier}`);
const callbackPort = queryParams.get("callback_port");
window.open(`/api/v1/sso/redirect/saml2/${ssoIdentifier}${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
window.close();
}}
isFullWidth
@@ -6,6 +6,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import {
Button,
DeleteActionModal,
@@ -26,9 +27,11 @@ import {
Th,
THead,
Tr,
UpgradePlanModal} from "@app/components/v2";
import { useWorkspace } from "@app/context";
UpgradePlanModal
} from "@app/components/v2";
import { useOrganization , useWorkspace } from "@app/context";
import { usePopUp, useToggle } from "@app/hooks";
import { useGetSSOConfig } from "@app/hooks/api";
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
import { OrgUser, Workspace } from "@app/hooks/api/types";
@@ -69,6 +72,9 @@ export const OrgMembersTable = ({
setCompleteInviteLink
}: Props) => {
const router = useRouter();
const { createNotification } = useNotificationContext();
const { currentOrg } = useOrganization();
const { data: ssoConfig, isLoading: isLoadingSSOConfig } = useGetSSOConfig(currentOrg?._id ?? "");
const [searchMemberFilter, setSearchMemberFilter] = useState("");
const {data: serverDetails } = useFetchServerStatus()
const { workspaces } = useWorkspace();
@@ -79,7 +85,7 @@ export const OrgMembersTable = ({
"upgradePlan",
"setUpEmail"
] as const);
useEffect(() => {
if (router.query.action === "invite") {
handlePopUpOpen("addMember");
@@ -152,6 +158,15 @@ export const OrgMembersTable = ({
<Button
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
if (!isLoadingSSOConfig && ssoConfig && ssoConfig.isActive) {
createNotification({
text: "You cannot invite users when SAML SSO is configured for your organization",
type: "error"
});
return;
}
if (isMoreUserNotAllowed) {
handlePopUpOpen("upgradePlan");
} else {
@@ -0,0 +1,15 @@
import { IPAllowlistSection } from "./components";
export const IPAllowlistPage = () => {
return (
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
<div className="max-w-7xl px-6 w-full">
<div className="my-6">
<p className="text-3xl font-semibold text-gray-200">IP Allowlist</p>
<div />
</div>
<IPAllowlistSection />
</div>
</div>
);
}
@@ -0,0 +1,193 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import {
Button,
FormControl,
Input,
Modal,
ModalContent
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import {
useAddTrustedIp,
useGetMyIp,
useUpdateTrustedIp
} from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = yup.object({
ipAddress: yup.string().required("IP address is required"),
comment: yup.string()
}).required();
export type FormData = yup.InferType<typeof schema>;
type Props = {
popUp: UsePopUpState<["trustedIp"]>;
handlePopUpClose: (popUpName: keyof UsePopUpState<["trustedIp"]>) => void;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["trustedIp"]>, state?: boolean) => void;
};
export const IPAllowlistModal = ({
popUp,
handlePopUpClose,
handlePopUpToggle
}: Props) => {
const { createNotification } = useNotificationContext();
const { data, isLoading } = useGetMyIp();
const { currentWorkspace } = useWorkspace();
const addTrustedIp = useAddTrustedIp();
const updateTrustedIp = useUpdateTrustedIp();
const {
control,
setValue,
handleSubmit,
reset,
formState: { isSubmitting }
} = useForm<FormData>({
resolver: yupResolver(schema)
});
useEffect(() => {
const trustedIpData = popUp?.trustedIp?.data as {
ipAddress: string;
comment: string;
prefix: number;
};
if (popUp?.trustedIp?.data) {
reset({
ipAddress: `${trustedIpData.ipAddress}${trustedIpData.prefix !== undefined ? `/${trustedIpData.prefix}` : ""}`,
comment: trustedIpData.comment
});
} else {
reset({
ipAddress: "",
comment: ""
});
}
}, [popUp?.trustedIp?.data]);
const onIPAllowlistModalSubmit = async ({
ipAddress,
comment
}: FormData) => {
try {
if (!currentWorkspace?._id) return;
if (popUp?.trustedIp?.data) {
await updateTrustedIp.mutateAsync({
workspaceId: currentWorkspace._id,
trustedIpId: (popUp?.trustedIp?.data as { trustedIpId: string })?.trustedIpId,
ipAddress,
comment,
isActive: true
});
} else {
await addTrustedIp.mutateAsync({
workspaceId: currentWorkspace._id,
ipAddress,
comment,
isActive: true
});
}
createNotification({
text: `Successfully ${popUp?.trustedIp?.data ? "updated" : "added"} trusted IP`,
type: "success"
});
reset();
handlePopUpClose("trustedIp");
} catch (err) {
createNotification({
text: `Failed to ${popUp?.trustedIp?.data ? "update" : "add"} trusted IP`,
type: "error"
});
}
}
return (
<Modal
isOpen={popUp?.trustedIp?.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("trustedIp", isOpen);
reset();
}}
>
<ModalContent title={popUp?.trustedIp?.data ? "Update IP" : "Add IP"}>
<form onSubmit={handleSubmit(onIPAllowlistModalSubmit)}>
<Controller
control={control}
defaultValue=""
name="ipAddress"
render={({ field, fieldState: { error } }) => (
<FormControl
label="IPv4/IPv6 Address / CIDR Notation"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
placeholder="123.456.789.0"
/>
</FormControl>
)}
/>
{!isLoading && data && (
<Button
colorSchema="secondary"
type="button"
onClick={() => setValue("ipAddress", data)}
className="mb-8"
>
Add current IP address
</Button>
)}
<Controller
control={control}
defaultValue=""
name="comment"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Comment"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
placeholder="My IP address"
/>
</FormControl>
)}
/>
<div className="mt-8 flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting}
>
{popUp?.trustedIp?.data ? "Update" : "Add"}
</Button>
<Button
colorSchema="secondary"
variant="plain"
onClick={() => handlePopUpClose("trustedIp")}
>
Cancel
</Button>
</div>
</form>
</ModalContent>
</Modal>
);
}
@@ -0,0 +1,105 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import {
Button,
DeleteActionModal,
UpgradePlanModal
} from "@app/components/v2";
import { useSubscription,useWorkspace } from "@app/context";
import {
useDeleteTrustedIp
} from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { IPAllowlistModal } from "./IPAllowlistModal";
import { IPAllowlistTable } from "./IPAllowlistTable";
export const IPAllowlistSection = () => {
const { createNotification } = useNotificationContext();
const { mutateAsync } = useDeleteTrustedIp();
const { subscription } = useSubscription();
const { currentWorkspace } = useWorkspace();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"trustedIp",
"deleteTrustedIp",
"upgradePlan"
] as const);
const onDeleteTrustedIpSubmit = async (trustedIpId: string) => {
try {
if (!currentWorkspace?._id) return;
await mutateAsync({
workspaceId: currentWorkspace._id,
trustedIpId
});
createNotification({
text: "Successfully deleted IP access range",
type: "success"
});
handlePopUpClose("deleteTrustedIp");
} catch (err) {
console.log(err);
createNotification({
text: "Failed to delete IP access range",
type: "error"
});
}
}
return (
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
<div className="flex items-center mb-8">
<h2 className="text-xl font-semibold flex-1 text-white">
IP Allowlist
</h2>
<Button
onClick={() => {
if (subscription?.ipAllowlisting) {
handlePopUpOpen("trustedIp")
} else {
handlePopUpOpen("upgradePlan");
}
}}
colorSchema="secondary"
isLoading={false}
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
Add IP
</Button>
</div>
<IPAllowlistTable
popUp={popUp}
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
<IPAllowlistModal
popUp={popUp}
handlePopUpClose={handlePopUpClose}
handlePopUpToggle={handlePopUpToggle}
/>
<DeleteActionModal
isOpen={popUp.deleteTrustedIp.isOpen}
title={`Are you sure want to delete ${
(popUp?.deleteTrustedIp?.data as { name: string })?.name || " "
}?`}
onChange={(isOpen) => handlePopUpToggle("deleteTrustedIp", isOpen)}
deleteKey="confirm"
onDeleteApproved={() =>
onDeleteTrustedIpSubmit((popUp?.deleteTrustedIp?.data as { trustedIpId: string })?.trustedIpId)
}
/>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
/>
</div>
);
}
@@ -0,0 +1,162 @@
import { faGlobe, faPencil, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
EmptyState,
IconButton,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
THead,
Tr,
UpgradePlanModal
} from "@app/components/v2";
import { useSubscription, useWorkspace } from "@app/context";
import {
useGetTrustedIps
} from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
popUp: UsePopUpState<["upgradePlan"]>;
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["trustedIp", "deleteTrustedIp", "upgradePlan"]>,
data?: {
trustedIpId: string;
ipAddress?: string;
comment?: string;
isActive?: boolean;
prefix?: number;
},
) => void;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["upgradePlan"]>, state?: boolean) => void;
};
export const IPAllowlistTable = ({
popUp,
handlePopUpOpen,
handlePopUpToggle
}: Props) => {
const { subscription } = useSubscription();
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetTrustedIps(currentWorkspace?._id ?? "");
const formatType = (type: string, prefix?: number) => {
return `${type.slice(0, 2).toUpperCase() + type.slice(2)} ${(prefix !== undefined) ? "CIDR" : ""}`;
}
return (
<div>
<TableContainer className="mt-4">
<Table>
<THead>
<Tr>
<Th className="flex-1">IP Address / Range</Th>
<Th className="flex-1">Format</Th>
<Th className="flex-1">Comment</Th>
{/* <Th className="flex-1">Status</Th> */}
<Th className="w-5" />
</Tr>
</THead>
<TBody>
{!isLoading && data && data?.length > 0 && data
.sort((a, b) => a.ipAddress.localeCompare(b.ipAddress))
.map(({
_id,
ipAddress,
comment,
type,
prefix,
isActive
}) => {
return (
<Tr
key={`ip-access-range-${_id}`}
className="h-10"
>
<Td>
{`${ipAddress}${(prefix !== undefined) ? `/${prefix}` : ""}`}
</Td>
<Td>
{formatType(type, prefix)}
</Td>
<Td>
{comment}
</Td>
{/* <Td>
<div className="flex items-center">
<FontAwesomeIcon
icon={faCircle}
color="#2ecc71"
/>
<p className="ml-4">Active</p>
</div>
</Td> */}
<Td className="flex items-center">
<IconButton
className="mr-3 py-2"
onClick={() => {
if (subscription?.ipAllowlisting) {
handlePopUpOpen("trustedIp", {
trustedIpId: _id,
ipAddress,
comment,
prefix,
isActive
});
} else {
handlePopUpOpen("upgradePlan");
}
}}
colorSchema="primary"
variant="plain"
ariaLabel="update"
>
<FontAwesomeIcon icon={faPencil} />
</IconButton>
<IconButton
onClick={() => {
if (subscription?.ipAllowlisting) {
handlePopUpOpen("deleteTrustedIp", {
trustedIpId: _id
});
} else {
handlePopUpOpen("upgradePlan");
}
}}
size="lg"
colorSchema="danger"
variant="plain"
ariaLabel="update"
>
<FontAwesomeIcon icon={faXmark} />
</IconButton>
</Td>
</Tr>
);
})}
{isLoading && <TableSkeleton columns={4} key="ip-access-ranges" />}
{!isLoading && data && data?.length === 0 && (
<Tr>
<Td colSpan={5}>
<EmptyState
title="No IP addresses added"
icon={faGlobe}
/>
</Td>
</Tr>
)}
</TBody>
</Table>
</TableContainer>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
/>
</div>
);
}
@@ -0,0 +1 @@
export { IPAllowlistSection } from "./IPAllowlistSection";
@@ -0,0 +1 @@
export { IPAllowlistPage } from "./IPAllowlistPage";
@@ -11,7 +11,8 @@ import {
Td,
Th,
THead,
Tr} from "@app/components/v2";
Tr
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import {
useDeleteOrgPmtMethod,
@@ -11,7 +11,8 @@ import {
Td,
Th,
THead,
Tr} from "@app/components/v2";
Tr
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import {
useGetOrgInvoices
@@ -102,7 +102,7 @@ export const AuthMethodSection = () => {
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
className="w-full bg-mineshaft-800 border border-mineshaft-600"
>
{authMethods.map((authMethod) => {
return (
@@ -18,7 +18,7 @@ import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["updateEnv", "deleteEnv", "deleteEnv", "upgradePlan"]>,
popUpName: keyof UsePopUpState<["updateEnv", "deleteEnv", "upgradePlan"]>,
{
name,
slug
@@ -69,13 +69,13 @@ export const UserInfoSSOStep = ({
const [nameError, setNameError] = useState(false);
const [organizationName, setOrganizationName] = useState("");
const [organizationNameError, setOrganizationNameError] = useState(false);
const [attributionSource, setAttributionSource] = useState("");
const [errors, setErrors] = useState<Errors>({});
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation();
useEffect(() => {
console.log("providerOrganizationName: ", providerOrganizationName);
if (providerOrganizationName) {
if (providerOrganizationName !== undefined) {
setOrganizationName(providerOrganizationName);
}
}, []);
@@ -112,10 +112,6 @@ export const UserInfoSSOStep = ({
const privateKey = encodeBase64(secretKeyUint8Array);
const publicKey = encodeBase64(publicKeyUint8Array);
localStorage.setItem("PRIVATE_KEY", privateKey);
console.log("make");
console.log("email: ", email);
console.log("password: ", password);
client.init(
{
@@ -175,7 +171,8 @@ export const UserInfoSSOStep = ({
providerAuthToken,
salt: result.salt,
verifier: result.verifier,
organizationName
organizationName,
attributionSource
});
// unset signup JWT token and set JWT token
@@ -213,7 +210,7 @@ export const UserInfoSSOStep = ({
setIsLoading(false);
}
};
return (
<div className="h-full mx-auto mb-36 w-max rounded-xl md:px-8 md:mb-16">
<p className="mx-8 mb-6 flex justify-center text-xl font-bold text-medium md:mx-16 text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
@@ -232,18 +229,31 @@ export const UserInfoSSOStep = ({
/>
{nameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your name</p>}
</div>
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Organization Name</p>
<Input
placeholder="Infisical"
value={organizationName}
onChange={(e) => setOrganizationName(e.target.value)}
isRequired
className="h-12"
disabled
/>
{organizationNameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your organization name</p>}
</div>
{providerOrganizationName === undefined && (
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Organization Name</p>
<Input
placeholder="Infisical"
value={organizationName}
onChange={(e) => setOrganizationName(e.target.value)}
isRequired
className="h-12"
disabled
/>
{organizationNameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your organization name</p>}
</div>
)}
{providerOrganizationName === undefined && (
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Where did you hear about us? <span className="font-light">(optional)</span></p>
<Input
placeholder=""
onChange={(e) => setAttributionSource(e.target.value)}
value={attributionSource}
className="h-12"
/>
</div>
)}
<div className="mt-2 flex lg:w-1/6 w-1/4 min-w-[20rem] max-h-60 w-full flex-col items-center justify-center rounded-lg py-2">
<InputField
label={t("section.password.password")}