Merge branch 'auth-providers' into dev

This commit is contained in:
miloschwartz
2025-04-23 22:08:37 -04:00
93 changed files with 5788 additions and 1608 deletions

View File

@@ -21,10 +21,8 @@ import {
} from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import CreateResourceForm from "./CreateResourceForm";
import { useState } from "react";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { set } from "zod";
import { formatAxiosError } from "@app/lib/api";
import { toast } from "@app/hooks/useToast";
import { createApiClient } from "@app/lib/api";
@@ -58,10 +56,8 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
const api = createApiClient(useEnvContext());
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [selectedResource, setSelectedResource] =
useState<ResourceRow | null>();
const [selectedResource, setSelectedResource] = useState<ResourceRow | null>();
const deleteResource = (resourceId: number) => {
api.delete(`/resource/${resourceId}`)
@@ -282,11 +278,6 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
return (
<>
<CreateResourceForm
open={isCreateModalOpen}
setOpen={setIsCreateModalOpen}
/>
{selectedResource && (
<ConfirmDeleteDialog
open={isDeleteModalOpen}
@@ -328,7 +319,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {
columns={columns}
data={resources}
createResource={() => {
setIsCreateModalOpen(true);
router.push(`/${orgId}/settings/resources/create`);
}}
/>
</>

View File

@@ -140,12 +140,6 @@ export default function SetResourcePasswordForm({
/>
</FormControl>
<FormMessage />
<FormDescription>
Users will be able to access
this resource by entering this
password. It must be at least 4
characters long.
</FormDescription>
</FormItem>
)}
/>

View File

@@ -147,33 +147,33 @@ export default function SetResourcePincodeForm({
<InputOTPGroup className="flex">
<InputOTPSlot
index={0}
obscured
/>
<InputOTPSlot
index={1}
obscured
/>
<InputOTPSlot
index={2}
obscured
/>
<InputOTPSlot
index={3}
obscured
/>
<InputOTPSlot
index={4}
obscured
/>
<InputOTPSlot
index={5}
obscured
/>
</InputOTPGroup>
</InputOTP>
</div>
</FormControl>
<FormMessage />
<FormDescription>
Users will be able to access
this resource by entering this
PIN code. It must be at least 6
digits long.
</FormDescription>
</FormItem>
)}
/>

View File

@@ -45,6 +45,9 @@ import { SwitchInput } from "@app/components/SwitchInput";
import { InfoPopup } from "@app/components/ui/info-popup";
import { Tag, TagInput } from "@app/components/tags/tag-input";
import { useRouter } from "next/navigation";
import { UserType } from "@server/types/UserTypes";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { InfoIcon } from "lucide-react";
const UsersRolesFormSchema = z.object({
roles: z.array(
@@ -175,7 +178,7 @@ export default function ResourceAuthenticationPage() {
setAllUsers(
usersResponse.data.data.users.map((user) => ({
id: user.id.toString(),
text: user.email
text: `${user.email || user.username}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}`
}))
);
@@ -183,7 +186,7 @@ export default function ResourceAuthenticationPage() {
"users",
resourceUsersResponse.data.data.users.map((i) => ({
id: i.userId.toString(),
text: i.email
text: `${i.email || i.username}${i.type !== UserType.Internal ? ` (${i.idpName})` : ""}`
}))
);
@@ -611,117 +614,127 @@ export default function ResourceAuthenticationPage() {
</SettingsSectionBody>
</SettingsSection>
{env.email.emailEnabled && (
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
One-time Passwords
</SettingsSectionTitle>
<SettingsSectionDescription>
Require email-based authentication for resource
access
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
<SwitchInput
id="whitelist-toggle"
label="Email Whitelist"
defaultChecked={resource.emailWhitelistEnabled}
onCheckedChange={setWhitelistEnabled}
/>
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
One-time Passwords
</SettingsSectionTitle>
<SettingsSectionDescription>
Require email-based authentication for resource
access
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
{!env.email.emailEnabled && (
<Alert variant="neutral" className="mb-4">
<InfoIcon className="h-4 w-4" />
<AlertTitle className="font-semibold">
SMTP Required
</AlertTitle>
<AlertDescription>
SMTP must be enabled on the server to use one-time password authentication.
</AlertDescription>
</Alert>
)}
<SwitchInput
id="whitelist-toggle"
label="Email Whitelist"
defaultChecked={resource.emailWhitelistEnabled}
onCheckedChange={setWhitelistEnabled}
disabled={!env.email.emailEnabled}
/>
{whitelistEnabled && (
<Form {...whitelistForm}>
<form id="whitelist-form">
<FormField
control={whitelistForm.control}
name="emails"
render={({ field }) => (
<FormItem>
<FormLabel>
<InfoPopup
text="Whitelisted Emails"
info="Only users with these email addresses will be able to access this resource. They will be prompted to enter a one-time password sent to their email. Wildcards (*@example.com) can be used to allow any email address from a domain."
/>
</FormLabel>
<FormControl>
{/* @ts-ignore */}
<TagInput
{...field}
activeTagIndex={
activeEmailTagIndex
}
size={"sm"}
validateTag={(
tag
) => {
return z
.string()
.email()
.or(
z
.string()
.regex(
/^\*@[\w.-]+\.[a-zA-Z]{2,}$/,
{
message:
"Invalid email address. Wildcard (*) must be the entire local part."
}
)
)
.safeParse(
tag
).success;
}}
setActiveTagIndex={
setActiveEmailTagIndex
}
placeholder="Enter an email"
tags={
whitelistForm.getValues()
.emails
}
setTags={(
newRoles
) => {
whitelistForm.setValue(
"emails",
newRoles as [
Tag,
...Tag[]
]
);
}}
allowDuplicates={
false
}
sortTags={true}
/>
</FormControl>
<FormDescription>
Press enter to add an
email after typing it in
the input field.
</FormDescription>
</FormItem>
)}
/>
</form>
</Form>
)}
</SettingsSectionBody>
<SettingsSectionFooter>
<Button
onClick={saveWhitelist}
form="whitelist-form"
loading={loadingSaveWhitelist}
disabled={loadingSaveWhitelist}
>
Save Whitelist
</Button>
</SettingsSectionFooter>
</SettingsSection>
)}
{whitelistEnabled && env.email.emailEnabled && (
<Form {...whitelistForm}>
<form id="whitelist-form">
<FormField
control={whitelistForm.control}
name="emails"
render={({ field }) => (
<FormItem>
<FormLabel>
<InfoPopup
text="Whitelisted Emails"
info="Only users with these email addresses will be able to access this resource. They will be prompted to enter a one-time password sent to their email. Wildcards (*@example.com) can be used to allow any email address from a domain."
/>
</FormLabel>
<FormControl>
{/* @ts-ignore */}
<TagInput
{...field}
activeTagIndex={
activeEmailTagIndex
}
size={"sm"}
validateTag={(
tag
) => {
return z
.string()
.email()
.or(
z
.string()
.regex(
/^\*@[\w.-]+\.[a-zA-Z]{2,}$/,
{
message:
"Invalid email address. Wildcard (*) must be the entire local part."
}
)
)
.safeParse(
tag
).success;
}}
setActiveTagIndex={
setActiveEmailTagIndex
}
placeholder="Enter an email"
tags={
whitelistForm.getValues()
.emails
}
setTags={(
newRoles
) => {
whitelistForm.setValue(
"emails",
newRoles as [
Tag,
...Tag[]
]
);
}}
allowDuplicates={
false
}
sortTags={true}
/>
</FormControl>
<FormDescription>
Press enter to add an
email after typing it in
the input field.
</FormDescription>
</FormItem>
)}
/>
</form>
</Form>
)}
</SettingsSectionBody>
<SettingsSectionFooter>
<Button
onClick={saveWhitelist}
form="whitelist-form"
loading={loadingSaveWhitelist}
disabled={loadingSaveWhitelist}
>
Save Whitelist
</Button>
</SettingsSectionFooter>
</SettingsSection>
</SettingsContainer>
</>
);