Fix first-login bootstrap to super_admin; allow admin settings via canAccessSettings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-08 03:23:20 +08:00
parent 9ff9635624
commit b3b5b6997e
3 changed files with 27 additions and 17 deletions

View file

@ -347,7 +347,12 @@ window.__ModuleLoader__.load({
'\u5de5\u53f7+token \u53cc\u6821\u9a8c\uff1bUAC \u6302\u6b7b\u65f6\u7528\u5e94\u6025\u8d26\u53f7 administrator \u5bc6\u7801\u767b\u5f55\u3002'),
),
!me && h('div', { className: 'uds-auth-settings-empty' }, '\u8bf7\u5148\u767b\u5f55\u540e\u67e5\u770b\u6b64\u9875'),
me && !canSettings && !canManage && h('div', { className: 'uds-auth-settings-empty' }, '\u5f53\u524d\u8d26\u53f7\u65e0\u8bbe\u7f6e\u6743\u9650'),
me && !canSettings && !canManage && h('div', { className: 'uds-auth-settings-empty' },
'\u5f53\u524d\u89d2\u8272\uff1a' + (me.role || 'user')
+ '\u3002\u9996\u4f4d\u626b\u7801\u767b\u5f55\u4e14 roles.json \u4e3a\u7a7a\u65f6\u4f1a\u81ea\u52a8\u6210\u4e3a\u8d85\u7ba1\uff1b'
+ '\u666e\u901a admin \u9700\u8d85\u7ba1\u5728\u300c\u7528\u6237\u7ba1\u7406\u300d\u63d0\u6743\u540e\u518d\u626b\u7801\u767b\u5f55\u3002'
+ '\u5e94\u6025\u8d26\u53f7 administrator \u9700\u8d85\u7ba1\u5148\u8bbe\u5bc6\u7801\uff0c\u518d\u5728\u767b\u5f55\u9762\u677f\u7528\u8d26\u5bc6\u767b\u5f55\u3002'
),
canSettings && h('div', { className: 'uds-auth-settings-card' },
h('h3', null, '\u90e8\u7f72\u914d\u7f6e'),
field('uacBaseUrl', 'UAC Base URL'),

View file

@ -546,15 +546,7 @@ async function handleAllRoutes(req, res) {
function handleRequest(req, res) {
const ctx2 = { req, res }
_authMiddleware(ctx2, async () => {
// 首次部署 bootstrap: 第一个有 UDS 凭证的用户 = super_admin
if (ctx2.empNo && !ctx2.role) {
const { role, bootstrapped } = await _rolesStore.bootstrapFirstUser(ctx2.empNo)
ctx2.role = role
ctx2.permissions = computePermissions(role)
if (bootstrapped) {
ctx.logger?.info?.(`[uds-auth] BOOTSTRAP: ${ctx2.empNo} is now super_admin`)
}
}
// bootstrap 已在 auth-middleware.resolveRole 中完成(勿用 !ctx2.role,getRole 恒有值)
const url = new URL(req.url, 'http://localhost').pathname.replace(API_PREFIX, '') || '/'
const method = req.method
@ -595,17 +587,17 @@ function handleRequest(req, res) {
await _apiHandlers.clearFallbackPassword(ctx2); return
}
// 配置端点 (super_admin only)
// 配置端点 (admin / super_admin:canAccessSettings)
if (url === '/api/config' && method === 'GET') {
if (!requirePermission(ctx2, 'super_admin')) {
return sendJSON(res, 403, { error: '只有超级管理员可以查看配置' })
if (!requirePermission(ctx2, 'canAccessSettings')) {
return sendJSON(res, 403, { error: '当前账号无设置权限' })
}
sendJSON(res, 200, { config: _currentConfig })
return
}
if (url === '/api/config' && method === 'POST') {
if (!requirePermission(ctx2, 'super_admin')) {
return sendJSON(res, 403, { error: '只有超级管理员可以修改配置' })
if (!requirePermission(ctx2, 'canAccessSettings')) {
return sendJSON(res, 403, { error: '当前账号无设置权限' })
}
let body = ''
for await (const chunk of req) body += chunk

View file

@ -48,6 +48,19 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
return userContext
}
async function resolveRole(empNo, kind) {
if (kind === 'fallback' || empNo === 'administrator') {
return rolesStore.getRole(empNo)
}
// 角色表空 → 首位登录者升为 super_admin;新用户默认 user;已有则返回原角色
// 注意:getRole() 对未知用户也会返回 'user',绝不能用 !role 判断是否已 bootstrap
const { role, bootstrapped } = await rolesStore.bootstrapFirstUser(empNo)
if (bootstrapped) {
console.info('[uds-auth] BOOTSTRAP: ' + empNo + ' is now super_admin')
}
return role
}
async function verifyEmpNoAndToken(empNo, token) {
const out = await searchUserByEmpNoToken({
userSearchUrl,
@ -114,7 +127,7 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
userContext,
)
}
const role = rolesStore.getRole(extracted.empNo)
const role = await resolveRole(extracted.empNo, extracted.kind)
ctx.userContext = userContext
ctx.empNo = extracted.empNo
ctx.role = role
@ -160,7 +173,7 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
userContext,
)
const role = rolesStore.getRole(profile.empNo)
const role = await resolveRole(profile.empNo, 'uds')
ctx.userContext = userContext
ctx.empNo = profile.empNo
ctx.role = role