Commit graph

569 commits

Author SHA1 Message Date
XiaoSeS
5916771237 fix(web): clarify pending avatars and space management
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-14 10:44:03 +08:00
XiaoSeS
be1529aeda fix(review): refresh moderation queue on entry
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 19:29:03 +08:00
XiaoSeS
efbb50f4aa fix(namespace): refresh invitation state before creating link
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 19:16:01 +08:00
XiaoSeS
3de13c033f style(namespace): remove trailing empty line
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 18:39:00 +08:00
XiaoSeS
71d0598d92 feat(review): unify skill avatar approval and space management
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 18:38:30 +08:00
XiaoSeS
bfb5298c9a fix(avatar): bound crop dialog and refresh inactive removal caches
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 15:50:50 +08:00
XiaoSeS
66571f709b fix(avatar): unify update flow and crop in a compact dialog
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 15:15:52 +08:00
XiaoSeS
b469b60997 fix(avatar): reuse open-source image cropping component
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 13:59:12 +08:00
XiaoSeS
c5f8483cbe fix(publish): refresh content review queue after submission
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 13:13:45 +08:00
XiaoSeS
d9c658a459 fix(avatar): refresh owner and review caches after mutations
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 12:54:18 +08:00
XiaoSeS
1a7ffbe6e2 feat(avatar): stage images before skill publication
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-13 11:15:35 +08:00
XiaoSeS
c48b09d3a0 fix(namespace): expose invitation confirmation errors
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 15:12:00 +08:00
XiaoSeS
5f2d47968c feat: combine Skill avatars and namespace invitations
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 13:42:31 +08:00
XiaoSeS
17a18b9319 fix(avatar): recover pending reviews and cover moderation flows
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 13:42:26 +08:00
XiaoSeS
b67ea7ddb8 feat(avatar): add review and owner interfaces
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 13:42:11 +08:00
XiaoSeS
ee17c1eb43 fix(invitation): enter namespace after member preview
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 12:52:04 +08:00
XiaoSeS
44a1623620 fix(invitation): align API with issue contract
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 12:48:18 +08:00
XiaoSeS
f12019cf14 fix(invitation): keep login return target on join route
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 12:32:12 +08:00
XiaoSeS
08fb846b15 fix(namespace): keep invitation labels unique and copy promptly
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 12:19:20 +08:00
XiaoSeS
7d36d42056 test(namespace): cover invitation mutation races
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 12:15:49 +08:00
XiaoSeS
27687713d2 fix(namespace): complete invitation review follow-up
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 12:06:34 +08:00
XiaoSeS
9c997f7eca feat(namespace): add one-time invitation links
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-12 11:55:47 +08:00
XiaoSeS
1bfbfdd720 test(auth): cover concurrent settings update in UI
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-10 16:38:07 +08:00
XiaoSeS
44a311ea80 feat(auth): improve system settings interactions
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-10 16:25:12 +08:00
XiaoSeS
f297fdfd09 fix(auth): verify initial grants and auth UI behavior
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-10 15:42:17 +08:00
XiaoSeS
df9b869d8e feat(auth): add system login settings and initial role grants
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-10 15:42:17 +08:00
XiaoSeS
84bda97dee feat(promotion): support reviewed global updates and revocation
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-09 18:14:59 +08:00
XiaoSeS
dc635a24d6 fix(auth): require secondary approval before account merge
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-08 12:03:55 +08:00
XiaoSeS
455cbb5ba5
Merge pull request #902 from iflytek/feature/enterprise-login-r1b2-org-admin
feat(auth): add organization creation control plane slice
2026-09-24 15:58:57 +08:00
dongmucat
0fd222d1e7
Merge pull request #879 from iflytek/feature/scanner-2-1-upgrade
fix(scanner): harden Scanner 2.1 integration
2026-09-24 15:47:20 +08:00
XiaoSeS
52e4e052a1 feat(auth): add organization creation control plane slice
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 14:43:17 +08:00
XiaoSeS
e0c5f7597d test(auth): expect home after registration
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 13:53:00 +08:00
XiaoSeS
69192fcf9b test(auth): match theme toggle accessible name
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 13:35:31 +08:00
XiaoSeS
f9e9496053 test(auth): stabilize registration layout screenshot
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 13:35:31 +08:00
XiaoSeS
3000375515 test(auth): cover registration viewport and artwork loading
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 13:35:31 +08:00
XiaoSeS
f8148f28f6 test(auth): cover direct routing and login layout boundaries
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 13:35:30 +08:00
XiaoSeS
e4ee6b6d9a fix(auth): keep login available during session check failures
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 13:35:30 +08:00
XiaoSeS
8249e84454 fix(auth): hide password routing implementation details
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 11:22:43 +08:00
XiaoSeS
0e9d2e2d3e fix(auth): redirect signed-in visitors away from login
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 11:22:43 +08:00
XiaoSeS
5be60043d5 test(auth): select password field precisely
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 10:32:21 +08:00
XiaoSeS
5aea7345b8 test(auth): cover registration and runtime return navigation
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 10:21:38 +08:00
XiaoSeS
1aa8d7bc85 fix(auth): guard registration OAuth hint and return paths
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 10:09:55 +08:00
XiaoSeS
5a8b796d03 fix(auth): defer organization discovery until backend contract
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 09:48:56 +08:00
XiaoSeS
5d4b40a5e5 fix(auth): return to origin or home after login
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-24 09:37:15 +08:00
XiaoSeS
ff077bfbe7 fix(auth): hide unavailable method catalog warning
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-23 17:12:34 +08:00
XiaoSeS
c8e6988485 feat(auth): refresh login and registration entry
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-23 17:02:50 +08:00
XiaoSeS
e8fad5962e
fix(web): refresh API proxy DNS after backend redeploy (#900)
Some checks are pending
Security / Dependency Review (push) Waiting to run
Security / CodeQL (java-kotlin) (push) Waiting to run
Security / CodeQL (javascript-typescript) (push) Waiting to run
Security / CodeQL (python) (push) Waiting to run
* fix(web): refresh API proxy DNS after backend redeploy

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(web): stabilize DNS replacement coverage

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(web): allow early resolver refresh after DNS change

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(web): use dynamic ports for nginx smoke

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

---------

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-23 15:51:08 +08:00
XiaoSeS
da317d94e9 feat(auth): add organization control-plane read APIs
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-22 16:38:10 +08:00
XiaoSeS
96f244b416 feat(auth): add DingTalk as a public login provider
Adds DingTalk (钉钉) as a public sign-in option: it authenticates a SkillHub
platform account and nothing more. No Organization membership, no directory
sync, no Namespace grants.

DingTalk deviates from standard OAuth at all three stages, one strategy each:

- authorize: its endpoint wants scope=openid, but declaring that scope in
  configuration makes Spring treat the registration as OIDC and attach a
  nonce, which DingTalk rejects. The scope is added by
  DingTalkAuthorizationRequestCustomizer instead, keeping this a plain OAuth2
  client. A test asserts the scope is present and the nonce is not.
- token: credentials go in a JSON body rather than a form, handled by
  DingTalkTokenResponseClient.
- userinfo: the token travels in x-acs-dingtalk-access-token rather than
  Authorization: Bearer.

Subject and email semantics, which decide whether a login can reach an
existing account:

- unionId is the only accepted subject. DingTalk also returns openId and
  userId, but they must not act as fallbacks: openId is scoped per app and
  userId per organization, so a login falling back to either would bind a
  different identity than a later login carrying unionId, splitting one
  person across two platform accounts.
- A blank or missing unionId fails the login.
- emailVerified is always false. DingTalk returns the email an organization
  admin recorded without attesting the user controls it.

The userinfo service only fetches attributes; account matching, provisioning
and session creation stay with the unified identity core. The reference
implementation called OAuthLoginFlowService.authenticate() from inside
loadUser, which decided the account before the core's gate ran.

Operational bounds match the Feishu adapter: connect and read timeouts, a
64 KB response cap, error descriptions and logs carrying only the exception
class or provider error code, and no logging in the claims extractor.
Unused PII is dropped rather than carried into the principal -- notably
mobile and stateCode.

Adds ProviderStrategyWiringTest, which loads the real application context.
The unit tests call package-visible constructors and so cannot catch Spring
wiring faults; a component with two constructors and no @Autowired marker
unit-tests green and then fails at startup. That happened during this work.

Adapted from the implementation in #467 by @konglong87, re-extracted onto
current main with the subject, structure and bounds changes above.

Part of R1-A2 (public Provider adapters) per
openspec/changes/enterprise-identity-platform/rollout-plan.md.

Co-authored-by: konglong87 <konglong87@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-09-21 15:15:41 +08:00
XiaoSeS
934cfa6ded
feat(auth): add Feishu as a public login provider (R1-A2) (#877)
Some checks are pending
Deploy Docs / build (push) Waiting to run
Deploy Docs / Deploy (push) Blocked by required conditions
Security / Dependency Review (push) Waiting to run
Security / CodeQL (java-kotlin) (push) Waiting to run
Security / CodeQL (javascript-typescript) (push) Waiting to run
Security / CodeQL (python) (push) Waiting to run
* feat(auth): let providers override OAuth userinfo loading

Some providers do not return a flat, standard userinfo payload, so
DefaultOAuth2UserService cannot read them. Add ProviderOAuth2UserService
so a provider can claim its own registration id and supply the loading
step, while everything after it stays shared.

The override runs inside the RemoteIdentityIoExecutor boundary added in
R1-A, so a provider's HTTP call does not hold the surrounding
transaction open. Registrations without an override keep using the
default user service unchanged.

Part of R1-A2 (public Provider adapters) per
openspec/changes/enterprise-identity-platform/rollout-plan.md.

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* feat(auth): add Feishu as a public login provider

Adds Feishu (Lark) as a public sign-in option: it authenticates a
SkillHub platform account and nothing more. No Organization membership,
no directory sync, no Namespace grants.

Feishu deviates from standard OAuth in two ways this handles:
its userinfo response is wrapped in a {code, msg, data} envelope, and it
reports errors with HTTP 200. FeishuOAuth2UserService unwraps that
envelope into flat attributes; FeishuClaimsExtractor maps them to the
shared OAuthClaims, so account decisions still run through the unified
identity core added in R1-A.

Subject and email semantics, which decide whether a login can reach an
existing account:

- open_id is the only subject. union_id stays in extra rather than
  acting as a fallback: a subject that can change between logins would
  split one person across two platform accounts. Promoting union_id
  later needs an explicit alias migration.
- A blank or missing open_id fails the login instead of binding the
  literal string "null".
- emailVerified is always false. Feishu emails are imported by an
  organization admin and never confirmed with the user, so they carry no
  verification signal and cannot be used to join an existing account.

Operational bounds: the userinfo call has connect and read timeouts so an
unresponsive Feishu endpoint cannot hold a login thread, and the
OAuth2Error description carries only the provider error code, because an
upstream message can quote the request URI and with it the access token.
Like the GitHub and GitLab extractors, the claims extractor logs nothing.

The login button follows the existing config-driven catalog: with no
client id configured, /api/v1/auth/methods does not list Feishu and no
button renders. No frontend code change is needed; the icon resolves by
provider name.

Adapted from the implementation in #696 by @yhd4711499, re-extracted onto
current main with the subject, logging and timeout changes above.

Part of R1-A2 (public Provider adapters) per
openspec/changes/enterprise-identity-platform/rollout-plan.md.

Co-authored-by: yhd4711499 <yhd4711499@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* fix(auth): bound Feishu userinfo response and stop subject leaking into displayName

Three defects found reviewing this batch against the R1-A2 spec.

Response size limit. The spec's scope line asks for "远程 I/O 超时与响应大小
限制"; only the timeouts were implemented, so a misconfigured or hostile
OAUTH2_FEISHU_BASE_URI could stream an unbounded body into the parser.
Reads at most 64 KB before parsing, mirroring the 10 MB cap the shared
WebClientConfig already applies. Uses InputStream.readNBytes rather than
adding commons-io or guava, neither of which skillhub-auth declares.

Synthesized displayName. Falling back to "feishu-<open_id>" wrote the
external subject into UserAccount.displayName and into
UserActivatedEvent, carrying it somewhere event consumers may log it --
against the R1-A gate that logs must not contain the subject. Now stops
at name -> en_name like the GitHub and GitLab extractors.

Unused mobile attribute. A phone number was extracted into the principal
attributes and read by nothing. It is PII the spec did not ask for and it
widened the redaction surface for free.

Also drops a constructor overload that only passed List.of() through, and
a test that duplicated the blank-subject path.

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* docs(auth): document the provider adapter contract and Feishu operator setup

AGENTS.md and CONTRIBUTING.md both require docs updates when auth flows or
deployment config change; this batch changed both and touched no docs.

03-authentication-design.md described adding a provider as "branch on
registrationId inside CustomOAuth2UserService", which the
ProviderOAuth2UserService strategy supersedes. Rewrites that recipe:
register an OAuthClaimsExtractor bean per provider, add a
ProviderOAuth2UserService only when the userinfo response is non-standard,
and note that the login page needs no code change. Also records the
provider-side obligations that are easy to get wrong -- stable subject with
no fallback, emailVerified only on proven ownership, bounded remote calls,
no subject in logs -- and un-comments the config example, which still
listed GitLab as a future possibility.

faq.md told operators to delete "the github and gitlab blocks" to hide SSO
buttons. That advice was already incomplete and gets worse per provider, so
it now explains the config-driven mechanism: an empty client id keeps the
entry off the login page, no file edit needed.

09-deployment.md listed only the GitHub credentials. Adds GitLab and Feishu,
and flags a deployment trap: Feishu emails are admin-imported so
emailVerified is always false, and skillhub.access-policy.mode=EMAIL_DOMAIN
denies every unverified email, which would reject all Feishu logins.

Squares the Feishu logo viewBox. It was 407.87x324.19 while login-button
renders it in a square w-5 h-5 box, so the mark was distorted.

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* feat(deploy): wire Feishu credentials into the release surfaces

.env.release.example advertised OAUTH2_FEISHU_* knobs that no deployment
path could actually deliver. compose.release.yml has no env_file, so every
variable must be listed explicitly, and the Helm chart and k8s base only
mapped the GitHub secret keys. Setting the documented variables therefore
did nothing.

Adds Feishu to compose.release.yml, the Helm secret template and values,
the k8s deployment and its secret example. GitLab had the identical gap, so
it is wired at the same time rather than leaving the example file half true.

validate-release-config.sh only checked that GitHub's id and secret appear
together. A half-configured provider renders a login button whose exchange
then fails, so the check now loops over all three providers. Its test gained
both-directions cases per provider plus a fully configured pass; reverting
the loop to GitHub-only makes them fail.

Also adds the provider's only failure log. Nothing downstream records a
Feishu userinfo failure -- OAuth2LoginFailureHandler does not log either --
so the previous code was silent on error. Logs the exception class and
Feishu's own error code, never the upstream msg, which can quote the access
token; a test asserts the code is present and the token is not.

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* fix(auth): use JSON token exchange for Feishu OAuth

Made-with: Proma
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* fix(auth): preserve Feishu OAuth browser redirect

Add safe phase-level OAuth diagnostics and redact callback credentials from request logs.

Made-with: Proma
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* docs(deploy): clarify Feishu OAuth configuration and validation

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* fix(deploy): pass Feishu redirect URI through releases

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* fix(deploy): pass S3 chunked encoding setting

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* fix(deploy): preserve default Feishu callback derivation

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

---------

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: yhd4711499 <yhd4711499@users.noreply.github.com>
2026-09-21 14:39:22 +08:00