fix: issues found in security audit (#1668)
This commit is contained in:
@@ -66,9 +66,13 @@ const getBoardAndPermissionsAsync = async (params: Props["params"]) => {
|
|||||||
export default async function BoardSettingsPage({ params, searchParams }: Props) {
|
export default async function BoardSettingsPage({ params, searchParams }: Props) {
|
||||||
const { board, permissions } = await getBoardAndPermissionsAsync(params);
|
const { board, permissions } = await getBoardAndPermissionsAsync(params);
|
||||||
const boardSettings = await getServerSettingByKeyAsync(db, "board");
|
const boardSettings = await getServerSettingByKeyAsync(db, "board");
|
||||||
const { hasFullAccess } = await getBoardPermissionsAsync(board);
|
const { hasFullAccess, hasChangeAccess } = await getBoardPermissionsAsync(board);
|
||||||
const t = await getScopedI18n("board.setting");
|
const t = await getScopedI18n("board.setting");
|
||||||
|
|
||||||
|
if (!hasChangeAccess) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Stack>
|
<Stack>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { notFound } from "next/navigation";
|
||||||
import { Stack, Title } from "@mantine/core";
|
import { Stack, Title } from "@mantine/core";
|
||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
|
import { auth } from "@homarr/auth/next";
|
||||||
import { getScopedI18n } from "@homarr/translation/server";
|
import { getScopedI18n } from "@homarr/translation/server";
|
||||||
|
|
||||||
import { CrawlingAndIndexingSettings } from "~/app/[locale]/manage/settings/_components/crawling-and-indexing.settings";
|
import { CrawlingAndIndexingSettings } from "~/app/[locale]/manage/settings/_components/crawling-and-indexing.settings";
|
||||||
@@ -20,6 +22,12 @@ export async function generateMetadata() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function SettingsPage() {
|
export default async function SettingsPage() {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user.permissions.includes("admin")) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
const serverSettings = await api.serverSettings.getAll();
|
const serverSettings = await api.serverSettings.getAll();
|
||||||
const tSettings = await getScopedI18n("management.page.settings");
|
const tSettings = await getScopedI18n("management.page.settings");
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
import { Alert, Anchor, Center, Group, Stack, Table, TableTbody, TableTd, TableTr, Text, Title } from "@mantine/core";
|
import { Alert, Anchor, Center, Group, Stack, Table, TableTbody, TableTd, TableTr, Text, Title } from "@mantine/core";
|
||||||
import { IconExclamationCircle } from "@tabler/icons-react";
|
import { IconExclamationCircle } from "@tabler/icons-react";
|
||||||
|
|
||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
import { env } from "@homarr/auth/env.mjs";
|
import { env } from "@homarr/auth/env.mjs";
|
||||||
|
import { auth } from "@homarr/auth/next";
|
||||||
import { isProviderEnabled } from "@homarr/auth/server";
|
import { isProviderEnabled } from "@homarr/auth/server";
|
||||||
import { everyoneGroup } from "@homarr/definitions";
|
import { everyoneGroup } from "@homarr/definitions";
|
||||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||||
@@ -24,6 +26,12 @@ interface GroupsDetailPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function GroupsDetailPage({ params, searchParams }: GroupsDetailPageProps) {
|
export default async function GroupsDetailPage({ params, searchParams }: GroupsDetailPageProps) {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user.permissions.includes("admin")) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
const t = await getI18n();
|
const t = await getI18n();
|
||||||
const tMembers = await getScopedI18n("management.page.group.setting.members");
|
const tMembers = await getScopedI18n("management.page.group.setting.members");
|
||||||
const group = await api.group.getById({ id: params.id });
|
const group = await api.group.getById({ id: params.id });
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { notFound } from "next/navigation";
|
||||||
import { Card, Group, Stack, Text, Title } from "@mantine/core";
|
import { Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
|
import { auth } from "@homarr/auth/next";
|
||||||
import { everyoneGroup } from "@homarr/definitions";
|
import { everyoneGroup } from "@homarr/definitions";
|
||||||
import { getScopedI18n } from "@homarr/translation/server";
|
import { getScopedI18n } from "@homarr/translation/server";
|
||||||
import { UserAvatar } from "@homarr/ui";
|
import { UserAvatar } from "@homarr/ui";
|
||||||
@@ -18,6 +20,12 @@ interface GroupsDetailPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function GroupsDetailPage({ params }: GroupsDetailPageProps) {
|
export default async function GroupsDetailPage({ params }: GroupsDetailPageProps) {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user.permissions.includes("admin")) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
const group = await api.group.getById({ id: params.id });
|
const group = await api.group.getById({ id: params.id });
|
||||||
const tGeneral = await getScopedI18n("management.page.group.setting.general");
|
const tGeneral = await getScopedI18n("management.page.group.setting.general");
|
||||||
const tGroupAction = await getScopedI18n("group.action");
|
const tGroupAction = await getScopedI18n("group.action");
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
import { Card, CardSection, Divider, Group, Stack, Text, Title } from "@mantine/core";
|
import { Card, CardSection, Divider, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
|
import { auth } from "@homarr/auth/next";
|
||||||
import { objectKeys } from "@homarr/common";
|
import { objectKeys } from "@homarr/common";
|
||||||
import type { GroupPermissionKey } from "@homarr/definitions";
|
import type { GroupPermissionKey } from "@homarr/definitions";
|
||||||
import { groupPermissions } from "@homarr/definitions";
|
import { groupPermissions } from "@homarr/definitions";
|
||||||
@@ -16,6 +18,12 @@ interface GroupPermissionsPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function GroupPermissionsPage({ params }: GroupPermissionsPageProps) {
|
export default async function GroupPermissionsPage({ params }: GroupPermissionsPageProps) {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user.permissions.includes("admin")) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
const group = await api.group.getById({ id: params.id });
|
const group = await api.group.getById({ id: params.id });
|
||||||
const tPermissions = await getScopedI18n("group.permission");
|
const tPermissions = await getScopedI18n("group.permission");
|
||||||
const t = await getI18n();
|
const t = await getI18n();
|
||||||
|
|||||||
@@ -575,11 +575,14 @@ export const boardRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
importOldmarrConfig: protectedProcedure.input(importJsonFileSchema).mutation(async ({ input, ctx }) => {
|
importOldmarrConfig: permissionRequiredProcedure
|
||||||
const content = await input.file.text();
|
.requiresPermission("board-create")
|
||||||
const oldmarr = oldmarrConfigSchema.parse(JSON.parse(content));
|
.input(importJsonFileSchema)
|
||||||
await importOldmarrAsync(ctx.db, oldmarr, input.configuration);
|
.mutation(async ({ input, ctx }) => {
|
||||||
}),
|
const content = await input.file.text();
|
||||||
|
const oldmarr = oldmarrConfigSchema.parse(JSON.parse(content));
|
||||||
|
await importOldmarrAsync(ctx.db, oldmarr, input.configuration);
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const noBoardWithSimilarNameAsync = async (db: Database, name: string, ignoredIds: string[] = []) => {
|
const noBoardWithSimilarNameAsync = async (db: Database, name: string, ignoredIds: string[] = []) => {
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import { invites } from "@homarr/db/schema/sqlite";
|
|||||||
import { selectInviteSchema } from "@homarr/db/validationSchemas";
|
import { selectInviteSchema } from "@homarr/db/validationSchemas";
|
||||||
import { z } from "@homarr/validation";
|
import { z } from "@homarr/validation";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, permissionRequiredProcedure } from "../trpc";
|
||||||
import { throwIfCredentialsDisabled } from "./invite/checks";
|
import { throwIfCredentialsDisabled } from "./invite/checks";
|
||||||
|
|
||||||
export const inviteRouter = createTRPCRouter({
|
export const inviteRouter = createTRPCRouter({
|
||||||
getAll: protectedProcedure
|
getAll: permissionRequiredProcedure
|
||||||
|
.requiresPermission("admin")
|
||||||
.output(
|
.output(
|
||||||
z.array(
|
z.array(
|
||||||
selectInviteSchema
|
selectInviteSchema
|
||||||
@@ -40,7 +41,8 @@ export const inviteRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
createInvite: protectedProcedure
|
createInvite: permissionRequiredProcedure
|
||||||
|
.requiresPermission("admin")
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
expirationDate: z.date(),
|
expirationDate: z.date(),
|
||||||
@@ -65,7 +67,8 @@ export const inviteRouter = createTRPCRouter({
|
|||||||
token,
|
token,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
deleteInvite: protectedProcedure
|
deleteInvite: permissionRequiredProcedure
|
||||||
|
.requiresPermission("admin")
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
|||||||
@@ -3,17 +3,18 @@ import type { ServerSettings } from "@homarr/server-settings";
|
|||||||
import { defaultServerSettingsKeys } from "@homarr/server-settings";
|
import { defaultServerSettingsKeys } from "@homarr/server-settings";
|
||||||
import { validation, z } from "@homarr/validation";
|
import { validation, z } from "@homarr/validation";
|
||||||
|
|
||||||
import { createTRPCRouter, onboardingProcedure, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, onboardingProcedure, permissionRequiredProcedure, publicProcedure } from "../trpc";
|
||||||
import { nextOnboardingStepAsync } from "./onboard/onboard-queries";
|
import { nextOnboardingStepAsync } from "./onboard/onboard-queries";
|
||||||
|
|
||||||
export const serverSettingsRouter = createTRPCRouter({
|
export const serverSettingsRouter = createTRPCRouter({
|
||||||
getCulture: publicProcedure.query(async ({ ctx }) => {
|
getCulture: publicProcedure.query(async ({ ctx }) => {
|
||||||
return await getServerSettingByKeyAsync(ctx.db, "culture");
|
return await getServerSettingByKeyAsync(ctx.db, "culture");
|
||||||
}),
|
}),
|
||||||
getAll: protectedProcedure.query(async ({ ctx }) => {
|
getAll: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => {
|
||||||
return await getServerSettingsAsync(ctx.db);
|
return await getServerSettingsAsync(ctx.db);
|
||||||
}),
|
}),
|
||||||
saveSettings: protectedProcedure
|
saveSettings: permissionRequiredProcedure
|
||||||
|
.requiresPermission("admin")
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
settingsKey: z.enum(defaultServerSettingsKeys),
|
settingsKey: z.enum(defaultServerSettingsKeys),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { inviteRouter } from "../invite";
|
|||||||
const defaultSession = {
|
const defaultSession = {
|
||||||
user: {
|
user: {
|
||||||
id: createId(),
|
id: createId(),
|
||||||
permissions: [],
|
permissions: ["admin"],
|
||||||
colorScheme: "light",
|
colorScheme: "light",
|
||||||
},
|
},
|
||||||
expires: new Date().toISOString(),
|
expires: new Date().toISOString(),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ vi.mock("@homarr/auth", () => ({ auth: () => ({}) as Session }));
|
|||||||
const defaultSession = {
|
const defaultSession = {
|
||||||
user: {
|
user: {
|
||||||
id: createId(),
|
id: createId(),
|
||||||
permissions: [],
|
permissions: ["admin"],
|
||||||
colorScheme: "light",
|
colorScheme: "light",
|
||||||
},
|
},
|
||||||
expires: new Date().toISOString(),
|
expires: new Date().toISOString(),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";
|
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, permissionRequiredProcedure } from "../trpc";
|
||||||
|
|
||||||
export const updateCheckerRouter = createTRPCRouter({
|
export const updateCheckerRouter = createTRPCRouter({
|
||||||
getAvailableUpdates: protectedProcedure.query(async () => {
|
getAvailableUpdates: permissionRequiredProcedure.requiresPermission("admin").query(async () => {
|
||||||
const handler = updateCheckerRequestHandler.handler({});
|
const handler = updateCheckerRequestHandler.handler({});
|
||||||
const data = await handler.getCachedOrUpdatedDataAsync({});
|
const data = await handler.getCachedOrUpdatedDataAsync({});
|
||||||
return data.data.availableUpdates;
|
return data.data.availableUpdates;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { controlsInputSchema } from "@homarr/integrations/types";
|
|||||||
import { dnsHoleRequestHandler } from "@homarr/request-handler/dns-hole";
|
import { dnsHoleRequestHandler } from "@homarr/request-handler/dns-hole";
|
||||||
|
|
||||||
import { createManyIntegrationMiddleware, createOneIntegrationMiddleware } from "../../middlewares/integration";
|
import { createManyIntegrationMiddleware, createOneIntegrationMiddleware } from "../../middlewares/integration";
|
||||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../../trpc";
|
||||||
|
|
||||||
export const dnsHoleRouter = createTRPCRouter({
|
export const dnsHoleRouter = createTRPCRouter({
|
||||||
summary: publicProcedure
|
summary: publicProcedure
|
||||||
@@ -62,7 +62,7 @@ export const dnsHoleRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
enable: publicProcedure
|
enable: protectedProcedure
|
||||||
.unstable_concat(createOneIntegrationMiddleware("interact", ...getIntegrationKindsByCategory("dnsHole")))
|
.unstable_concat(createOneIntegrationMiddleware("interact", ...getIntegrationKindsByCategory("dnsHole")))
|
||||||
.mutation(async ({ ctx: { integration } }) => {
|
.mutation(async ({ ctx: { integration } }) => {
|
||||||
const client = integrationCreator(integration);
|
const client = integrationCreator(integration);
|
||||||
@@ -75,7 +75,7 @@ export const dnsHoleRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
disable: publicProcedure
|
disable: protectedProcedure
|
||||||
.input(controlsInputSchema)
|
.input(controlsInputSchema)
|
||||||
.unstable_concat(createOneIntegrationMiddleware("interact", ...getIntegrationKindsByCategory("dnsHole")))
|
.unstable_concat(createOneIntegrationMiddleware("interact", ...getIntegrationKindsByCategory("dnsHole")))
|
||||||
.mutation(async ({ ctx: { integration }, input }) => {
|
.mutation(async ({ ctx: { integration }, input }) => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { indexerManagerRequestHandler } from "@homarr/request-handler/indexer-ma
|
|||||||
|
|
||||||
import type { IntegrationAction } from "../../middlewares/integration";
|
import type { IntegrationAction } from "../../middlewares/integration";
|
||||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../../trpc";
|
||||||
|
|
||||||
const createIndexerManagerIntegrationMiddleware = (action: IntegrationAction) =>
|
const createIndexerManagerIntegrationMiddleware = (action: IntegrationAction) =>
|
||||||
createManyIntegrationMiddleware(action, ...getIntegrationKindsByCategory("indexerManager"));
|
createManyIntegrationMiddleware(action, ...getIntegrationKindsByCategory("indexerManager"));
|
||||||
@@ -54,7 +54,7 @@ export const indexerManagerRouter = createTRPCRouter({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
testAllIndexers: publicProcedure
|
testAllIndexers: protectedProcedure
|
||||||
.unstable_concat(createIndexerManagerIntegrationMiddleware("interact"))
|
.unstable_concat(createIndexerManagerIntegrationMiddleware("interact"))
|
||||||
.mutation(async ({ ctx }) => {
|
.mutation(async ({ ctx }) => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import { eq } from "@homarr/db";
|
|||||||
import { items } from "@homarr/db/schema/sqlite";
|
import { items } from "@homarr/db/schema/sqlite";
|
||||||
import { z } from "@homarr/validation";
|
import { z } from "@homarr/validation";
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../../trpc";
|
||||||
|
|
||||||
export const notebookRouter = createTRPCRouter({
|
export const notebookRouter = createTRPCRouter({
|
||||||
updateContent: publicProcedure
|
updateContent: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
itemId: z.string(),
|
itemId: z.string(),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { z } from "@homarr/validation";
|
|||||||
|
|
||||||
import type { IntegrationAction } from "../../middlewares/integration";
|
import type { IntegrationAction } from "../../middlewares/integration";
|
||||||
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
|
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
|
||||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../../trpc";
|
||||||
|
|
||||||
const createSmartHomeIntegrationMiddleware = (action: IntegrationAction) =>
|
const createSmartHomeIntegrationMiddleware = (action: IntegrationAction) =>
|
||||||
createOneIntegrationMiddleware(action, ...getIntegrationKindsByCategory("smartHomeServer"));
|
createOneIntegrationMiddleware(action, ...getIntegrationKindsByCategory("smartHomeServer"));
|
||||||
@@ -41,7 +41,7 @@ export const smartHomeRouter = createTRPCRouter({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
switchEntity: publicProcedure
|
switchEntity: protectedProcedure
|
||||||
.unstable_concat(createSmartHomeIntegrationMiddleware("interact"))
|
.unstable_concat(createSmartHomeIntegrationMiddleware("interact"))
|
||||||
.input(z.object({ entityId: z.string() }))
|
.input(z.object({ entityId: z.string() }))
|
||||||
.mutation(async ({ ctx: { integration }, input }) => {
|
.mutation(async ({ ctx: { integration }, input }) => {
|
||||||
@@ -53,7 +53,7 @@ export const smartHomeRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return success;
|
return success;
|
||||||
}),
|
}),
|
||||||
executeAutomation: publicProcedure
|
executeAutomation: protectedProcedure
|
||||||
.unstable_concat(createSmartHomeIntegrationMiddleware("interact"))
|
.unstable_concat(createSmartHomeIntegrationMiddleware("interact"))
|
||||||
.input(z.object({ automationId: z.string() }))
|
.input(z.object({ automationId: z.string() }))
|
||||||
.mutation(async ({ ctx: { integration }, input }) => {
|
.mutation(async ({ ctx: { integration }, input }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user