add clients to int api

This commit is contained in:
miloschwartz 2025-07-30 21:31:16 -07:00
parent 35a68703c2
commit d38656e026
No known key found for this signature in database
9 changed files with 158 additions and 14 deletions

View file

@ -9,6 +9,7 @@ services:
- "3000:3000"
- "3001:3001"
- "3002:3002"
- "3003:3003"
environment:
- NODE_ENV=development
- ENVIRONMENT=dev
@ -26,4 +27,4 @@ services:
- ./postcss.config.mjs:/app/postcss.config.mjs
- ./eslint.config.js:/app/eslint.config.js
- ./config:/app/config
restart: no
restart: no

View file

@ -1022,6 +1022,11 @@
"actionDeleteIdpOrg": "Delete IDP Org Policy",
"actionListIdpOrgs": "List IDP Orgs",
"actionUpdateIdpOrg": "Update IDP Org",
"actionCreateClient": "Create Client",
"actionDeleteClient": "Delete Client",
"actionUpdateClient": "Update Client",
"actionListClients": "List Clients",
"actionGetClient": "Get Client",
"noneSelected": "None selected",
"orgNotFound2": "No organizations found.",
"searchProgress": "Search...",

View file

@ -10,3 +10,4 @@ export * from "./verifyApiKeySetResourceUsers";
export * from "./verifyAccessTokenAccess";
export * from "./verifyApiKeyIsRoot";
export * from "./verifyApiKeyApiKeyAccess";
export * from "./verifyApiKeyClientAccess";

View file

@ -0,0 +1,86 @@
import { Request, Response, NextFunction } from "express";
import { clients, db } from "@server/db";
import { apiKeyOrg } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
export async function verifyApiKeyClientAccess(
req: Request,
res: Response,
next: NextFunction
) {
try {
const apiKey = req.apiKey;
const clientId = parseInt(
req.params.clientId || req.body.clientId || req.query.clientId
);
if (!apiKey) {
return next(
createHttpError(HttpCode.UNAUTHORIZED, "Key not authenticated")
);
}
if (isNaN(clientId)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid client ID")
);
}
const client = await db
.select()
.from(clients)
.where(eq(clients.clientId, clientId))
.limit(1);
if (client.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Client with ID ${clientId} not found`
)
);
}
if (!client[0].orgId) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
`Client with ID ${clientId} does not have an organization ID`
)
);
}
if (!req.apiKeyOrg) {
const apiKeyOrgRes = await db
.select()
.from(apiKeyOrg)
.where(
and(
eq(apiKeyOrg.apiKeyId, apiKey.apiKeyId),
eq(apiKeyOrg.orgId, client[0].orgId)
)
);
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 site access"
)
);
}
}

View file

@ -18,28 +18,28 @@ const getClientSchema = z
})
.strict();
async function query(clientId: number) {
async function query(clientId: number, orgId: string) {
// Get the client
const [client] = await db
.select()
.from(clients)
.where(eq(clients.clientId, clientId))
.where(and(eq(clients.clientId, clientId), eq(clients.orgId, orgId)))
.limit(1);
if (!client) {
return null;
}
// Get the siteIds associated with this client
const sites = await db
.select({ siteId: clientSites.siteId })
.from(clientSites)
.where(eq(clientSites.clientId, clientId));
// Add the siteIds to the client object
return {
...client,
siteIds: sites.map(site => site.siteId)
siteIds: sites.map((site) => site.siteId)
};
}
@ -75,9 +75,9 @@ export async function getClient(
);
}
const { clientId } = parsedParams.data;
const { clientId, orgId } = parsedParams.data;
const client = await query(clientId);
const client = await query(clientId, orgId);
if (!client) {
return next(
@ -98,4 +98,4 @@ export async function getClient(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
}

View file

@ -23,7 +23,7 @@ const pickClientDefaultsSchema = z
registry.registerPath({
method: "get",
path: "/site/{siteId}/pick-client-defaults",
path: "/org/{orgId}/pick-client-defaults",
description: "Return pre-requisite data for creating a client.",
tags: [OpenAPITags.Client, OpenAPITags.Site],
request: {

View file

@ -5,7 +5,7 @@ import * as domain from "./domain";
import * as target from "./target";
import * as user from "./user";
import * as role from "./role";
// import * as client from "./client";
import * as client from "./client";
import * as accessToken from "./accessToken";
import * as apiKeys from "./apiKeys";
import * as idp from "./idp";
@ -20,7 +20,8 @@ import {
verifyApiKeyUserAccess,
verifyApiKeySetResourceUsers,
verifyApiKeyAccessTokenAccess,
verifyApiKeyIsRoot
verifyApiKeyIsRoot,
verifyApiKeyClientAccess
} from "@server/middlewares";
import HttpCode from "@server/types/HttpCode";
import { Router } from "express";
@ -513,3 +514,45 @@ authenticated.get(
verifyApiKeyHasAction(ActionsEnum.listIdpOrgs),
idp.listIdpOrgPolicies
);
authenticated.get(
"/org/:orgId/pick-client-defaults",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createClient),
client.pickClientDefaults
);
authenticated.get(
"/org/:orgId/clients",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listClients),
client.listClients
);
authenticated.get(
"/org/:orgId/client/:clientId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.getClient),
client.getClient
);
authenticated.put(
"/org/:orgId/client",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createClient),
client.createClient
);
authenticated.delete(
"/client/:clientId",
verifyApiKeyClientAccess,
verifyApiKeyHasAction(ActionsEnum.deleteClient),
client.deleteClient
);
authenticated.post(
"/client/:clientId",
verifyApiKeyClientAccess,
verifyApiKeyHasAction(ActionsEnum.updateClient),
client.updateClient
);

View file

@ -705,4 +705,4 @@ export default function Page() {
)}
</>
);
}
}

View file

@ -82,6 +82,14 @@ function getActionsCategories(root: boolean) {
[t('actionDeleteResourceRule')]: "deleteResourceRule",
[t('actionListResourceRules')]: "listResourceRules",
[t('actionUpdateResourceRule')]: "updateResourceRule"
},
"Client": {
[t('actionCreateClient')]: "createClient",
[t('actionDeleteClient')]: "deleteClient",
[t('actionUpdateClient')]: "updateClient",
[t('actionListClients')]: "listClients",
[t('actionGetClient')]: "getClient"
}
};