文件
hyb-oa/server/src/plugins/auth.js
T

142 行
3.9 KiB
JavaScript

const fastifyJwt = require("@fastify/jwt");
const fp = require("fastify-plugin");
const { AppError } = require("../utils/AppError");
const {
resolvePermissionsForRoles,
hasPermission,
resolveEntityPermission,
} = require("../constants/permissions");
async function authPlugin(fastify) {
const { jwtSecret, accessTokenTtl } = fastify.config;
await fastify.register(fastifyJwt, {
secret: jwtSecret,
});
fastify.decorateRequest("employee", null);
fastify.decorate("issueAccessToken", (employee) => {
return fastify.jwt.sign(
{
sub: employee.id,
tokenVersion: employee.token_version,
},
{ expiresIn: accessTokenTtl },
);
});
fastify.decorate("hasPermission", (employee, permissionCode) => {
return hasPermission(employee?.permissionCodes ?? [], permissionCode);
});
fastify.decorate("assertPermission", (employee, permissionCode, details = {}) => {
if (!permissionCode || !fastify.hasPermission(employee, permissionCode)) {
throw new AppError({
code: "PERM_001",
message: "无权限",
statusCode: 403,
details: { permission: permissionCode, ...details },
});
}
return permissionCode;
});
fastify.decorate("assertEntityPermission", (employee, entityType, action) => {
const permissionCode = resolveEntityPermission(entityType, action);
if (!permissionCode) {
throw new AppError({
code: "VALID_001",
message: "参数校验失败",
statusCode: 409,
details: { entityType, action, permission: "unsupported_entity_action" },
});
}
return fastify.assertPermission(employee, permissionCode, { entityType, action });
});
fastify.decorate("requirePermission", (permissionCode) => {
return async (request) => {
await fastify.authenticate(request);
fastify.assertPermission(request.employee, permissionCode);
};
});
fastify.decorate("requireEntityPermission", (entityType, action) => {
return async (request) => {
await fastify.authenticate(request);
fastify.assertEntityPermission(request.employee, entityType, action);
};
});
fastify.decorate("authenticate", async (request) => {
try {
await request.jwtVerify();
} catch {
throw new AppError({
code: "AUTH_004",
message: "Access Token 已过期/无效",
statusCode: 401,
});
}
const employeeId = request.user?.sub;
const tokenVersion = request.user?.tokenVersion;
if (typeof employeeId !== "string" || typeof tokenVersion !== "number") {
throw new AppError({
code: "AUTH_004",
message: "Access Token 已过期/无效",
statusCode: 401,
});
}
const employee = await fastify.prisma.employee.findUnique({
where: { id: employeeId },
select: {
id: true,
status: true,
token_version: true,
department_id: true,
legal_entity_id: true,
roles: { select: { role_code: true } },
},
});
if (!employee || employee.status !== "active") {
throw new AppError({
code: "AUTH_003",
message: "账号已离职/禁用",
statusCode: 403,
});
}
if (tokenVersion < employee.token_version) {
throw new AppError({
code: "AUTH_004",
message: "Access Token 已过期/无效",
statusCode: 401,
});
}
const roleCodes = employee.roles.map((item) => item.role_code);
const permissionCodes = resolvePermissionsForRoles(roleCodes);
request.employee = {
id: employee.id,
status: employee.status,
token_version: employee.token_version,
department_id: employee.department_id,
legal_entity_id: employee.legal_entity_id ?? null,
roleCodes,
permissionCodes,
};
if (request.log && typeof request.log.child === "function") {
request.log = request.log.child({ actorId: employee.id });
}
});
}
module.exports = fp(authPlugin, { name: "auth" });