From 9a683c8e35e12089da62d2153a390c44f15f6647 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 26 Feb 2025 18:44:16 +0100 Subject: [PATCH] add auth enabling hints --- .../ui/client/schema/auth/use-bknd-auth.ts | 27 +++++++++++- .../ui/components/form/Formy/components.tsx | 3 +- .../form/json-schema-form/Field.tsx | 3 +- app/src/ui/routes/auth/_auth.root.tsx | 44 ++++++++++++++----- app/src/ui/routes/auth/auth.index.tsx | 6 +-- app/src/ui/routes/auth/auth.settings.tsx | 9 ++-- 6 files changed, 70 insertions(+), 22 deletions(-) diff --git a/app/src/ui/client/schema/auth/use-bknd-auth.ts b/app/src/ui/client/schema/auth/use-bknd-auth.ts index bd1f91a..b735b61 100644 --- a/app/src/ui/client/schema/auth/use-bknd-auth.ts +++ b/app/src/ui/client/schema/auth/use-bknd-auth.ts @@ -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, diff --git a/app/src/ui/components/form/Formy/components.tsx b/app/src/ui/components/form/Formy/components.tsx index b1cea07..1b15ff0 100644 --- a/app/src/ui/components/form/Formy/components.tsx +++ b/app/src/ui/components/form/Formy/components.tsx @@ -209,7 +209,8 @@ export const Switch = forwardRef< { console.log("setting", bool); diff --git a/app/src/ui/components/form/json-schema-form/Field.tsx b/app/src/ui/components/form/json-schema-form/Field.tsx index b9a74e9..7d87366 100644 --- a/app/src/ui/components/form/json-schema-form/Field.tsx +++ b/app/src/ui/components/form/json-schema-form/Field.tsx @@ -12,6 +12,7 @@ import { coerce, isType, isTypeSchema } from "./utils"; export type FieldProps = { onChange?: (e: ChangeEvent) => void; placeholder?: string; + disabled?: boolean; } & Omit; export const Field = (props: FieldProps) => { @@ -49,7 +50,7 @@ const FieldImpl = ({ name, onChange, placeholder, required: _required, ...props return ; } - const disabled = schema.readOnly ?? "const" in schema ?? false; + const disabled = props.disabled ?? schema.readOnly ?? "const" in schema ?? false; const handleChange = useEvent((e: ChangeEvent) => { const value = coerce(e.target.value, schema as any, { required }); diff --git a/app/src/ui/routes/auth/_auth.root.tsx b/app/src/ui/routes/auth/_auth.root.tsx index 4fa7693..9eddcdd 100644 --- a/app/src/ui/routes/auth/_auth.root.tsx +++ b/app/src/ui/routes/auth/_auth.root.tsx @@ -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 ( <> + } @@ -32,22 +32,42 @@ export function AuthRoot({ children }) { Users + {!config.enabled && } Roles & Permissions + {!config.enabled ? ( + + ) : $auth.roles.none ? ( + + ) : !$auth.roles.has_admin ? ( + + ) : null} - + Strategies + {!config.enabled && } - + Settings @@ -59,6 +79,10 @@ export function AuthRoot({ children }) { ); } +const AuthWarning = ({ title }) => ( + +); + export function AuthEmpty() { useBrowserTitle(["Auth"]); return ; diff --git a/app/src/ui/routes/auth/auth.index.tsx b/app/src/ui/routes/auth/auth.index.tsx index ea2f7f4..c1f0c34 100644 --- a/app/src/ui/routes/auth/auth.index.tsx +++ b/app/src/ui/routes/auth/auth.index.tsx @@ -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 } ]} /> diff --git a/app/src/ui/routes/auth/auth.settings.tsx b/app/src/ui/routes/auth/auth.settings.tsx index 1c64ded..fba8913 100644 --- a/app/src/ui/routes/auth/auth.settings.tsx +++ b/app/src/ui/routes/auth/auth.settings.tsx @@ -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={
Guard Enabled - {!hasRoles && ( - + {!$auth.roles.has_admin && ( + )}
} + disabled={$auth.roles.none} description="When enabled, enforces permissions on all routes. Make sure to create roles first." descriptionPlacement="top" />