mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-13 02:20:45 +08:00
Enable default emergency account
This commit is contained in:
parent
70bde46eff
commit
07cfbefb6c
9 changed files with 43 additions and 31 deletions
|
|
@ -56,7 +56,7 @@ Durable state defaults to `~/.uds-auth/` (roles / owners / credentials / bridge
|
|||
## Admin / emergency
|
||||
|
||||
- `super_admin` / `admin` / `fallback_admin` share the same admin-class permissions (identity labels, not stronger isolation).
|
||||
- No shared default password. Set emergency password in Settings (≥10 chars; `Admin@123` rejected). Legacy default hashes are cleared on upgrade.
|
||||
- The emergency account is enabled by default: username `administrator`, password `Admin@123`. Change it in Settings after first sign-in. Admins can also disable it. Older roles files without an explicit emergency-account state receive this default; an explicitly disabled account stays disabled. Historical SHA-256 hashes of `Admin@123` migrate to scrypt.
|
||||
- Local unlock: `node scripts/seal-local-admin.mjs` (interactive / `--stdin` — **no argv passphrase**) → `UDS_AUTH_LOCAL_ADMIN_BOX`.
|
||||
- Removing a user disables the account by default (open registration cannot auto-revive).
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ skillCredentialTtlSeconds: 604800
|
|||
## 管理员与应急
|
||||
|
||||
- **三类管理员同权**(`super_admin` / `admin` / `fallback_admin`):用户管理、设置、建工作区;标签不是更强隔离。
|
||||
- **无共享默认密码**。须在设置中显式设置应急口令(≥10 位,禁止 `Admin@123`)。旧安装若仍存该默认 hash,升级后自动禁用直至重置。
|
||||
- **应急账号默认开启**:账号 `administrator`,密码 `Admin@123`。首次登录后建议立即在设置中修改;管理员也可关闭该账号。旧 roles 文件未记录应急账号状态时会启用此默认账号;已明确关闭的状态会保留。历史 `Admin@123` SHA-256 hash 会迁移为 scrypt hash。
|
||||
- **本机密钥解锁**:`node scripts/seal-local-admin.mjs`(交互输入,勿把口令放 argv)→ 设置 `UDS_AUTH_LOCAL_ADMIN_BOX` → 登录面板解密。
|
||||
- **停用账号**:删除用户默认写入 disabled 状态,不会因开放注册自动复活。
|
||||
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ skillCredentialTtlSeconds: 604800
|
|||
outboundAllowedHosts: icenterapi.zte.com.cn,icentermsg.dt.zte.com.cn
|
||||
# Optional exact Host allowlist for /uds-auth writes; empty = Origin must match Host only
|
||||
trustedHosts: ''
|
||||
# Emergency fallback password is NOT enabled by default. Set via Settings (min 10 chars).
|
||||
# Legacy Admin@123 hashes are refused on upgrade.
|
||||
# Emergency fallback account is enabled by default: administrator / Admin@123.
|
||||
# Change or disable it from Settings after signing in.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# 升级到 0.3.10
|
||||
|
||||
本版修复密码并发写入、Desktop 凭据附带范围、Gateway 全局接口遗漏、扫码浏览器绑定、任务授权注入和安全状态迁移。先备份 profile 中的安全状态及密钥,再替换插件并重启 Host;不要删除 roles/owners 文件,也不要恢复历史共享默认密码。
|
||||
本版修复密码并发写入、Desktop 凭据附带范围、Gateway 全局接口遗漏、扫码浏览器绑定、任务授权注入和安全状态迁移。先备份 profile 中的安全状态及密钥,再替换插件并重启 Host;不要删除 roles/owners 文件。
|
||||
|
||||
## 登录和 Desktop
|
||||
|
||||
应急密码入口始终显示;未设置密码时保持禁用。已登录的授权管理员可在「账号 / 权限」设置至少 10 位的新密码。Local Admin 密封盒解锁保持独立,轮换或关闭应急密码不撤销密封盒会话。
|
||||
应急账号默认启用,账号为 `administrator`、密码为 `Admin@123`;首次登录后请尽快在「账号 / 权限」设置新密码,也可在此关闭应急账号。旧 roles 文件若没有应急账号状态会启用默认账号;显式关闭状态会保留。Local Admin 密封盒解锁保持独立,轮换或关闭应急密码不撤销密封盒会话。
|
||||
|
||||
Desktop 只把 bridge 附到 `dsh-app://app` 或 Host 启动时提供的 `__DSH_TRANSPORT__.streamBaseUrl` 对应 authority。启动信息缺失时绝不向其他本机端口外发凭据;需要支持提供这个正式 transport 字段的 DSH 版本。带 bridge 的 fetch 拒绝重定向,保留原 Request headers。一次性 WS ticket 只用于 GET `/api/remote.mux` 的真正 WebSocket upgrade;普通 HTTP、其他路径和重放均无效。构造 WebSocket 仍采用同步 XHR 取票,需真实 Desktop 验证该协议桥。
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ window.__ModuleLoader__.load({
|
|||
"ui.settingsTitle": "UAC 认证",
|
||||
"ui.settingsIntro": "工号+token 双校验;UAC 挂死时用应急账号 administrator 密码登录。",
|
||||
"ui.loginRequiredPage": "请先登录后查看此页",
|
||||
"ui.roleHint": "当前角色:{role}。超管只是身份标签,与管理员权限相同;显式配置的初始管理员在首次登录时获得超管身份,也可在「用户管理」设为管理员。应急账号 administrator 需先设密码,再在登录面板用账密登录。",
|
||||
"ui.roleHint": "当前角色:{role}。超管只是身份标签,与管理员权限相同;显式配置的初始管理员在首次登录时获得超管身份,也可在「用户管理」设为管理员。应急账号默认账号 administrator、密码 Admin@123,首次登录后请修改。",
|
||||
"ui.deployConfig": "部署配置",
|
||||
"ui.userSearchUrl": "用户搜索 URL(token 校验)",
|
||||
"ui.workspaceRoot": "工作区根目录(空=$DSH_HOME/user-workspaces)",
|
||||
|
|
@ -78,8 +78,8 @@ window.__ModuleLoader__.load({
|
|||
"ui.refreshQr": "刷新二维码",
|
||||
"ui.fallbackLink": "UAC 不可用?应急账号登录",
|
||||
"ui.fallbackDisabledLink": "应急登录(未启用)",
|
||||
"ui.fallbackDisabledHint": "应急密码未配置或已停用。请用已登录的管理员在「账户 / 权限」中设置新的独立强密码后重试。旧共享默认密码不可用。",
|
||||
"ui.fallbackDisabledDetail": "当前未启用应急密码。用已授权管理员登录后,在设置中配置新密码即可恢复此入口。",
|
||||
"ui.fallbackDisabledHint": "应急账号已关闭。请用已登录的管理员在「账户 / 权限」中重新设置并启用。",
|
||||
"ui.fallbackDisabledDetail": "当前未启用应急账号。用已授权管理员登录后,在设置中重新设置密码即可恢复此入口。",
|
||||
"ui.fallbackLogin": "应急登录",
|
||||
"ui.fallbackDetail": "UAC / 扫码不可用时使用",
|
||||
"ui.localKeyLink": "本机密钥解锁",
|
||||
|
|
@ -180,7 +180,7 @@ window.__ModuleLoader__.load({
|
|||
"ui.settingsTitle": "UAC Auth",
|
||||
"ui.settingsIntro": "EmpNo + token verification; when UAC is down, sign in with emergency account administrator.",
|
||||
"ui.loginRequiredPage": "Sign in to view this page",
|
||||
"ui.roleHint": "Current role: {role}. Super admin is only an identity label with the same permissions as admin; the explicitly configured initial administrator receives that identity on first login, or grant admin in User management. Set the emergency password before using administrator on the login panel.",
|
||||
"ui.roleHint": "Current role: {role}. Super admin is only an identity label with the same permissions as admin; the explicitly configured initial administrator receives that identity on first login, or grant admin in User management. The emergency account defaults to administrator / Admin@123; change it after first sign-in.",
|
||||
"ui.deployConfig": "Deploy config",
|
||||
"ui.userSearchUrl": "User search URL (token verify)",
|
||||
"ui.workspaceRoot": "Workspace root (empty=$DSH_HOME/user-workspaces)",
|
||||
|
|
@ -225,8 +225,8 @@ window.__ModuleLoader__.load({
|
|||
"ui.refreshQr": "Refresh QR",
|
||||
"ui.fallbackLink": "UAC down? Emergency account",
|
||||
"ui.fallbackDisabledLink": "Emergency login (disabled)",
|
||||
"ui.fallbackDisabledHint": "Emergency password is not configured or was disabled. Ask a signed-in admin to set a new strong password under Account / Permissions. The old shared default password cannot be used.",
|
||||
"ui.fallbackDisabledDetail": "Emergency password is off. After an authorized admin sets a new password in settings, this entry works again.",
|
||||
"ui.fallbackDisabledHint": "The emergency account is disabled. Ask a signed-in admin to set a new password and enable it under Account / Permissions.",
|
||||
"ui.fallbackDisabledDetail": "The emergency account is off. An authorized admin can set its password again in settings to restore this entry.",
|
||||
"ui.fallbackLogin": "Emergency login",
|
||||
"ui.fallbackDetail": "Use when UAC / QR is unavailable",
|
||||
"ui.localKeyLink": "Unlock with local key",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const MESSAGES = {
|
|||
'ui.settingsTitle': 'UAC 认证',
|
||||
'ui.settingsIntro': '工号+token 双校验;UAC 挂死时用应急账号 administrator 密码登录。',
|
||||
'ui.loginRequiredPage': '请先登录后查看此页',
|
||||
'ui.roleHint': '当前角色:{role}。超管只是身份标签,与管理员权限相同;显式配置的初始管理员在首次登录时获得超管身份,也可在「用户管理」设为管理员。应急账号 administrator 需先设密码,再在登录面板用账密登录。',
|
||||
'ui.roleHint': '当前角色:{role}。超管只是身份标签,与管理员权限相同;显式配置的初始管理员在首次登录时获得超管身份,也可在「用户管理」设为管理员。应急账号默认账号 administrator、密码 Admin@123,首次登录后请修改。',
|
||||
'ui.deployConfig': '部署配置',
|
||||
'ui.userSearchUrl': '用户搜索 URL(token 校验)',
|
||||
'ui.workspaceRoot': '工作区根目录(空=$DSH_HOME/user-workspaces)',
|
||||
|
|
@ -213,7 +213,7 @@ export const MESSAGES = {
|
|||
'ui.settingsTitle': 'UAC Auth',
|
||||
'ui.settingsIntro': 'EmpNo + token verification; when UAC is down, sign in with emergency account administrator.',
|
||||
'ui.loginRequiredPage': 'Sign in to view this page',
|
||||
'ui.roleHint': 'Current role: {role}. Super admin is only an identity label with the same permissions as admin; the explicitly configured initial administrator receives that identity on first login, or grant admin in User management. Set the emergency password before using administrator on the login panel.',
|
||||
'ui.roleHint': 'Current role: {role}. Super admin is only an identity label with the same permissions as admin; the explicitly configured initial administrator receives that identity on first login, or grant admin in User management. The emergency account defaults to administrator / Admin@123; change it after first sign-in.',
|
||||
'ui.deployConfig': 'Deploy config',
|
||||
'ui.userSearchUrl': 'User search URL (token verify)',
|
||||
'ui.workspaceRoot': 'Workspace root (empty=$DSH_HOME/user-workspaces)',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const ACCOUNT_STATUS = {
|
|||
DISABLED: 'disabled',
|
||||
}
|
||||
|
||||
/** Known shared default from pre-0.3 installs — must never remain usable (R10). */
|
||||
/** Shared emergency default requested for first boot and legacy migrations. */
|
||||
const LEGACY_DEFAULT_SHA256 = createHash('sha256').update('Admin@123').digest('hex')
|
||||
|
||||
/**
|
||||
|
|
@ -141,7 +141,7 @@ export function canToggleViewAllSessions(role) {
|
|||
return isAdminClass(role)
|
||||
}
|
||||
|
||||
/** Historical default — never enable automatically (R10). */
|
||||
/** Default emergency credentials. Rotate the password after first sign-in. */
|
||||
export const DEFAULT_FALLBACK_PASSWORD = 'Admin@123'
|
||||
export const DEFAULT_FALLBACK_USERNAME = 'administrator'
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ export const DEFAULT_FALLBACK_USERNAME = 'administrator'
|
|||
* RolesStore — 角色存储
|
||||
* 内存 Map + 可选 JSON 文件持久化
|
||||
*
|
||||
* fallback admin: 默认关闭;须在设置中显式设置 scrypt 口令(≥10,禁 Admin@123)。
|
||||
* fallback admin: first boot uses the shared Admin@123 password; admins can rotate or disable it.
|
||||
* 登录路径: POST /uds-auth/api/fallback/login { username, password }
|
||||
*/
|
||||
export class RolesStore {
|
||||
|
|
@ -170,18 +170,21 @@ export class RolesStore {
|
|||
this._saveTimer = null
|
||||
}
|
||||
|
||||
/**
|
||||
* New installs do NOT enable a shared default password (SEC-08).
|
||||
* Admins must set a password explicitly via settings / setFallbackPassword.
|
||||
*/
|
||||
/** Seed the requested default emergency password on first initialization. */
|
||||
_ensureDefaultFallback() {
|
||||
// no-op — kept for call-site compatibility
|
||||
if (this._fallbackPasswordHash !== null) return false
|
||||
this._fallbackPasswordHash = hashPasswordScrypt(DEFAULT_FALLBACK_PASSWORD)
|
||||
this._fallbackNeedsRehash = false
|
||||
this._fallbackCredVersion = Math.max(1, this._fallbackCredVersion + 1)
|
||||
this._dirty = true
|
||||
return true
|
||||
}
|
||||
|
||||
// === 持久化 ===
|
||||
|
||||
async init() {
|
||||
let loadedOk = false
|
||||
let seedDefaultFallback = !this._rolesFile
|
||||
if (this._rolesFile) {
|
||||
try {
|
||||
const raw = await readFile(this._rolesFile, 'utf-8')
|
||||
|
|
@ -196,6 +199,9 @@ export class RolesStore {
|
|||
}
|
||||
if (Object.prototype.hasOwnProperty.call(data, 'fallbackPasswordHash')) {
|
||||
this._fallbackPasswordHash = data.fallbackPasswordHash || null
|
||||
} else {
|
||||
// Upgrade older roles files that predate explicit fallback state.
|
||||
seedDefaultFallback = true
|
||||
}
|
||||
this._fallbackCredVersion = Number.isSafeInteger(data.fallbackCredVersion)
|
||||
&& data.fallbackCredVersion >= 0 ? data.fallbackCredVersion : 0
|
||||
|
|
@ -207,9 +213,10 @@ export class RolesStore {
|
|||
loadedOk = true
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
// First boot — empty roles, fallback disabled until explicitly configured.
|
||||
// First boot — seed and persist the requested default credentials.
|
||||
seedDefaultFallback = true
|
||||
console.info(
|
||||
'[uds-auth] roles file missing; fallback password disabled until configured'
|
||||
'[uds-auth] roles file missing; enabling default emergency account administrator'
|
||||
+ ` (set initialAdmin via UDS_AUTH_INITIAL_ADMIN)`,
|
||||
)
|
||||
} else {
|
||||
|
|
@ -219,11 +226,12 @@ export class RolesStore {
|
|||
}
|
||||
}
|
||||
}
|
||||
// R10: known shared default hash cannot remain enabled after upgrade.
|
||||
// Upgrade the historical SHA-256 default to a salted scrypt hash.
|
||||
if (loadedOk && this._fallbackPasswordHash
|
||||
&& String(this._fallbackPasswordHash).toLowerCase() === LEGACY_DEFAULT_SHA256) {
|
||||
console.warn('[uds-auth] refusing legacy shared default fallback password — disabled until reset')
|
||||
this._fallbackPasswordHash = null
|
||||
console.warn('[uds-auth] migrating legacy default fallback password to scrypt')
|
||||
this._fallbackPasswordHash = hashPasswordScrypt(DEFAULT_FALLBACK_PASSWORD)
|
||||
this._fallbackCredVersion = Math.max(1, this._fallbackCredVersion + 1)
|
||||
this._dirty = true
|
||||
await this._save()
|
||||
} else if (loadedOk && this._fallbackPasswordHash
|
||||
|
|
@ -231,6 +239,10 @@ export class RolesStore {
|
|||
this._fallbackNeedsRehash = true
|
||||
console.warn('[uds-auth] legacy SHA-256 fallback hash loaded — will upgrade on next successful login')
|
||||
}
|
||||
if (seedDefaultFallback && !this._loadFailed) {
|
||||
this._ensureDefaultFallback()
|
||||
await this.flush()
|
||||
}
|
||||
if (loadedOk && this._fallbackPasswordHash) {
|
||||
console.info('[uds-auth] fallback_admin password loaded from roles file')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ test('R13/R14: QR challenge store + live identity patch + docs (source)', () =>
|
|||
const acl = readFileSync(join(root, 'lib/dsh-acl.js'), 'utf8')
|
||||
assert.match(acl, /__udsAuthIdentityLive/)
|
||||
const readme = readFileSync(join(root, 'README.zh.md'), 'utf8')
|
||||
assert.doesNotMatch(readme, /Admin@123.*首次启动自动启用/)
|
||||
assert.match(readme, /administrator.*Admin@123/)
|
||||
assert.match(readme, /mintTaskCapability|UDS_TASK_CAPABILITY/)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -234,11 +234,11 @@ test('SEC-06: identity-cache never restores a principal', () => {
|
|||
assert.equal(peekBrowserIdentity(), null)
|
||||
})
|
||||
|
||||
test('SEC-08: new RolesStore does not enable shared default password', async () => {
|
||||
test('SEC-08: new RolesStore enables the default emergency password', async () => {
|
||||
const store = new RolesStore({})
|
||||
await store.init()
|
||||
assert.equal(store.isFallbackEnabled(), false)
|
||||
assert.equal(store.verifyFallback(DEFAULT_FALLBACK_PASSWORD, '127.0.0.1'), false)
|
||||
assert.equal(store.isFallbackEnabled(), true)
|
||||
assert.equal(store.verifyFallback(DEFAULT_FALLBACK_PASSWORD, '127.0.0.1'), true)
|
||||
})
|
||||
|
||||
test('SEC-09: empty roles + non-initial admin cannot bootstrap via middleware helper', async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue