mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
🚧 add device approval in the roles page
This commit is contained in:
@@ -1548,6 +1548,8 @@
|
||||
"IntervalSeconds": "Healthy Interval",
|
||||
"timeoutSeconds": "Timeout (sec)",
|
||||
"timeIsInSeconds": "Time is in seconds",
|
||||
"requireDeviceApproval": "Require Device Approvals",
|
||||
"requireDeviceApprovalDescription": "Users with this role need their devices approved by an admin before they can access resources",
|
||||
"retryAttempts": "Retry Attempts",
|
||||
"expectedResponseCodes": "Expected Response Codes",
|
||||
"expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.",
|
||||
|
||||
@@ -255,7 +255,9 @@ export const siteResources = sqliteTable("siteResources", {
|
||||
aliasAddress: text("aliasAddress"),
|
||||
tcpPortRangeString: text("tcpPortRangeString").notNull().default("*"),
|
||||
udpPortRangeString: text("udpPortRangeString").notNull().default("*"),
|
||||
disableIcmp: integer("disableIcmp", { mode: "boolean" }).notNull().default(false)
|
||||
disableIcmp: integer("disableIcmp", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false)
|
||||
});
|
||||
|
||||
export const clientSiteResources = sqliteTable("clientSiteResources", {
|
||||
@@ -796,7 +798,10 @@ export const idpOidcConfig = sqliteTable("idpOidcConfig", {
|
||||
identifierPath: text("identifierPath").notNull(),
|
||||
emailPath: text("emailPath"),
|
||||
namePath: text("namePath"),
|
||||
scopes: text("scopes").notNull()
|
||||
scopes: text("scopes").notNull(),
|
||||
approvalState: text("approvalState")
|
||||
.$type<"pending" | "approved" | "denied">()
|
||||
.default("approved")
|
||||
});
|
||||
|
||||
export const licenseKey = sqliteTable("licenseKey", {
|
||||
|
||||
@@ -36,7 +36,8 @@ async function queryRoles(orgId: string, limit: number, offset: number) {
|
||||
isAdmin: roles.isAdmin,
|
||||
name: roles.name,
|
||||
description: roles.description,
|
||||
orgName: orgs.name
|
||||
orgName: orgs.name,
|
||||
requireDeviceApproval: roles.requireDeviceApproval
|
||||
})
|
||||
.from(roles)
|
||||
.leftJoin(orgs, eq(roles.orgId, orgs.orgId))
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
@@ -27,11 +28,13 @@ import {
|
||||
CredenzaTitle
|
||||
} from "@app/components/Credenza";
|
||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||
import { CreateRoleBody, CreateRoleResponse } from "@server/routers/role";
|
||||
import type { CreateRoleBody, CreateRoleResponse } from "@server/routers/role";
|
||||
import { formatAxiosError } from "@app/lib/api";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { CheckboxWithLabel } from "./ui/checkbox";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
|
||||
type CreateRoleFormProps = {
|
||||
open: boolean;
|
||||
@@ -46,10 +49,12 @@ export default function CreateRoleForm({
|
||||
}: CreateRoleFormProps) {
|
||||
const { org } = useOrgContext();
|
||||
const t = useTranslations();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string({ message: t("nameRequired") }).max(32),
|
||||
description: z.string().max(255).optional()
|
||||
description: z.string().max(255).optional(),
|
||||
requireDeviceApproval: z.boolean().optional()
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -60,7 +65,8 @@ export default function CreateRoleForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: ""
|
||||
description: "",
|
||||
requireDeviceApproval: false
|
||||
}
|
||||
});
|
||||
|
||||
@@ -159,6 +165,36 @@ export default function CreateRoleForm({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{isPaidUser && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requireDeviceApproval"
|
||||
render={({ field }) => (
|
||||
<FormItem className="pt-3">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
value={undefined}
|
||||
defaultChecked={
|
||||
field.value
|
||||
}
|
||||
label={t(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t(
|
||||
"requireDeviceApprovalDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
</Form>
|
||||
</CredenzaBody>
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from "@app/components/ui/dropdown-menu";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { ArrowUpDown, Crown, MoreHorizontal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { RolesDataTable } from "@app/components/RolesDataTable";
|
||||
import { Role } from "@server/db";
|
||||
import CreateRoleForm from "@app/components/CreateRoleForm";
|
||||
import DeleteRoleForm from "@app/components/DeleteRoleForm";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { RolesDataTable } from "@app/components/RolesDataTable";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { Role } from "@server/db";
|
||||
import { ArrowUpDown } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Switch } from "./ui/switch";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
|
||||
export type RoleRow = Role;
|
||||
|
||||
@@ -41,6 +35,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
|
||||
const api = createApiClient(useEnvContext());
|
||||
|
||||
const { org } = useOrgContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
|
||||
const t = useTranslations();
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
@@ -86,6 +81,32 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
|
||||
friendlyName: t("description"),
|
||||
header: () => <span className="p-3">{t("description")}</span>
|
||||
},
|
||||
|
||||
...(isPaidUser
|
||||
? ([
|
||||
{
|
||||
accessorKey: "requireDeviceApproval",
|
||||
friendlyName: t("requireDeviceApproval"),
|
||||
header: () => (
|
||||
<span className="p-3">
|
||||
{t("requireDeviceApproval")}
|
||||
</span>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Switch
|
||||
defaultChecked={
|
||||
!!row.original.requireDeviceApproval
|
||||
}
|
||||
disabled={!!row.original.isAdmin}
|
||||
onCheckedChange={(val) => {
|
||||
// ...
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
] as ExtendedColumnDef<RoleRow>[])
|
||||
: []),
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
enableHiding: false,
|
||||
|
||||
Reference in New Issue
Block a user