mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-02 16:56:39 +00:00
Compare commits
5 Commits
1.16.2-s.0
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
280cbb6e22 | ||
|
|
c20babcb53 | ||
|
|
768eebe2cd | ||
|
|
81c1a1da9c | ||
|
|
52f26396ac |
@@ -1102,6 +1102,12 @@
|
|||||||
"actionGetUser": "Get User",
|
"actionGetUser": "Get User",
|
||||||
"actionGetOrgUser": "Get Organization User",
|
"actionGetOrgUser": "Get Organization User",
|
||||||
"actionListOrgDomains": "List Organization Domains",
|
"actionListOrgDomains": "List Organization Domains",
|
||||||
|
"actionGetDomain": "Get Domain",
|
||||||
|
"actionCreateOrgDomain": "Create Domain",
|
||||||
|
"actionUpdateOrgDomain": "Update Domain",
|
||||||
|
"actionDeleteOrgDomain": "Delete Domain",
|
||||||
|
"actionGetDNSRecords": "Get DNS Records",
|
||||||
|
"actionRestartOrgDomain": "Restart Domain",
|
||||||
"actionCreateSite": "Create Site",
|
"actionCreateSite": "Create Site",
|
||||||
"actionDeleteSite": "Delete Site",
|
"actionDeleteSite": "Delete Site",
|
||||||
"actionGetSite": "Get Site",
|
"actionGetSite": "Get Site",
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ export * from "./verifyApiKeyApiKeyAccess";
|
|||||||
export * from "./verifyApiKeyClientAccess";
|
export * from "./verifyApiKeyClientAccess";
|
||||||
export * from "./verifyApiKeySiteResourceAccess";
|
export * from "./verifyApiKeySiteResourceAccess";
|
||||||
export * from "./verifyApiKeyIdpAccess";
|
export * from "./verifyApiKeyIdpAccess";
|
||||||
|
export * from "./verifyApiKeyDomainAccess";
|
||||||
|
|||||||
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"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,8 @@ import {
|
|||||||
verifyApiKeyClientAccess,
|
verifyApiKeyClientAccess,
|
||||||
verifyApiKeySiteResourceAccess,
|
verifyApiKeySiteResourceAccess,
|
||||||
verifyApiKeySetResourceClients,
|
verifyApiKeySetResourceClients,
|
||||||
verifyLimits
|
verifyLimits,
|
||||||
|
verifyApiKeyDomainAccess
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
@@ -347,6 +348,56 @@ authenticated.get(
|
|||||||
domain.listDomains
|
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(
|
authenticated.get(
|
||||||
"/org/:orgId/invitations",
|
"/org/:orgId/invitations",
|
||||||
verifyApiKeyOrgAccess,
|
verifyApiKeyOrgAccess,
|
||||||
|
|||||||
@@ -69,15 +69,16 @@ export function LayoutMobileMenu({
|
|||||||
<SheetDescription className="sr-only">
|
<SheetDescription className="sr-only">
|
||||||
{t("navbarDescription")}
|
{t("navbarDescription")}
|
||||||
</SheetDescription>
|
</SheetDescription>
|
||||||
<div className="flex-1 overflow-y-auto relative">
|
<div className="w-full border-b border-border">
|
||||||
<div className="px-1">
|
<div className="px-1 shrink-0">
|
||||||
<OrgSelector
|
<OrgSelector
|
||||||
orgId={orgId}
|
orgId={orgId}
|
||||||
orgs={orgs}
|
orgs={orgs}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full border-b border-border" />
|
</div>
|
||||||
<div className="px-3 pt-3">
|
<div className="flex-1 overflow-y-auto relative">
|
||||||
|
<div className="px-3">
|
||||||
{!isAdminPage &&
|
{!isAdminPage &&
|
||||||
user.serverAdmin && (
|
user.serverAdmin && (
|
||||||
<div className="mb-1">
|
<div className="mb-1">
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ function getActionsCategories(root: boolean) {
|
|||||||
[t("actionListInvitations")]: "listInvitations",
|
[t("actionListInvitations")]: "listInvitations",
|
||||||
[t("actionRemoveUser")]: "removeUser",
|
[t("actionRemoveUser")]: "removeUser",
|
||||||
[t("actionListUsers")]: "listUsers",
|
[t("actionListUsers")]: "listUsers",
|
||||||
[t("actionListOrgDomains")]: "listOrgDomains",
|
|
||||||
[t("updateOrgUser")]: "updateOrgUser",
|
[t("updateOrgUser")]: "updateOrgUser",
|
||||||
[t("createOrgUser")]: "createOrgUser",
|
[t("createOrgUser")]: "createOrgUser",
|
||||||
[t("actionApplyBlueprint")]: "applyBlueprint",
|
[t("actionApplyBlueprint")]: "applyBlueprint",
|
||||||
@@ -39,6 +38,16 @@ function getActionsCategories(root: boolean) {
|
|||||||
[t("actionGetBlueprint")]: "getBlueprint"
|
[t("actionGetBlueprint")]: "getBlueprint"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Domain: {
|
||||||
|
[t("actionListOrgDomains")]: "listOrgDomains",
|
||||||
|
[t("actionGetDomain")]: "getDomain",
|
||||||
|
[t("actionCreateOrgDomain")]: "createOrgDomain",
|
||||||
|
[t("actionUpdateOrgDomain")]: "updateOrgDomain",
|
||||||
|
[t("actionDeleteOrgDomain")]: "deleteOrgDomain",
|
||||||
|
[t("actionGetDNSRecords")]: "getDNSRecords",
|
||||||
|
[t("actionRestartOrgDomain")]: "restartOrgDomain"
|
||||||
|
},
|
||||||
|
|
||||||
Site: {
|
Site: {
|
||||||
[t("actionCreateSite")]: "createSite",
|
[t("actionCreateSite")]: "createSite",
|
||||||
[t("actionDeleteSite")]: "deleteSite",
|
[t("actionDeleteSite")]: "deleteSite",
|
||||||
|
|||||||
Reference in New Issue
Block a user