Expose administration View surfaces

This commit is contained in:
2026-07-28 21:04:54 +02:00
parent bf1ecc54b3
commit 1b57df7753
3 changed files with 89 additions and 5 deletions

View File

@@ -42,6 +42,7 @@ from govoplan_core.core.modules import (
RoleTemplate, RoleTemplate,
) )
from govoplan_core.core.people import CAPABILITY_ACCESS_PEOPLE_SEARCH from govoplan_core.core.people import CAPABILITY_ACCESS_PEOPLE_SEARCH
from govoplan_core.core.views import ViewSurface
def _permission(scope: str, label: str, description: str, category: str, level: str) -> PermissionDefinition: def _permission(scope: str, label: str, description: str, category: str, level: str) -> PermissionDefinition:
@@ -254,6 +255,10 @@ ADMIN_READ_SCOPES = (
"access:account:read", "access:account:read",
"access:governance:read", "access:governance:read",
"access:function:read", "access:function:read",
"views:definition:read",
"views:assignment:read",
"views:system_definition:read",
"views:system_assignment:read",
) )
ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = (
@@ -676,6 +681,22 @@ manifest = ModuleManifest(
package_name="@govoplan/access-webui", package_name="@govoplan/access-webui",
routes=(FrontendRoute(path="/admin", component="AdminPage", required_any=ADMIN_READ_SCOPES, order=900),), routes=(FrontendRoute(path="/admin", component="AdminPage", required_any=ADMIN_READ_SCOPES, order=900),),
nav_items=(NavItem(path="/admin", label="Admin", icon="admin", required_any=ADMIN_READ_SCOPES, order=900),), nav_items=(NavItem(path="/admin", label="Admin", icon="admin", required_any=ADMIN_READ_SCOPES, order=900),),
view_surfaces=(
ViewSurface(id="access.admin.system-tenants", module_id="access", kind="section", label="System tenants", order=10),
ViewSurface(id="access.admin.system-roles", module_id="access", kind="section", label="System roles", order=20),
ViewSurface(id="access.admin.system-users", module_id="access", kind="section", label="System users", order=50),
ViewSurface(id="access.admin.system-credentials", module_id="access", kind="section", label="System credentials", order=80),
ViewSurface(id="access.admin.tenant-roles", module_id="access", kind="section", label="Tenant roles", order=10),
ViewSurface(id="access.admin.tenant-function-mappings", module_id="access", kind="section", label="Function mappings", order=20),
ViewSurface(id="access.admin.tenant-groups", module_id="access", kind="section", label="Tenant groups", order=30),
ViewSurface(id="access.admin.tenant-users", module_id="access", kind="section", label="Tenant users", order=40),
ViewSurface(id="access.admin.tenant-credentials", module_id="access", kind="section", label="Tenant credentials", order=70),
ViewSurface(id="access.admin.tenant-api-keys", module_id="access", kind="section", label="Tenant API keys", order=80),
ViewSurface(id="access.admin.tenant-settings", module_id="access", kind="section", label="Tenant settings", order=90),
ViewSurface(id="access.admin.group-credentials", module_id="access", kind="section", label="Group credentials", order=30),
ViewSurface(id="access.admin.user-credentials", module_id="access", kind="section", label="User credentials", order=30),
ViewSurface(id="access.settings.credentials", module_id="access", kind="section", label="Personal credentials", order=30),
),
), ),
capability_factories={ capability_factories={
CAPABILITY_AUTH_API_PRINCIPAL_PROVIDER: _api_principal_provider, CAPABILITY_AUTH_API_PRINCIPAL_PROVIDER: _api_principal_provider,

View File

@@ -26,7 +26,13 @@ import ApiKeysPanel from "./ApiKeysPanel";
import FileConnectorsPanel from "./FileConnectorsPanel"; import FileConnectorsPanel from "./FileConnectorsPanel";
import MailProfilesPanel from "./MailProfilesPanel"; import MailProfilesPanel from "./MailProfilesPanel";
import CredentialEnvelopesPanel from "./CredentialEnvelopesPanel"; import CredentialEnvelopesPanel from "./CredentialEnvelopesPanel";
import { usePlatformUiCapabilities, usePlatformUiCapability } from "@govoplan/core-webui"; import {
isViewSurfaceVisible,
useEffectiveView,
usePlatformUiCapabilities,
usePlatformUiCapability,
useViewSurfaces
} from "@govoplan/core-webui";
type AdminSection = string; type AdminSection = string;
type OrderedAdminNavItem = { id: AdminSection; label: string; order: number }; type OrderedAdminNavItem = { id: AdminSection; label: string; order: number };
@@ -62,6 +68,28 @@ const handledAdminSectionIds = new Set<string>([
"tenant-user-credentials" "tenant-user-credentials"
]); ]);
const builtInAdminSurfaceIds: Record<string, string> = {
"system-tenants": "access.admin.system-tenants",
"system-roles": "access.admin.system-roles",
"system-users": "access.admin.system-users",
"system-credentials": "access.admin.system-credentials",
"tenant-roles": "access.admin.tenant-roles",
"tenant-function-role-mappings": "access.admin.tenant-function-mappings",
"tenant-groups": "access.admin.tenant-groups",
"tenant-users": "access.admin.tenant-users",
"tenant-credentials": "access.admin.tenant-credentials",
"tenant-api-keys": "access.admin.tenant-api-keys",
"tenant-settings": "access.admin.tenant-settings",
"tenant-group-credentials": "access.admin.group-credentials",
"tenant-user-credentials": "access.admin.user-credentials",
"system-mail-servers": "mail.admin.system-servers",
"tenant-mail-servers": "mail.admin.tenant-servers",
"tenant-group-mail-servers": "mail.admin.group-servers",
"tenant-user-mail-servers": "mail.admin.user-servers",
"tenant-group-file-connectors": "files.admin.group-connectors",
"tenant-user-file-connectors": "files.admin.user-connectors"
};
export default function AdminPage({ export default function AdminPage({
settings, settings,
auth, auth,
@@ -75,14 +103,23 @@ export default function AdminPage({
const fileConnectorsUi = usePlatformUiCapability<FilesConnectorsUiCapability>("files.connectors"); const fileConnectorsUi = usePlatformUiCapability<FilesConnectorsUiCapability>("files.connectors");
const organizationFunctionPicker = usePlatformUiCapability<OrganizationFunctionPickerUiCapability>("organizations.functionPicker"); const organizationFunctionPicker = usePlatformUiCapability<OrganizationFunctionPickerUiCapability>("organizations.functionPicker");
const adminSectionCapabilities = usePlatformUiCapabilities<AdminSectionsUiCapability>("admin.sections"); const adminSectionCapabilities = usePlatformUiCapabilities<AdminSectionsUiCapability>("admin.sections");
const effectiveView = useEffectiveView();
const viewSurfaces = useViewSurfaces();
const mailProfilesAvailable = Boolean(mailProfilesUi); const mailProfilesAvailable = Boolean(mailProfilesUi);
const fileConnectorsAvailable = Boolean(fileConnectorsUi); const fileConnectorsAvailable = Boolean(fileConnectorsUi);
const contributedSections = useMemo( const contributedSections = useMemo(
() => () =>
adminSectionCapabilities adminSectionCapabilities
.flatMap((capability) => capability.sections) .flatMap((capability) => capability.sections)
.filter((section) =>
isViewSurfaceVisible(
effectiveView,
section.surfaceId,
viewSurfaces
)
)
.sort((left, right) => (left.order ?? 100) - (right.order ?? 100)), .sort((left, right) => (left.order ?? 100) - (right.order ?? 100)),
[adminSectionCapabilities] [adminSectionCapabilities, effectiveView, viewSurfaces]
); );
const contributionById = useMemo(() => { const contributionById = useMemo(() => {
const mapped = new Map<string, AdminSectionContribution>(); const mapped = new Map<string, AdminSectionContribution>();
@@ -126,8 +163,16 @@ export default function AdminPage({
if (hasScope(auth, "admin:users:read")) sections.add("tenant-user-credentials"); if (hasScope(auth, "admin:users:read")) sections.add("tenant-user-credentials");
if (hasScope(auth, "admin:groups:read")) sections.add("tenant-group-credentials"); if (hasScope(auth, "admin:groups:read")) sections.add("tenant-group-credentials");
} }
return sections; return new Set(
}, [auth, contributedSections, fileConnectorsAvailable, mailProfilesAvailable, organizationFunctionPicker]); [...sections].filter((sectionId) =>
isViewSurfaceVisible(
effectiveView,
builtInAdminSurfaceIds[sectionId],
viewSurfaces
)
)
);
}, [auth, contributedSections, effectiveView, fileConnectorsAvailable, mailProfilesAvailable, organizationFunctionPicker, viewSurfaces]);
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const requestedSection = searchParams.get("section") as AdminSection | null; const requestedSection = searchParams.get("section") as AdminSection | null;
const fallbackSection = available.has("overview") ? "overview" : (Array.from(available)[0] ?? "overview"); const fallbackSection = available.has("overview") ? "overview" : (Array.from(available)[0] ?? "overview");

View File

@@ -10,6 +10,23 @@ const translations = {
de: generatedTranslations.de de: generatedTranslations.de
}; };
const accessAdminSurfaces = [
{ id: "access.admin.system-tenants", moduleId: "access", kind: "section" as const, label: "System tenants", order: 10 },
{ id: "access.admin.system-roles", moduleId: "access", kind: "section" as const, label: "System roles", order: 20 },
{ id: "access.admin.system-users", moduleId: "access", kind: "section" as const, label: "System users", order: 50 },
{ id: "access.admin.system-credentials", moduleId: "access", kind: "section" as const, label: "System credentials", order: 80 },
{ id: "access.admin.tenant-roles", moduleId: "access", kind: "section" as const, label: "Tenant roles", order: 10 },
{ id: "access.admin.tenant-function-mappings", moduleId: "access", kind: "section" as const, label: "Function mappings", order: 20 },
{ id: "access.admin.tenant-groups", moduleId: "access", kind: "section" as const, label: "Tenant groups", order: 30 },
{ id: "access.admin.tenant-users", moduleId: "access", kind: "section" as const, label: "Tenant users", order: 40 },
{ id: "access.admin.tenant-credentials", moduleId: "access", kind: "section" as const, label: "Tenant credentials", order: 70 },
{ id: "access.admin.tenant-api-keys", moduleId: "access", kind: "section" as const, label: "Tenant API keys", order: 80 },
{ id: "access.admin.tenant-settings", moduleId: "access", kind: "section" as const, label: "Tenant settings", order: 90 },
{ id: "access.admin.group-credentials", moduleId: "access", kind: "section" as const, label: "Group credentials", order: 30 },
{ id: "access.admin.user-credentials", moduleId: "access", kind: "section" as const, label: "User credentials", order: 30 },
{ id: "access.settings.credentials", moduleId: "access", kind: "section" as const, label: "Personal credentials", order: 30 }
];
function renderAdminRoute({ settings, auth, onAuthChange }: PlatformRouteContext) { function renderAdminRoute({ settings, auth, onAuthChange }: PlatformRouteContext) {
if (!onAuthChange) { if (!onAuthChange) {
throw new Error("i18n:govoplan-access.the_access_admin_route_requires_the_platform_aut.0173a45f"); throw new Error("i18n:govoplan-access.the_access_admin_route_requires_the_platform_aut.0173a45f");
@@ -22,6 +39,7 @@ export const accessModule: PlatformWebModule = {
label: "i18n:govoplan-access.access.2f81a22d", label: "i18n:govoplan-access.access.2f81a22d",
version: "1.0.0", version: "1.0.0",
translations, translations,
viewSurfaces: accessAdminSurfaces,
navItems: [ navItems: [
{ to: "/admin", label: "i18n:govoplan-access.admin.4e7afebc", iconName: "admin", anyOf: adminReadScopes, order: 900 }], { to: "/admin", label: "i18n:govoplan-access.admin.4e7afebc", iconName: "admin", anyOf: adminReadScopes, order: 900 }],
@@ -30,4 +48,4 @@ export const accessModule: PlatformWebModule = {
}; };
export default accessModule; export default accessModule;