From b9b8ca22f1322a06f350e7514015fe76e62e4e45 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:10:20 +0800 Subject: [PATCH] fix(auth): enable local bootstrap admin by default (#141) * feat(profile): add field policy config and GET response Add per-field edit policy configuration (editable, requiresReview) to application.yml and expose it in the GET /api/v1/user/profile response via a new fieldPolicies map, enabling the frontend to dynamically render fields based on backend-driven policies. * feat(profile): implement mixed submission in PATCH Split profile update changes by per-field policy: fields without requiresReview apply immediately, fields with requiresReview enter the pending queue. Return PARTIALLY_APPLIED status with appliedFields and pendingFields maps when both paths trigger in a single request. * feat(web): rewrite profile page with dynamic fields Render profile fields dynamically from backend fieldPolicies, with whole-page read/edit mode toggle. Handle APPLIED, PENDING_REVIEW, and PARTIALLY_APPLIED responses. Add i18n keys for review hints and partial-apply toasts in both en and zh. * merge: resolve deepwiki.yml conflict with package-lock.json fix * api schema * fix(profile): align moderation defaults and tests * ci(deepwiki): run crawler on release publish * fix(auth): enable local bootstrap admin by default --- README.md | 16 +++-- README_zh.md | 12 +++- docs/09-deployment.md | 2 + docs/dev-workflow.md | 9 +++ .../docs/01-getting-started/quick-start.md | 10 ++- .../deployment/single-machine.md | 1 + .../current/01-getting-started/quick-start.md | 12 +++- .../deployment/single-machine.md | 1 + .../src/main/resources/application-local.yml | 3 + .../BootstrapAdminPropertiesBindingTest.java | 72 +++++++++++++++++++ 10 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 server/skillhub-app/src/test/java/com/iflytek/skillhub/config/BootstrapAdminPropertiesBindingTest.java diff --git a/README.md b/README.md index 76fffe10..61c8b121 100644 --- a/README.md +++ b/README.md @@ -99,18 +99,26 @@ Then open: - Web UI: `http://localhost:3000` - Backend API: `http://localhost:8080` -Local profile seeds two mock-auth users automatically: +By default, `make dev-all` starts the backend with the `local` profile. +In that mode, local development keeps the mock-auth users below and also +creates a password-based bootstrap admin account by default: - `local-user` for normal publishing and namespace operations - `local-admin` with `SUPER_ADMIN` for review and admin flows Use them with the `X-Mock-User-Id` header in local development. -The backend can bootstrap a local-login super admin for first-time access -when you explicitly set `BOOTSTRAP_ADMIN_ENABLED=true`: +The local bootstrap admin is enabled by default in `application-local.yml`: - username: `BOOTSTRAP_ADMIN_USERNAME` (`admin` by default) -- password: `BOOTSTRAP_ADMIN_PASSWORD` (`ChangeMe!2026` by default) +- password: `BOOTSTRAP_ADMIN_PASSWORD` + In local app-level fallback it is `ChangeMe!2026`. +- For local source startup, set the environment variable + `BOOTSTRAP_ADMIN_ENABLED=false` before starting the backend. +- For container or release environments, configure the same value in + `.env.release` or the Compose environment. +- The shipped release template still uses `replace-this-admin-password` and + must be changed before first login. Stop everything with: diff --git a/README_zh.md b/README_zh.md index 39594037..6bc164e1 100644 --- a/README_zh.md +++ b/README_zh.md @@ -67,12 +67,22 @@ curl -fsSL https://imageless.oss-cn-beijing.aliyuncs.com/runtime.sh | sh -s -- u ### 默认账户 -本地开发环境提供两个模拟用户: +默认执行 `make dev-all` 时,后端以 `local` profile 启动。 +在这个模式下,本地开发会保留下面两个模拟用户,同时默认创建一个可账号密码登录的 bootstrap 管理员: - `local-user` — 普通用户,用于发布和命名空间操作 - `local-admin` — 超级管理员,用于审核和管理流程 在本地开发中使用 `X-Mock-User-Id` 请求头切换用户。 +本地 bootstrap 管理员默认已在 `application-local.yml` 中开启,本地登录账号来自以下配置: + +- 用户名:`BOOTSTRAP_ADMIN_USERNAME`,默认是 `admin` +- 密码:`BOOTSTRAP_ADMIN_PASSWORD` + - 本地应用默认回退值是 `ChangeMe!2026` +- 本地源码启动如需关闭自动创建,请在启动后端前设置环境变量 + `BOOTSTRAP_ADMIN_ENABLED=false` +- 容器或发布环境则在 `.env.release` 或 Compose 环境变量中配置该值 +- 发布模板 `.env.release.example` 里的占位值仍然是 `replace-this-admin-password` ### 停止服务 diff --git a/docs/09-deployment.md b/docs/09-deployment.md index c61b7a66..2eec3067 100644 --- a/docs/09-deployment.md +++ b/docs/09-deployment.md @@ -60,6 +60,7 @@ - `BOOTSTRAP_ADMIN_ENABLED=true` - `BOOTSTRAP_ADMIN_USERNAME` - `BOOTSTRAP_ADMIN_PASSWORD` + - 从 `.env.release.example` 复制时,初始占位值是 `replace-this-admin-password` 建议: @@ -210,6 +211,7 @@ docker compose --env-file .env.release -f compose.release.yml up -d - 按云厂商 OSS / S3 兼容参数填写 `SKILLHUB_STORAGE_S3_*` - 设置非默认的 `POSTGRES_PASSWORD` - 如果要启用首登管理员,再额外设置 `BOOTSTRAP_ADMIN_ENABLED=true` 与非默认的 `BOOTSTRAP_ADMIN_PASSWORD` + - 不要直接保留模板里的 `replace-this-admin-password` 3. 启动前校验 - 运行 `make validate-release-config` - 确认没有 `replace-me`、`change-this-*`、`ChangeMe!2026` 之类的占位值 diff --git a/docs/dev-workflow.md b/docs/dev-workflow.md index c5f6dd4d..d79e62a8 100644 --- a/docs/dev-workflow.md +++ b/docs/dev-workflow.md @@ -49,6 +49,15 @@ Two mock users are available in local mode (no password needed): | `local-user` | Regular user | `X-Mock-User-Id: local-user` | | `local-admin` | Super admin | `X-Mock-User-Id: local-admin` | +Local development also creates a password-based bootstrap admin by default. +Use `BOOTSTRAP_ADMIN_USERNAME` / `BOOTSTRAP_ADMIN_PASSWORD` to log in through +the normal local account form. The default local fallback credentials are +`admin` / `ChangeMe!2026`. +To disable it for local source startup, set the environment variable +`BOOTSTRAP_ADMIN_ENABLED=false` before starting the backend. +For container or release environments, set the same value in `.env.release` +or the Compose environment. + ### Useful commands | Command | Description | diff --git a/document/docs/01-getting-started/quick-start.md b/document/docs/01-getting-started/quick-start.md index 14ebf2fa..e8671d0d 100644 --- a/document/docs/01-getting-started/quick-start.md +++ b/document/docs/01-getting-started/quick-start.md @@ -34,7 +34,7 @@ make dev-all ## 开发用户 -本地开发环境预置了两个测试用户: +默认本地开发环境预置了两个模拟测试用户: | 用户 | 角色 | 说明 | |------|------|------| @@ -42,6 +42,14 @@ make dev-all | `local-admin` | 超级管理员 | 拥有所有权限,包括审核和用户管理 | 使用 `X-Mock-User-Id` 请求头在本地开发中模拟用户登录。 +本地开发环境还会默认创建可密码登录的 bootstrap 管理员账号: + +- 用户名:`admin` +- 密码:`ChangeMe!2026` + +本地源码启动如需覆盖或关闭,请在启动后端前设置 `BOOTSTRAP_ADMIN_*` +环境变量。 +容器或发布环境则通过 `.env.release` 或 Compose 环境变量配置。 ## 常用命令 diff --git a/document/docs/02-administration/deployment/single-machine.md b/document/docs/02-administration/deployment/single-machine.md index 43e44d33..bbd60e67 100644 --- a/document/docs/02-administration/deployment/single-machine.md +++ b/document/docs/02-administration/deployment/single-machine.md @@ -55,6 +55,7 @@ curl -i http://127.0.0.1:8080/actuator/health ## 首登配置 1. 使用 `BOOTSTRAP_ADMIN_USERNAME` 和 `BOOTSTRAP_ADMIN_PASSWORD` 登录 + 从 `.env.release.example` 复制配置时,默认占位值是 `replace-this-admin-password`,需要先改掉 2. 立即修改管理员密码 3. 配置企业 SSO(可选) 4. 创建团队命名空间 diff --git a/document/i18n/en/docusaurus-plugin-content-docs/current/01-getting-started/quick-start.md b/document/i18n/en/docusaurus-plugin-content-docs/current/01-getting-started/quick-start.md index e751fd51..6b4c9de9 100644 --- a/document/i18n/en/docusaurus-plugin-content-docs/current/01-getting-started/quick-start.md +++ b/document/i18n/en/docusaurus-plugin-content-docs/current/01-getting-started/quick-start.md @@ -34,7 +34,7 @@ After successful startup, you can access through the following addresses: ## Development Users -The local development environment comes with two test users: +By default, the local development environment comes with two mock test users: | User | Role | Description | |------|------|-------------| @@ -42,6 +42,16 @@ The local development environment comes with two test users: | `local-admin` | Super admin | Has all permissions including review and user management | Use the `X-Mock-User-Id` request header to simulate user login in local development. +The local development environment also creates a password-based bootstrap +admin by default: + +- username: `admin` +- password: `ChangeMe!2026` + +For local source startup, override or disable it with `BOOTSTRAP_ADMIN_*` +environment variables before starting the backend. +For container or release environments, configure the same values in +`.env.release` or the Compose environment. ## Common Commands diff --git a/document/i18n/en/docusaurus-plugin-content-docs/current/02-administration/deployment/single-machine.md b/document/i18n/en/docusaurus-plugin-content-docs/current/02-administration/deployment/single-machine.md index 2372fb26..42cec636 100644 --- a/document/i18n/en/docusaurus-plugin-content-docs/current/02-administration/deployment/single-machine.md +++ b/document/i18n/en/docusaurus-plugin-content-docs/current/02-administration/deployment/single-machine.md @@ -55,6 +55,7 @@ curl -i http://127.0.0.1:8080/actuator/health ## First Login Configuration 1. Login with `BOOTSTRAP_ADMIN_USERNAME` and `BOOTSTRAP_ADMIN_PASSWORD` + When copied from `.env.release.example`, the shipped placeholder is `replace-this-admin-password`, so change it before first login 2. Change admin password immediately 3. Configure enterprise SSO (optional) 4. Create team namespaces diff --git a/server/skillhub-app/src/main/resources/application-local.yml b/server/skillhub-app/src/main/resources/application-local.yml index 08ff728a..583ef9fc 100644 --- a/server/skillhub-app/src/main/resources/application-local.yml +++ b/server/skillhub-app/src/main/resources/application-local.yml @@ -27,6 +27,9 @@ skillhub: auth: mock: enabled: true + bootstrap: + admin: + enabled: ${BOOTSTRAP_ADMIN_ENABLED:true} profile: moderation: machine-review: ${SKILLHUB_PROFILE_MACHINE_REVIEW_ENABLED:true} diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/BootstrapAdminPropertiesBindingTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/BootstrapAdminPropertiesBindingTest.java new file mode 100644 index 00000000..c19409d4 --- /dev/null +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/BootstrapAdminPropertiesBindingTest.java @@ -0,0 +1,72 @@ +package com.iflytek.skillhub.config; + +import com.iflytek.skillhub.bootstrap.BootstrapAdminProperties; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.env.YamlPropertySourceLoader; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.env.SystemEnvironmentPropertySource; +import org.springframework.core.io.ClassPathResource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BootstrapAdminPropertiesBindingTest { + + @Test + void defaultConfig_keepsBootstrapAdminDisabled() throws IOException { + BootstrapAdminProperties properties = bindProperties( + List.of("application.yml"), + Map.of() + ); + + assertFalse(properties.isEnabled()); + } + + @Test + void localProfile_enablesBootstrapAdminByDefault() throws IOException { + BootstrapAdminProperties properties = bindProperties( + List.of("application-local.yml", "application.yml"), + Map.of() + ); + + assertTrue(properties.isEnabled()); + } + + @Test + void localProfile_allowsEnvironmentVariablesToDisableBootstrapAdmin() throws IOException { + BootstrapAdminProperties properties = bindProperties( + List.of("application-local.yml", "application.yml"), + Map.of("BOOTSTRAP_ADMIN_ENABLED", "false") + ); + + assertFalse(properties.isEnabled()); + } + + private BootstrapAdminProperties bindProperties(List resourceNames, + Map envVars) throws IOException { + ConfigurableEnvironment environment = new StandardEnvironment(); + environment.getPropertySources().addFirst(new SystemEnvironmentPropertySource("test-env", envVars)); + + YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); + for (String resourceName : resourceNames) { + List> propertySources = loader.load( + resourceName, + new ClassPathResource(resourceName) + ); + for (org.springframework.core.env.PropertySource propertySource : propertySources) { + environment.getPropertySources().addLast(propertySource); + } + } + ConfigurationPropertySources.attach(environment); + + return Binder.get(environment) + .bind("skillhub.bootstrap.admin", BootstrapAdminProperties.class) + .orElseThrow(() -> new IllegalStateException("Failed to bind bootstrap admin properties")); + } +}