Merge pull request #1733 from Fredkiss3/feat-blueprint-ui-on-dashboard

feat: blueprint ui on dashboard
This commit is contained in:
Owen Schwartz
2025-10-29 20:45:31 -07:00
committed by GitHub
45 changed files with 1695 additions and 142 deletions

View File

@@ -119,6 +119,8 @@ export enum ActionsEnum {
updateLoginPage = "updateLoginPage",
getLoginPage = "getLoginPage",
deleteLoginPage = "deleteLoginPage",
listBlueprints = "listBlueprints",
getBlueprint = "getBlueprint",
applyBlueprint = "applyBlueprint",
viewLogs = "viewLogs",
exportLogs = "exportLogs"
@@ -198,7 +200,6 @@ export async function checkUserActionPermission(
.limit(1);
return roleActionPermission.length > 0;
} catch (error) {
console.error("Error checking user action permission:", error);
throw createHttpError(

View File

@@ -25,7 +25,6 @@ export const domains = pgTable("domains", {
preferWildcardCert: boolean("preferWildcardCert")
});
export const dnsRecords = pgTable("dnsRecords", {
id: serial("id").primaryKey(),
domainId: varchar("domainId")
@@ -34,7 +33,7 @@ export const dnsRecords = pgTable("dnsRecords", {
recordType: varchar("recordType").notNull(), // "NS" | "CNAME" | "A" | "TXT"
baseDomain: varchar("baseDomain"),
value: varchar("value").notNull(),
verified: boolean("verified").notNull().default(false),
verified: boolean("verified").notNull().default(false)
});
export const orgs = pgTable("orgs", {
@@ -703,6 +702,21 @@ export const setupTokens = pgTable("setupTokens", {
dateUsed: varchar("dateUsed")
});
// Blueprint runs
export const blueprints = pgTable("blueprints", {
blueprintId: serial("blueprintId").primaryKey(),
orgId: text("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
name: varchar("name").notNull(),
source: varchar("source").notNull(),
createdAt: integer("createdAt").notNull(),
succeeded: boolean("succeeded").notNull(),
contents: text("contents").notNull(),
message: text("message")
});
export const requestAuditLog = pgTable(
"requestAuditLog",
{
@@ -790,6 +804,7 @@ export type SetupToken = InferSelectModel<typeof setupTokens>;
export type HostMeta = InferSelectModel<typeof hostMeta>;
export type TargetHealthCheck = InferSelectModel<typeof targetHealthCheck>;
export type IdpOidcConfig = InferSelectModel<typeof idpOidcConfig>;
export type Blueprint = InferSelectModel<typeof blueprints>;
export type LicenseKey = InferSelectModel<typeof licenseKey>;
export type SecurityKey = InferSelectModel<typeof securityKeys>;
export type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;

View File

@@ -13,12 +13,17 @@ bootstrapVolume();
function createDb() {
const sqlite = new Database(location);
return DrizzleSqlite(sqlite, { schema });
return DrizzleSqlite(sqlite, {
schema,
logger: process.env.NODE_ENV === "development"
});
}
export const db = createDb();
export default db;
export type Transaction = Parameters<Parameters<typeof db["transaction"]>[0]>[0];
export type Transaction = Parameters<
Parameters<(typeof db)["transaction"]>[0]
>[0];
function checkFileExists(filePath: string): boolean {
try {

View File

@@ -749,6 +749,23 @@ export const idpOrg = sqliteTable("idpOrg", {
orgMapping: text("orgMapping")
});
// Blueprint runs
export const blueprints = sqliteTable("blueprints", {
blueprintId: integer("blueprintId").primaryKey({
autoIncrement: true
}),
orgId: text("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
name: text("name").notNull(),
source: text("source").notNull(),
createdAt: integer("createdAt").notNull(),
succeeded: integer("succeeded", { mode: "boolean" }).notNull(),
contents: text("contents").notNull(),
message: text("message")
});
export const requestAuditLog = sqliteTable(
"requestAuditLog",
{
@@ -837,6 +854,7 @@ export type SetupToken = InferSelectModel<typeof setupTokens>;
export type HostMeta = InferSelectModel<typeof hostMeta>;
export type TargetHealthCheck = InferSelectModel<typeof targetHealthCheck>;
export type IdpOidcConfig = InferSelectModel<typeof idpOidcConfig>;
export type Blueprint = InferSelectModel<typeof blueprints>;
export type LicenseKey = InferSelectModel<typeof licenseKey>;
export type SecurityKey = InferSelectModel<typeof securityKeys>;
export type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;

View File

@@ -1,22 +1,35 @@
import { db, newts, Target } from "@server/db";
import { db, newts, blueprints, Blueprint } from "@server/db";
import { Config, ConfigSchema } from "./types";
import { ProxyResourcesResults, updateProxyResources } from "./proxyResources";
import { fromError } from "zod-validation-error";
import logger from "@server/logger";
import { resources, targets, sites } from "@server/db";
import { eq, and, asc, or, ne, count, isNotNull } from "drizzle-orm";
import { sites } from "@server/db";
import { eq, and, isNotNull } from "drizzle-orm";
import { addTargets as addProxyTargets } from "@server/routers/newt/targets";
import { addTargets as addClientTargets } from "@server/routers/client/targets";
import {
ClientResourcesResults,
updateClientResources
} from "./clientResources";
import { BlueprintSource } from "@server/routers/blueprints/types";
import { stringify as stringifyYaml } from "yaml";
import { faker } from "@faker-js/faker";
export async function applyBlueprint(
orgId: string,
configData: unknown,
siteId?: number
): Promise<void> {
type ApplyBlueprintArgs = {
orgId: string;
configData: unknown;
name?: string;
siteId?: number;
source?: BlueprintSource;
};
export async function applyBlueprint({
orgId,
configData,
siteId,
name,
source = "API"
}: ApplyBlueprintArgs): Promise<Blueprint> {
// Validate the input data
const validationResult = ConfigSchema.safeParse(configData);
if (!validationResult.success) {
@@ -24,6 +37,9 @@ export async function applyBlueprint(
}
const config: Config = validationResult.data;
let blueprintSucceeded: boolean = false;
let blueprintMessage: string;
let error: any | null = null;
try {
let proxyResourcesResults: ProxyResourcesResults = [];
@@ -120,10 +136,42 @@ export async function applyBlueprint(
);
}
}
} catch (error) {
logger.error(`Failed to update database from config: ${error}`);
throw error;
blueprintSucceeded = true;
blueprintMessage = "Blueprint applied successfully";
} catch (err) {
blueprintSucceeded = false;
blueprintMessage = `Blueprint applied with errors: ${err}`;
logger.error(blueprintMessage);
error = err;
}
let blueprint: Blueprint | null = null;
await db.transaction(async (trx) => {
const newBlueprint = await trx
.insert(blueprints)
.values({
orgId,
name:
name ??
`${faker.word.adjective()} ${faker.word.adjective()} ${faker.word.noun()}`,
contents: stringifyYaml(configData),
createdAt: Math.floor(Date.now() / 1000),
succeeded: blueprintSucceeded,
message: blueprintMessage,
source
})
.returning();
blueprint = newBlueprint[0];
});
if (!blueprint || (source !== "UI" && !blueprintSucceeded)) {
// ^^^^^^^^^^^^^^^ The UI considers a failed blueprint as a valid response
throw error ?? "Unknown Server Error";
}
return blueprint;
}
// await updateDatabaseFromConfig("org_i21aifypnlyxur2", {

View File

@@ -30,7 +30,12 @@ export async function applyNewtDockerBlueprint(
logger.debug(`Received Docker blueprint: ${JSON.stringify(blueprint)}`);
// Update the blueprint in the database
await applyBlueprint(site.orgId, blueprint, site.siteId);
await applyBlueprint({
orgId: site.orgId,
configData: blueprint,
siteId: site.siteId,
source: "NEWT"
});
} catch (error) {
logger.error(`Failed to update database from config: ${error}`);
await sendToClient(newtId, {

View File

@@ -9,7 +9,7 @@ const nextPort = config.getRawConfig().server.next_port;
export async function createNextServer() {
// const app = next({ dev });
const app = next({ dev: process.env.ENVIRONMENT !== "prod" });
const app = next({ dev: process.env.ENVIRONMENT !== "prod", turbopack: true });
const handle = app.getRequestHandler();
await app.prepare();

View File

@@ -15,5 +15,6 @@ export enum OpenAPITags {
Idp = "Identity Provider",
Client = "Client",
ApiKey = "API Key",
Domain = "Domain"
Domain = "Domain",
Blueprint = "Blueprint"
}

View File

@@ -1,30 +1,12 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { eq } from "drizzle-orm";
import {
apiKeyOrg,
apiKeys,
domains,
Org,
orgDomains,
orgs,
roleActions,
roles,
userOrgs,
users,
actions
} 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 config from "@server/lib/config";
import { fromError } from "zod-validation-error";
import { defaultRoleAllowedActions } from "../role";
import { OpenAPITags, registry } from "@server/openApi";
import { isValidCIDR } from "@server/lib/validators";
import { applyBlueprint as applyBlueprintFunc } from "@server/lib/blueprints/applyBlueprint";
import { applyBlueprint } from "@server/lib/blueprints/applyBlueprint";
const applyBlueprintSchema = z
.object({
@@ -41,8 +23,8 @@ const applyBlueprintParamsSchema = z
registry.registerPath({
method: "put",
path: "/org/{orgId}/blueprint",
description: "Apply a base64 encoded blueprint to an organization",
tags: [OpenAPITags.Org],
description: "Apply a base64 encoded JSON blueprint to an organization",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
request: {
params: applyBlueprintParamsSchema,
body: {
@@ -56,7 +38,7 @@ registry.registerPath({
responses: {}
});
export async function applyBlueprint(
export async function applyJSONBlueprint(
req: Request,
res: Response,
next: NextFunction
@@ -100,7 +82,11 @@ export async function applyBlueprint(
const blueprintParsed = JSON.parse(decoded);
// Update the blueprint in the database
await applyBlueprintFunc(orgId, blueprintParsed);
await applyBlueprint({
orgId,
configData: blueprintParsed,
source: "API"
});
} catch (error) {
logger.error(`Failed to update database from config: ${error}`);
return next(

View File

@@ -0,0 +1,146 @@
import { OpenAPITags, registry } from "@server/openApi";
import z from "zod";
import { applyBlueprint } from "@server/lib/blueprints/applyBlueprint";
import { NextFunction, Request, Response } from "express";
import logger from "@server/logger";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
import { fromZodError } from "zod-validation-error";
import response from "@server/lib/response";
import { type Blueprint } from "@server/db";
import { parse as parseYaml } from "yaml";
import { ConfigSchema } from "@server/lib/blueprints/types";
const applyBlueprintSchema = z
.object({
name: z.string().min(1).max(255),
blueprint: z
.string()
.min(1)
.superRefine((val, ctx) => {
try {
parseYaml(val);
} catch (error) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid YAML: ${error instanceof Error ? error.message : "Unknown error"}`
});
}
})
})
.strict();
const applyBlueprintParamsSchema = z
.object({
orgId: z.string()
})
.strict();
export type CreateBlueprintResponse = Blueprint;
registry.registerPath({
method: "put",
path: "/org/{orgId}/blueprint",
description: "Create and apply a YAML blueprint to an organization",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
request: {
params: applyBlueprintParamsSchema,
body: {
content: {
"application/json": {
schema: applyBlueprintSchema
}
}
}
},
responses: {}
});
export async function applyYAMLBlueprint(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = applyBlueprintParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromZodError(parsedParams.error)
)
);
}
const { orgId } = parsedParams.data;
const parsedBody = applyBlueprintSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromZodError(parsedBody.error)
)
);
}
const { blueprint: contents, name } = parsedBody.data;
logger.debug(`Received blueprint:`, contents);
const parsedConfig = parseYaml(contents);
// apply the validation in advance so that error concerning the format are ruled out first
const validationResult = ConfigSchema.safeParse(parsedConfig);
if (!validationResult.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromZodError(validationResult.error)
)
);
}
let blueprint: Blueprint | null = null;
let error: string | null = null;
try {
blueprint = await applyBlueprint({
orgId,
name,
source: "UI",
configData: parsedConfig
});
} catch (err) {
// We do nothing, the error is thrown for the other APIs & websockets for backwards compatibility
// for this API, the error is already saved in the blueprint and we don't need to handle it
logger.error(err);
if (err instanceof Error) {
error = err.message;
}
}
if (!blueprint) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
error
? error
: "An unknown error occurred while applying the blueprint"
)
);
}
return response(res, {
data: blueprint,
success: true,
error: false,
message: "Done",
status: HttpCode.CREATED
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -0,0 +1,110 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { blueprints, orgs } from "@server/db";
import { eq, and } 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 stoi from "@server/lib/stoi";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { BlueprintData } from "./types";
const getBlueprintSchema = z
.object({
blueprintId: z
.string()
.transform(stoi)
.pipe(z.number().int().positive()),
orgId: z.string()
})
.strict();
async function query(blueprintId: number, orgId: string) {
// Get the client
const [blueprint] = await db
.select({
blueprintId: blueprints.blueprintId,
name: blueprints.name,
source: blueprints.source,
succeeded: blueprints.succeeded,
orgId: blueprints.orgId,
createdAt: blueprints.createdAt,
message: blueprints.message,
contents: blueprints.contents
})
.from(blueprints)
.leftJoin(orgs, eq(blueprints.orgId, orgs.orgId))
.where(
and(
eq(blueprints.blueprintId, blueprintId),
eq(blueprints.orgId, orgId)
)
)
.limit(1);
if (!blueprint) {
return null;
}
return blueprint;
}
export type GetBlueprintResponse = BlueprintData;
registry.registerPath({
method: "get",
path: "/org/{orgId}/blueprint/{blueprintId}",
description: "Get a blueprint by its blueprint ID.",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
request: {
params: getBlueprintSchema
},
responses: {}
});
export async function getBlueprint(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = getBlueprintSchema.safeParse(req.params);
if (!parsedParams.success) {
logger.error(
`Error parsing params: ${fromError(parsedParams.error).toString()}`
);
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { orgId, blueprintId } = parsedParams.data;
const blueprint = await query(blueprintId, orgId);
if (!blueprint) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Client not found")
);
}
return response<GetBlueprintResponse>(res, {
data: blueprint as BlueprintData,
success: true,
error: false,
message: "Client retrieved successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -0,0 +1,4 @@
export * from "./listBlueprints";
export * from "./applyYAMLBlueprint";
export * from "./applyJSONBlueprint";
export * from "./getBlueprint";

View File

@@ -0,0 +1,144 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db, blueprints, orgs } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { sql, eq, desc } from "drizzle-orm";
import logger from "@server/logger";
import { fromZodError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { BlueprintData } from "./types";
const listBluePrintsParamsSchema = z
.object({
orgId: z.string()
})
.strict();
const listBluePrintsSchema = z
.object({
limit: z
.string()
.optional()
.default("1000")
.transform(Number)
.pipe(z.number().int().nonnegative()),
offset: z
.string()
.optional()
.default("0")
.transform(Number)
.pipe(z.number().int().nonnegative())
})
.strict();
async function queryBlueprints(orgId: string, limit: number, offset: number) {
const res = await db
.select({
blueprintId: blueprints.blueprintId,
name: blueprints.name,
source: blueprints.source,
succeeded: blueprints.succeeded,
orgId: blueprints.orgId,
createdAt: blueprints.createdAt
})
.from(blueprints)
.leftJoin(orgs, eq(blueprints.orgId, orgs.orgId))
.where(eq(blueprints.orgId, orgId))
.orderBy(desc(blueprints.createdAt))
.limit(limit)
.offset(offset);
return res;
}
export type ListBlueprintsResponse = {
blueprints: NonNullable<
Pick<
BlueprintData,
| "blueprintId"
| "name"
| "source"
| "succeeded"
| "orgId"
| "createdAt"
>[]
>;
pagination: { total: number; limit: number; offset: number };
};
registry.registerPath({
method: "get",
path: "/org/{orgId}/blueprints",
description: "List all blueprints for a organization.",
tags: [OpenAPITags.Org, OpenAPITags.Blueprint],
request: {
params: z.object({
orgId: z.string()
}),
query: listBluePrintsSchema
},
responses: {}
});
export async function listBlueprints(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedQuery = listBluePrintsSchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromZodError(parsedQuery.error)
)
);
}
const { limit, offset } = parsedQuery.data;
const parsedParams = listBluePrintsParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromZodError(parsedParams.error)
)
);
}
const { orgId } = parsedParams.data;
const blueprintsList = await queryBlueprints(
orgId.toString(),
limit,
offset
);
const [{ count }] = await db
.select({ count: sql<number>`count(*)` })
.from(blueprints);
return response<ListBlueprintsResponse>(res, {
data: {
blueprints:
blueprintsList as ListBlueprintsResponse["blueprints"],
pagination: {
total: count,
limit,
offset
}
},
success: true,
error: false,
message: "Blueprints retrieved successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -0,0 +1,7 @@
import type { Blueprint } from "@server/db";
export type BlueprintSource = "API" | "UI" | "NEWT";
export type BlueprintData = Omit<Blueprint, "source"> & {
source: BlueprintSource;
};

View File

@@ -13,6 +13,7 @@ import * as siteResource from "./siteResource";
import * as supporterKey from "./supporterKey";
import * as accessToken from "./accessToken";
import * as idp from "./idp";
import * as blueprints from "./blueprints";
import * as apiKeys from "./apiKeys";
import * as logs from "./auditLogs";
import HttpCode from "@server/types/HttpCode";
@@ -895,6 +896,27 @@ authenticated.get(
logs.exportRequestAuditLogs
);
authenticated.get(
"/org/:orgId/blueprints",
verifyOrgAccess,
verifyUserHasAction(ActionsEnum.listBlueprints),
blueprints.listBlueprints
);
authenticated.put(
"/org/:orgId/blueprint",
verifyOrgAccess,
verifyUserHasAction(ActionsEnum.applyBlueprint),
blueprints.applyYAMLBlueprint
);
authenticated.get(
"/org/:orgId/blueprint/:blueprintId",
verifyOrgAccess,
verifyUserHasAction(ActionsEnum.getBlueprint),
blueprints.getBlueprint
);
// Auth routes
export const authRouter = Router();
unauthenticated.use("/auth", authRouter);

View File

@@ -1,5 +1,6 @@
import * as site from "./site";
import * as org from "./org";
import * as blueprints from "./blueprints";
import * as resource from "./resource";
import * as domain from "./domain";
import * as target from "./target";
@@ -52,7 +53,7 @@ authenticated.put(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.createOrg),
logActionAudit(ActionsEnum.createOrg),
org.createOrg,
org.createOrg
);
authenticated.get(
@@ -74,7 +75,7 @@ authenticated.post(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.updateOrg),
logActionAudit(ActionsEnum.updateOrg),
org.updateOrg,
org.updateOrg
);
authenticated.delete(
@@ -82,7 +83,7 @@ authenticated.delete(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.deleteOrg),
logActionAudit(ActionsEnum.deleteOrg),
org.deleteOrg,
org.deleteOrg
);
authenticated.put(
@@ -90,7 +91,7 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createSite),
logActionAudit(ActionsEnum.createSite),
site.createSite,
site.createSite
);
authenticated.get(
@@ -126,7 +127,7 @@ authenticated.post(
verifyApiKeySiteAccess,
verifyApiKeyHasAction(ActionsEnum.updateSite),
logActionAudit(ActionsEnum.updateSite),
site.updateSite,
site.updateSite
);
authenticated.delete(
@@ -134,7 +135,7 @@ authenticated.delete(
verifyApiKeySiteAccess,
verifyApiKeyHasAction(ActionsEnum.deleteSite),
logActionAudit(ActionsEnum.deleteSite),
site.deleteSite,
site.deleteSite
);
authenticated.get(
@@ -149,7 +150,7 @@ authenticated.put(
verifyApiKeySiteAccess,
verifyApiKeyHasAction(ActionsEnum.createSiteResource),
logActionAudit(ActionsEnum.createSiteResource),
siteResource.createSiteResource,
siteResource.createSiteResource
);
authenticated.get(
@@ -183,7 +184,7 @@ authenticated.post(
verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.updateSiteResource),
logActionAudit(ActionsEnum.updateSiteResource),
siteResource.updateSiteResource,
siteResource.updateSiteResource
);
authenticated.delete(
@@ -193,7 +194,7 @@ authenticated.delete(
verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.deleteSiteResource),
logActionAudit(ActionsEnum.deleteSiteResource),
siteResource.deleteSiteResource,
siteResource.deleteSiteResource
);
authenticated.put(
@@ -201,7 +202,7 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createResource),
logActionAudit(ActionsEnum.createResource),
resource.createResource,
resource.createResource
);
authenticated.put(
@@ -209,7 +210,7 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createResource),
logActionAudit(ActionsEnum.createResource),
resource.createResource,
resource.createResource
);
authenticated.get(
@@ -245,7 +246,7 @@ authenticated.post(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.inviteUser),
logActionAudit(ActionsEnum.inviteUser),
user.inviteUser,
user.inviteUser
);
authenticated.get(
@@ -274,7 +275,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.updateResource),
logActionAudit(ActionsEnum.updateResource),
resource.updateResource,
resource.updateResource
);
authenticated.delete(
@@ -282,7 +283,7 @@ authenticated.delete(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.deleteResource),
logActionAudit(ActionsEnum.deleteResource),
resource.deleteResource,
resource.deleteResource
);
authenticated.put(
@@ -290,7 +291,7 @@ authenticated.put(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.createTarget),
logActionAudit(ActionsEnum.createTarget),
target.createTarget,
target.createTarget
);
authenticated.get(
@@ -305,7 +306,7 @@ authenticated.put(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.createResourceRule),
logActionAudit(ActionsEnum.createResourceRule),
resource.createResourceRule,
resource.createResourceRule
);
authenticated.get(
@@ -320,7 +321,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.updateResourceRule),
logActionAudit(ActionsEnum.updateResourceRule),
resource.updateResourceRule,
resource.updateResourceRule
);
authenticated.delete(
@@ -328,7 +329,7 @@ authenticated.delete(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.deleteResourceRule),
logActionAudit(ActionsEnum.deleteResourceRule),
resource.deleteResourceRule,
resource.deleteResourceRule
);
authenticated.get(
@@ -343,7 +344,7 @@ authenticated.post(
verifyApiKeyTargetAccess,
verifyApiKeyHasAction(ActionsEnum.updateTarget),
logActionAudit(ActionsEnum.updateTarget),
target.updateTarget,
target.updateTarget
);
authenticated.delete(
@@ -351,7 +352,7 @@ authenticated.delete(
verifyApiKeyTargetAccess,
verifyApiKeyHasAction(ActionsEnum.deleteTarget),
logActionAudit(ActionsEnum.deleteTarget),
target.deleteTarget,
target.deleteTarget
);
authenticated.put(
@@ -359,7 +360,7 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createRole),
logActionAudit(ActionsEnum.createRole),
role.createRole,
role.createRole
);
authenticated.get(
@@ -374,7 +375,7 @@ authenticated.delete(
verifyApiKeyRoleAccess,
verifyApiKeyHasAction(ActionsEnum.deleteRole),
logActionAudit(ActionsEnum.deleteRole),
role.deleteRole,
role.deleteRole
);
authenticated.get(
@@ -390,7 +391,7 @@ authenticated.post(
verifyApiKeyUserAccess,
verifyApiKeyHasAction(ActionsEnum.addUserRole),
logActionAudit(ActionsEnum.addUserRole),
user.addUserRole,
user.addUserRole
);
authenticated.post(
@@ -399,7 +400,7 @@ authenticated.post(
verifyApiKeyRoleAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceRoles),
logActionAudit(ActionsEnum.setResourceRoles),
resource.setResourceRoles,
resource.setResourceRoles
);
authenticated.post(
@@ -408,7 +409,7 @@ authenticated.post(
verifyApiKeySetResourceUsers,
verifyApiKeyHasAction(ActionsEnum.setResourceUsers),
logActionAudit(ActionsEnum.setResourceUsers),
resource.setResourceUsers,
resource.setResourceUsers
);
authenticated.post(
@@ -416,7 +417,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourcePassword),
logActionAudit(ActionsEnum.setResourcePassword),
resource.setResourcePassword,
resource.setResourcePassword
);
authenticated.post(
@@ -424,7 +425,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourcePincode),
logActionAudit(ActionsEnum.setResourcePincode),
resource.setResourcePincode,
resource.setResourcePincode
);
authenticated.post(
@@ -432,7 +433,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceHeaderAuth),
logActionAudit(ActionsEnum.setResourceHeaderAuth),
resource.setResourceHeaderAuth,
resource.setResourceHeaderAuth
);
authenticated.post(
@@ -440,7 +441,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
logActionAudit(ActionsEnum.setResourceWhitelist),
resource.setResourceWhitelist,
resource.setResourceWhitelist
);
authenticated.post(
@@ -469,7 +470,7 @@ authenticated.post(
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.generateAccessToken),
logActionAudit(ActionsEnum.generateAccessToken),
accessToken.generateAccessToken,
accessToken.generateAccessToken
);
authenticated.delete(
@@ -477,7 +478,7 @@ authenticated.delete(
verifyApiKeyAccessTokenAccess,
verifyApiKeyHasAction(ActionsEnum.deleteAcessToken),
logActionAudit(ActionsEnum.deleteAcessToken),
accessToken.deleteAccessToken,
accessToken.deleteAccessToken
);
authenticated.get(
@@ -506,7 +507,7 @@ authenticated.post(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.updateUser),
logActionAudit(ActionsEnum.updateUser),
user.updateUser2FA,
user.updateUser2FA
);
authenticated.get(
@@ -528,7 +529,7 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createOrgUser),
logActionAudit(ActionsEnum.createOrgUser),
user.createOrgUser,
user.createOrgUser
);
authenticated.post(
@@ -537,7 +538,7 @@ authenticated.post(
verifyApiKeyUserAccess,
verifyApiKeyHasAction(ActionsEnum.updateOrgUser),
logActionAudit(ActionsEnum.updateOrgUser),
user.updateOrgUser,
user.updateOrgUser
);
authenticated.delete(
@@ -546,7 +547,7 @@ authenticated.delete(
verifyApiKeyUserAccess,
verifyApiKeyHasAction(ActionsEnum.removeUser),
logActionAudit(ActionsEnum.removeUser),
user.removeUserOrg,
user.removeUserOrg
);
// authenticated.put(
@@ -567,7 +568,7 @@ authenticated.post(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.setApiKeyActions),
logActionAudit(ActionsEnum.setApiKeyActions),
apiKeys.setApiKeyActions,
apiKeys.setApiKeyActions
);
authenticated.get(
@@ -582,7 +583,7 @@ authenticated.put(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.createApiKey),
logActionAudit(ActionsEnum.createApiKey),
apiKeys.createOrgApiKey,
apiKeys.createOrgApiKey
);
authenticated.delete(
@@ -590,7 +591,7 @@ authenticated.delete(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.deleteApiKey),
logActionAudit(ActionsEnum.deleteApiKey),
apiKeys.deleteApiKey,
apiKeys.deleteApiKey
);
authenticated.put(
@@ -598,7 +599,7 @@ authenticated.put(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.createIdp),
logActionAudit(ActionsEnum.createIdp),
idp.createOidcIdp,
idp.createOidcIdp
);
authenticated.post(
@@ -606,7 +607,7 @@ authenticated.post(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.updateIdp),
logActionAudit(ActionsEnum.updateIdp),
idp.updateOidcIdp,
idp.updateOidcIdp
);
authenticated.get(
@@ -628,7 +629,7 @@ authenticated.put(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.createIdpOrg),
logActionAudit(ActionsEnum.createIdpOrg),
idp.createIdpOrgPolicy,
idp.createIdpOrgPolicy
);
authenticated.post(
@@ -636,7 +637,7 @@ authenticated.post(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.updateIdpOrg),
logActionAudit(ActionsEnum.updateIdpOrg),
idp.updateIdpOrgPolicy,
idp.updateIdpOrgPolicy
);
authenticated.delete(
@@ -644,7 +645,7 @@ authenticated.delete(
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.deleteIdpOrg),
logActionAudit(ActionsEnum.deleteIdpOrg),
idp.deleteIdpOrgPolicy,
idp.deleteIdpOrgPolicy
);
authenticated.get(
@@ -684,7 +685,7 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.createClient),
logActionAudit(ActionsEnum.createClient),
client.createClient,
client.createClient
);
authenticated.delete(
@@ -693,7 +694,7 @@ authenticated.delete(
verifyApiKeyClientAccess,
verifyApiKeyHasAction(ActionsEnum.deleteClient),
logActionAudit(ActionsEnum.deleteClient),
client.deleteClient,
client.deleteClient
);
authenticated.post(
@@ -702,7 +703,7 @@ authenticated.post(
verifyApiKeyClientAccess,
verifyApiKeyHasAction(ActionsEnum.updateClient),
logActionAudit(ActionsEnum.updateClient),
client.updateClient,
client.updateClient
);
authenticated.put(
@@ -710,5 +711,5 @@ authenticated.put(
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.applyBlueprint),
logActionAudit(ActionsEnum.applyBlueprint),
org.applyBlueprint,
blueprints.applyJSONBlueprint
);

View File

@@ -43,7 +43,12 @@ export const handleApplyBlueprintMessage: MessageHandler = async (context) => {
try {
const blueprintParsed = JSON.parse(blueprint);
// Update the blueprint in the database
await applyBlueprint(site.orgId, blueprintParsed, site.siteId);
await applyBlueprint({
orgId: site.orgId,
configData: blueprintParsed,
siteId: site.siteId,
source: "NEWT"
});
} catch (error) {
logger.error(`Failed to update database from config: ${error}`);
return {

View File

@@ -7,5 +7,4 @@ export * from "./checkId";
export * from "./getOrgOverview";
export * from "./listOrgs";
export * from "./pickOrgDefaults";
export * from "./applyBlueprint";
export * from "./checkOrgUserAccess";