mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 06:28:11 +00:00
final review changes
This commit is contained in:
+158
-66
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { SingleValue } from "react-select";
|
import { SingleValue } from "react-select";
|
||||||
import { faCopy, faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
import { faCopy, faQuestionCircle, faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -8,14 +8,21 @@ import { z } from "zod";
|
|||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
FilterableSelect,
|
FilterableSelect,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
ModalClose
|
ModalClose,
|
||||||
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { ROUTE_PATHS } from "@app/const/routes";
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
import { useOrganization } from "@app/context";
|
import {
|
||||||
|
OrgPermissionIdentityActions,
|
||||||
|
OrgPermissionSubjects,
|
||||||
|
useOrganization,
|
||||||
|
useOrgPermission
|
||||||
|
} from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityTokenAuth,
|
useAddIdentityTokenAuth,
|
||||||
useCreateTokenIdentityTokenAuth,
|
useCreateTokenIdentityTokenAuth,
|
||||||
@@ -27,7 +34,7 @@ import { slugSchema } from "@app/lib/schemas";
|
|||||||
|
|
||||||
import { RelayOption } from "./RelayOption";
|
import { RelayOption } from "./RelayOption";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const baseFormSchema = z.object({
|
||||||
name: slugSchema({ field: "name" }),
|
name: slugSchema({ field: "name" }),
|
||||||
instanceDomain: z.string().url("Must be a valid URL").or(z.literal("")),
|
instanceDomain: z.string().url("Must be a valid URL").or(z.literal("")),
|
||||||
relay: z
|
relay: z
|
||||||
@@ -39,7 +46,10 @@ const formSchema = z.object({
|
|||||||
{ required_error: "Relay is required" }
|
{ required_error: "Relay is required" }
|
||||||
)
|
)
|
||||||
.nullable()
|
.nullable()
|
||||||
.refine((val) => val !== null, { message: "Relay is required" }),
|
.refine((val) => val !== null, { message: "Relay is required" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const formSchemaWithIdentity = baseFormSchema.extend({
|
||||||
identity: z
|
identity: z
|
||||||
.object(
|
.object(
|
||||||
{
|
{
|
||||||
@@ -52,6 +62,10 @@ const formSchema = z.object({
|
|||||||
.refine((val) => val !== null, { message: "Identity is required" })
|
.refine((val) => val !== null, { message: "Identity is required" })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const formSchemaWithToken = baseFormSchema.extend({
|
||||||
|
identityToken: z.string().min(1, "Token is required")
|
||||||
|
});
|
||||||
|
|
||||||
export const GatewayCliDeploymentMethod = () => {
|
export const GatewayCliDeploymentMethod = () => {
|
||||||
const { protocol, hostname, port } = window.location;
|
const { protocol, hostname, port } = window.location;
|
||||||
const portSuffix = port && port !== "80" ? `:${port}` : "";
|
const portSuffix = port && port !== "80" ? `:${port}` : "";
|
||||||
@@ -61,6 +75,7 @@ export const GatewayCliDeploymentMethod = () => {
|
|||||||
from: ROUTE_PATHS.Organization.NetworkingPage.path
|
from: ROUTE_PATHS.Organization.NetworkingPage.path
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [autogenerateToken, setAutogenerateToken] = useState(true);
|
||||||
const [step, setStep] = useState<"form" | "command">("form");
|
const [step, setStep] = useState<"form" | "command">("form");
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [instanceDomain, setInstanceDomain] = useState(siteURL);
|
const [instanceDomain, setInstanceDomain] = useState(siteURL);
|
||||||
@@ -90,6 +105,12 @@ export const GatewayCliDeploymentMethod = () => {
|
|||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const organizationId = currentOrg?.id || "";
|
const organizationId = currentOrg?.id || "";
|
||||||
|
|
||||||
|
const { permission } = useOrgPermission();
|
||||||
|
const canCreateToken = permission.can(
|
||||||
|
OrgPermissionIdentityActions.CreateToken,
|
||||||
|
OrgPermissionSubjects.Identity
|
||||||
|
);
|
||||||
|
|
||||||
const { data: identityMembershipOrgsData, isPending: isIdentitiesLoading } =
|
const { data: identityMembershipOrgsData, isPending: isIdentitiesLoading } =
|
||||||
useGetIdentityMembershipOrgs({
|
useGetIdentityMembershipOrgs({
|
||||||
organizationId,
|
organizationId,
|
||||||
@@ -105,48 +126,68 @@ export const GatewayCliDeploymentMethod = () => {
|
|||||||
|
|
||||||
const handleGenerateCommand = async () => {
|
const handleGenerateCommand = async () => {
|
||||||
setFormErrors([]);
|
setFormErrors([]);
|
||||||
const validation = formSchema.safeParse({ name, relay, identity, instanceDomain });
|
|
||||||
if (!validation.success) {
|
|
||||||
setFormErrors(validation.error.issues);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validatedIdentity = validation.data.identity;
|
if (canCreateToken && autogenerateToken) {
|
||||||
|
const validation = formSchemaWithIdentity.safeParse({
|
||||||
try {
|
name,
|
||||||
const { data: identityTokenAuth } = await refetch();
|
relay,
|
||||||
if (!identityTokenAuth) {
|
identity,
|
||||||
await addIdentityTokenAuth({
|
instanceDomain
|
||||||
identityId: validatedIdentity.id,
|
});
|
||||||
organizationId,
|
if (!validation.success) {
|
||||||
accessTokenTTL: 2592000,
|
setFormErrors(validation.error.issues);
|
||||||
accessTokenMaxTTL: 2592000,
|
return;
|
||||||
accessTokenNumUsesLimit: 0,
|
|
||||||
accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
|
|
||||||
});
|
|
||||||
createNotification({
|
|
||||||
text: "Token authentication has been automatically enabled for the selected identity. By default, it is configured to allow all IP addresses with a default token TTL of 30 days. You can manage these settings in Access Control.",
|
|
||||||
type: "warning"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await createToken({
|
const validatedIdentity = validation.data.identity;
|
||||||
identityId: validatedIdentity.id,
|
|
||||||
name: `gateway token for ${name} (autogenerated)`
|
try {
|
||||||
});
|
const { data: identityTokenAuth } = await refetch();
|
||||||
setIdentityToken(token.accessToken);
|
if (!identityTokenAuth) {
|
||||||
createNotification({
|
await addIdentityTokenAuth({
|
||||||
text: "Automatically generated a token for the selected identity.",
|
identityId: validatedIdentity.id,
|
||||||
type: "info"
|
organizationId,
|
||||||
|
accessTokenTTL: 2592000,
|
||||||
|
accessTokenMaxTTL: 2592000,
|
||||||
|
accessTokenNumUsesLimit: 0,
|
||||||
|
accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
|
||||||
|
});
|
||||||
|
createNotification({
|
||||||
|
text: "Token authentication has been automatically enabled for the selected identity. By default, it is configured to allow all IP addresses with a default token TTL of 30 days. You can manage these settings in Access Control.",
|
||||||
|
type: "warning"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await createToken({
|
||||||
|
identityId: validatedIdentity.id,
|
||||||
|
name: `gateway token for ${name} (autogenerated)`
|
||||||
|
});
|
||||||
|
setIdentityToken(token.accessToken);
|
||||||
|
createNotification({
|
||||||
|
text: "Automatically generated a token for the selected identity.",
|
||||||
|
type: "info"
|
||||||
|
});
|
||||||
|
setStep("command");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to generate token for the selected identity",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
setIdentityToken("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const validation = formSchemaWithToken.safeParse({
|
||||||
|
name,
|
||||||
|
relay,
|
||||||
|
identityToken,
|
||||||
|
instanceDomain
|
||||||
});
|
});
|
||||||
|
if (!validation.success) {
|
||||||
|
setFormErrors(validation.error.issues);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setStep("command");
|
setStep("command");
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
createNotification({
|
|
||||||
text: "Failed to generate token for the selected identity",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
setIdentityToken("");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -239,8 +280,8 @@ export const GatewayCliDeploymentMethod = () => {
|
|||||||
{errors.relay && <p className="mt-1 text-sm text-red">{errors.relay}</p>}
|
{errors.relay && <p className="mt-1 text-sm text-red">{errors.relay}</p>}
|
||||||
|
|
||||||
<FormLabel
|
<FormLabel
|
||||||
label="Instance Domain"
|
label="Infisical Instance Host Address"
|
||||||
tooltipText="The domain of the infisical instance that's accessible by the gateway."
|
tooltipText="The host address of the infisical instance that's accessible by the gateway."
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
@@ -251,28 +292,79 @@ export const GatewayCliDeploymentMethod = () => {
|
|||||||
/>
|
/>
|
||||||
{errors.instanceDomain && <p className="mt-1 text-sm text-red">{errors.instanceDomain}</p>}
|
{errors.instanceDomain && <p className="mt-1 text-sm text-red">{errors.instanceDomain}</p>}
|
||||||
|
|
||||||
<FormLabel
|
{canCreateToken && autogenerateToken ? (
|
||||||
label="Identity"
|
<>
|
||||||
tooltipText="The identity that your gateway will use for authentication."
|
<FormLabel
|
||||||
className="mt-4"
|
label="Identity"
|
||||||
/>
|
tooltipText="The identity that your gateway will use for authentication."
|
||||||
<FilterableSelect
|
className="mt-4"
|
||||||
value={identity}
|
/>
|
||||||
onChange={(e) =>
|
<FilterableSelect
|
||||||
setIdentity(
|
value={identity}
|
||||||
e as SingleValue<{
|
onChange={(e) =>
|
||||||
id: string;
|
setIdentity(
|
||||||
name: string;
|
e as SingleValue<{
|
||||||
}>
|
id: string;
|
||||||
)
|
name: string;
|
||||||
}
|
}>
|
||||||
isLoading={isIdentitiesLoading}
|
)
|
||||||
placeholder="Select identity..."
|
}
|
||||||
options={identityMembershipOrgs.map((membership) => membership.identity)}
|
isLoading={isIdentitiesLoading}
|
||||||
getOptionValue={(option) => option.id}
|
placeholder="Select identity..."
|
||||||
getOptionLabel={(option) => option.name}
|
options={identityMembershipOrgs.map((membership) => membership.identity)}
|
||||||
/>
|
getOptionValue={(option) => option.id}
|
||||||
{errors.identity && <p className="mt-1 text-sm text-red">{errors.identity}</p>}
|
getOptionLabel={(option) => option.name}
|
||||||
|
/>
|
||||||
|
{errors.identity && <p className="mt-1 text-sm text-red">{errors.identity}</p>}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FormLabel
|
||||||
|
label="Identity Token"
|
||||||
|
tooltipText="The identity token that your relay will use for authentication."
|
||||||
|
className="mt-4"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={identityToken}
|
||||||
|
onChange={(e) => setIdentityToken(e.target.value)}
|
||||||
|
placeholder="Enter identity token..."
|
||||||
|
isError={Boolean(errors.identityToken)}
|
||||||
|
/>
|
||||||
|
{errors.identityToken && <p className="mt-1 text-sm text-red">{errors.identityToken}</p>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{canCreateToken && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<Checkbox
|
||||||
|
isChecked={autogenerateToken}
|
||||||
|
onCheckedChange={(e) => {
|
||||||
|
setAutogenerateToken(Boolean(e));
|
||||||
|
}}
|
||||||
|
id="autogenerate-token"
|
||||||
|
className="mr-2"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span>Automatically enable token auth and generate a token for identity</span>
|
||||||
|
<Tooltip
|
||||||
|
className="max-w-md"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
Token authentication will be automatically enabled for the selected identity if
|
||||||
|
it isn't already configured. By default, it will be configured to allow all IP
|
||||||
|
addresses with a token TTL of 30 days. You can manage these settings in Access
|
||||||
|
Control.
|
||||||
|
<br />
|
||||||
|
<br />A token will automatically be generated to be used with the CLI command.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="mt-0.5 ml-1" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mt-6 flex items-center">
|
<div className="mt-6 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
+41
-25
@@ -1,17 +1,19 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { SingleValue } from "react-select";
|
import { SingleValue } from "react-select";
|
||||||
import { faCopy, faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
import { faCopy, faQuestionCircle, faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
FilterableSelect,
|
FilterableSelect,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
ModalClose
|
ModalClose,
|
||||||
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
OrgPermissionIdentityActions,
|
OrgPermissionIdentityActions,
|
||||||
@@ -19,7 +21,6 @@ import {
|
|||||||
useOrganization,
|
useOrganization,
|
||||||
useOrgPermission
|
useOrgPermission
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useAddIdentityTokenAuth,
|
useAddIdentityTokenAuth,
|
||||||
useCreateTokenIdentityTokenAuth,
|
useCreateTokenIdentityTokenAuth,
|
||||||
@@ -56,7 +57,7 @@ export const RelayCliDeploymentMethod = () => {
|
|||||||
const portSuffix = port && port !== "80" ? `:${port}` : "";
|
const portSuffix = port && port !== "80" ? `:${port}` : "";
|
||||||
const siteURL = `${protocol}//${hostname}${portSuffix}`;
|
const siteURL = `${protocol}//${hostname}${portSuffix}`;
|
||||||
|
|
||||||
const [addTokenManually, setAddTokenManually] = useState(false);
|
const [autogenerateToken, setAutogenerateToken] = useState(true);
|
||||||
const [step, setStep] = useState<"form" | "command">("form");
|
const [step, setStep] = useState<"form" | "command">("form");
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [host, setHost] = useState("");
|
const [host, setHost] = useState("");
|
||||||
@@ -104,7 +105,7 @@ export const RelayCliDeploymentMethod = () => {
|
|||||||
const handleGenerateCommand = async () => {
|
const handleGenerateCommand = async () => {
|
||||||
setFormErrors([]);
|
setFormErrors([]);
|
||||||
|
|
||||||
if (canCreateToken) {
|
if (canCreateToken && autogenerateToken) {
|
||||||
const validation = formSchemaWithIdentity.safeParse({ name, host, instanceDomain, identity });
|
const validation = formSchemaWithIdentity.safeParse({ name, host, instanceDomain, identity });
|
||||||
if (!validation.success) {
|
if (!validation.success) {
|
||||||
setFormErrors(validation.error.issues);
|
setFormErrors(validation.error.issues);
|
||||||
@@ -244,8 +245,8 @@ export const RelayCliDeploymentMethod = () => {
|
|||||||
{errors.host && <p className="mt-1 text-sm text-red">{errors.host}</p>}
|
{errors.host && <p className="mt-1 text-sm text-red">{errors.host}</p>}
|
||||||
|
|
||||||
<FormLabel
|
<FormLabel
|
||||||
label="Instance Domain"
|
label="Infisical Instance Host Address"
|
||||||
tooltipText="The domain of the infisical instance that's accessible by the relay."
|
tooltipText="The host address of the infisical instance that's accessible by the relay."
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
@@ -256,7 +257,7 @@ export const RelayCliDeploymentMethod = () => {
|
|||||||
/>
|
/>
|
||||||
{errors.instanceDomain && <p className="mt-1 text-sm text-red">{errors.instanceDomain}</p>}
|
{errors.instanceDomain && <p className="mt-1 text-sm text-red">{errors.instanceDomain}</p>}
|
||||||
|
|
||||||
{canCreateToken && !addTokenManually ? (
|
{canCreateToken && autogenerateToken ? (
|
||||||
<>
|
<>
|
||||||
<FormLabel
|
<FormLabel
|
||||||
label="Identity"
|
label="Identity"
|
||||||
@@ -279,15 +280,6 @@ export const RelayCliDeploymentMethod = () => {
|
|||||||
getOptionValue={(option) => option.id}
|
getOptionValue={(option) => option.id}
|
||||||
getOptionLabel={(option) => option.name}
|
getOptionLabel={(option) => option.name}
|
||||||
/>
|
/>
|
||||||
<p className="mt-1 text-xs text-yellow">
|
|
||||||
Selecting an identity will automatically enable token auth and generate a token.{" "}
|
|
||||||
<button
|
|
||||||
onClick={() => setAddTokenManually(true)}
|
|
||||||
className="cursor-pointer underline transition-opacity hover:opacity-70"
|
|
||||||
>
|
|
||||||
Add token manually
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
{errors.identity && <p className="mt-1 text-sm text-red">{errors.identity}</p>}
|
{errors.identity && <p className="mt-1 text-sm text-red">{errors.identity}</p>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -303,18 +295,42 @@ export const RelayCliDeploymentMethod = () => {
|
|||||||
placeholder="Enter identity token..."
|
placeholder="Enter identity token..."
|
||||||
isError={Boolean(errors.identityToken)}
|
isError={Boolean(errors.identityToken)}
|
||||||
/>
|
/>
|
||||||
{canCreateToken && (
|
|
||||||
<button
|
|
||||||
onClick={() => setAddTokenManually(false)}
|
|
||||||
className="mt-1 cursor-pointer text-xs text-mineshaft-400 underline transition-opacity hover:opacity-70"
|
|
||||||
>
|
|
||||||
Autogenerate token for identity
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{errors.identityToken && <p className="mt-1 text-sm text-red">{errors.identityToken}</p>}
|
{errors.identityToken && <p className="mt-1 text-sm text-red">{errors.identityToken}</p>}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{canCreateToken && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<Checkbox
|
||||||
|
isChecked={autogenerateToken}
|
||||||
|
onCheckedChange={(e) => {
|
||||||
|
setAutogenerateToken(Boolean(e));
|
||||||
|
}}
|
||||||
|
id="autogenerate-token"
|
||||||
|
className="mr-2"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span>Automatically enable token auth and generate a token for identity</span>
|
||||||
|
<Tooltip
|
||||||
|
className="max-w-md"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
Token authentication will be automatically enabled for the selected identity if
|
||||||
|
it isn't already configured. By default, it will be configured to allow all IP
|
||||||
|
addresses with a token TTL of 30 days. You can manage these settings in Access
|
||||||
|
Control.
|
||||||
|
<br />
|
||||||
|
<br />A token will automatically be generated to be used with the CLI command.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="mt-0.5 ml-1" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mt-6 flex items-center">
|
<div className="mt-6 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
|
|||||||
Reference in New Issue
Block a user