mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-12 15:30:53 +02:00
Merge branch 'aig' of github.com:fosrl/pangolin into aig
This commit is contained in:
@@ -855,12 +855,16 @@
|
||||
"authMethodsSave": "Save Settings",
|
||||
"policyAuthStackTitle": "Authentication",
|
||||
"policyAuthStackDescription": "Control which authentication methods are required to access this resource",
|
||||
"policyAuthInferenceStackDescription": "Choose which users and roles can authenticate to this AI gateway",
|
||||
"policyAuthOrLogicTitle": "Multiple authentication methods active",
|
||||
"policyAuthOrLogicBanner": "Visitors may authenticate using any one of the active methods below. They do not need to complete all of them.",
|
||||
"policyAuthMethodActive": "Active",
|
||||
"policyAuthMethodOff": "Off",
|
||||
"policyAuthSsoTitle": "Platform SSO",
|
||||
"policyAuthSsoDescription": "Require sign-in through your organization's identity provider",
|
||||
"policyAuthInferenceSsoDescription": "Selected users and roles can authenticate to the gateway using their identity API key",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users can retrieve their key by signing in with their identity provider at <resourceLink></resourceLink>, where it will be shown after login.",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users can retrieve their key by signing in with their identity provider at this resource's URL, where it will be shown after login.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} users, {roles} roles",
|
||||
"policyAuthSsoDefaultIdp": "Default provider",
|
||||
"policyAuthAddDefaultIdentityProvider": "Add Default Identity Provider",
|
||||
|
||||
@@ -372,30 +372,6 @@ export default function VirtualApiKeysTable({
|
||||
},
|
||||
cell: ({ row }) => moment(row.original.createdAt).format("lll")
|
||||
},
|
||||
{
|
||||
accessorKey: "expiresAt",
|
||||
friendlyName: t("expires"),
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
{t("expires")}
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const expiresAt = row.original.expiresAt;
|
||||
if (expiresAt) {
|
||||
return moment(expiresAt).format("lll");
|
||||
}
|
||||
return t("never");
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
enableHiding: false,
|
||||
|
||||
@@ -29,6 +29,9 @@ export type PolicyAuthSsoSectionProps = {
|
||||
usersEditor: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
idpDisabled?: boolean;
|
||||
ssoLocked?: boolean;
|
||||
title?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export function PolicyAuthSsoSection({
|
||||
@@ -40,7 +43,10 @@ export function PolicyAuthSsoSection({
|
||||
rolesEditor,
|
||||
usersEditor,
|
||||
disabled,
|
||||
idpDisabled
|
||||
idpDisabled,
|
||||
ssoLocked,
|
||||
title,
|
||||
description
|
||||
}: PolicyAuthSsoSectionProps) {
|
||||
const t = useTranslations();
|
||||
const [showIdpSelect, setShowIdpSelect] = useState(skipToIdpId != null);
|
||||
@@ -52,22 +58,34 @@ export function PolicyAuthSsoSection({
|
||||
}, [skipToIdpId]);
|
||||
|
||||
const idpSelectDisabled = idpDisabled ?? disabled;
|
||||
const ssoActive = ssoLocked || sso;
|
||||
const ssoTitle = title ?? t("policyAuthSsoTitle");
|
||||
const ssoDescription = description ?? t("policyAuthSsoDescription");
|
||||
|
||||
return (
|
||||
<SettingsFormGrid>
|
||||
<SettingsFormCell span="full">
|
||||
<SwitchInput
|
||||
id="policy-auth-sso"
|
||||
label={t("policyAuthSsoTitle")}
|
||||
description={t("policyAuthSsoDescription")}
|
||||
checked={sso}
|
||||
disabled={disabled}
|
||||
onCheckedChange={onSsoChange}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
{!ssoLocked && (
|
||||
<SettingsFormCell span="full">
|
||||
<SwitchInput
|
||||
id="policy-auth-sso"
|
||||
label={ssoTitle}
|
||||
description={ssoDescription}
|
||||
checked={ssoActive}
|
||||
disabled={disabled}
|
||||
onCheckedChange={onSsoChange}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
)}
|
||||
|
||||
{sso && (
|
||||
{ssoActive && (
|
||||
<>
|
||||
{ssoLocked && ssoDescription && (
|
||||
<SettingsFormCell span="full">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{ssoDescription}
|
||||
</p>
|
||||
</SettingsFormCell>
|
||||
)}
|
||||
<SettingsFormCell span="full">
|
||||
<FormItem>
|
||||
<FormLabel>{t("roles")}</FormLabel>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "@app/components/roles-selector";
|
||||
import { UsersSelector } from "@app/components/users-selector";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { Alert, AlertDescription } from "@app/components/ui/alert";
|
||||
import { Form, FormField } from "@app/components/ui/form";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
@@ -28,8 +29,10 @@ import type { GetResourcePolicyResponse } from "@server/routers/policy";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { ExternalLink, InfoIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { toUnicode } from "punycode";
|
||||
import {
|
||||
useActionState,
|
||||
useContext,
|
||||
@@ -108,9 +111,21 @@ export function PolicyAuthStackSectionEdit({
|
||||
const resourceContext = useContext(ResourceContext);
|
||||
const api = createApiClient(useEnvContext());
|
||||
|
||||
const isInferenceResource = resourceContext?.resource.mode === "inference";
|
||||
const isResourceOverlay = resourceId !== undefined;
|
||||
const authReadonly = readonly || isResourceOverlay;
|
||||
|
||||
const inferenceResourceUrl = useMemo(() => {
|
||||
if (!isInferenceResource || !resourceContext?.resource) {
|
||||
return null;
|
||||
}
|
||||
const { ssl, fullDomain } = resourceContext.resource;
|
||||
if (!fullDomain) {
|
||||
return null;
|
||||
}
|
||||
return `${ssl ? "https" : "http"}://${toUnicode(fullDomain)}`;
|
||||
}, [isInferenceResource, resourceContext?.resource]);
|
||||
|
||||
const policyRoleItems = useMemo<OverlaySelectedRole[]>(
|
||||
() =>
|
||||
policy.roles.map((r) => ({
|
||||
@@ -264,6 +279,12 @@ export function PolicyAuthStackSectionEdit({
|
||||
const overlayRoles = combinedRoles.filter((r) => !r.isAdmin);
|
||||
const overlayUsers = combinedUsers;
|
||||
|
||||
useEffect(() => {
|
||||
if (isInferenceResource && !form.getValues("sso")) {
|
||||
form.setValue("sso", true);
|
||||
}
|
||||
}, [isInferenceResource, form]);
|
||||
|
||||
const [, formAction, isSubmitting] = useActionState(onSubmit, null);
|
||||
const [isSavingOverlay, setIsSavingOverlay] = useState(false);
|
||||
|
||||
@@ -294,7 +315,7 @@ export function PolicyAuthStackSectionEdit({
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/access-control`,
|
||||
{
|
||||
sso: payload.sso,
|
||||
sso: isInferenceResource ? true : payload.sso,
|
||||
userIds: payload.users.map((user) => user.id),
|
||||
roleIds: payload.roles.map((role) => Number(role.id)),
|
||||
skipToIdpId: payload.skipToIdpId
|
||||
@@ -302,94 +323,101 @@ export function PolicyAuthStackSectionEdit({
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.sso = isInferenceResource ? true : payload.sso;
|
||||
|
||||
if (!isInferenceResource) {
|
||||
if (passcodeActive && payload.password?.password) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/password`,
|
||||
{ password: payload.password.password }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.passwordId = policy.passwordId ?? -1;
|
||||
} else if (!passcodeActive && passcodeOnServerRef.current) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/password`,
|
||||
{ password: null }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.passwordId = null;
|
||||
}
|
||||
|
||||
if (pinActive && payload.pincode?.pincode?.length === 6) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/pincode`,
|
||||
{ pincode: payload.pincode.pincode }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.pincodeId = policy.pincodeId ?? -1;
|
||||
} else if (!pinActive && pincodeOnServerRef.current) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/pincode`,
|
||||
{ pincode: null }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.pincodeId = null;
|
||||
}
|
||||
|
||||
if (
|
||||
headerAuthActive &&
|
||||
payload.headerAuth?.user &&
|
||||
payload.headerAuth?.password
|
||||
) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/header-auth`,
|
||||
{ headerAuth: payload.headerAuth }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.headerAuth = {
|
||||
id: policy.headerAuth?.id ?? -1,
|
||||
extendedCompability:
|
||||
payload.headerAuth.extendedCompatibility ?? true
|
||||
};
|
||||
} else if (!headerAuthActive && headerAuthOnServerRef.current) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/header-auth`,
|
||||
{ headerAuth: null }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.headerAuth = {
|
||||
id: null,
|
||||
extendedCompability: null
|
||||
} as unknown as GetResourcePolicyResponse["headerAuth"];
|
||||
}
|
||||
|
||||
if (passcodeActive && payload.password?.password) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/password`,
|
||||
{ password: payload.password.password }
|
||||
`/resource-policy/${policy.resourcePolicyId}/whitelist`,
|
||||
{
|
||||
emailWhitelistEnabled:
|
||||
payload.emailWhitelistEnabled,
|
||||
emails: payload.emails?.map((e) => e.text) ?? []
|
||||
}
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.passwordId = policy.passwordId ?? -1;
|
||||
} else if (!passcodeActive && passcodeOnServerRef.current) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/password`,
|
||||
{ password: null }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.passwordId = null;
|
||||
policyUpdates.emailWhitelistEnabled = payload.emailWhitelistEnabled;
|
||||
}
|
||||
|
||||
if (pinActive && payload.pincode?.pincode?.length === 6) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/pincode`,
|
||||
{ pincode: payload.pincode.pincode }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.pincodeId = policy.pincodeId ?? -1;
|
||||
} else if (!pinActive && pincodeOnServerRef.current) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/pincode`,
|
||||
{ pincode: null }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.pincodeId = null;
|
||||
}
|
||||
|
||||
if (
|
||||
headerAuthActive &&
|
||||
payload.headerAuth?.user &&
|
||||
payload.headerAuth?.password
|
||||
) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/header-auth`,
|
||||
{ headerAuth: payload.headerAuth }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.headerAuth = {
|
||||
id: policy.headerAuth?.id ?? -1,
|
||||
extendedCompability:
|
||||
payload.headerAuth.extendedCompatibility ?? true
|
||||
};
|
||||
} else if (!headerAuthActive && headerAuthOnServerRef.current) {
|
||||
requests.push(
|
||||
api
|
||||
.put(
|
||||
`/resource-policy/${policy.resourcePolicyId}/header-auth`,
|
||||
{ headerAuth: null }
|
||||
)
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.headerAuth = {
|
||||
id: null,
|
||||
extendedCompability: null
|
||||
} as unknown as GetResourcePolicyResponse["headerAuth"];
|
||||
}
|
||||
|
||||
requests.push(
|
||||
api
|
||||
.put(`/resource-policy/${policy.resourcePolicyId}/whitelist`, {
|
||||
emailWhitelistEnabled: payload.emailWhitelistEnabled,
|
||||
emails: payload.emails?.map((e) => e.text) ?? []
|
||||
})
|
||||
.catch(handleError)
|
||||
);
|
||||
policyUpdates.emailWhitelistEnabled = payload.emailWhitelistEnabled;
|
||||
|
||||
try {
|
||||
const results = await Promise.all(requests);
|
||||
if (results.every((res) => res && res.status === 200)) {
|
||||
@@ -411,13 +439,17 @@ export function PolicyAuthStackSectionEdit({
|
||||
|
||||
updatePolicy(policyUpdates);
|
||||
|
||||
resourceContext?.updateAuthInfo({
|
||||
sso: payload.sso,
|
||||
whitelist: payload.emailWhitelistEnabled,
|
||||
password: passcodeOnServerRef.current,
|
||||
pincode: pincodeOnServerRef.current,
|
||||
headerAuth: headerAuthOnServerRef.current
|
||||
});
|
||||
resourceContext?.updateAuthInfo(
|
||||
isInferenceResource
|
||||
? { sso: true }
|
||||
: {
|
||||
sso: payload.sso,
|
||||
whitelist: payload.emailWhitelistEnabled,
|
||||
password: passcodeOnServerRef.current,
|
||||
pincode: pincodeOnServerRef.current,
|
||||
headerAuth: headerAuthOnServerRef.current
|
||||
}
|
||||
);
|
||||
|
||||
toast({
|
||||
title: t("success"),
|
||||
@@ -514,19 +546,53 @@ export function PolicyAuthStackSectionEdit({
|
||||
{t("policyAuthStackTitle")}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{t("policyAuthStackDescription")}
|
||||
{isInferenceResource
|
||||
? t("policyAuthInferenceStackDescription")
|
||||
: t("policyAuthStackDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
{isResourceOverlay && (
|
||||
<SharedPolicyResourceNotice section="authentication" />
|
||||
)}
|
||||
{isInferenceResource && (
|
||||
<Alert variant="neutral">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
{inferenceResourceUrl
|
||||
? t.rich(
|
||||
"policyAuthInferenceIdentityKeyHelp",
|
||||
{
|
||||
resourceLink: () => (
|
||||
<a
|
||||
href={
|
||||
inferenceResourceUrl
|
||||
}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
{inferenceResourceUrl}
|
||||
<ExternalLink className="ml-1 inline size-3.5 shrink-0 align-text-bottom" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
)
|
||||
: t(
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl"
|
||||
)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<SettingsSectionForm variant="half">
|
||||
<PolicyAuthSsoSection
|
||||
sso={Boolean(sso)}
|
||||
onSsoChange={(active) =>
|
||||
form.setValue("sso", active)
|
||||
}
|
||||
sso={Boolean(sso) || isInferenceResource}
|
||||
onSsoChange={(active) => {
|
||||
if (isInferenceResource) {
|
||||
return;
|
||||
}
|
||||
form.setValue("sso", active);
|
||||
}}
|
||||
skipToIdpId={skipToIdpId}
|
||||
onSkipToIdpChange={(id) =>
|
||||
form.setValue("skipToIdpId", id)
|
||||
@@ -534,6 +600,12 @@ export function PolicyAuthStackSectionEdit({
|
||||
allIdps={allIdps}
|
||||
disabled={authReadonly}
|
||||
idpDisabled={authReadonly}
|
||||
ssoLocked={isInferenceResource}
|
||||
description={
|
||||
isInferenceResource
|
||||
? t("policyAuthInferenceSsoDescription")
|
||||
: undefined
|
||||
}
|
||||
rolesEditor={
|
||||
isResourceOverlay ? (
|
||||
<RolesSelector
|
||||
@@ -605,100 +677,132 @@ export function PolicyAuthStackSectionEdit({
|
||||
}
|
||||
/>
|
||||
|
||||
<PolicyAuthOtherMethodsSection
|
||||
pinActive={pinActive}
|
||||
passcodeActive={passcodeActive}
|
||||
emailWhitelistEnabled={Boolean(
|
||||
emailWhitelistEnabled
|
||||
)}
|
||||
headerAuthActive={headerAuthActive}
|
||||
headerAuthUser={headerAuth?.user ?? ""}
|
||||
emailCount={emails.length}
|
||||
emailEnabled={emailEnabled}
|
||||
disabled={authReadonly}
|
||||
onConfigure={openMethodEditor}
|
||||
onTogglePincode={(active) =>
|
||||
handleToggle("pincode", active, () => {
|
||||
setPinActive(false);
|
||||
form.setValue("pincode", null);
|
||||
})
|
||||
}
|
||||
onTogglePasscode={(active) =>
|
||||
handleToggle("passcode", active, () => {
|
||||
setPasscodeActive(false);
|
||||
form.setValue("password", null);
|
||||
})
|
||||
}
|
||||
onToggleEmail={(active) =>
|
||||
handleToggle("email", active, () =>
|
||||
form.setValue(
|
||||
"emailWhitelistEnabled",
|
||||
false
|
||||
{!isInferenceResource && (
|
||||
<PolicyAuthOtherMethodsSection
|
||||
pinActive={pinActive}
|
||||
passcodeActive={passcodeActive}
|
||||
emailWhitelistEnabled={Boolean(
|
||||
emailWhitelistEnabled
|
||||
)}
|
||||
headerAuthActive={headerAuthActive}
|
||||
headerAuthUser={headerAuth?.user ?? ""}
|
||||
emailCount={emails.length}
|
||||
emailEnabled={emailEnabled}
|
||||
disabled={authReadonly}
|
||||
onConfigure={openMethodEditor}
|
||||
onTogglePincode={(active) =>
|
||||
handleToggle("pincode", active, () => {
|
||||
setPinActive(false);
|
||||
form.setValue("pincode", null);
|
||||
})
|
||||
}
|
||||
onTogglePasscode={(active) =>
|
||||
handleToggle("passcode", active, () => {
|
||||
setPasscodeActive(false);
|
||||
form.setValue("password", null);
|
||||
})
|
||||
}
|
||||
onToggleEmail={(active) =>
|
||||
handleToggle("email", active, () =>
|
||||
form.setValue(
|
||||
"emailWhitelistEnabled",
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
onToggleHeaderAuth={(active) =>
|
||||
handleToggle("headerAuth", active, () => {
|
||||
setHeaderAuthActive(false);
|
||||
form.setValue("headerAuth", null);
|
||||
})
|
||||
}
|
||||
/>
|
||||
}
|
||||
onToggleHeaderAuth={(active) =>
|
||||
handleToggle(
|
||||
"headerAuth",
|
||||
active,
|
||||
() => {
|
||||
setHeaderAuthActive(false);
|
||||
form.setValue(
|
||||
"headerAuth",
|
||||
null
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</SettingsSectionForm>
|
||||
|
||||
<PincodeCredenza
|
||||
open={editingMethod === "pincode"}
|
||||
onOpenChange={(open) => !open && closeCredenza()}
|
||||
defaultPincode={pincode?.pincode ?? ""}
|
||||
onSave={(value) => {
|
||||
form.setValue("pincode", { pincode: value });
|
||||
setPinActive(true);
|
||||
}}
|
||||
/>
|
||||
{!isInferenceResource && (
|
||||
<>
|
||||
<PincodeCredenza
|
||||
open={editingMethod === "pincode"}
|
||||
onOpenChange={(open) =>
|
||||
!open && closeCredenza()
|
||||
}
|
||||
defaultPincode={pincode?.pincode ?? ""}
|
||||
onSave={(value) => {
|
||||
form.setValue("pincode", {
|
||||
pincode: value
|
||||
});
|
||||
setPinActive(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<PasscodeCredenza
|
||||
open={editingMethod === "passcode"}
|
||||
onOpenChange={(open) => !open && closeCredenza()}
|
||||
defaultPassword={password?.password ?? ""}
|
||||
existingConfigured={Boolean(policy.passwordId)}
|
||||
onSave={(value) => {
|
||||
form.setValue("password", { password: value });
|
||||
setPasscodeActive(true);
|
||||
}}
|
||||
/>
|
||||
<PasscodeCredenza
|
||||
open={editingMethod === "passcode"}
|
||||
onOpenChange={(open) =>
|
||||
!open && closeCredenza()
|
||||
}
|
||||
defaultPassword={password?.password ?? ""}
|
||||
existingConfigured={Boolean(
|
||||
policy.passwordId
|
||||
)}
|
||||
onSave={(value) => {
|
||||
form.setValue("password", {
|
||||
password: value
|
||||
});
|
||||
setPasscodeActive(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<EmailCredenza
|
||||
open={editingMethod === "email"}
|
||||
onOpenChange={(open) => !open && closeCredenza()}
|
||||
emailEnabled={emailEnabled}
|
||||
disabled={authReadonly}
|
||||
emails={emails}
|
||||
onSave={(value) => {
|
||||
form.setValue("emails", value);
|
||||
form.setValue("emailWhitelistEnabled", true);
|
||||
}}
|
||||
/>
|
||||
<EmailCredenza
|
||||
open={editingMethod === "email"}
|
||||
onOpenChange={(open) =>
|
||||
!open && closeCredenza()
|
||||
}
|
||||
emailEnabled={emailEnabled}
|
||||
disabled={authReadonly}
|
||||
emails={emails}
|
||||
onSave={(value) => {
|
||||
form.setValue("emails", value);
|
||||
form.setValue(
|
||||
"emailWhitelistEnabled",
|
||||
true
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<HeaderAuthCredenza
|
||||
open={editingMethod === "headerAuth"}
|
||||
onOpenChange={(open) => !open && closeCredenza()}
|
||||
defaultValues={
|
||||
headerAuth
|
||||
? {
|
||||
user: headerAuth.user,
|
||||
password: headerAuth.password,
|
||||
extendedCompatibility:
|
||||
headerAuth.extendedCompatibility ??
|
||||
true
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
existingConfigured={Boolean(policy.headerAuth?.id)}
|
||||
onSave={(value) => {
|
||||
form.setValue("headerAuth", value);
|
||||
setHeaderAuthActive(true);
|
||||
}}
|
||||
/>
|
||||
<HeaderAuthCredenza
|
||||
open={editingMethod === "headerAuth"}
|
||||
onOpenChange={(open) =>
|
||||
!open && closeCredenza()
|
||||
}
|
||||
defaultValues={
|
||||
headerAuth
|
||||
? {
|
||||
user: headerAuth.user,
|
||||
password: headerAuth.password,
|
||||
extendedCompatibility:
|
||||
headerAuth.extendedCompatibility ??
|
||||
true
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
existingConfigured={Boolean(
|
||||
policy.headerAuth?.id
|
||||
)}
|
||||
onSave={(value) => {
|
||||
form.setValue("headerAuth", value);
|
||||
setHeaderAuthActive(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user