fix(portal): security hardening for pilot readiness
- C1: Rewrite /api/usage to resolve teamId server-side from tenant CR; customers can no longer pass arbitrary teamId (IDOR fix) - C2: Remove POST /api/tenants — tenants are only created via admin approval flow - H1: Validate packages against catalog, workspaceFiles against allowlist, and field lengths in PATCH /api/tenants/[name] - H2: Remove full ZITADEL profile claims logging from JWT callback - H3: Add safeError() utility; sanitize all error responses to clients, toggle raw errors via PORTAL_DEBUG_ERRORS=true - H4/H5: Escape HTML entities in all email templates (contactName, companyName, adminNotes)
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getSessionUser } from "@/lib/session";
|
||||
import { listTenants, getTenant, createTenant } from "@/lib/k8s";
|
||||
import type { PiecedTenantSpec } from "@/types";
|
||||
import { listTenants } from "@/lib/k8s";
|
||||
|
||||
export async function GET() {
|
||||
const user = await getSessionUser();
|
||||
@@ -20,37 +19,3 @@ export async function GET() {
|
||||
);
|
||||
return NextResponse.json(own);
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const user = await getSessionUser();
|
||||
if (!user)
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
if (!user.isPlatform && !user.roles.includes("owner")) {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = (await request.json()) as {
|
||||
name: string;
|
||||
spec: PiecedTenantSpec;
|
||||
};
|
||||
|
||||
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(body.name) || body.name.length > 63) {
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid tenant name: lowercase alphanumeric and hyphens, 2-63 chars" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const existing = await getTenant(body.name);
|
||||
if (existing) {
|
||||
return NextResponse.json(
|
||||
{ error: "Tenant already exists" },
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
const tenant = await createTenant(body.name, body.spec, {
|
||||
"pieced.ch/zitadel-org-id": user.orgId,
|
||||
});
|
||||
return NextResponse.json(tenant, { status: 201 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user