Skip to content
This repository was archived by the owner on Jul 2, 2026. It is now read-only.

Commit fc3164f

Browse files
fix: scope admin configuration changes to the target configuration
1 parent 5932aaa commit fc3164f

3 files changed

Lines changed: 58 additions & 3 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { getUserAccess } from '$lib/discord/get-user-access'
2+
3+
/**
4+
* Returns whether the given Discord user administers the specified configuration.
5+
* A configuration's id is the Discord guild id, so access is resolved directly
6+
* from the configuration id.
7+
*/
8+
export async function isConfigurationAdmin(
9+
discordUserId: string | undefined,
10+
configurationId: string | undefined
11+
): Promise<boolean> {
12+
if (!discordUserId || !configurationId) return false
13+
try {
14+
const access = await getUserAccess(discordUserId, configurationId)
15+
return access.isAdmin
16+
} catch {
17+
// no access when the membership lookup fails
18+
return false
19+
}
20+
}

apps/discord-bot-frontend/src/routes/api/admin/configure/+server.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@ import type { RequestHandler } from '@sveltejs/kit'
22
import { json } from '@sveltejs/kit'
33
import { prisma } from '$lib/db'
44
import { ACCESS_LEVELS } from '$lib/constants'
5+
import { isConfigurationAdmin } from '$lib/server/require-configuration-admin'
56

6-
export const POST: RequestHandler = async ({ request }) => {
7+
export const POST: RequestHandler = async ({ request, locals }) => {
78
const { id, name, adminRoles, staffRoles, contributorRoles } =
89
await request.json()
10+
11+
// Ensure the caller administers the target configuration. Creating a new
12+
// configuration remains available to a guild owner (getUserAccess falls back
13+
// to owner status when no configuration exists yet).
14+
if (!locals.session?.user) {
15+
return new Response('Unauthorized', { status: 401 })
16+
}
17+
if (!(await isConfigurationAdmin(locals.session.user.discordUserId, id))) {
18+
return new Response('Forbidden', { status: 403 })
19+
}
20+
921
const record = await prisma.configuration.findUnique({
1022
where: { id },
1123
include: {
@@ -115,8 +127,17 @@ export const POST: RequestHandler = async ({ request }) => {
115127
}
116128
}
117129

118-
export const DELETE: RequestHandler = async ({ request }) => {
130+
export const DELETE: RequestHandler = async ({ request, locals }) => {
119131
const { id } = await request.json()
132+
133+
// Ensure the caller administers the target configuration.
134+
if (!locals.session?.user) {
135+
return new Response('Unauthorized', { status: 401 })
136+
}
137+
if (!(await isConfigurationAdmin(locals.session.user.discordUserId, id))) {
138+
return new Response('Forbidden', { status: 403 })
139+
}
140+
120141
return json(
121142
await prisma.configuration.delete({
122143
where: { id },

apps/discord-bot-frontend/src/routes/api/admin/feature/+server.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { FEATURE_CODES } from '$lib/constants'
22
import { type RequestHandler } from '@sveltejs/kit'
33
import { prisma } from '$lib/db'
4+
import { isConfigurationAdmin } from '$lib/server/require-configuration-admin'
45

56
type Payload = {
67
/**
@@ -17,7 +18,7 @@ type Payload = {
1718
enabled: boolean
1819
}
1920

20-
export const POST: RequestHandler = async ({ request }) => {
21+
export const POST: RequestHandler = async ({ request, locals }) => {
2122
let body: Payload
2223
try {
2324
/**
@@ -41,6 +42,19 @@ export const POST: RequestHandler = async ({ request }) => {
4142
return new Response('Invalid request', { status: 400 })
4243
}
4344

45+
// Ensure the caller administers the target configuration.
46+
if (!locals.session?.user) {
47+
return new Response('Unauthorized', { status: 401 })
48+
}
49+
if (
50+
!(await isConfigurationAdmin(
51+
locals.session.user.discordUserId,
52+
configurationId
53+
))
54+
) {
55+
return new Response('Forbidden', { status: 403 })
56+
}
57+
4458
try {
4559
const updated = await prisma.configuration.update({
4660
where: {

0 commit comments

Comments
 (0)