mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 11:26:33 +00:00
- Swap to using ms in some frontend areas - Rename button from "Clear
All Lockouts" to "Reset All Lockouts" - Add a tooltip to the red lock icon on auth row - Make the red lock icon go away after resetting all lockouts
This commit is contained in:
@@ -23,29 +23,6 @@ export const formatDateTime = ({
|
|||||||
return format(date, dateFormat);
|
return format(date, dateFormat);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to convert duration to seconds
|
|
||||||
export const durationToSeconds = (
|
|
||||||
value: number,
|
|
||||||
unit: "s" | "m" | "h" | "d" | "w" | "y"
|
|
||||||
): number => {
|
|
||||||
switch (unit) {
|
|
||||||
case "s":
|
|
||||||
return value;
|
|
||||||
case "m":
|
|
||||||
return value * 60;
|
|
||||||
case "h":
|
|
||||||
return value * 60 * 60;
|
|
||||||
case "d":
|
|
||||||
return value * 60 * 60 * 24;
|
|
||||||
case "w":
|
|
||||||
return value * 60 * 60 * 24 * 7;
|
|
||||||
case "y":
|
|
||||||
return value * 60 * 60 * 24 * 365;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to convert seconds to value and unit
|
// Helper function to convert seconds to value and unit
|
||||||
export const getObjectFromSeconds = (
|
export const getObjectFromSeconds = (
|
||||||
totalSeconds: number,
|
totalSeconds: number,
|
||||||
|
|||||||
+8
-14
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
|||||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
@@ -20,7 +21,7 @@ import {
|
|||||||
Tabs
|
Tabs
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { durationToSeconds, getObjectFromSeconds } from "@app/helpers/datetime";
|
import { getObjectFromSeconds } from "@app/helpers/datetime";
|
||||||
import {
|
import {
|
||||||
useAddIdentityUniversalAuth,
|
useAddIdentityUniversalAuth,
|
||||||
useGetIdentityUniversalAuth,
|
useGetIdentityUniversalAuth,
|
||||||
@@ -117,11 +118,9 @@ const schema = z
|
|||||||
|
|
||||||
if (isAnyParseError) return;
|
if (isAnyParseError) return;
|
||||||
|
|
||||||
const lockoutDurationInSeconds = durationToSeconds(parsedLockoutDuration, lockoutDurationUnit);
|
const lockoutDurationInSeconds = ms(`${parsedLockoutDuration}${lockoutDurationUnit}`) / 1000;
|
||||||
const lockoutCounterResetInSeconds = durationToSeconds(
|
const lockoutCounterResetInSeconds =
|
||||||
parsedLockoutCounterReset,
|
ms(`${parsedLockoutCounterReset}${lockoutCounterResetUnit}`) / 1000;
|
||||||
lockoutCounterResetUnit
|
|
||||||
);
|
|
||||||
|
|
||||||
if (lockoutDurationInSeconds > 86400 || lockoutDurationInSeconds < 30) {
|
if (lockoutDurationInSeconds > 86400 || lockoutDurationInSeconds < 30) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
@@ -279,14 +278,9 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
try {
|
try {
|
||||||
if (!identityId) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
const lockoutDurationSeconds = durationToSeconds(
|
const lockoutDurationSeconds = ms(`${lockoutDurationValue}${lockoutDurationUnit}`) / 1000;
|
||||||
Number(lockoutDurationValue),
|
const lockoutCounterResetSeconds =
|
||||||
lockoutDurationUnit
|
ms(`${lockoutCounterResetValue}${lockoutCounterResetUnit}`) / 1000;
|
||||||
);
|
|
||||||
const lockoutCounterResetSeconds = durationToSeconds(
|
|
||||||
Number(lockoutCounterResetValue),
|
|
||||||
lockoutCounterResetUnit
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// update universal auth configuration
|
// update universal auth configuration
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ const Page = () => {
|
|||||||
authMethod={popUp.viewAuthMethod.data?.authMethod}
|
authMethod={popUp.viewAuthMethod.data?.authMethod}
|
||||||
lockedOut={popUp.viewAuthMethod.data?.lockedOut || false}
|
lockedOut={popUp.viewAuthMethod.data?.lockedOut || false}
|
||||||
identityId={identityId}
|
identityId={identityId}
|
||||||
|
onResetAllLockouts={popUp.viewAuthMethod.data?.refetchIdentity}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+7
-4
@@ -2,7 +2,7 @@ import { faCog, faLock, faPlus } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import { Button } from "@app/components/v2";
|
import { Button, Tooltip } from "@app/components/v2";
|
||||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||||
import { IdentityAuthMethod, identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api";
|
import { IdentityAuthMethod, identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
@@ -16,7 +16,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: Props) => {
|
export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: Props) => {
|
||||||
const { data } = useGetIdentityById(identityId);
|
const { data, refetch } = useGetIdentityById(identityId);
|
||||||
|
|
||||||
return data ? (
|
return data ? (
|
||||||
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
@@ -31,7 +31,8 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
handlePopUpOpen("viewAuthMethod", {
|
handlePopUpOpen("viewAuthMethod", {
|
||||||
authMethod,
|
authMethod,
|
||||||
lockedOut: data.identity.activeLockoutAuthMethods.includes(authMethod)
|
lockedOut: data.identity.activeLockoutAuthMethods.includes(authMethod),
|
||||||
|
refetchIdentity: refetch
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -40,7 +41,9 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
|
|||||||
<span>{identityAuthToNameMap[authMethod]}</span>
|
<span>{identityAuthToNameMap[authMethod]}</span>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{data.identity.activeLockoutAuthMethods.includes(authMethod) && (
|
{data.identity.activeLockoutAuthMethods.includes(authMethod) && (
|
||||||
<FontAwesomeIcon icon={faLock} size="xs" className="text-red-400/50" />
|
<Tooltip content="Auth method has active lockouts">
|
||||||
|
<FontAwesomeIcon icon={faLock} size="xs" className="text-red-400/50" />
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<FontAwesomeIcon icon={faCog} size="xs" className="text-mineshaft-400" />
|
<FontAwesomeIcon icon={faCog} size="xs" className="text-mineshaft-400" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+11
-3
@@ -41,6 +41,7 @@ type Props = {
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onOpenChange: (isOpen: boolean) => void;
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
onDeleteAuthMethod: () => void;
|
onDeleteAuthMethod: () => void;
|
||||||
|
onResetAllLockouts: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TRevokeOptions = {
|
type TRevokeOptions = {
|
||||||
@@ -52,8 +53,12 @@ export const Content = ({
|
|||||||
identityId,
|
identityId,
|
||||||
authMethod,
|
authMethod,
|
||||||
lockedOut,
|
lockedOut,
|
||||||
onDeleteAuthMethod
|
onDeleteAuthMethod,
|
||||||
}: Pick<Props, "authMethod" | "lockedOut" | "identityId" | "onDeleteAuthMethod">) => {
|
onResetAllLockouts
|
||||||
|
}: Pick<
|
||||||
|
Props,
|
||||||
|
"authMethod" | "lockedOut" | "identityId" | "onDeleteAuthMethod" | "onResetAllLockouts"
|
||||||
|
>) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
@@ -161,6 +166,7 @@ export const Content = ({
|
|||||||
<Component
|
<Component
|
||||||
identityId={identityId}
|
identityId={identityId}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
|
onResetAllLockouts={onResetAllLockouts}
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
@@ -188,7 +194,8 @@ export const ViewIdentityAuthModal = ({
|
|||||||
onOpenChange,
|
onOpenChange,
|
||||||
authMethod,
|
authMethod,
|
||||||
identityId,
|
identityId,
|
||||||
lockedOut
|
lockedOut,
|
||||||
|
onResetAllLockouts
|
||||||
}: Omit<Props, "onDeleteAuthMethod">) => {
|
}: Omit<Props, "onDeleteAuthMethod">) => {
|
||||||
if (!identityId || !authMethod) return null;
|
if (!identityId || !authMethod) return null;
|
||||||
|
|
||||||
@@ -200,6 +207,7 @@ export const ViewIdentityAuthModal = ({
|
|||||||
authMethod={authMethod}
|
authMethod={authMethod}
|
||||||
lockedOut={lockedOut}
|
lockedOut={lockedOut}
|
||||||
onDeleteAuthMethod={() => onOpenChange(false)}
|
onDeleteAuthMethod={() => onOpenChange(false)}
|
||||||
|
onResetAllLockouts={() => onResetAllLockouts()}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
+4
-2
@@ -26,7 +26,8 @@ export const ViewIdentityUniversalAuthContent = ({
|
|||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
onDelete,
|
onDelete,
|
||||||
popUp,
|
popUp,
|
||||||
lockedOut
|
lockedOut,
|
||||||
|
onResetAllLockouts
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityUniversalAuth(identityId);
|
const { data, isPending } = useGetIdentityUniversalAuth(identityId);
|
||||||
const { data: clientSecrets = [], isPending: clientSecretsPending } =
|
const { data: clientSecrets = [], isPending: clientSecretsPending } =
|
||||||
@@ -48,6 +49,7 @@ export const ViewIdentityUniversalAuthContent = ({
|
|||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
setLockedOutState(false);
|
setLockedOutState(false);
|
||||||
|
onResetAllLockouts();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -124,7 +126,7 @@ export const ViewIdentityUniversalAuthContent = ({
|
|||||||
isLoading={isClearLockoutsPending}
|
isLoading={isClearLockoutsPending}
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
>
|
>
|
||||||
Clear All Lockouts
|
Reset All Lockouts
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
|
|||||||
+1
@@ -10,4 +10,5 @@ export type ViewAuthMethodProps = {
|
|||||||
) => void;
|
) => void;
|
||||||
popUp: UsePopUpState<["revokeAuthMethod", "upgradePlan", "identityAuthMethod"]>;
|
popUp: UsePopUpState<["revokeAuthMethod", "upgradePlan", "identityAuthMethod"]>;
|
||||||
lockedOut: boolean;
|
lockedOut: boolean;
|
||||||
|
onResetAllLockouts: () => void;
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-6
@@ -1,13 +1,14 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2";
|
import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
import { durationToSeconds, getObjectFromSeconds } from "@app/helpers/datetime";
|
import { getObjectFromSeconds } from "@app/helpers/datetime";
|
||||||
import { useUpdateOrg } from "@app/hooks/api";
|
import { useUpdateOrg } from "@app/hooks/api";
|
||||||
|
|
||||||
const MAX_SHARED_SECRET_LIFETIME_SECONDS = 30 * 24 * 60 * 60; // 30 days in seconds
|
const MAX_SHARED_SECRET_LIFETIME_SECONDS = 30 * 24 * 60 * 60; // 30 days in seconds
|
||||||
@@ -25,7 +26,7 @@ const formSchema = z
|
|||||||
.superRefine((data, ctx) => {
|
.superRefine((data, ctx) => {
|
||||||
const { maxLifetimeValue, maxLifetimeUnit } = data;
|
const { maxLifetimeValue, maxLifetimeUnit } = data;
|
||||||
|
|
||||||
const durationInSeconds = durationToSeconds(maxLifetimeValue, maxLifetimeUnit);
|
const durationInSeconds = ms(`${maxLifetimeValue}${maxLifetimeUnit}`) / 1000;
|
||||||
|
|
||||||
if (durationInSeconds > MAX_SHARED_SECRET_LIFETIME_SECONDS) {
|
if (durationInSeconds > MAX_SHARED_SECRET_LIFETIME_SECONDS) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
@@ -90,10 +91,8 @@ export const OrgSecretShareLimitSection = () => {
|
|||||||
|
|
||||||
const handleFormSubmit = async (formData: TForm) => {
|
const handleFormSubmit = async (formData: TForm) => {
|
||||||
try {
|
try {
|
||||||
const maxSharedSecretLifetimeSeconds = durationToSeconds(
|
const maxSharedSecretLifetimeSeconds =
|
||||||
formData.maxLifetimeValue,
|
ms(`${formData.maxLifetimeValue}${formData.maxLifetimeUnit}`) / 1000;
|
||||||
formData.maxLifetimeUnit
|
|
||||||
);
|
|
||||||
|
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
orgId: currentOrg.id,
|
orgId: currentOrg.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user