···11import { useSignal } from "@preact/signals";
22import { sphereState, sphereHandle, refreshSphere } from "@exosphere/client/sphere";
33-import { canDo } from "@exosphere/client/permissions";
33+import { canDo, canAccessSettings, canManageModules } from "@exosphere/client/permissions";
44import { useLocation } from "@exosphere/client/router";
55import { useQuery } from "@exosphere/client/hooks";
66import * as ui from "@exosphere/client/ui.css";
77import * as ss from "./sphere-settings.css.ts";
88import { SettingsLayout } from "./settings-layout.tsx";
99+import { NotFoundPage } from "./not-found.tsx";
910import {
1011 getSphereModules,
1112 enableModule as apiEnableModule,
···161162 const handle = sphereHandle.value;
162163163164 if (!data || !handle) return null;
165165+ if (!canAccessSettings()) return <NotFoundPage />;
164166165167 const section = resolveSection(path ?? "");
168168+ if (section === "modules" && !canManageModules()) return <NotFoundPage />;
166169167170 return (
168171 <SettingsLayout active={section}>
+25
packages/client/src/permissions.ts
···1313export function useCanDo(module: string, action: string) {
1414 return computed(() => canDo(module, action));
1515}
1616+1717+/** True if the user can manage the sphere's enabled modules. */
1818+export function canManageModules(): boolean {
1919+ return canDo("sphere", "enableModule") || canDo("sphere", "disableModule");
2020+}
2121+2222+/** True if the user can manage members (invite, revoke, update role). */
2323+export function canManageMembers(): boolean {
2424+ return (
2525+ canDo("sphere", "inviteMember") ||
2626+ canDo("sphere", "revokeMember") ||
2727+ canDo("sphere", "updateMemberRole")
2828+ );
2929+}
3030+3131+/** True if the user has at least one settings-related permission.
3232+ * Used to gate the Settings entry point and the Settings page itself. */
3333+export function canAccessSettings(): boolean {
3434+ return (
3535+ canManageModules() ||
3636+ canManageMembers() ||
3737+ canDo("sphere", "manageLabels") ||
3838+ canDo("sphere", "updatePermissions")
3939+ );
4040+}