Package-private JPA repository interfaces cannot be proxied by Spring Data's
JpaRepositoryFactory when using Spring Boot devtools or certain class loader
configurations, causing UnsatisfiedDependencyException at startup.
Signed-off-by: konglong87 <38234954+konglong87@users.noreply.github.com>
DingTalkOAuth2UserService was directly calling IdentityBindingService.bindOrCreate(),
bypassing access policy evaluation. Refactored to delegate to
OAuthLoginFlowService.authenticate() for consistent policy + binding,
matching the pattern used by CustomOAuth2UserService.
Also aligned the returned DefaultOAuth2User structure (providerLogin
attribute key, authorities from platformRoles) with CustomOAuth2UserService
so OAuth2LoginSuccessHandler works uniformly across all providers.
Signed-off-by: konglong87 <38234954+konglong87@users.noreply.github.com>
DingTalk (钉钉) uses a non-standard OAuth2 flow that requires:
- JSON body for token exchange (instead of form-urlencoded)
- Custom header (x-acs-dingtalk-access-token) for user info requests
This PR integrates DingTalk by leveraging the existing OAuthClaimsExtractor
strategy pattern, adding three provider-specific components:
- DingTalkClaimsExtractor: maps DingTalk user fields to normalized OAuthClaims
- DingTalkTokenResponseClient: handles DingTalk's JSON token exchange
- DingTalkOAuth2UserService: fetches user info via DingTalk's custom header
SecurityConfig uses delegating wrappers to route DingTalk requests to
these custom components while preserving standard behavior for all other
providers (GitHub, GitLab, OIDC).
No changes needed to OAuthLoginFlowService, IdentityBindingService,
AuthMethodCatalog, or frontend LoginButton — all are provider-agnostic.
Signed-off-by: konglong87 <38234954+konglong87@users.noreply.github.com>
Add the GitHub Trending (Trendshift) badge and AAIF Associate Member badge to both the English and Chinese README badge sections.
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
* fix(api): tell callers why a request was forbidden
The scope filter already computes an exact reason ("Missing API token
scope: skill:delete", "API token cannot access endpoint: /x") and the
access-denied handler discarded it, returning a bare "Forbidden" for
every case: missing scope, endpoint closed to API tokens, and paths
that simply don't exist. Clients cannot tell those apart, so they
guess — the published CLI reports every 403 as "token may lack
required scope", which sent us debugging token scopes for an hour when
the real causes were a revoked token and a mistyped namespace path.
The reason now rides in the response via a new error.forbidden.detail
message (en + zh), and is logged alongside the exception type.
Signed-off-by: Gal Eyal <gal.e@popai.health>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(api): safely expose API token denial reasons
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
The shared RedisTemplate uses GenericJackson2JsonRedisSerializer with
the application ObjectMapper, which embeds no type information, so
stored DeviceCodeData deserializes as a LinkedHashMap. The typed casts
in pollToken and authorizeDeviceCode then throw ClassCastException on
every call, making the whole device authorization flow unusable
(every poll returns 500).
Convert the raw value with ObjectMapper.convertValue instead of
casting; this reads both the current untyped map format and any typed
format, so no stored-data migration is needed. Adds bean setters to
DeviceCodeData for map conversion and regression tests that feed the
service exactly what Redis returns in production (untyped maps).
Fixes#604
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gal Eyal <gal.e@popai.health>
* fix(publish): delete review tasks of any status when replacing a version
Re-uploading a rejected version under the same version number returned
HTTP 500. deleteReplaceableVersionArtifacts only removed a PENDING review
task, but a rejected version owns a REJECTED one; that row kept a foreign
key on the skill_version, so the subsequent delete hit a constraint
violation that surfaced as a 500.
Delete every review task attached to the version instead.
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
* test(publish): drop the spring-test dependency from the new test
skillhub-domain has no spring-test on its test classpath, so
ReflectionTestUtils does not resolve there. Use plain JDK reflection for
setting the generated id and invoking the private method.
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
* fix(publish): constrain rejected version replacement
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* test(publish): verify replaced review is deleted
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* test(e2e): use generated API response types
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>