mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 13:27:22 +00:00
improvement: allow free number entry for max views in secret sharing
This commit is contained in:
+47
-14
@@ -71,7 +71,8 @@ const formSchema = z
|
|||||||
maxLifetimeUnit: z.enum(["m", "h", "d"], {
|
maxLifetimeUnit: z.enum(["m", "h", "d"], {
|
||||||
invalid_type_error: "Please select a valid time unit"
|
invalid_type_error: "Please select a valid time unit"
|
||||||
}),
|
}),
|
||||||
maxViewLimit: z.string()
|
maxViewLimit: z.string(),
|
||||||
|
shouldLimitView: z.boolean()
|
||||||
})
|
})
|
||||||
.superRefine((data, ctx) => {
|
.superRefine((data, ctx) => {
|
||||||
const { maxLifetimeValue, maxLifetimeUnit } = data;
|
const { maxLifetimeValue, maxLifetimeUnit } = data;
|
||||||
@@ -112,8 +113,8 @@ const formSchema = z
|
|||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
const viewLimitOptions = [
|
const viewLimitOptions = [
|
||||||
{ label: "1", value: 1 },
|
{ label: "Unlimited", value: false },
|
||||||
{ label: "Unlimited", value: -1 }
|
{ label: "Limited", value: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
export const OrgSecretShareLimitSection = () => {
|
export const OrgSecretShareLimitSection = () => {
|
||||||
@@ -125,13 +126,15 @@ export const OrgSecretShareLimitSection = () => {
|
|||||||
return {
|
return {
|
||||||
maxLifetimeValue: initialLifetime.maxLifetimeValue,
|
maxLifetimeValue: initialLifetime.maxLifetimeValue,
|
||||||
maxLifetimeUnit: initialLifetime.maxLifetimeUnit,
|
maxLifetimeUnit: initialLifetime.maxLifetimeUnit,
|
||||||
maxViewLimit: currentOrg?.maxSharedSecretViewLimit?.toString() || "-1"
|
maxViewLimit: currentOrg?.maxSharedSecretViewLimit?.toString() || "1",
|
||||||
|
shouldLimitView: Boolean(currentOrg?.maxSharedSecretViewLimit)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
formState: { isSubmitting, isDirty },
|
formState: { isSubmitting, isDirty },
|
||||||
|
watch,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset
|
reset
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
@@ -139,6 +142,8 @@ export const OrgSecretShareLimitSection = () => {
|
|||||||
defaultValues: getDefaultFormValues()
|
defaultValues: getDefaultFormValues()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const shouldLimitView = watch("shouldLimitView");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentOrg) {
|
if (currentOrg) {
|
||||||
reset(getDefaultFormValues());
|
reset(getDefaultFormValues());
|
||||||
@@ -154,8 +159,7 @@ export const OrgSecretShareLimitSection = () => {
|
|||||||
|
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
orgId: currentOrg.id,
|
orgId: currentOrg.id,
|
||||||
maxSharedSecretViewLimit:
|
maxSharedSecretViewLimit: formData.shouldLimitView ? Number(formData.maxViewLimit) : null,
|
||||||
formData.maxViewLimit === "-1" ? null : Number(formData.maxViewLimit),
|
|
||||||
maxSharedSecretLifetime: maxSharedSecretLifetimeSeconds
|
maxSharedSecretLifetime: maxSharedSecretLifetimeSeconds
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -249,26 +253,32 @@ export const OrgSecretShareLimitSection = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex max-w-sm">
|
<div className="flex max-w-sm items-end gap-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="maxViewLimit"
|
name="shouldLimitView"
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
render={({ field: { onChange, value, ...field }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Max Views"
|
label="Max Views"
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
className="w-full"
|
className="w-48"
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={field.value}
|
defaultValue={value.toString()}
|
||||||
{...field}
|
value={value.toString()}
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => onChange(e === "true")}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
position="popper"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
|
{...field}
|
||||||
>
|
>
|
||||||
{viewLimitOptions.map(({ label, value: viewLimitValue }) => (
|
{viewLimitOptions.map(({ label, value: viewLimitValue }) => (
|
||||||
<SelectItem value={String(viewLimitValue || "")} key={label}>
|
<SelectItem
|
||||||
|
value={viewLimitValue.toString()}
|
||||||
|
key={viewLimitValue.toString()}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -276,6 +286,29 @@ export const OrgSecretShareLimitSection = () => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{shouldLimitView && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="maxViewLimit"
|
||||||
|
render={({ field: { onChange, value, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
className="w-48"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
{...field}
|
||||||
|
min={1}
|
||||||
|
max={1000}
|
||||||
|
type="number"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ const expiresInOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const viewLimitOptions = [
|
const viewLimitOptions = [
|
||||||
{ label: "1", value: 1 },
|
{ label: "Unlimited", value: false },
|
||||||
{ label: "Unlimited", value: -1 }
|
{ label: "Limited", value: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
@@ -45,6 +45,7 @@ const schema = z.object({
|
|||||||
secret: z.string().min(1),
|
secret: z.string().min(1),
|
||||||
expiresIn: z.string(),
|
expiresIn: z.string(),
|
||||||
viewLimit: z.string(),
|
viewLimit: z.string(),
|
||||||
|
shouldLimitView: z.boolean(),
|
||||||
accessType: z.nativeEnum(SecretSharingAccessType).optional(),
|
accessType: z.nativeEnum(SecretSharingAccessType).optional(),
|
||||||
emails: z
|
emails: z
|
||||||
.string()
|
.string()
|
||||||
@@ -96,25 +97,25 @@ export const ShareSecretForm = ({
|
|||||||
? expiresInOptions.filter((v) => v.value / 1000 <= maxSharedSecretLifetime)
|
? expiresInOptions.filter((v) => v.value / 1000 <= maxSharedSecretLifetime)
|
||||||
: expiresInOptions;
|
: expiresInOptions;
|
||||||
|
|
||||||
const filteredViewLimitOptions = maxSharedSecretViewLimit
|
|
||||||
? viewLimitOptions.filter((v) => v.value > 0 && v.value <= maxSharedSecretViewLimit)
|
|
||||||
: viewLimitOptions;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
reset,
|
reset,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting },
|
||||||
|
watch
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
secret: value || "",
|
secret: value || "",
|
||||||
viewLimit: filteredViewLimitOptions[filteredViewLimitOptions.length - 1].value.toString(),
|
viewLimit: maxSharedSecretViewLimit?.toString() ?? "1",
|
||||||
|
shouldLimitView: Boolean(maxSharedSecretViewLimit),
|
||||||
expiresIn:
|
expiresIn:
|
||||||
filteredExpiresInOptions[Math.min(filteredExpiresInOptions.length - 1, 2)].value.toString()
|
filteredExpiresInOptions[Math.min(filteredExpiresInOptions.length - 1, 2)].value.toString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isLimitingView = watch("shouldLimitView");
|
||||||
|
|
||||||
const onFormSubmit = async ({
|
const onFormSubmit = async ({
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
@@ -122,7 +123,8 @@ export const ShareSecretForm = ({
|
|||||||
expiresIn,
|
expiresIn,
|
||||||
viewLimit,
|
viewLimit,
|
||||||
accessType,
|
accessType,
|
||||||
emails
|
emails,
|
||||||
|
shouldLimitView
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
const expiresAt = new Date(new Date().getTime() + Number(expiresIn));
|
const expiresAt = new Date(new Date().getTime() + Number(expiresIn));
|
||||||
@@ -134,7 +136,7 @@ export const ShareSecretForm = ({
|
|||||||
password,
|
password,
|
||||||
secretValue: secret,
|
secretValue: secret,
|
||||||
expiresAt,
|
expiresAt,
|
||||||
expiresAfterViews: viewLimit === "-1" ? undefined : Number(viewLimit),
|
expiresAfterViews: shouldLimitView ? Number(viewLimit) : undefined,
|
||||||
accessType,
|
accessType,
|
||||||
emails: processedEmails
|
emails: processedEmails
|
||||||
});
|
});
|
||||||
@@ -320,44 +322,69 @@ export const ShareSecretForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<div className="flex w-full items-end gap-2">
|
||||||
control={control}
|
{maxSharedSecretViewLimit === null && (
|
||||||
name="viewLimit"
|
<Controller
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
control={control}
|
||||||
<FormControl
|
name="shouldLimitView"
|
||||||
label="Max Views"
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
errorText={error?.message}
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Max Views"
|
||||||
helperText={
|
errorText={error?.message}
|
||||||
viewLimitOptions.length !== filteredViewLimitOptions.length ? (
|
isError={Boolean(error)}
|
||||||
<span className="text-yellow-500">
|
className="flex-1"
|
||||||
Limited to{" "}
|
>
|
||||||
{filteredViewLimitOptions[filteredViewLimitOptions.length - 1].label} by
|
<Select
|
||||||
organization
|
defaultValue={field.value.toString()}
|
||||||
</span>
|
onValueChange={(e) => onChange(e === "true")}
|
||||||
) : undefined
|
className="w-full"
|
||||||
}
|
position="popper"
|
||||||
>
|
{...field}
|
||||||
<Select
|
value={field.value.toString()}
|
||||||
defaultValue={field.value}
|
dropdownContainerClassName="max-w-none"
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{viewLimitOptions.map(({ label, value: viewLimitValue }) => (
|
|
||||||
<SelectItem
|
|
||||||
value={String(viewLimitValue || "")}
|
|
||||||
key={label}
|
|
||||||
isDisabled={!filteredViewLimitOptions.some((v) => v.label === label)}
|
|
||||||
>
|
>
|
||||||
{label}
|
{viewLimitOptions.map(({ label, value: viewLimitValue }) => (
|
||||||
</SelectItem>
|
<SelectItem
|
||||||
))}
|
value={viewLimitValue.toString()}
|
||||||
</Select>
|
key={viewLimitValue.toString()}
|
||||||
</FormControl>
|
>
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
{isLimitingView && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="viewLimit"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label={maxSharedSecretViewLimit ? "Max Views" : undefined}
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
className="flex-1"
|
||||||
|
helperText={
|
||||||
|
maxSharedSecretViewLimit
|
||||||
|
? `Limited to ${maxSharedSecretViewLimit} by organization.`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
onChange={onChange}
|
||||||
|
{...field}
|
||||||
|
min={1}
|
||||||
|
max={maxSharedSecretViewLimit ?? 1000}
|
||||||
|
type="number"
|
||||||
|
className="h-[37px]"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{!isPublic && (
|
{!isPublic && (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|||||||
Reference in New Issue
Block a user