add auth enabling hints

This commit is contained in:
dswbx
2025-02-26 18:44:16 +01:00
parent 2a9c1be151
commit 9a683c8e35
6 changed files with 70 additions and 22 deletions

View File

@@ -1,8 +1,9 @@
import type { AppAuthSchema } from "auth/auth-schema";
import { useBknd } from "ui/client/bknd";
import { routes } from "ui/lib/routes";
export function useBkndAuth() {
const { config, schema, actions: bkndActions } = useBknd();
const { config, schema, actions: bkndActions, app } = useBknd();
const actions = {
config: {
@@ -33,7 +34,29 @@ export function useBkndAuth() {
}
}
};
const $auth = {};
const minimum_permissions = [
"system.access.admin",
"system.access.api",
"system.config.read",
"system.config.read.secrets",
"system.build"
];
const $auth = {
roles: {
none: Object.keys(config.auth.roles ?? {}).length === 0,
minimum_permissions,
has_admin: Object.entries(config.auth.roles ?? {}).some(
([name, role]) =>
role.implicit_allow ||
minimum_permissions.every((p) => role.permissions?.includes(p))
)
},
routes: {
settings: app.getSettingsPath(["auth"]),
listUsers: app.getAbsolutePath("/data/" + routes.data.entity.list(config.auth.entity_name))
}
};
return {
$auth,

View File

@@ -209,7 +209,8 @@ export const Switch = forwardRef<
<RadixSwitch.Root
className={clsx(
"relative cursor-pointer rounded-full bg-muted border-2 border-transparent outline-none ring-1 dark:ring-primary/10 ring-primary/20 data-[state=checked]:ring-primary/60 data-[state=checked]:bg-primary/60 appearance-none transition-colors hover:bg-muted/80",
SwitchSizes[props.size ?? "md"].root
SwitchSizes[props.size ?? "md"].root,
props.disabled && "opacity-50 !cursor-not-allowed"
)}
onCheckedChange={(bool) => {
console.log("setting", bool);

View File

@@ -12,6 +12,7 @@ import { coerce, isType, isTypeSchema } from "./utils";
export type FieldProps = {
onChange?: (e: ChangeEvent<any>) => void;
placeholder?: string;
disabled?: boolean;
} & Omit<FieldwrapperProps, "children" | "schema">;
export const Field = (props: FieldProps) => {
@@ -49,7 +50,7 @@ const FieldImpl = ({ name, onChange, placeholder, required: _required, ...props
return <ArrayField path={name} />;
}
const disabled = schema.readOnly ?? "const" in schema ?? false;
const disabled = props.disabled ?? schema.readOnly ?? "const" in schema ?? false;
const handleChange = useEvent((e: ChangeEvent<HTMLInputElement>) => {
const value = coerce(e.target.value, schema as any, { required });

View File

@@ -1,23 +1,23 @@
import { IconFingerprint } from "@tabler/icons-react";
import { TbSettings } from "react-icons/tb";
import { useBknd } from "ui/client/bknd";
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
import { IconButton } from "ui/components/buttons/IconButton";
import { Empty } from "ui/components/display/Empty";
import { Icon } from "ui/components/display/Icon";
import { Link } from "ui/components/wouter/Link";
import { useBrowserTitle } from "ui/hooks/use-browser-title";
import * as AppShell from "ui/layouts/AppShell/AppShell";
import { routes, useNavigate } from "ui/lib/routes";
import { routes } from "ui/lib/routes";
export function AuthRoot({ children }) {
const { app, config } = useBknd();
const users_entity = config.auth.entity_name;
const { config, $auth } = useBkndAuth();
return (
<>
<AppShell.Sidebar>
<AppShell.SectionHeader
right={
<Link href={app.getSettingsPath(["auth"])}>
<Link href={$auth.routes.settings}>
<IconButton Icon={TbSettings} />
</Link>
}
@@ -32,22 +32,42 @@ export function AuthRoot({ children }) {
</AppShell.SidebarLink>
<AppShell.SidebarLink
as={Link}
href={app.getAbsolutePath("/data/" + routes.data.entity.list(users_entity))}
disabled={!config.auth.enabled}
href={$auth.routes.listUsers}
disabled={!config.enabled}
className="justify-between"
>
Users
{!config.enabled && <AuthWarning title="Auth is not enabled." />}
</AppShell.SidebarLink>
<AppShell.SidebarLink
as={Link}
href={routes.auth.roles.list()}
disabled={!config.auth.enabled}
disabled={!config.enabled}
className="justify-between"
>
Roles & Permissions
{!config.enabled ? (
<AuthWarning title="Auth is not enabled." />
) : $auth.roles.none ? (
<AuthWarning title="No roles defined." />
) : !$auth.roles.has_admin ? (
<AuthWarning title="No admin role defined." />
) : null}
</AppShell.SidebarLink>
<AppShell.SidebarLink as={Link} href={routes.auth.strategies()}>
<AppShell.SidebarLink
as={Link}
href={routes.auth.strategies()}
disabled={!config.enabled}
className="justify-between"
>
Strategies
{!config.enabled && <AuthWarning title="Auth is not enabled." />}
</AppShell.SidebarLink>
<AppShell.SidebarLink as={Link} href={routes.auth.settings()}>
<AppShell.SidebarLink
as={Link}
href={routes.auth.settings()}
className="justify-between"
>
Settings
</AppShell.SidebarLink>
</nav>
@@ -59,6 +79,10 @@ export function AuthRoot({ children }) {
);
}
const AuthWarning = ({ title }) => (
<Icon.Warning title={title} className="size-5 pointer-events-auto" />
);
export function AuthEmpty() {
useBrowserTitle(["Auth"]);
return <Empty Icon={IconFingerprint} title="Not implemented yet" />;

View File

@@ -21,7 +21,7 @@ export function AuthIndex() {
const usersLink = app.getAbsolutePath("/data/" + routes.data.entity.list(users_entity));
const rolesLink = routes.auth.roles.list();
const strategiesLink = app.getSettingsPath(["auth", "strategies"]);
const strategiesLink = routes.auth.strategies();
return (
<>
@@ -50,7 +50,7 @@ export function AuthIndex() {
value={!enabled ? 0 : rolesTotal}
actions={[
{ label: "View all", href: rolesLink },
{ label: "Add new", variant: "default", href: rolesLink }
{ label: "Manage", variant: "default", href: rolesLink }
]}
/>
<KpiCard
@@ -58,7 +58,7 @@ export function AuthIndex() {
value={!enabled ? 0 : strategiesTotal}
actions={[
{ label: "View all", href: strategiesLink },
{ label: "Add new", variant: "default", href: strategiesLink }
{ label: "Manage", variant: "default", href: strategiesLink }
]}
/>
</div>

View File

@@ -15,7 +15,6 @@ import {
} from "ui/components/form/json-schema-form";
import { useBrowserTitle } from "ui/hooks/use-browser-title";
import * as AppShell from "ui/layouts/AppShell/AppShell";
import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2";
import { create } from "zustand";
import { combine } from "zustand/middleware";
@@ -52,9 +51,8 @@ const formConfig = {
};
function AuthSettingsInternal() {
const { config, schema: _schema, actions } = useBkndAuth();
const { config, schema: _schema, actions, $auth } = useBkndAuth();
const schema = JSON.parse(JSON.stringify(_schema));
const hasRoles = Object.keys(config.roles ?? {}).length > 0;
schema.properties.jwt.required = ["alg"];
@@ -105,11 +103,12 @@ function AuthSettingsInternal() {
label={
<div className="flex flex-row gap-2 items-center">
<span>Guard Enabled</span>
{!hasRoles && (
<Icon.Warning title="No roles defined. Enabling the guard will block all requests." />
{!$auth.roles.has_admin && (
<Icon.Warning title="No admin roles defined. Enabling the guard will likely block all requests." />
)}
</div>
}
disabled={$auth.roles.none}
description="When enabled, enforces permissions on all routes. Make sure to create roles first."
descriptionPlacement="top"
/>