mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-08 21:37:11 +00:00
Merge branch 'dev' into feat/resource-policies
This commit is contained in:
@@ -14,4 +14,5 @@ export * from "./verifyApiKeyApiKeyAccess";
|
||||
export * from "./verifyApiKeyClientAccess";
|
||||
export * from "./verifyApiKeySiteResourceAccess";
|
||||
export * from "./verifyApiKeyIdpAccess";
|
||||
export * from "./verifyApiKeyDomainAccess";
|
||||
export * from "./verifyApiKeyResourcePolicyAccess";
|
||||
|
||||
90
server/middlewares/integration/verifyApiKeyDomainAccess.ts
Normal file
90
server/middlewares/integration/verifyApiKeyDomainAccess.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db, domains, orgDomains, apiKeyOrg } from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import createHttpError from "http-errors";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
|
||||
export async function verifyApiKeyDomainAccess(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
try {
|
||||
const apiKey = req.apiKey;
|
||||
const domainId =
|
||||
req.params.domainId || req.body.domainId || req.query.domainId;
|
||||
const orgId = req.params.orgId;
|
||||
|
||||
if (!apiKey) {
|
||||
return next(
|
||||
createHttpError(HttpCode.UNAUTHORIZED, "Key not authenticated")
|
||||
);
|
||||
}
|
||||
|
||||
if (!domainId) {
|
||||
return next(
|
||||
createHttpError(HttpCode.BAD_REQUEST, "Invalid domain ID")
|
||||
);
|
||||
}
|
||||
|
||||
if (apiKey.isRoot) {
|
||||
// Root keys can access any domain in any org
|
||||
return next();
|
||||
}
|
||||
|
||||
// Verify domain exists and belongs to the organization
|
||||
const [domain] = await db
|
||||
.select()
|
||||
.from(domains)
|
||||
.innerJoin(orgDomains, eq(orgDomains.domainId, domains.domainId))
|
||||
.where(
|
||||
and(
|
||||
eq(orgDomains.domainId, domainId),
|
||||
eq(orgDomains.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (!domain) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
`Domain with ID ${domainId} not found in organization ${orgId}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Verify the API key has access to this organization
|
||||
if (!req.apiKeyOrg) {
|
||||
const apiKeyOrgRes = await db
|
||||
.select()
|
||||
.from(apiKeyOrg)
|
||||
.where(
|
||||
and(
|
||||
eq(apiKeyOrg.apiKeyId, apiKey.apiKeyId),
|
||||
eq(apiKeyOrg.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
req.apiKeyOrg = apiKeyOrgRes[0];
|
||||
}
|
||||
|
||||
if (!req.apiKeyOrg) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"Key does not have access to this organization"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
"Error verifying domain access"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
verifyApiKeySiteResourceAccess,
|
||||
verifyApiKeySetResourceClients,
|
||||
verifyLimits,
|
||||
verifyApiKeyDomainAccess,
|
||||
verifyApiKeyResourcePolicyAccess
|
||||
} from "@server/middlewares";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
@@ -349,6 +350,56 @@ authenticated.get(
|
||||
domain.listDomains
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
"/org/:orgId/domain/:domainId",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyApiKeyDomainAccess,
|
||||
verifyApiKeyHasAction(ActionsEnum.getDomain),
|
||||
domain.getDomain
|
||||
);
|
||||
|
||||
authenticated.put(
|
||||
"/org/:orgId/domain",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyApiKeyHasAction(ActionsEnum.createOrgDomain),
|
||||
logActionAudit(ActionsEnum.createOrgDomain),
|
||||
domain.createOrgDomain
|
||||
);
|
||||
|
||||
authenticated.patch(
|
||||
"/org/:orgId/domain/:domainId",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyApiKeyDomainAccess,
|
||||
verifyApiKeyHasAction(ActionsEnum.updateOrgDomain),
|
||||
domain.updateOrgDomain
|
||||
);
|
||||
|
||||
authenticated.delete(
|
||||
"/org/:orgId/domain/:domainId",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyApiKeyDomainAccess,
|
||||
verifyApiKeyHasAction(ActionsEnum.deleteOrgDomain),
|
||||
logActionAudit(ActionsEnum.deleteOrgDomain),
|
||||
domain.deleteAccountDomain
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
"/org/:orgId/domain/:domainId/dns-records",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyApiKeyDomainAccess,
|
||||
verifyApiKeyHasAction(ActionsEnum.getDNSRecords),
|
||||
domain.getDNSRecords
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/org/:orgId/domain/:domainId/restart",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyApiKeyDomainAccess,
|
||||
verifyApiKeyHasAction(ActionsEnum.restartOrgDomain),
|
||||
logActionAudit(ActionsEnum.restartOrgDomain),
|
||||
domain.restartOrgDomain
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
"/org/:orgId/invitations",
|
||||
verifyApiKeyOrgAccess,
|
||||
|
||||
@@ -278,7 +278,7 @@ export async function listSites(
|
||||
|
||||
// we need to add `as` so that drizzle filters the result as a subquery
|
||||
const countQuery = db.$count(
|
||||
querySitesBase().where(and(...conditions))
|
||||
querySitesBase().where(and(...conditions)).as("filtered_sites")
|
||||
);
|
||||
|
||||
const siteListQuery = baseQuery
|
||||
|
||||
@@ -193,7 +193,7 @@ export async function listAllSiteResourcesByOrg(
|
||||
const baseQuery = querySiteResourcesBase().where(and(...conditions));
|
||||
|
||||
const countQuery = db.$count(
|
||||
querySiteResourcesBase().where(and(...conditions))
|
||||
querySiteResourcesBase().where(and(...conditions)).as("filtered_site_resources")
|
||||
);
|
||||
|
||||
const [siteResourcesList, totalCount] = await Promise.all([
|
||||
|
||||
@@ -31,12 +31,23 @@ const listSiteResourcesQuerySchema = z.object({
|
||||
sort_by: z
|
||||
.enum(["name"])
|
||||
.optional()
|
||||
.catch(undefined),
|
||||
.catch(undefined)
|
||||
.openapi({
|
||||
type: "string",
|
||||
enum: ["name"],
|
||||
description: "Field to sort by"
|
||||
}),
|
||||
order: z
|
||||
.enum(["asc", "desc"])
|
||||
.optional()
|
||||
.default("asc")
|
||||
.catch("asc")
|
||||
.openapi({
|
||||
type: "string",
|
||||
enum: ["asc", "desc"],
|
||||
default: "asc",
|
||||
description: "Sort order"
|
||||
})
|
||||
});
|
||||
|
||||
export type ListSiteResourcesResponse = {
|
||||
|
||||
Reference in New Issue
Block a user