Merge branch 'dev' into feat/resource-policies

This commit is contained in:
Fred KISSIE
2026-03-04 16:46:33 +01:00
138 changed files with 469 additions and 727 deletions
@@ -43,7 +43,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/access-token",
description: "Generate a new access token for a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.AccessToken],
tags: [OpenAPITags.PublicResource, OpenAPITags.AccessToken],
request: {
params: generateAccssTokenParamsSchema,
body: {
@@ -122,7 +122,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/access-tokens",
description: "List all access tokens in an organization.",
tags: [OpenAPITags.Org, OpenAPITags.AccessToken],
tags: [OpenAPITags.AccessToken],
request: {
params: z.object({
orgId: z.string()
@@ -135,8 +135,8 @@ registry.registerPath({
registry.registerPath({
method: "get",
path: "/resource/{resourceId}/access-tokens",
description: "List all access tokens in an organization.",
tags: [OpenAPITags.Resource, OpenAPITags.AccessToken],
description: "List all access tokens for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.AccessToken],
request: {
params: z.object({
resourceId: z.number()
+1 -1
View File
@@ -37,7 +37,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/api-key",
description: "Create a new API key scoped to the organization.",
tags: [OpenAPITags.Org, OpenAPITags.ApiKey],
tags: [OpenAPITags.ApiKey],
request: {
params: paramsSchema,
body: {
+1 -1
View File
@@ -18,7 +18,7 @@ registry.registerPath({
method: "delete",
path: "/org/{orgId}/api-key/{apiKeyId}",
description: "Delete an API key.",
tags: [OpenAPITags.Org, OpenAPITags.ApiKey],
tags: [OpenAPITags.ApiKey],
request: {
params: paramsSchema
},
+1 -1
View File
@@ -48,7 +48,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/api-key/{apiKeyId}/actions",
description: "List all actions set for an API key.",
tags: [OpenAPITags.Org, OpenAPITags.ApiKey],
tags: [OpenAPITags.ApiKey],
request: {
params: paramsSchema,
query: querySchema
+1 -1
View File
@@ -52,7 +52,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/api-keys",
description: "List all API keys for an organization",
tags: [OpenAPITags.Org, OpenAPITags.ApiKey],
tags: [OpenAPITags.ApiKey],
request: {
params: paramsSchema,
query: querySchema
+1 -1
View File
@@ -25,7 +25,7 @@ registry.registerPath({
path: "/org/{orgId}/api-key/{apiKeyId}/actions",
description:
"Set actions for an API key. This will replace any existing actions.",
tags: [OpenAPITags.Org, OpenAPITags.ApiKey],
tags: [OpenAPITags.ApiKey],
request: {
params: paramsSchema,
body: {
@@ -20,7 +20,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/logs/request",
description: "Query the request audit log for an organization",
tags: [OpenAPITags.Org],
tags: [OpenAPITags.Logs],
request: {
query: queryAccessAuditLogsQuery.omit({
limit: true,
@@ -151,7 +151,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/logs/analytics",
description: "Query the request audit analytics for an organization",
tags: [OpenAPITags.Org],
tags: [OpenAPITags.Logs],
request: {
query: queryAccessAuditLogsQuery,
params: queryRequestAuditLogsParams
@@ -182,7 +182,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/logs/request",
description: "Query the request audit log for an organization",
tags: [OpenAPITags.Org],
tags: [OpenAPITags.Logs],
request: {
query: queryAccessAuditLogsQuery,
params: queryRequestAuditLogsParams
@@ -20,7 +20,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/blueprint",
description: "Apply a base64 encoded JSON blueprint to an organization",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
tags: [OpenAPITags.Blueprint],
request: {
params: applyBlueprintParamsSchema,
body: {
@@ -43,7 +43,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/blueprint",
description: "Create and apply a YAML blueprint to an organization",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
tags: [OpenAPITags.Blueprint],
request: {
params: applyBlueprintParamsSchema,
body: {
+1 -1
View File
@@ -53,7 +53,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/blueprint/{blueprintId}",
description: "Get a blueprint by its blueprint ID.",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
tags: [OpenAPITags.Blueprint],
request: {
params: getBlueprintSchema
},
+1 -1
View File
@@ -67,7 +67,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/blueprints",
description: "List all blueprints for a organization.",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
tags: [OpenAPITags.Blueprint],
request: {
params: z.object({
orgId: z.string()
+1 -1
View File
@@ -48,7 +48,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/client",
description: "Create a new client for an organization.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.Client],
request: {
params: createClientParamsSchema,
body: {
+1 -1
View File
@@ -49,7 +49,7 @@ registry.registerPath({
path: "/org/{orgId}/user/{userId}/client",
description:
"Create a new client for a user and associate it with an existing olm.",
tags: [OpenAPITags.Client, OpenAPITags.Org, OpenAPITags.User],
tags: [OpenAPITags.Client],
request: {
params: paramsSchema,
body: {
+1 -1
View File
@@ -243,7 +243,7 @@ registry.registerPath({
path: "/org/{orgId}/client/{niceId}",
description:
"Get a client by orgId and niceId. NiceId is a readable ID for the site and unique on a per org basis.",
tags: [OpenAPITags.Org, OpenAPITags.Site],
tags: [OpenAPITags.Site],
request: {
params: z.object({
orgId: z.string(),
+1 -1
View File
@@ -237,7 +237,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/clients",
description: "List all clients for an organization.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.Client],
request: {
query: listClientsSchema,
params: listClientsParamsSchema
+1 -1
View File
@@ -256,7 +256,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/user-devices",
description: "List all user devices for an organization.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.Client],
request: {
query: listUserDevicesSchema,
params: listUserDevicesParamsSchema
+1 -1
View File
@@ -23,7 +23,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/pick-client-defaults",
description: "Return pre-requisite data for creating a client.",
tags: [OpenAPITags.Client, OpenAPITags.Site],
tags: [OpenAPITags.Client],
request: {
params: pickClientDefaultsSchema
},
+1 -1
View File
@@ -59,7 +59,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/domains",
description: "List all domains for a organization.",
tags: [OpenAPITags.Org],
tags: [OpenAPITags.Domain],
request: {
params: z.object({
orgId: z.string()
-33
View File
@@ -693,13 +693,6 @@ authenticated.post(
resource.setResourceUsers
);
authenticated.get(
"/resource-policy/:resourcePolicyId/roles",
verifyResourcePolicyAccess,
verifyUserHasAction(ActionsEnum.listResourcePolicyRoles),
resource.listResourcePolicyRoles
);
authenticated.put(
"/resource-policy/:resourcePolicyId/access-control",
verifyResourcePolicyAccess,
@@ -708,32 +701,6 @@ authenticated.put(
policy.setResourcePolicyAccessControl
);
authenticated.get(
"/resource-policy/:resourcePolicyId/users",
verifyResourcePolicyAccess,
verifyUserHasAction(ActionsEnum.listResourcePolicyUsers),
resource.listResourcePolicyUsers
);
authenticated.post(
"/resource-policy/:resourcePolicyId/roles",
verifyResourcePolicyAccess,
verifyRoleAccess,
verifyLimits,
verifyUserHasAction(ActionsEnum.setResourcePolicyRoles),
logActionAudit(ActionsEnum.setResourcePolicyRoles),
resource.setResourcePolicyRoles
);
authenticated.post(
"/resource-policy/:resourcePolicyId/users",
verifyResourcePolicyAccess,
verifyLimits,
verifyUserHasAction(ActionsEnum.setResourcePolicyUsers),
logActionAudit(ActionsEnum.setResourcePolicyUsers),
resource.setResourcePolicyUsers
);
authenticated.put(
"/resource-policy/:resourcePolicyId/password",
verifyResourcePolicyAccess,
+1 -1
View File
@@ -27,7 +27,7 @@ registry.registerPath({
method: "put",
path: "/idp/{idpId}/org/{orgId}",
description: "Create an IDP policy for an existing IDP on an organization.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema,
body: {
+1 -1
View File
@@ -37,7 +37,7 @@ registry.registerPath({
method: "put",
path: "/idp/oidc",
description: "Create an OIDC IdP.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
body: {
content: {
+1 -1
View File
@@ -21,7 +21,7 @@ registry.registerPath({
method: "delete",
path: "/idp/{idpId}",
description: "Delete IDP.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema
},
+1 -1
View File
@@ -19,7 +19,7 @@ registry.registerPath({
method: "delete",
path: "/idp/{idpId}/org/{orgId}",
description: "Create an OIDC IdP for an organization.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema
},
+1 -1
View File
@@ -34,7 +34,7 @@ registry.registerPath({
method: "get",
path: "/idp/{idpId}",
description: "Get an IDP by its IDP ID.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema
},
+1 -1
View File
@@ -48,7 +48,7 @@ registry.registerPath({
method: "get",
path: "/idp/{idpId}/org",
description: "List all org policies on an IDP.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema,
query: querySchema
+1 -1
View File
@@ -58,7 +58,7 @@ registry.registerPath({
method: "get",
path: "/idp",
description: "List all IDP in the system.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
query: querySchema
},
+1 -1
View File
@@ -26,7 +26,7 @@ registry.registerPath({
method: "post",
path: "/idp/{idpId}/org/{orgId}",
description: "Update an IDP org policy.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema,
body: {
+1 -1
View File
@@ -42,7 +42,7 @@ registry.registerPath({
method: "post",
path: "/idp/{idpId}/oidc",
description: "Update an OIDC IdP.",
tags: [OpenAPITags.Idp],
tags: [OpenAPITags.GlobalIdp],
request: {
params: paramsSchema,
body: {
+1 -1
View File
@@ -230,7 +230,7 @@ export async function buildTargetConfigurationForNewtClient(siteId: number) {
!target.hcMethod
) {
logger.debug(
`Skipping target ${target.targetId} due to missing health check fields`
`Skipping adding target health check ${target.targetId} due to missing health check fields`
);
return null; // Skip targets with missing health check fields
}
@@ -35,7 +35,7 @@ registry.registerPath({
path: "/resource-policy/{resourceId}/access-control",
description:
"Set access control users for a resource policy, including SSO, users, roles, Identity provider.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.Policy, OpenAPITags.User],
request: {
params: setResourcePolicyAccessControlParamsSchema,
body: {
@@ -29,7 +29,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/header-auth",
description:
"Set or update the header authentication for a resource policy. If user and password is not provided, it will remove the header authentication.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyHeaderAuthParamsSchema,
body: {
@@ -24,7 +24,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/password",
description:
"Set the password for a resource policy. Setting the password to null will remove it.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyPasswordParamsSchema,
body: {
@@ -27,7 +27,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/pincode",
description:
"Set the PIN code for a resource policy. Setting the PIN code to null will remove it.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyPincodeParamsSchema,
body: {
@@ -78,10 +78,7 @@ export async function setResourcePolicyPincode(
await trx
.delete(resourcePolicyPincode)
.where(
eq(
resourcePolicyPincode.resourcePolicyId,
resourcePolicyId
)
eq(resourcePolicyPincode.resourcePolicyId, resourcePolicyId)
);
if (pincode) {
@@ -32,7 +32,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/whitelist",
description:
"Set email whitelist for a resource policy. This will replace all existing emails.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyWhitelistParamsSchema,
body: {
@@ -29,7 +29,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/whitelist/add",
description: "Add a single email to the resource whitelist.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: addEmailToResourceWhitelistParamsSchema,
body: {
+1 -1
View File
@@ -29,7 +29,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/roles/add",
description: "Add a single role to a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
request: {
params: addRoleToResourceParamsSchema,
body: {
+1 -1
View File
@@ -29,7 +29,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/users/add",
description: "Add a single user to a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PublicResource, OpenAPITags.User],
request: {
params: addUserToResourceParamsSchema,
body: {
+1 -1
View File
@@ -78,7 +78,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/resource",
description: "Create a resource.",
tags: [OpenAPITags.Org, OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: createResourceParamsSchema,
body: {
@@ -31,7 +31,7 @@ registry.registerPath({
method: "put",
path: "/resource/{resourceId}/rule",
description: "Create a resource rule.",
tags: [OpenAPITags.Resource, OpenAPITags.Rule],
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: {
params: createResourceRuleParamsSchema,
body: {
+1 -1
View File
@@ -22,7 +22,7 @@ registry.registerPath({
method: "delete",
path: "/resource/{resourceId}",
description: "Delete a resource.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: deleteResourceSchema
},
@@ -19,7 +19,7 @@ registry.registerPath({
method: "delete",
path: "/resource/{resourceId}/rule/{ruleId}",
description: "Delete a resource rule.",
tags: [OpenAPITags.Resource, OpenAPITags.Rule],
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: {
params: deleteResourceRuleSchema
},
+2 -2
View File
@@ -54,7 +54,7 @@ registry.registerPath({
path: "/org/{orgId}/resource/{niceId}",
description:
"Get a resource by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.",
tags: [OpenAPITags.Org, OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: z.object({
orgId: z.string(),
@@ -68,7 +68,7 @@ registry.registerPath({
method: "get",
path: "/resource/{resourceId}",
description: "Get a resource by resourceId.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: z.object({
resourceId: z.number()
@@ -31,7 +31,7 @@ registry.registerPath({
method: "get",
path: "/resource/{resourceId}/whitelist",
description: "Get the whitelist of emails for a specific resource.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: getResourceWhitelistSchema
},
-4
View File
@@ -31,7 +31,3 @@ export * from "./addUserToResource";
export * from "./removeUserFromResource";
export * from "./listAllResourceNames";
export * from "./removeEmailFromResourceWhitelist";
export * from "./listResourcePolicyRoles";
export * from "./listResourcePolicyUsers";
export * from "./setResourcePolicyRoles";
export * from "./setResourcePolicyUsers";
@@ -33,7 +33,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/resources-names",
description: "List all resource names for an organization.",
tags: [OpenAPITags.Org, OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: z.object({
orgId: z.string()
@@ -1,80 +0,0 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { roleResources, roles } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const listResourcePolicyRolesSchema = z.strictObject({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
async function query(resourcePolicyId: number) {
return await db
.selectDistinct({
roleId: roles.roleId,
name: roles.name,
description: roles.description,
isAdmin: roles.isAdmin
})
.from(roleResources)
.innerJoin(roles, eq(roleResources.roleId, roles.roleId))
.where(eq(roleResources.resourcePolicyId, resourcePolicyId));
}
export type ListResourcePolicyRolesResponse = {
roles: NonNullable<Awaited<ReturnType<typeof query>>>;
};
registry.registerPath({
method: "get",
path: "/resource-policy/{resourcePolicyId}/roles",
description: "List all roles for a resource policy.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
request: {
params: listResourcePolicyRolesSchema
},
responses: {}
});
export async function listResourcePolicyRoles(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listResourcePolicyRolesSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const policyRolesList = await query(resourcePolicyId);
return response<ListResourcePolicyRolesResponse>(res, {
data: {
roles: policyRolesList
},
success: true,
error: false,
message: "Resource policy roles retrieved successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
@@ -1,85 +0,0 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { idp, userResources, users } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
const listResourcePolicyUsersSchema = z.strictObject({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
async function queryUsers(resourcePolicyId: number) {
return await db
.selectDistinct({
userId: userResources.userId,
username: users.username,
type: users.type,
idpName: idp.name,
idpId: users.idpId,
email: users.email
})
.from(userResources)
.innerJoin(users, eq(userResources.userId, users.userId))
.leftJoin(idp, eq(users.idpId, idp.idpId))
.where(eq(userResources.resourcePolicyId, resourcePolicyId));
}
export type ListResourcePolicyUsersResponse = {
users: NonNullable<Awaited<ReturnType<typeof queryUsers>>>;
};
registry.registerPath({
method: "get",
path: "/resource-policy/{resourcePolicyId}/users",
description: "List all users for a resource policy.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
request: {
params: listResourcePolicyUsersSchema
},
responses: {}
});
export async function listResourcePolicyUsers(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = listResourcePolicyUsersSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const policyUsersList = await queryUsers(resourcePolicyId);
return response<ListResourcePolicyUsersResponse>(res, {
data: {
users: policyUsersList
},
success: true,
error: false,
message: "Resource policy users retrieved successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ registry.registerPath({
method: "get",
path: "/resource/{resourceId}/roles",
description: "List all roles for a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
request: {
params: listResourceRolesSchema
},
+1 -1
View File
@@ -56,7 +56,7 @@ registry.registerPath({
method: "get",
path: "/resource/{resourceId}/rules",
description: "List rules for a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Rule],
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: {
params: listResourceRulesParamsSchema,
query: listResourceRulesSchema
+1 -1
View File
@@ -38,7 +38,7 @@ registry.registerPath({
method: "get",
path: "/resource/{resourceId}/users",
description: "List all users for a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PublicResource, OpenAPITags.User],
request: {
params: listResourceUsersSchema
},
+1 -1
View File
@@ -225,7 +225,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/resources",
description: "List resources for an organization.",
tags: [OpenAPITags.Org, OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: z.object({
orgId: z.string()
@@ -29,7 +29,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/whitelist/remove",
description: "Remove a single email from the resource whitelist.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: removeEmailFromResourceWhitelistParamsSchema,
body: {
@@ -29,7 +29,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/roles/remove",
description: "Remove a single role from a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
request: {
params: removeRoleFromResourceParamsSchema,
body: {
@@ -29,7 +29,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/users/remove",
description: "Remove a single user from a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PublicResource, OpenAPITags.User],
request: {
params: removeUserFromResourceParamsSchema,
body: {
@@ -29,7 +29,7 @@ registry.registerPath({
path: "/resource/{resourceId}/header-auth",
description:
"Set or update the header authentication for a resource. If user and password is not provided, it will remove the header authentication.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: setResourceAuthMethodsParamsSchema,
body: {
@@ -25,7 +25,7 @@ registry.registerPath({
path: "/resource/{resourceId}/password",
description:
"Set the password for a resource. Setting the password to null will remove it.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: setResourceAuthMethodsParamsSchema,
body: {
@@ -29,7 +29,7 @@ registry.registerPath({
path: "/resource/{resourceId}/pincode",
description:
"Set the PIN code for a resource. Setting the PIN code to null will remove it.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: setResourceAuthMethodsParamsSchema,
body: {
@@ -1,165 +0,0 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resourcePolicies, resources, roleResources, roles } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { eq, and, ne, inArray } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const setResourcePolicyRolesBodySchema = z.strictObject({
roleIds: z.array(z.int().positive())
});
const setResourcePolicyRolesParamsSchema = z.strictObject({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
registry.registerPath({
method: "post",
path: "/resource-policy/{resourcePolicyId}/roles",
description:
"Set roles for a resource policy. This will replace all existing roles across all resources under this policy.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
request: {
params: setResourcePolicyRolesParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyRolesBodySchema
}
}
}
},
responses: {}
});
export async function setResourcePolicyRoles(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = setResourcePolicyRolesBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { roleIds } = parsedBody.data;
const parsedParams = setResourcePolicyRolesParamsSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const [policy] = await db
.select()
.from(resourcePolicies)
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId))
.limit(1);
if (!policy) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource policy not found")
);
}
// Check if any of the roleIds are admin roles
const rolesToCheck = await db
.select()
.from(roles)
.where(
and(
inArray(roles.roleId, roleIds),
eq(roles.orgId, policy.orgId)
)
);
const hasAdminRole = rolesToCheck.some((role) => role.isAdmin);
if (hasAdminRole) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Admin role cannot be assigned to resource policies"
)
);
}
// Get admin role IDs for this org to exclude from deletion
const adminRoles = await db
.select()
.from(roles)
.where(
and(eq(roles.isAdmin, true), eq(roles.orgId, policy.orgId))
);
const adminRoleIds = adminRoles.map((role) => role.roleId);
// Get all resources under this policy
const policyResources = await db
.select({ resourceId: resources.resourceId })
.from(resources)
.where(eq(resources.resourcePolicyId, resourcePolicyId));
await db.transaction(async (trx) => {
// Delete existing role associations for this policy (excluding admin roles)
if (adminRoleIds.length > 0) {
await trx.delete(roleResources).where(
and(
eq(roleResources.resourcePolicyId, resourcePolicyId),
ne(roleResources.roleId, adminRoleIds[0])
)
);
} else {
await trx
.delete(roleResources)
.where(
eq(roleResources.resourcePolicyId, resourcePolicyId)
);
}
// Insert new role associations for each resource under the policy
if (roleIds.length > 0 && policyResources.length > 0) {
await Promise.all(
policyResources.flatMap(({ resourceId }) =>
roleIds.map((roleId) =>
trx
.insert(roleResources)
.values({ roleId, resourceId, resourcePolicyId })
.returning()
)
)
);
}
return response(res, {
data: {},
success: true,
error: false,
message: "Roles set for resource policy successfully",
status: HttpCode.CREATED
});
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
@@ -1,124 +0,0 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resourcePolicies, resources, userResources } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { eq } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const setResourcePolicyUsersBodySchema = z.strictObject({
userIds: z.array(z.string())
});
const setResourcePolicyUsersParamsSchema = z.strictObject({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
registry.registerPath({
method: "post",
path: "/resource-policy/{resourcePolicyId}/users",
description:
"Set users for a resource policy. This will replace all existing users across all resources under this policy.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
request: {
params: setResourcePolicyUsersParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyUsersBodySchema
}
}
}
},
responses: {}
});
export async function setResourcePolicyUsers(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = setResourcePolicyUsersBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { userIds } = parsedBody.data;
const parsedParams = setResourcePolicyUsersParamsSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const [policy] = await db
.select()
.from(resourcePolicies)
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId))
.limit(1);
if (!policy) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource policy not found")
);
}
// Get all resources under this policy
const policyResources = await db
.select({ resourceId: resources.resourceId })
.from(resources)
.where(eq(resources.resourcePolicyId, resourcePolicyId));
await db.transaction(async (trx) => {
// Delete existing user associations for this policy
await trx
.delete(userResources)
.where(eq(userResources.resourcePolicyId, resourcePolicyId));
// Insert new user associations for each resource under the policy
if (userIds.length > 0 && policyResources.length > 0) {
await Promise.all(
policyResources.flatMap(({ resourceId }) =>
userIds.map((userId) =>
trx
.insert(userResources)
.values({ userId, resourceId, resourcePolicyId })
.returning()
)
)
);
}
return response(res, {
data: {},
success: true,
error: false,
message: "Users set for resource policy successfully",
status: HttpCode.CREATED
});
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ registry.registerPath({
path: "/resource/{resourceId}/roles",
description:
"Set roles for a resource. This will replace all existing roles.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
request: {
params: setResourceRolesParamsSchema,
body: {
+1 -1
View File
@@ -23,7 +23,7 @@ registry.registerPath({
path: "/resource/{resourceId}/users",
description:
"Set users for a resource. This will replace all existing users.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PublicResource, OpenAPITags.User],
request: {
params: setUserResourcesParamsSchema,
body: {
@@ -32,7 +32,7 @@ registry.registerPath({
path: "/resource/{resourceId}/whitelist",
description:
"Set email whitelist for a resource. This will replace all existing emails.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: setResourceWhitelistParamsSchema,
body: {
+44 -1
View File
@@ -101,6 +101,49 @@ const updateHttpResourceBodySchema = z
{
error: "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header."
}
)
.refine(
(data) => {
if (data.headers) {
// HTTP header names must be valid token characters (RFC 7230)
const validHeaderName = /^[a-zA-Z0-9!#$%&'*+\-.^_`|~]+$/;
return data.headers.every((h) => validHeaderName.test(h.name));
}
return true;
},
{
error: "Header names may only contain valid HTTP token characters (letters, digits, and !#$%&'*+-.^_`|~)."
}
)
.refine(
(data) => {
if (data.headers) {
// HTTP header values must be visible ASCII or horizontal whitespace, no control chars (RFC 7230)
const validHeaderValue = /^[\t\x20-\x7E]*$/;
return data.headers.every((h) => validHeaderValue.test(h.value));
}
return true;
},
{
error: "Header values may only contain printable ASCII characters and horizontal whitespace."
}
)
.refine(
(data) => {
if (data.headers) {
// Reject Traefik template syntax {{word}} in names or values
const templatePattern = /\{\{[^}]+\}\}/;
return data.headers.every(
(h) =>
!templatePattern.test(h.name) &&
!templatePattern.test(h.value)
);
}
return true;
},
{
error: "Header names and values must not contain template expressions such as {{value}}."
}
);
export type UpdateResourceResponse = Resource;
@@ -136,7 +179,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}",
description: "Update a resource.",
tags: [OpenAPITags.Resource],
tags: [OpenAPITags.PublicResource],
request: {
params: updateResourceParamsSchema,
body: {
@@ -38,7 +38,7 @@ registry.registerPath({
method: "post",
path: "/resource/{resourceId}/rule/{ruleId}",
description: "Update a resource rule.",
tags: [OpenAPITags.Resource, OpenAPITags.Rule],
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: {
params: updateResourceRuleParamsSchema,
body: {
+1 -1
View File
@@ -45,7 +45,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/role",
description: "Create a role.",
tags: [OpenAPITags.Org, OpenAPITags.Role],
tags: [OpenAPITags.Role],
request: {
params: createRoleParamsSchema,
body: {
+2 -2
View File
@@ -7,7 +7,7 @@ import { and, eq, inArray, sql } from "drizzle-orm";
import { ActionsEnum } from "@server/auth/actions";
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import { z } from "zod";
import { object, z } from "zod";
import { fromError } from "zod-validation-error";
const listRolesParamsSchema = z.strictObject({
@@ -64,7 +64,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/roles",
description: "List roles.",
tags: [OpenAPITags.Org, OpenAPITags.Role],
tags: [OpenAPITags.Role],
request: {
params: listRolesParamsSchema,
query: listRolesSchema
+2 -2
View File
@@ -58,7 +58,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/site",
description: "Create a new site.",
tags: [OpenAPITags.Site, OpenAPITags.Org],
tags: [OpenAPITags.Site],
request: {
params: createSiteParamsSchema,
body: {
@@ -292,7 +292,7 @@ export async function createSite(
if (type == "newt") {
[newSite] = await trx
.insert(sites)
.values({
.values({ // NOTE: NO SUBNET OR EXIT NODE ID PASSED IN HERE BECAUSE ITS NOW CHOSEN ON CONNECT
orgId,
name,
niceId,
+1 -1
View File
@@ -51,7 +51,7 @@ registry.registerPath({
path: "/org/{orgId}/site/{niceId}",
description:
"Get a site by orgId and niceId. NiceId is a readable ID for the site and unique on a per org basis.",
tags: [OpenAPITags.Org, OpenAPITags.Site],
tags: [OpenAPITags.Site],
request: {
params: z.object({
orgId: z.string(),
+1 -1
View File
@@ -180,7 +180,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/sites",
description: "List all sites in an organization",
tags: [OpenAPITags.Org, OpenAPITags.Site],
tags: [OpenAPITags.Site],
request: {
params: listSitesParamsSchema,
query: listSitesSchema
+1 -1
View File
@@ -35,7 +35,7 @@ registry.registerPath({
path: "/org/{orgId}/pick-site-defaults",
description:
"Return pre-requisite data for creating a site, such as the exit node, subnet, Newt credentials, etc.",
tags: [OpenAPITags.Org, OpenAPITags.Site],
tags: [OpenAPITags.Site],
request: {
params: z.object({
orgId: z.string()
@@ -30,7 +30,7 @@ registry.registerPath({
path: "/site-resource/{siteResourceId}/clients/add",
description:
"Add a single client to a site resource. Clients with a userId cannot be added.",
tags: [OpenAPITags.Resource, OpenAPITags.Client],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
request: {
params: addClientToSiteResourceParamsSchema,
body: {
@@ -30,7 +30,7 @@ registry.registerPath({
method: "post",
path: "/site-resource/{siteResourceId}/roles/add",
description: "Add a single role to a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: {
params: addRoleToSiteResourceParamsSchema,
body: {
@@ -30,7 +30,7 @@ registry.registerPath({
method: "post",
path: "/site-resource/{siteResourceId}/users/add",
description: "Add a single user to a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: {
params: addUserToSiteResourceParamsSchema,
body: {
@@ -114,7 +114,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/site-resource",
description: "Create a new site resource.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: createSiteResourceParamsSchema,
body: {
@@ -23,7 +23,7 @@ registry.registerPath({
method: "delete",
path: "/site-resource/{siteResourceId}",
description: "Delete a site resource.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: deleteSiteResourceParamsSchema
},
@@ -65,7 +65,7 @@ registry.registerPath({
method: "get",
path: "/site-resource/{siteResourceId}",
description: "Get a specific site resource by siteResourceId.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: z.object({
siteResourceId: z.number(),
@@ -80,7 +80,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/site/{siteId}/resource/nice/{niceId}",
description: "Get a specific site resource by niceId.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: z.object({
niceId: z.string(),
@@ -112,7 +112,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/site-resources",
description: "List all site resources for an organization.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: listAllSiteResourcesByOrgParamsSchema,
query: listAllSiteResourcesByOrgQuerySchema
@@ -39,7 +39,7 @@ registry.registerPath({
method: "get",
path: "/site-resource/{siteResourceId}/clients",
description: "List all clients for a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Client],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
request: {
params: listSiteResourceClientsSchema
},
@@ -40,7 +40,7 @@ registry.registerPath({
method: "get",
path: "/site-resource/{siteResourceId}/roles",
description: "List all roles for a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: {
params: listSiteResourceRolesSchema
},
@@ -43,7 +43,7 @@ registry.registerPath({
method: "get",
path: "/site-resource/{siteResourceId}/users",
description: "List all users for a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: {
params: listSiteResourceUsersSchema
},
@@ -58,7 +58,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/site/{siteId}/resources",
description: "List site resources for a site.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: listSiteResourcesParamsSchema,
query: listSiteResourcesQuerySchema
@@ -30,7 +30,7 @@ registry.registerPath({
path: "/site-resource/{siteResourceId}/clients/remove",
description:
"Remove a single client from a site resource. Clients with a userId cannot be removed.",
tags: [OpenAPITags.Resource, OpenAPITags.Client],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
request: {
params: removeClientFromSiteResourceParamsSchema,
body: {
@@ -30,7 +30,7 @@ registry.registerPath({
method: "post",
path: "/site-resource/{siteResourceId}/roles/remove",
description: "Remove a single role from a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: {
params: removeRoleFromSiteResourceParamsSchema,
body: {
@@ -30,7 +30,7 @@ registry.registerPath({
method: "post",
path: "/site-resource/{siteResourceId}/users/remove",
description: "Remove a single user from a site resource.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: {
params: removeUserFromSiteResourceParamsSchema,
body: {
@@ -30,7 +30,7 @@ registry.registerPath({
path: "/site-resource/{siteResourceId}/clients",
description:
"Set clients for a site resource. This will replace all existing clients. Clients with a userId cannot be added.",
tags: [OpenAPITags.Resource, OpenAPITags.Client],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
request: {
params: setSiteResourceClientsParamsSchema,
body: {
@@ -31,7 +31,7 @@ registry.registerPath({
path: "/site-resource/{siteResourceId}/roles",
description:
"Set roles for a site resource. This will replace all existing roles.",
tags: [OpenAPITags.Resource, OpenAPITags.Role],
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: {
params: setSiteResourceRolesParamsSchema,
body: {
@@ -31,7 +31,7 @@ registry.registerPath({
path: "/site-resource/{siteResourceId}/users",
description:
"Set users for a site resource. This will replace all existing users.",
tags: [OpenAPITags.Resource, OpenAPITags.User],
tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: {
params: setSiteResourceUsersParamsSchema,
body: {
@@ -121,7 +121,7 @@ registry.registerPath({
method: "post",
path: "/site-resource/{siteResourceId}",
description: "Update a site resource.",
tags: [OpenAPITags.Client, OpenAPITags.Org],
tags: [OpenAPITags.PrivateResource],
request: {
params: updateSiteResourceParamsSchema,
body: {
+1 -1
View File
@@ -58,7 +58,7 @@ registry.registerPath({
method: "put",
path: "/resource/{resourceId}/target",
description: "Create a target for a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Target],
tags: [OpenAPITags.PublicResource, OpenAPITags.Target],
request: {
params: createTargetParamsSchema,
body: {
+1 -1
View File
@@ -88,7 +88,7 @@ registry.registerPath({
method: "get",
path: "/resource/{resourceId}/targets",
description: "List targets for a resource.",
tags: [OpenAPITags.Resource, OpenAPITags.Target],
tags: [OpenAPITags.PublicResource, OpenAPITags.Target],
request: {
params: listTargetsParamsSchema,
query: listTargetsSchema
+1 -1
View File
@@ -37,7 +37,7 @@ registry.registerPath({
method: "put",
path: "/org/{orgId}/user",
description: "Create an organization user.",
tags: [OpenAPITags.User, OpenAPITags.Org],
tags: [OpenAPITags.User],
request: {
params: paramsSchema,
body: {
+1 -1
View File
@@ -55,7 +55,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/user/{userId}",
description: "Get a user in an organization.",
tags: [OpenAPITags.Org, OpenAPITags.User],
tags: [OpenAPITags.User],
request: {
params: getOrgUserParamsSchema
},
+1 -1
View File
@@ -35,7 +35,7 @@ registry.registerPath({
path: "/org/{orgId}/user-by-username",
description:
"Get a user in an organization by username. When idpId is not passed, only internal users are searched (username is globally unique for them). For external (OIDC) users, pass idpId to search by username within that identity provider.",
tags: [OpenAPITags.Org, OpenAPITags.User],
tags: [OpenAPITags.User],
request: {
params: getOrgUserByUsernameParamsSchema,
query: getOrgUserByUsernameQuerySchema
+1 -1
View File
@@ -44,7 +44,7 @@ registry.registerPath({
method: "post",
path: "/org/{orgId}/create-invite",
description: "Invite a user to join an organization.",
tags: [OpenAPITags.Org],
tags: [OpenAPITags.Invitation],
request: {
params: inviteUserParamsSchema,
body: {
+1 -1
View File
@@ -54,7 +54,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/invitations",
description: "List invitations in an organization.",
tags: [OpenAPITags.Org, OpenAPITags.Invitation],
tags: [OpenAPITags.Invitation],
request: {
params: listInvitationsParamsSchema,
query: listInvitationsQuerySchema
+1 -1
View File
@@ -69,7 +69,7 @@ registry.registerPath({
method: "get",
path: "/org/{orgId}/users",
description: "List users in an organization.",
tags: [OpenAPITags.Org, OpenAPITags.User],
tags: [OpenAPITags.User],
request: {
params: listUsersParamsSchema,
query: listUsersSchema
+1 -1
View File
@@ -19,7 +19,7 @@ registry.registerPath({
method: "delete",
path: "/org/{orgId}/invitations/{inviteId}",
description: "Remove an open invitation from an organization",
tags: [OpenAPITags.Org],
tags: [OpenAPITags.Invitation],
request: {
params: removeInvitationParamsSchema
},

Some files were not shown because too many files have changed in this diff Show More