🚧 prepare tables for auth methods

This commit is contained in:
Fred KISSIE
2026-03-03 03:20:03 +01:00
parent 0ddcce6fe1
commit 590f2c29b3
8 changed files with 406 additions and 9 deletions

View File

@@ -141,7 +141,10 @@ export enum ActionsEnum {
listResourcePolicyRoles = "listResourcePolicyRoles",
setResourcePolicyRoles = "setResourcePolicyRoles",
listResourcePolicyUsers = "listResourcePolicyUsers",
setResourcePolicyUsers = "setResourcePolicyUsers"
setResourcePolicyUsers = "setResourcePolicyUsers",
setResourcePolicyPassword = "setResourcePolicyPassword",
setResourcePolicyPincode = "setResourcePolicyPincode",
setResourcePolicyHeaderAuth = "setResourcePolicyHeaderAuth"
}
export async function checkUserActionPermission(

View File

@@ -516,11 +516,6 @@ export const resourceHeaderAuthExtendedCompatibility = pgTable(
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
resourcePolicyId: integer("resourcePolicyId")
.notNull()
.references(() => resourcePolicies.resourcePolicyId, {
onDelete: "cascade"
}),
extendedCompatibilityIsActivated: boolean(
"extendedCompatibilityIsActivated"
)
@@ -571,9 +566,6 @@ export const resourcePolicyPassword = pgTable("resourcePolicyPassword", {
export const resourcePolicyHeaderAuth = pgTable("resourcePolicyHeaderAuth", {
headerAuthId: serial("headerAuthId").primaryKey(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
headerAuthHash: varchar("headerAuthHash").notNull(),
resourcePolicyId: integer("resourcePolicyId")
.notNull()

View File

@@ -734,6 +734,33 @@ authenticated.post(
resource.setResourcePolicyUsers
);
authenticated.post(
"/resource-policy/:resourcePolicyId/password",
verifyResourcePolicyAccess,
verifyLimits,
verifyUserHasAction(ActionsEnum.setResourcePolicyPassword),
logActionAudit(ActionsEnum.setResourcePolicyPassword),
policy.setResourcePolicyPassword
);
authenticated.post(
"/resource-policy/:resourcePolicyId/pincode",
verifyResourcePolicyAccess,
verifyLimits,
verifyUserHasAction(ActionsEnum.setResourcePolicyPincode),
logActionAudit(ActionsEnum.setResourcePolicyPincode),
policy.setResourcePolicyPincode
);
authenticated.post(
"/resource-policy/:resourcePolicyId/header-auth",
verifyResourcePolicyAccess,
verifyLimits,
verifyUserHasAction(ActionsEnum.setResourcePolicyHeaderAuth),
logActionAudit(ActionsEnum.setResourcePolicyHeaderAuth),
policy.setResourcePolicyHeaderAuth
);
authenticated.post(
`/resource/:resourceId/password`,
verifyResourceAccess,

View File

@@ -632,6 +632,33 @@ authenticated.put(
policy.setResourcePolicyAccessControl
);
authenticated.post(
"/resource-policy/:resourcePolicyId/password",
verifyApiKeyResourcePolicyAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyPassword),
logActionAudit(ActionsEnum.setResourcePolicyPassword),
policy.setResourcePolicyPassword
);
authenticated.post(
"/resource-policy/:resourcePolicyId/pincode",
verifyApiKeyResourcePolicyAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyPincode),
logActionAudit(ActionsEnum.setResourcePolicyPincode),
policy.setResourcePolicyPincode
);
authenticated.post(
"/resource-policy/:resourcePolicyId/header-auth",
verifyApiKeyResourcePolicyAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyHeaderAuth),
logActionAudit(ActionsEnum.setResourcePolicyHeaderAuth),
policy.setResourcePolicyHeaderAuth
);
authenticated.post(
"/resource/:resourceId/roles/add",
verifyApiKeyResourceAccess,

View File

@@ -1,3 +1,6 @@
export * from "./getResourcePolicy";
export * from "./updateResourcePolicy";
export * from "./setResourcePolicyAccessControl";
export * from "./setResourcePolicyPassword";
export * from "./setResourcePolicyPincode";
export * from "./setResourcePolicyHeaderAuth";

View File

@@ -0,0 +1,130 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import {
db,
resourcePolicyHeaderAuth,
resourcePolicyHeaderAuthExtendedCompatibility
} from "@server/db";
import { eq } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { fromError } from "zod-validation-error";
import { response } from "@server/lib/response";
import logger from "@server/logger";
import { hashPassword } from "@server/auth/password";
import { OpenAPITags, registry } from "@server/openApi";
const setResourcePolicyHeaderAuthParamsSchema = z.object({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
const setResourcePolicyHeaderAuthBodySchema = z.strictObject({
user: z.string().min(4).max(100).nullable(),
password: z.string().min(4).max(100).nullable(),
extendedCompatibility: z.boolean().nullable()
});
registry.registerPath({
method: "post",
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],
request: {
params: setResourcePolicyHeaderAuthParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyHeaderAuthBodySchema
}
}
}
},
responses: {}
});
export async function setResourcePolicyHeaderAuth(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = setResourcePolicyHeaderAuthParamsSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const parsedBody = setResourcePolicyHeaderAuthBodySchema.safeParse(
req.body
);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const { user, password, extendedCompatibility } = parsedBody.data;
await db.transaction(async (trx) => {
await trx
.delete(resourcePolicyHeaderAuth)
.where(
eq(
resourcePolicyHeaderAuth.resourcePolicyId,
resourcePolicyId
)
);
await trx
.delete(resourcePolicyHeaderAuthExtendedCompatibility)
.where(
eq(
resourcePolicyHeaderAuthExtendedCompatibility.resourcePolicyId,
resourcePolicyId
)
);
if (user && password && extendedCompatibility !== null) {
const headerAuthHash = await hashPassword(
Buffer.from(`${user}:${password}`).toString("base64")
);
await Promise.all([
trx
.insert(resourcePolicyHeaderAuth)
.values({ resourcePolicyId, headerAuthHash }),
trx
.insert(resourcePolicyHeaderAuthExtendedCompatibility)
.values({
resourcePolicyId,
extendedCompatibilityIsActivated:
extendedCompatibility
})
]);
}
});
return response(res, {
data: {},
success: true,
error: false,
message: "Header Authentication set successfully",
status: HttpCode.CREATED
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -0,0 +1,106 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resourcePolicyPassword } from "@server/db";
import { eq } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { fromError } from "zod-validation-error";
import { response } from "@server/lib/response";
import logger from "@server/logger";
import { hashPassword } from "@server/auth/password";
import { OpenAPITags, registry } from "@server/openApi";
const setResourcePolicyPasswordParamsSchema = z.object({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
const setResourcePolicyPasswordBodySchema = z.strictObject({
password: z.string().min(4).max(100).nullable()
});
registry.registerPath({
method: "post",
path: "/resource-policy/{resourcePolicyId}/password",
description:
"Set the password for a resource policy. Setting the password to null will remove it.",
tags: [OpenAPITags.Resource],
request: {
params: setResourcePolicyPasswordParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyPasswordBodySchema
}
}
}
},
responses: {}
});
export async function setResourcePolicyPassword(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = setResourcePolicyPasswordParamsSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const parsedBody = setResourcePolicyPasswordBodySchema.safeParse(
req.body
);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const { password } = parsedBody.data;
await db.transaction(async (trx) => {
await trx
.delete(resourcePolicyPassword)
.where(
eq(
resourcePolicyPassword.resourcePolicyId,
resourcePolicyId
)
);
if (password) {
const passwordHash = await hashPassword(password);
await trx
.insert(resourcePolicyPassword)
.values({ resourcePolicyId, passwordHash });
}
});
return response(res, {
data: {},
success: true,
error: false,
message: "Resource policy password set successfully",
status: HttpCode.CREATED
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -0,0 +1,109 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resourcePolicyPincode } from "@server/db";
import { eq } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { fromError } from "zod-validation-error";
import { response } from "@server/lib/response";
import logger from "@server/logger";
import { hashPassword } from "@server/auth/password";
import { OpenAPITags, registry } from "@server/openApi";
const setResourcePolicyPincodeParamsSchema = z.object({
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
});
const setResourcePolicyPincodeBodySchema = z.strictObject({
pincode: z
.string()
.regex(/^\d{6}$/)
.or(z.null())
});
registry.registerPath({
method: "post",
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],
request: {
params: setResourcePolicyPincodeParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyPincodeBodySchema
}
}
}
},
responses: {}
});
export async function setResourcePolicyPincode(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = setResourcePolicyPincodeParamsSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const parsedBody = setResourcePolicyPincodeBodySchema.safeParse(
req.body
);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { resourcePolicyId } = parsedParams.data;
const { pincode } = parsedBody.data;
await db.transaction(async (trx) => {
await trx
.delete(resourcePolicyPincode)
.where(
eq(
resourcePolicyPincode.resourcePolicyId,
resourcePolicyId
)
);
if (pincode) {
const pincodeHash = await hashPassword(pincode);
await trx
.insert(resourcePolicyPincode)
.values({ resourcePolicyId, pincodeHash, digitLength: 6 });
}
});
return response(res, {
data: {},
success: true,
error: false,
message: "Resource policy PIN code set successfully",
status: HttpCode.CREATED
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}