228 lines
8.6 KiB
TypeScript
228 lines
8.6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { initAdminPassword, verifyAdminPassword, updateLastLogin, isAdminEnabled, getAdminMeta } from '@/lib/admin/password';
|
|
import { setAdminSessionCookie, clearAdminSessionCookie, requireAdminAuth, getClientIP } from '@/lib/admin/session';
|
|
import { checkRateLimit } from '@/lib/admin/rate-limit';
|
|
import { auditLog } from '@/lib/admin/audit';
|
|
import { logger } from '@/lib/logger';
|
|
import { getStalwartCredentials } from '@/lib/stalwart/credentials';
|
|
|
|
/**
|
|
* Check if the current user is a Stalwart admin by attempting an
|
|
* admin-only JMAP method call. Stalwart v0.16 removed the REST management
|
|
* API (including the /api/account permissions endpoint) and exposes all
|
|
* administration through JMAP under the `urn:stalwart:jmap` capability.
|
|
*
|
|
* `x:Account/query` requires the `sysAccountQuery` permission, which is
|
|
* granted to admins and tenant admins. If the method returns a result,
|
|
* the caller is an admin; a method error (typically `forbidden`) means
|
|
* the caller is a regular user.
|
|
*
|
|
* Results are cached briefly per (server, user) — the admin panel's
|
|
* layout and dashboard page between them trigger this three times on
|
|
* every navigation, and admin status does not change on that timescale.
|
|
*/
|
|
const ADMIN_CHECK_CACHE_MS = 60_000;
|
|
const ADMIN_CHECK_TIMEOUT_MS = 10_000;
|
|
const adminCheckCache = new Map<string, { value: boolean; expires: number }>();
|
|
|
|
async function fetchWithTimeout(url: string, init: Parameters<typeof fetch>[1]): Promise<Response> {
|
|
const controller = new AbortController();
|
|
const timer = setTimeout(() => controller.abort(), ADMIN_CHECK_TIMEOUT_MS);
|
|
try {
|
|
return await fetch(url, { ...init, signal: controller.signal });
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
}
|
|
|
|
async function checkStalwartAdmin(request: NextRequest): Promise<boolean> {
|
|
const creds = await getStalwartCredentials(request);
|
|
if (!creds) return false;
|
|
|
|
const cacheKey = `${creds.serverUrl} |