From 1297e87c5af567b95c769e56bc4cb7236a86c650 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:02:43 +0800 Subject: [PATCH] fix(deploy): complete DingTalk runtime configuration Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .env.release.example | 2 ++ .../skillhub/templates/server-deployment.yaml | 10 +++++++++ .../skillhub/tests/configuration-contracts.sh | 10 +++++++++ charts/skillhub/values.schema.json | 13 +++++++++++- charts/skillhub/values.yaml | 5 +++++ compose.release.yml | 1 + deploy/k8s/base/backend-deployment.yaml | 6 ++++++ docs/03-authentication-design.md | 4 +++- docs/09-deployment.md | 7 ++++++- scripts/tests/validate-release-config-test.sh | 21 +++++++++++++++++++ scripts/validate-release-config.sh | 9 ++++++++ .../src/main/resources/application.yml | 2 +- 12 files changed, 86 insertions(+), 4 deletions(-) diff --git a/.env.release.example b/.env.release.example index add0f570..ca295b72 100644 --- a/.env.release.example +++ b/.env.release.example @@ -146,6 +146,8 @@ OAUTH2_DINGTALK_CLIENT_ID= OAUTH2_DINGTALK_CLIENT_SECRET= OAUTH2_DINGTALK_AUTHORIZE_URI=https://login.dingtalk.com OAUTH2_DINGTALK_BASE_URI=https://api.dingtalk.com +# Optional; defaults to {baseUrl}/login/oauth2/code/dingtalk. +OAUTH2_DINGTALK_REDIRECT_URI= OAUTH2_DINGTALK_DISPLAY_NAME=钉钉 # Optional: OIDC login (e.g. Keycloak, Okta, Azure AD). diff --git a/charts/skillhub/templates/server-deployment.yaml b/charts/skillhub/templates/server-deployment.yaml index 244bc4c9..c6b99807 100644 --- a/charts/skillhub/templates/server-deployment.yaml +++ b/charts/skillhub/templates/server-deployment.yaml @@ -394,6 +394,16 @@ spec: name: {{ include "skillhub.secretName" . }} key: oauth2-dingtalk-client-secret optional: true + - name: OAUTH2_DINGTALK_AUTHORIZE_URI + value: {{ .Values.oauth2.dingtalk.authorizeBaseUri | quote }} + - name: OAUTH2_DINGTALK_BASE_URI + value: {{ .Values.oauth2.dingtalk.apiBaseUri | quote }} + {{- with .Values.oauth2.dingtalk.redirectUri }} + - name: OAUTH2_DINGTALK_REDIRECT_URI + value: {{ . | quote }} + {{- end }} + - name: OAUTH2_DINGTALK_DISPLAY_NAME + value: {{ .Values.oauth2.dingtalk.displayName | quote }} {{- if .Values.server.javaOpts }} - name: JAVA_OPTS diff --git a/charts/skillhub/tests/configuration-contracts.sh b/charts/skillhub/tests/configuration-contracts.sh index d255db0a..b1dfbbd3 100755 --- a/charts/skillhub/tests/configuration-contracts.sh +++ b/charts/skillhub/tests/configuration-contracts.sh @@ -42,6 +42,9 @@ grep -A1 -F 'name: SKILLHUB_SUITE_REVIEW_WRITES_ENABLED' "$TMP_DIR/default.yaml" if grep -Fq 'name: OAUTH2_FEISHU_REDIRECT_URI' "$TMP_DIR/default.yaml"; then fail "default Helm rendering must omit an empty Feishu redirect URI so Spring can derive baseUrl" fi +if grep -Fq 'name: OAUTH2_DINGTALK_REDIRECT_URI' "$TMP_DIR/default.yaml"; then + fail "default Helm rendering must omit an empty DingTalk redirect URI so Spring can derive baseUrl" +fi render feishu-redirect "$CHART_DIR" \ --set-string oauth2.feishu.redirectUri=https://skills.example.com/login/oauth2/code/feishu \ @@ -50,6 +53,13 @@ grep -A1 -F 'name: OAUTH2_FEISHU_REDIRECT_URI' "$TMP_DIR/feishu-redirect.yaml" \ | grep -Fq 'value: "https://skills.example.com/login/oauth2/code/feishu"' \ || fail "Helm must inject an explicitly configured Feishu redirect URI" +render dingtalk-redirect "$CHART_DIR" \ + --set-string oauth2.dingtalk.redirectUri=https://skills.example.com/login/oauth2/code/dingtalk \ + --show-only templates/server-deployment.yaml >"$TMP_DIR/dingtalk-redirect.yaml" +grep -A1 -F 'name: OAUTH2_DINGTALK_REDIRECT_URI' "$TMP_DIR/dingtalk-redirect.yaml" \ + | grep -Fq 'value: "https://skills.example.com/login/oauth2/code/dingtalk"' \ + || fail "Helm must inject an explicitly configured DingTalk redirect URI" + render suite-review-enabled "$CHART_DIR" \ --set server.suiteReviewWritesEnabled=true \ --show-only templates/server-deployment.yaml >"$TMP_DIR/suite-review-enabled.yaml" diff --git a/charts/skillhub/values.schema.json b/charts/skillhub/values.schema.json index 6d3bf510..83b76ac5 100644 --- a/charts/skillhub/values.schema.json +++ b/charts/skillhub/values.schema.json @@ -37,7 +37,7 @@ "oauth2": { "type": "object", "additionalProperties": false, - "required": ["feishu"], + "required": ["feishu", "dingtalk"], "properties": { "feishu": { "type": "object", @@ -50,6 +50,17 @@ "userInfoUri": { "type": "string", "format": "uri" }, "redirectUri": { "type": "string" } } + }, + "dingtalk": { + "type": "object", + "additionalProperties": false, + "required": ["authorizeBaseUri", "apiBaseUri", "redirectUri", "displayName"], + "properties": { + "authorizeBaseUri": { "type": "string", "format": "uri" }, + "apiBaseUri": { "type": "string", "format": "uri" }, + "redirectUri": { "type": "string" }, + "displayName": { "type": "string", "minLength": 1 } + } } } }, diff --git a/charts/skillhub/values.yaml b/charts/skillhub/values.yaml index 555ace85..5e38e21a 100644 --- a/charts/skillhub/values.yaml +++ b/charts/skillhub/values.yaml @@ -29,6 +29,11 @@ oauth2: tokenUri: https://accounts.feishu.cn/oauth/v3/token userInfoUri: https://open.feishu.cn/open-apis/authen/v1/user_info redirectUri: "" + dingtalk: + authorizeBaseUri: https://login.dingtalk.com + apiBaseUri: https://api.dingtalk.com + redirectUri: "" + displayName: 钉钉 builtinSkills: enabled: true diff --git a/compose.release.yml b/compose.release.yml index 77cc571a..bb55a8f1 100644 --- a/compose.release.yml +++ b/compose.release.yml @@ -132,6 +132,7 @@ services: OAUTH2_DINGTALK_CLIENT_SECRET: ${OAUTH2_DINGTALK_CLIENT_SECRET:-local-placeholder} OAUTH2_DINGTALK_AUTHORIZE_URI: ${OAUTH2_DINGTALK_AUTHORIZE_URI:-https://login.dingtalk.com} OAUTH2_DINGTALK_BASE_URI: ${OAUTH2_DINGTALK_BASE_URI:-https://api.dingtalk.com} + OAUTH2_DINGTALK_REDIRECT_URI: ${OAUTH2_DINGTALK_REDIRECT_URI:-${SKILLHUB_PUBLIC_BASE_URL:-http://localhost}/login/oauth2/code/dingtalk} OAUTH2_DINGTALK_DISPLAY_NAME: ${OAUTH2_DINGTALK_DISPLAY_NAME:-钉钉} SPRING_MAIL_HOST: ${SPRING_MAIL_HOST:-} SPRING_MAIL_PORT: ${SPRING_MAIL_PORT:-25} diff --git a/deploy/k8s/base/backend-deployment.yaml b/deploy/k8s/base/backend-deployment.yaml index 01319d16..a7a39648 100644 --- a/deploy/k8s/base/backend-deployment.yaml +++ b/deploy/k8s/base/backend-deployment.yaml @@ -262,6 +262,12 @@ spec: name: skillhub-secret key: oauth2-dingtalk-client-secret optional: true + - name: OAUTH2_DINGTALK_AUTHORIZE_URI + value: "https://login.dingtalk.com" + - name: OAUTH2_DINGTALK_BASE_URI + value: "https://api.dingtalk.com" + - name: OAUTH2_DINGTALK_DISPLAY_NAME + value: "钉钉" volumeMounts: - name: skillhub-storage mountPath: /var/lib/skillhub/storage diff --git a/docs/03-authentication-design.md b/docs/03-authentication-design.md index 478e3420..1d264616 100644 --- a/docs/03-authentication-design.md +++ b/docs/03-authentication-design.md @@ -288,7 +288,9 @@ spring: # 故意不声明 scope:钉钉的授权端点要 scope=openid,但在这里声明会让 # Spring 把该注册当成 OIDC 客户端并附加 nonce,而钉钉不接受 nonce。 # scope 由 DingTalkAuthorizationRequestCustomizer 在请求阶段补上。 - client-authentication-method: none + # 钉钉是 confidential client,只是由自定义 token client 把 secret 放进 JSON body。 + # 不使用 none,避免 Spring 自动添加本实现无法应答的 PKCE challenge。 + client-authentication-method: client_secret_post authorization-grant-type: authorization_code provider: feishu: diff --git a/docs/09-deployment.md b/docs/09-deployment.md index 24ca4121..6b7d30b2 100644 --- a/docs/09-deployment.md +++ b/docs/09-deployment.md @@ -315,7 +315,12 @@ services: exchange 的 endpoint。`OAUTH2_FEISHU_PROTOCOL_VERSION` 只允许 `v2` 或 `v3`, 默认 `v3`,不会自动 fallback。 - 钉钉:`OAUTH2_DINGTALK_CLIENT_ID` / `OAUTH2_DINGTALK_CLIENT_SECRET` - (分别填应用的 AppKey 与 AppSecret) + (分别填应用的 AppKey 与 AppSecret)。在钉钉开发者后台登记 + `https://<公网域名>/login/oauth2/code/dingtalk`,并为用户信息接口开通所需权限。 + `OAUTH2_DINGTALK_REDIRECT_URI` 可在动态端口或特殊反向代理场景显式覆盖;Compose + 默认根据 `SKILLHUB_PUBLIC_BASE_URL` 生成回调,Helm/K8s 未设置时由 Spring 使用 + `{baseUrl}`。国际版或网关场景可覆盖 `OAUTH2_DINGTALK_AUTHORIZE_URI` 与 + `OAUTH2_DINGTALK_BASE_URI`。 留空即不展示该入口,无需改配置文件。注意:飞书和钉钉的邮箱都由企业管理员导入、 未经用户确认,因此 `emailVerified` 恒为 false;若在 `application.yml` 中把 diff --git a/scripts/tests/validate-release-config-test.sh b/scripts/tests/validate-release-config-test.sh index 83e73a70..c241e308 100755 --- a/scripts/tests/validate-release-config-test.sh +++ b/scripts/tests/validate-release-config-test.sh @@ -107,6 +107,27 @@ write_env "$invalid_feishu_redirect_env" "release-download-secret-32-bytes-minim printf '%s\n' "OAUTH2_FEISHU_REDIRECT_URI=https://skillhub.example.com/login/oauth2/code/feishu?bad=1" >>"$invalid_feishu_redirect_env" expect_fail "$invalid_feishu_redirect_env" "OAUTH2_FEISHU_REDIRECT_URI must not contain a query" +valid_dingtalk_env="$tmp/valid-dingtalk.env" +write_env "$valid_dingtalk_env" "release-download-secret-32-bytes-minimum" +cat >>"$valid_dingtalk_env" <<'EOF' +OAUTH2_DINGTALK_CLIENT_ID=ding-test +OAUTH2_DINGTALK_CLIENT_SECRET=dingtalk-test-secret +OAUTH2_DINGTALK_AUTHORIZE_URI=https://login.dingtalk.com +OAUTH2_DINGTALK_BASE_URI=https://api.dingtalk.com +OAUTH2_DINGTALK_REDIRECT_URI=https://skillhub.example.com/login/oauth2/code/dingtalk +EOF +"$SCRIPT" "$valid_dingtalk_env" >/dev/null + +invalid_dingtalk_base_env="$tmp/invalid-dingtalk-base.env" +write_env "$invalid_dingtalk_base_env" "release-download-secret-32-bytes-minimum" +printf '%s\n' "OAUTH2_DINGTALK_BASE_URI=https://api.dingtalk.com/" >>"$invalid_dingtalk_base_env" +expect_fail "$invalid_dingtalk_base_env" "OAUTH2_DINGTALK_BASE_URI must not have a trailing slash" + +invalid_dingtalk_redirect_env="$tmp/invalid-dingtalk-redirect.env" +write_env "$invalid_dingtalk_redirect_env" "release-download-secret-32-bytes-minimum" +printf '%s\n' "OAUTH2_DINGTALK_REDIRECT_URI=https://skillhub.example.com/callback?bad=1" >>"$invalid_dingtalk_redirect_env" +expect_fail "$invalid_dingtalk_redirect_env" "OAUTH2_DINGTALK_REDIRECT_URI must not contain a query" + disabled_builtin_skills_env="$tmp/disabled-builtin-skills.env" write_env "$disabled_builtin_skills_env" "release-download-secret-32-bytes-minimum" printf '%s\n' "SKILLHUB_BUILTIN_SKILLS_ENABLED=false" >>"$disabled_builtin_skills_env" diff --git a/scripts/validate-release-config.sh b/scripts/validate-release-config.sh index c7189304..356d241d 100755 --- a/scripts/validate-release-config.sh +++ b/scripts/validate-release-config.sh @@ -406,6 +406,15 @@ for feishu_endpoint in OAUTH2_FEISHU_AUTHORIZATION_URI OAUTH2_FEISHU_TOKEN_URI O fi done +for dingtalk_endpoint in OAUTH2_DINGTALK_AUTHORIZE_URI OAUTH2_DINGTALK_BASE_URI OAUTH2_DINGTALK_REDIRECT_URI; do + eval "dingtalk_endpoint_value=\${$dingtalk_endpoint:-}" + if [ -n "$dingtalk_endpoint_value" ]; then + validate_url "$dingtalk_endpoint" + fi +done +validate_no_trailing_slash OAUTH2_DINGTALK_AUTHORIZE_URI +validate_no_trailing_slash OAUTH2_DINGTALK_BASE_URI + if [ "$errors" -gt 0 ]; then echo "Release config validation failed: $errors error(s), $warnings warning(s)." >&2 exit 1 diff --git a/server/skillhub-app/src/main/resources/application.yml b/server/skillhub-app/src/main/resources/application.yml index 6579da34..7e580256 100644 --- a/server/skillhub-app/src/main/resources/application.yml +++ b/server/skillhub-app/src/main/resources/application.yml @@ -58,7 +58,7 @@ spring: scope: - read:user - user:email - redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" + redirect-uri: "${OAUTH2_DINGTALK_REDIRECT_URI:{baseUrl}/login/oauth2/code/{registrationId}}" client-name: GitHub authorization-grant-type: authorization_code gitlab: