feat: add more group permissions (#1453)

* feat: add more group permissions

* feat: restrict access with app permissions

* feat: restrict access with search-engine permissions

* feat: restrict access with media permissions

* refactor: remove permissions for users, groups and invites

* test: adjust app router tests with app permissions

* fix: integration page accessible without session

* fix: search for users, groups and integrations shown to unauthenticated users

* chore: address pull request feedback
This commit is contained in:
Meier Lukas
2024-11-17 21:31:25 +01:00
committed by GitHub
parent 879aa1152f
commit 0ee343b99e
31 changed files with 575 additions and 208 deletions

View File

@@ -1,3 +1,6 @@
import { useSession } from "@homarr/auth/client";
import type { SearchGroup } from "../../lib/group";
import type { SearchMode } from "../../lib/mode";
import { appsSearchGroup } from "./apps-search-group";
import { boardsSearchGroup } from "./boards-search-group";
@@ -6,5 +9,14 @@ import { integrationsSearchGroup } from "./integrations-search-group";
export const appIntegrationBoardMode = {
modeKey: "appIntegrationBoard",
character: "#",
groups: [appsSearchGroup, integrationsSearchGroup, boardsSearchGroup],
useGroups() {
const { data: session } = useSession();
const groups: SearchGroup[] = [boardsSearchGroup];
if (!session?.user) {
return groups;
}
return groups.concat([appsSearchGroup, integrationsSearchGroup]);
},
} satisfies SearchMode;

View File

@@ -1,11 +1,11 @@
import { Group, Text, useMantineColorScheme } from "@mantine/core";
import {
IconBox,
IconCategoryPlus,
IconFileImport,
IconLanguage,
IconMailForward,
IconMoon,
IconPackage,
IconPlug,
IconSun,
IconUserPlus,
@@ -113,9 +113,10 @@ export const commandMode = {
},
{
commandKey: "newApp",
icon: IconPackage,
icon: IconBox,
name: tOption("newApp.label"),
useInteraction: interaction.link(() => ({ href: "/manage/apps/new" })),
hidden: !session?.user.permissions.includes("app-create"),
},
{
commandKey: "newIntegration",

View File

@@ -1,6 +1,7 @@
import { Group, Kbd, Text } from "@mantine/core";
import { IconBook2, IconBrandDiscord, IconBrandGithub } from "@tabler/icons-react";
import { useSession } from "@homarr/auth/client";
import { createDocumentationLink } from "@homarr/definitions";
import { useScopedI18n } from "@homarr/translation/client";
@@ -18,58 +19,67 @@ const searchModesWithoutHelp = [userGroupMode, appIntegrationBoardMode, external
const helpMode = {
modeKey: "help",
character: "?",
groups: [
createGroup({
keyPath: "character",
title: (t) => t("search.mode.help.group.mode.title"),
options: searchModesWithoutHelp.map(({ character, modeKey }) => ({ character, modeKey })),
Component: ({ modeKey, character }) => {
const t = useScopedI18n(`search.mode.${modeKey}`);
useGroups() {
const { data: session } = useSession();
const visibleSearchModes: SearchMode[] = [appIntegrationBoardMode, externalMode, commandMode, pageMode];
return (
<Group px="md" py="xs" w="100%" wrap="nowrap" align="center" justify="space-between">
<Text>{t("help")}</Text>
<Kbd size="sm">{character}</Kbd>
if (session?.user.permissions.includes("admin")) {
visibleSearchModes.unshift(userGroupMode);
}
return [
createGroup({
keyPath: "character",
title: (t) => t("search.mode.help.group.mode.title"),
options: visibleSearchModes.map(({ character, modeKey }) => ({ character, modeKey })),
Component: ({ modeKey, character }) => {
const t = useScopedI18n(`search.mode.${modeKey}`);
return (
<Group px="md" py="xs" w="100%" wrap="nowrap" align="center" justify="space-between">
<Text>{t("help")}</Text>
<Kbd size="sm">{character}</Kbd>
</Group>
);
},
filter: () => true,
useInteraction: interaction.mode(({ modeKey }) => ({ mode: modeKey })),
}),
createGroup({
keyPath: "href",
title: (t) => t("search.mode.help.group.help.title"),
useOptions() {
const t = useScopedI18n("search.mode.help.group.help.option");
return [
{
label: t("documentation.label"),
icon: IconBook2,
href: createDocumentationLink("/docs/getting-started"),
},
{
label: t("submitIssue.label"),
icon: IconBrandGithub,
href: "https://github.com/ajnart/homarr/issues/new/choose",
},
{
label: t("discord.label"),
icon: IconBrandDiscord,
href: "https://discord.com/invite/aCsmEV5RgA",
},
];
},
Component: (props) => (
<Group px="md" py="xs" w="100%" wrap="nowrap" align="center">
<props.icon />
<Text>{props.label}</Text>
</Group>
);
},
filter: () => true,
useInteraction: interaction.mode(({ modeKey }) => ({ mode: modeKey })),
}),
createGroup({
keyPath: "href",
title: (t) => t("search.mode.help.group.help.title"),
useOptions() {
const t = useScopedI18n("search.mode.help.group.help.option");
return [
{
label: t("documentation.label"),
icon: IconBook2,
href: createDocumentationLink("/docs/getting-started"),
},
{
label: t("submitIssue.label"),
icon: IconBrandGithub,
href: "https://github.com/ajnart/homarr/issues/new/choose",
},
{
label: t("discord.label"),
icon: IconBrandDiscord,
href: "https://discord.com/invite/aCsmEV5RgA",
},
];
},
Component: (props) => (
<Group px="md" py="xs" w="100%" wrap="nowrap" align="center">
<props.icon />
<Text>{props.label}</Text>
</Group>
),
filter: () => true,
useInteraction: interaction.link(({ href }) => ({ href, newTab: true })),
}),
],
),
filter: () => true,
useInteraction: interaction.link(({ href }) => ({ href, newTab: true })),
}),
];
},
} satisfies SearchMode;
export const searchModes = [...searchModesWithoutHelp, helpMode] as const;

View File

@@ -130,7 +130,7 @@ export const pagesSearchGroup = createGroup<{
icon: IconLogs,
path: "/manage/tools/logs",
name: t("manageLog.label"),
hidden: !session?.user.permissions.includes("admin"),
hidden: !session?.user.permissions.includes("other-view-logs"),
},
{
icon: IconReport,