change log in

This commit is contained in:
oliver 2026-09-08 00:28:58 +08:00
parent 9cf026f605
commit 8ca42dd973
211 changed files with 48417 additions and 109 deletions

View file

@ -0,0 +1,94 @@
import { UdsClient } from '../uds/client.js'
import { UdsValidator } from '../uds/validator.js'
import { ROLES, computePermissions } from '../roles.js'
/**
* auth-middleware: 从 PORTALSSOUser (empNo) 取用户,注入 role/permissions
*
* 三条路径:
* 1. 有 empNo + 有 session → 续期,注入
* 2. 有 empNo + 无 session → 创建 session + bootstrap role
* 3. 无 empNo → 未登录,交给下游
*/
export function createAuthMiddleware(config, sessionStore, rolesStore) {
const udsClient = new UdsClient(config.udsAuth)
const validatorConfig = {
...config.udsAuth,
authMode: config.udsAuth?.authMode || 'trust',
}
const udsValidator = new UdsValidator(validatorConfig)
const { cookieMaxAge, slidingExpiration, slidingInterval } = config.session
function extractEmpNo(cookieHeader) {
// 优先取 UDS 的 empNo,其次取 fallback administrator
const udsMatch = cookieHeader?.match(/(?:PORTALSSOUser|ZTEDPGSSOUser)=([^;]+)/)
if (udsMatch) return decodeURIComponent(udsMatch[1].trim())
const fbMatch = cookieHeader?.match(/UDS_FALLBACK_USER=([^;]+)/)
if (fbMatch) return decodeURIComponent(fbMatch[1].trim())
return null
}
async function authMiddleware(ctx, next) {
const { req } = ctx
const cookieHeader = req.headers.cookie || ''
const empNo = extractEmpNo(cookieHeader)
if (!empNo) return next()
// 1. 有 session → 续期
let userContext = await sessionStore.get(empNo)
if (userContext) {
if (slidingExpiration && userContext.lastActiveAt) {
const lastActive = new Date(userContext.lastActiveAt).getTime()
if (Date.now() - lastActive >= slidingInterval) {
userContext.lastActiveAt = new Date().toISOString()
await sessionStore.setex(empNo, Math.floor(cookieMaxAge / 1000), userContext)
}
}
} else {
// 2. 无 session → 创建
// 信任模式下,empNo 来自我们自己的 QR 登录流程设置的 cookie,
// 即使没有完整的 header 凭据也应建立会话。
const credentials = udsValidator.extractCredentials(req)
if (udsValidator.authMode === 'trust') {
// trust 模式:只要有 empNo 就建立会话;token 可选
userContext = udsValidator.buildUserContext(
credentials || { empNo, token: '', lang: 'zh-CN' },
false,
)
} else {
// verify 模式:必须有有效凭据
if (!credentials || !udsValidator.validateCredentials(credentials)) {
return next()
}
userContext = udsValidator.buildUserContext(credentials, false)
}
userContext.authenticatedAt = new Date().toISOString()
userContext.lastActiveAt = new Date().toISOString()
userContext.isAuthenticated = true
await sessionStore.setex(empNo, Math.floor(cookieMaxAge / 1000), userContext)
}
// 注入 role + permissions
const role = rolesStore.getRole(empNo)
ctx.userContext = userContext
ctx.empNo = empNo
ctx.role = role
ctx.permissions = computePermissions(role)
return next()
}
authMiddleware.udsClient = udsClient
authMiddleware.udsValidator = udsValidator
return authMiddleware
}
/**
* 权限守卫 — 拦截非 super_admin 的管理 API
* 用法: 在路由里 call guard(req, res, ctx, 'canManageUsers') → true 表示通过
*/
export function requirePermission(ctx, permission) {
if (!ctx?.permissions) return false
if (permission === 'super_admin') return ctx.role === ROLES.SUPER_ADMIN || ctx.role === ROLES.FALLBACK_ADMIN
return !!ctx.permissions[permission]
}