Commit graph

889 commits

Author SHA1 Message Date
jangrui
89099c70d0 fix(db): 迁移版本 V42 改为 V44 避免与 main 冲突
upstream main 已存在 V42__audit_log_created_at_timestamptz.sql,本 PR 的
identity_binding 级联删除迁移改用 V44(main 当前最高版本为 V43),消除
FlywayMigrationGuardrailTest 检测到的版本号冲突。

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 14:16:24 +08:00
jangrui
b6984accc0 fix(auth): LDAP 登录不再占用数据库事务连接
LocalAuthService.login 移除方法级事务,LDAP 目录网络调用(最长 connect+read 超时)在无事务上下文中执行,本地凭据路径的数据库操作由 TransactionTemplate 显式包裹,避免目录慢速/不可达时放大连接池占用。

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 13:39:00 +08:00
jangrui
88263eba36 fix(db): identity_binding 外键级联删除
账号被删除时其身份绑定随之外键级联清理,配合 LdapAuthService 的孤儿绑定自愈,防止残留绑定阻塞目录身份重新建号。

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 13:39:00 +08:00
jangrui
d7e41ec9ff fix(web): api-error 展示后端业务消息并忽略 pnpm 本地缓存
- 403 与 5xx 分支优先展示后端本地化业务消息(账号禁用、目录不可用、TLS 配置错误),无消息时回退通用文案
- .gitignore 增加 .pnpm-store/

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 13:39:00 +08:00
jangrui
34e01171a6 fix(auth): 完成 LDAP 绑定流程、LDAPS 信任库与并发/孤儿加固
- 显式绑定端点 POST /api/v1/auth/ldap/bind:登录用户以 LDAP 凭据证明目录身份所有权后绑定到当前账号,解决 409 邮箱冲突后无自助入口的问题;前端设置页新增 LDAP 绑定卡片
- LDAPS 自定义 truststore 真实生效:JDK 21 JNDI 不读取 javax.net.ssl.trustStore* 环境项且无 factory.socket 注入点(反编译验证),改为 EnvironmentPostProcessor 在启动早期将自定义 CA 合并进 JVM 信任库;集成测试覆盖真实 LDAPS 登录成功(证书链含 BasicConstraints + 有效期修复)
- 连接层改用 Spring LDAP LdapContextSource(无 base 双重拼接、异常转换保持 JNDI 分类语义),移除自建 JNDI env 与空壳配置类
- email 并发碰撞:首登 check-and-insert 按 email 条带锁串行化,不同 subject 同 email 并发首登只建一个账号(集成测试覆盖)
- 孤儿 identity_binding 自愈:绑定指向已删除账号时删除残留绑定并按首登重建,不再永久 500;loginName 随登录名变更刷新
- isTlsFailure 识别 CertPathValidatorException 链;属性配置错误使用独立消息键;'*' '+' 属性请求回退仅限协议级错误
- LdapIntegrationTest 独立 surefire fork,避免全量测试中 JSSE 默认 SSLContext 被先行缓存导致 LDAPS 用例失效
- 集成测试 ensureMember 真实化(seed global namespace),移除 MockBean

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 13:39:00 +08:00
jangrui
70dcc9d5f6 fix(auth): 加固 LDAP 并发首登、空密码与 TLS 配置边界
- 并发首登同一 LDAP subject 时,唯一约束冲突经 REQUIRES_NEW 子事务回滚后按 subject 重查命中既有账号,双方登录均成功且不重复建号
- null/空密码在 authenticateLdap 显式归类 401,避免 Hashtable NPE 导致 500
- 已绑定用户 email 变更增加碰撞检查(排除自身账号),与首登 409 规则一致
- LDAPS 支持自定义 truststore 配置(tls-trust-store*),企业自签 CA 可用
- 属性名白名单补 displayNameFallbackAttribute;LDAP 配置启动期校验(url/base/超时)
- LDAP 登录成功与开始日志降为 debug
- 新增并发首登集成测试(真实 OpenLDAP)、空密码 401 用例、email 刷新碰撞单测、truststore env 单测、LdapProperties 校验单测

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 11:39:03 +08:00
jangrui
f8ddd2fdc4 test(auth): 新增 LDAP 集成测试覆盖登录全链路与边界场景
- OpenLDAP(Testcontainers)全链路:默认 entryUUID 首登建号绑定、重复登录不重复建号、无邮箱占位与 cn 回退、邮箱碰撞 409、错误密码 401、属性变更同步刷新、LDAPS TLS 错误分类
- 目录不可用:真实 JNDI 连接关闭端口,断言 503 directoryUnavailable;不依赖 Docker,任意环境可运行
- LDAP disabled 启动:完整上下文启动,断言 LdapAuthService Bean 缺席且本地登录降级 401
- 统一 Testcontainers 版本:junit-jupiter 移除显式 1.20.3,由 Spring Boot BOM 统一管理为 1.19.5

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 10:51:38 +08:00
jangrui
fe4784861f fix(auth): 修复 LDAP 身份模型、操作属性获取与错误分类
按 PR #437 review (CHANGES_REQUESTED + 改进方案) 系统修复 LDAP 登录安全与稳定性问题:

身份模型重构:
- 锚定稳定目录标识 (entryUUID/objectGUID) 而非输入侧 username,复用 IdentityBinding 绑定,首次登录持久化、重复登录按 subject 命中,避免重复建号
- 禁止邮箱静默合并/继承:邮箱已被任意账号占用即抛 409 (跨 provider 与同 issuer 双 subject 均拒绝);ldap:{username}@internal 降级为纯占位,不承担身份键
- 规范化 AD objectGUID 二进制属性为稳定 GUID 字符串 (混合字节序),并补回归单测

操作属性获取:
- getUserAttributes 显式请求 "*"+"+" 属性集,OpenLDAP entryUUID / AD objectGUID 等操作属性可正常返回,默认 subject 配置不再导致登录 503;目录拒绝该语法时回退默认属性集

安全与错误分类:
- 属性名白名单校验防 JNDI 注入,isValidUsername 改预编译 Pattern
- 区分 TLS/证书错误与目录不可用 (isTlsFailure 遍历 cause 链),新增 error.auth.ldap.tlsError 中英文消息键
- bind 成功后的目录错误由 500 折叠 401 改为透传 503;LocalAuthService 透传 403/503/409,不再误导为密码错误
- displayName fallback 与超时参数可配置化 (默认 cn / 5s+10s);修正 application.yml LDAP 配置段缩进

架构清理:
- LdapAuthService 改用 JNDI 直连,移除 LdapTemplate 死依赖与失效配置;LdapAuthService 条件化、LocalAuthService 改 ObjectProvider 可选注入

测试补齐 (单测):
- LdapAuthServiceTest 行为单测:邮箱碰撞拒绝、重复登录按 subject 命中不重复建号、属性更新刷新、禁用账号拒绝、subject 缺失 503、cn fallback
- LdapSubjectGuidTest:objectGUID 二进制规范化回归
- LdapAuthServiceTest isTlsFailure 分类单测;LocalAuthServiceTest 错误传播单测

Signed-off-by: jangrui <admin@jangrui.com>
2026-08-01 10:51:38 +08:00
jangrui
9e178711fe fix(auth): address LDAP security and stability issues from code review
- Prevent spring-boot-starter-data-ldap AutoConfiguration side effects
  by setting spring.ldap.urls="" to avoid connection attempts when disabled
- Add conditional LdapAutoConfiguration that only creates LdapTemplate
  when skillhub.ldap.enabled=true
- Fix resource leaks in LdapAuthService (DirContext, NamingEnumeration)
- Add safeLogHost() to prevent credential exposure in logs
- Add connection timeouts (5s connect, 10s read) to prevent hangs
- Add LDAP injection prevention via isValidUsername() validation

Signed-off-by: jangrui <admin@jangrui.com>
2026-06-04 07:17:26 +08:00
jangrui
ea3bf08e19 fix(tests): 移除 LocalAuthServiceTest 中不必要的 ldapProperties stub
将 ldapProperties.isEnabled() 的默认 stub 从 setUp() 移到
login_withUnknownUsername_stillPerformsDummyPasswordCheck 中,
消除 7 个测试方法的 UnnecessaryStubbingException。

Signed-off-by: jangrui <admin@jangrui.com>
2026-06-04 07:07:59 +08:00
jangrui
d09d6172c1 fix: 修复编译和启动错误
- application.yml: 合并重复的 management.health 节点,解决 DuplicateKeyException
- LocalAuthServiceTest.java: 补充 PlatformPrincipal/Set import 和类闭合括号

Signed-off-by: jangrui <admin@jangrui.com>
2026-06-04 07:07:59 +08:00
jangrui
9ff3baf756 fix: 修复 Gemini Code Assist 指出的关键问题
基于 PR #283 的代码审查反馈,修复了多个关键问题:

## 🔴 Critical 修复

### 1. 修复 LDAP 账号重复创建问题
**问题**: 如果 LDAP 用户的 mail 属性缺失或为空,每次登录都会创建新的 UserAccount,导致:
- 同一用户产生多个账号
- 用户数据在不同会话间丢失
- 数据库中出现大量重复账号

**修复**:
- 当 mail 属性为空时,使用 "ldap:{username}@internal" 作为唯一标识符
- 确保同一 LDAP 用户始终映射到同一个本地账号
- 保持用户数据的连续性

```java
// 修复前:email 为 null 时会创建新账号
String normalizedEmail = email != null ? email.toLowerCase() : null;

// 修复后:使用稳定的唯一标识符
String normalizedEmail = email != null ? email.toLowerCase() : "ldap:" + username + "@internal";
```

## 🟠 High 优先级修复

### 2. 修复测试代码中的 PlatformPrincipal 构造函数错误
**问题**: 测试中使用了错误的构造函数参数,导致编译错误

**修复**: 更正为正确的参数顺序:
```java
// 修复前
new PlatformPrincipal(userId, displayName, email, Set.of(), Set.of(), Set.of())

// 修复后
new PlatformPrincipal(userId, displayName, email, null, "ldap", Set.of("USER"))
```

### 3. 修复资源泄露问题
**问题**: `NamingEnumeration<SearchResult>` 没有正确关闭,导致 LDAP 连接泄露

**修复**: 在 finally 块中显式关闭 NamingEnumeration
```java
finally {
    if (results != null) {
        try {
            results.close();
        } catch (Exception e) {
            log.warn("Failed to close LDAP search results", e);
        }
    }
    closeContext(ctx);
}
```

## 📝 测试覆盖

- 添加了 3 个新测试用例验证 LDAP 回退功能
- 所有测试用例使用正确的构造函数
- 确保原有测试不受 LDAP 功能影响

## 🔗 相关链接

- 原始 PR: #283
- 修复 PR: #437
- Issue: #260

这些修复确保了 LDAP 认证功能的稳定性和正确性。

Signed-off-by: jangrui <admin@jangrui.com>
2026-06-04 07:07:59 +08:00
jangrui
48c15ca30c fix(tests): 明确配置 LDAP mock 默认行为并添加 LDAP 回退测试
问题分析:
- 原有测试未显式设置 ldapProperties.isEnabled() 的返回值
- 虽然 Mockito 默认返回 false,但为了测试稳定性应显式配置
- 缺少对 LDAP 回退功能的测试覆盖

修复内容:
1. 在 setUp() 中显式设置 ldapProperties.isEnabled() 返回 false
   - 确保所有原有测试不受 LDAP 功能影响
   - 提高测试的可读性和维护性

2. 添加三个新的测试用例:
   - login_withUnknownUsername_fallsBackToLdap_whenEnabled
     验证 LDAP 启用时,本地用户不存在会回退到 LDAP 认证
   - login_withUnknownUsername_fails_whenLdapAuthenticationFails
     验证 LDAP 认证失败时正确抛出异常
   - login_withUnknownUsername_fails_whenLdapDisabled
     验证 LDAP 禁用时不会调用 LDAP 服务

这些修改确保了:
- 原有测试的稳定性和可预测性
- LDAP 回退功能的正确性
- 测试覆盖的完整性

Refs: https://github.com/iflytek/skillhub/pull/283
Signed-off-by: jangrui <admin@jangrui.com>
2026-06-04 07:07:59 +08:00
Shawn Chen
0b4714f735 新增支持LDAP登录https://github.com/iflytek/skillhub/issues/260
Signed-off-by: jangrui <admin@jangrui.com>
2026-06-04 07:07:59 +08:00
dongmucat
098616dcb6
Merge pull request #436 from iflytek/cli-bump-0.1.6
chore(cli): bump version to 0.1.6
2026-05-14 17:45:01 +08:00
dongmucat
a9bc076f81 chore(cli): bump version to 0.1.6 2026-05-14 17:18:46 +08:00
dongmucat
356e507cf9
Merge pull request #434 from iflytek/worktree-fix-promotion-download
fix(promotion): copy bundleReady and downloadReady when promoting skill to global
2026-05-14 15:50:06 +08:00
dongmucat
afa6b6c834
Merge pull request #417 from iflytek/fix/admin-skill-scan-bypass
fix(security): trigger security scan for admin-published skills
2026-05-14 14:50:53 +08:00
dongmucat
9dfbed2ef1
Merge pull request #422 from iflytek/feat/cli-auto-build
feat(cli): add automated build and publish workflow
2026-05-14 14:50:27 +08:00
dongmucat
2af0bf184b fix(promotion): copy bundleReady and downloadReady when promoting skill to global
When approving a promotion, the new SkillVersion was created without copying
bundleReady and downloadReady from the source version, causing the download
button to be permanently disabled for promoted skills.
2026-05-14 14:32:36 +08:00
Cheney
f59a10e36f chore(ci): remove temporary publish-script test workflow 2026-05-13 09:28:28 +08:00
Cheney
48174c9ad2 fix(cli): match 'push' anywhere in git args, not just $1
The script calls `git -C /path push ...` so the first arg is `-C`,
not `push`. Use glob match on full args instead.
2026-05-13 09:27:44 +08:00
dongmucat
5ccb7f9cf7
Merge pull request #423 from iflytek/feat/landing-cli-tab
feat(web): add CLI install tab on landing quick start
2026-05-13 09:27:39 +08:00
Cheney
dad06b465d fix(cli): fix exit code capture in tests using git wrappers
The `status="$(env ... printf | bash ... && echo 0 || echo $?)"` pattern
doesn't correctly capture the script's exit code because the command
substitution and pipe interact poorly. Use direct assignment with
`|| status=$?` instead.
2026-05-13 09:26:33 +08:00
Cheney
935054cc9e fix(cli): use git wrapper for push-failure test
The old approach (breaking origin URL) caused `git pull` to fail
before reaching the push step. Use a git wrapper that only fails
on `push` so the rest of the script runs normally.
2026-05-13 09:18:19 +08:00
Cheney
c520f38135 fix(cli): remove unreliable race-condition test, renumber tests
Remove test 7 (remote tag race condition) — the scenario is nearly
impossible with the new baseline sync logic and too complex to
reliably simulate. Fix variable naming inconsistencies from the
renumbering.
2026-05-13 09:16:19 +08:00
Cheney
1c29cfac57 test(cli): add debug logging to race-condition test wrapper 2026-05-12 18:05:02 +08:00
Cheney
85a758bbdd fix(cli): rewrite test 7 to cover real remote tag race condition
Old test 7 used `--no-tags` config to prevent fetch from pulling the
remote tag, but that doesn't reflect any real-world scenario. With the
new baseline sync logic, a pre-existing remote tag would be synced
into the local version, eliminating the conflict path the test claimed
to cover.

Replace with a git wrapper that injects the conflicting tag into origin
right before the script's `ls-remote` check, which simulates a real
race between two developers attempting to release the same version.
2026-05-12 18:02:02 +08:00
Cheney
c1c12c56eb fix(cli): gitignore test scaffolding files in publish-cli tests
Tests write stdout.log/stderr.log into the test repo root, which made
`git status --porcelain` non-empty and broke test 3 (non-main branch
abort) by tripping the dirty-tree check first.

Add a .gitignore to the test fixture repo to filter out these files.
2026-05-12 17:57:32 +08:00
Cheney
1420ffac56 test(ci): add temporary workflow to test publish-cli script
This workflow runs scripts/tests/publish-cli-test.sh in CI to verify
the publish script changes. Will be removed after verification.
2026-05-12 17:53:12 +08:00
Cheney
eeb2540a3e fix(cli): align checkout ref across all workflow jobs
publish-npm and create-release now checkout the same ref as
build-and-test (the input tag or push ref), preventing source
mismatch between npm package and GitHub Release artifacts.
2026-05-12 17:29:58 +08:00
Cheney
70b962a4c8 fix(cli): harden release pipeline per PR #422 review
1. npm version check: three-state logic (exists/missing/error) to prevent
   silent skip on network failures, registry 5xx, or auth issues.

2. workflow_dispatch: checkout the specified tag and validate SHA matches,
   preventing builds from wrong ref.

3. Atomic push: use `git push --atomic` and detect unpushed tags via
   `git ls-remote` instead of `--no-merged` (catches branch-pushed-but-
   tag-failed state).
2026-05-12 17:15:35 +08:00
Cheney
8126faa452 fix(cli): detect and guide recovery of unpushed release artifacts
Add pre-flight check in publish-cli.sh to detect unpushed commits and tags
from previous failed pushes. When detected, the script exits with clear
recovery instructions:

1. Retry push (for transient network failures)
2. Rollback and re-release (for clean restart)

This prevents the baseline sync logic from skipping failed versions when
local tags participate in version calculation after a push failure.

Addresses feedback from dongmucat in PR #422.
2026-05-12 16:14:47 +08:00
Cheney
159886b76d fix(cli): ensure create-release depends on publish-npm and rewrite publish-cli tests
1. Update release-cli.yml to make create-release depend on publish-npm with proper skip_npm handling, preventing half-released state where GitHub Release exists but npm package is unavailable.

2. Rewrite publish-cli-test.sh to cover the new publish flow: main branch check, dirty tree detection, tag baseline sync, version bumping, tag conflict detection, user cancellation, and atomic push verification.
2026-05-12 16:12:57 +08:00
dongjiang
41b1d03cfc
Add AGENTS.md and SKILL.md to support AI tools (#393)
Signed-off-by: dongjiang <dongjiang1989@126.com>
2026-05-12 15:35:17 +08:00
dongmucat
fed4eeb2b9 fix(web): make quick start tab icons exhaustive 2026-05-12 14:08:29 +08:00
dongmucat
8931f6d241 feat(web): add CLI install tab on landing quick start
Add a third peer tab 'CLI' to LandingQuickStartSection that surfaces the
official install command 'npm i -g @astron-team/skillhub'. Layout uses
grid-cols-1 md:grid-cols-3 so mobile shows tabs stacked and desktop
shows three equal-width columns.

Addresses iflytek/skillhub#419 (homepage Quick Start part only).
2026-05-12 11:05:02 +08:00
Cheney
490ddfa548 fix(cli): push branch and tag atomically in publish-cli.sh 2026-05-12 11:01:50 +08:00
Cheney
378216c6da feat(cli): add automated build and publish workflow
- Add release-cli.yml GitHub Actions workflow: build, test, npm publish,
  and GitHub Release triggered by cli-v* tags
- Rewrite scripts/publish-cli.sh: local bump + commit + tag + push,
  enforces main branch, idempotent tag checks
- Add concurrency group and release idempotency to workflow
- Add make publish-cli / publish-cli-minor / publish-cli-major targets
- Add cli/RELEASE.md documenting the full release process
2026-05-12 10:32:06 +08:00
dongmucat
15e55e8055
Merge pull request #418 from iflytek/fix/cli-update-registry
fix(cli): respect configured npm registry
2026-05-11 15:18:29 +08:00
dongmucat
836267fd45 fix(cli): respect configured npm registry 2026-05-11 14:53:20 +08:00
dongmucat
554cad5b2e
Merge pull request #416 from iflytek/fix/cli-publish-version-sync
fix(cli): sync publish version flow
2026-05-11 14:14:32 +08:00
dongmucat
cebad0bbd7 refactor(security): use explicit SCANNING check in processScanResult
Gemini review feedback: the previous != PUBLISHED condition was too broad
and could inadvertently overwrite terminal states like REJECTED or YANKED.
Now explicitly check == SCANNING before transitioning status.
2026-05-11 13:56:08 +08:00
dongmucat
299659bf93 fix(cli): avoid publish temp file leak 2026-05-11 13:43:31 +08:00
dongmucat
ec4598efec fix(security): trigger security scan for admin-published skills
Super admin auto-publish flow was skipping security scanning entirely.
Now triggerScan is called regardless of autoPublish flag, while preserving
the PUBLISHED status (scan runs as post-publish audit rather than blocking).

Closes #415
2026-05-11 11:36:08 +08:00
dongmucat
e7aecc4050 fix(cli): sync publish version flow 2026-05-11 11:00:10 +08:00
dongmucat
84914c9d94
Merge pull request #359 from iflytek/feature/skillhub-cli-v1
Some checks failed
Deploy Docs / build (push) Has been cancelled
Deploy Docs / Deploy (push) Has been cancelled
feat(cli): add SkillHub CLI v1
2026-05-09 17:25:24 +08:00
dongmucat
d77a7d67c9 fix(cli): return correct exit code for network failures
- Handle 502/503 status codes as network errors (EXIT.network = 3)
- Previously these were treated as generic errors (EXIT.generic = 1)
- Fixes integration tests for login and search network failure scenarios
- Bump version to 0.1.4
2026-05-09 17:09:16 +08:00
dongmucat
3aef0ed4bc
Merge pull request #412 from iflytek/fix/namespace-management-gaps
fix(namespace): close namespace management gaps (#351)
2026-05-09 16:07:10 +08:00
dongmucat
ab06c75737 Merge branch 'main' into fix/namespace-management-gaps
Resolved conflicts by keeping both sides:
- web/src/api/client.ts: preserve paginated listMembers(slug, {page,size})
  and add delete(slug) from main.
- web/src/shared/hooks/use-namespace-queries.ts: keep useUpdateNamespace
  and useTransferNamespaceOwnership from this branch, plus useDeleteNamespace
  from main.
2026-05-09 15:13:44 +08:00