{"record":{"id":"6e675bacaf98714b","repo":"Wei-Shaw/sub2api","slug":"dingtalk-internal-only-requires-app-type-internal","errorCode":null,"errorMessage":"dingtalk: internal_only requires app_type=internal","messagePattern":"dingtalk: internal_only requires app_type=internal","errorType":"validation","errorClass":"ErrDingTalkV1AppTypeMismatch","httpStatus":null,"severity":"error","filePath":"backend/internal/config/validate_dingtalk.go","lineNumber":13,"sourceCode":"// Package config 包含钉钉连接配置的校验逻辑。\n//\n// internal_only 模式安全模型（方案 A）：\n// 不再要求 admin 填写 InternalCorpID 做二次 corpID 比对。\n// 安全边界由钉钉\"企业内部应用\"类型本身保证——只有应用所属企业的员工才能完成 OAuth，\n// 因此 ValidateDingTalkConfig 只要求 app_type=internal（V1），不再要求 InternalCorpID 非空（原 V3 已删除）。\n// InternalCorpID 字段保留，admin 可选填；若填写，checkDingTalkCorpAllowed 不会使用它做约束。\npackage config\n\nimport \"errors\"\n\nvar (\n\tErrDingTalkV1AppTypeMismatch = errors.New(\"dingtalk: internal_only requires app_type=internal\")\n\tErrDingTalkV4InvalidAppKind  = errors.New(\"dingtalk: dingtalk_app_kind must be internal_app\")\n)\n\nfunc ValidateDingTalkConfig(cfg DingTalkConnectConfig) error {\n\tif !cfg.Enabled {\n\t\treturn nil\n\t}\n\tif cfg.DingTalkAppKind != \"internal_app\" {\n\t\treturn ErrDingTalkV4InvalidAppKind\n\t}\n\tif cfg.CorpRestrictionPolicy == \"internal_only\" {\n\t\tif cfg.AppType != \"internal\" {\n\t\t\treturn ErrDingTalkV1AppTypeMismatch\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/backend/internal/config/validate_dingtalk.go#L1-L31","documentation":"Returned by ValidateDingTalkConfig in backend/internal/config/validate_dingtalk.go when CorpRestrictionPolicy is \"internal_only\" but AppType is not \"internal\". The internal_only security model (Plan A) relies on DingTalk 'enterprise internal app' semantics — only employees of the app's own corporation can complete OAuth — so the config must declare app_type=internal. The old requirement for a non-empty InternalCorpID (V3) was removed; the field remains optional and unused for constraint.","triggerScenarios":"Starting the backend with dingtalk config where corp_restriction_policy=internal_only and app_type is empty, \"corp\", or any value other than \"internal\"; also fails startup if dingtalk_app_kind != \"internal_app\" (ErrDingTalkV4InvalidAppKind, checked first). Only cfg.Enabled=true configurations are validated.","commonSituations":"Copying a config from an older deployment that used app_type values like \"corp\" or left app_type unset; upgrading to the version where V3 corpID comparison was replaced by V1 app_type check; mixing settings from a third-party (服务商) DingTalk app with internal_only policy.","solutions":["Set app_type=internal in the dingtalk config section (and keep dingtalk_app_kind=internal_app).","If you intentionally use a non-internal app, change corp_restriction_policy away from \"internal_only\" (e.g. a policy supported for that app type).","If you relied on the old InternalCorpID check, remove it — it is no longer required nor used as a constraint.","Re-run the config validator / restart and confirm the error is gone."],"exampleFix":"# before\ndingtalk:\n  enabled: true\n  dingtalk_app_kind: internal_app\n  corp_restriction_policy: internal_only\n  app_type: \"\"        # or \"corp\"\n  internal_corp_id: \"dingxxxx\"   # legacy, no longer sufficient\n\n# after\ndingtalk:\n  enabled: true\n  dingtalk_app_kind: internal_app\n  corp_restriction_policy: internal_only\n  app_type: internal","handlingStrategy":"validation","validationCode":"func validDingTalkCfg(cfg DingTalkConnectConfig) error {\n    if !cfg.Enabled { return nil }\n    if cfg.DingTalkAppKind != \"internal_app\" { return ErrDingTalkV4InvalidAppKind }\n    if cfg.CorpRestrictionPolicy == \"internal_only\" && cfg.AppType != \"internal\" {\n        return ErrDingTalkV1AppTypeMismatch\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := config.ValidateDingTalkConfig(cfg); err != nil {\n    if errors.Is(err, config.ErrDingTalkV1AppTypeMismatch) {\n        log.Fatalf(\"config: set dingtalk app_type=internal (or change corp_restriction_policy): %v\", err)\n    }\n    log.Fatalf(\"config: invalid dingtalk config: %v\", err)\n}","preventionTips":["Run the backend's config validation command (or a pre-deploy dry run) before restarting with new dingtalk settings.","Codify allowed combos in config templates: internal_only always pairs with app_type=internal and dingtalk_app_kind=internal_app.","After upgrades, re-read the config package comments — the InternalCorpID requirement (old V3) was deliberately removed; stale configs relying on it now fail differently.","Add a CI check that lints config files against ValidateDingTalkConfig."],"tags":["config","dingtalk","oauth","validation","backend","startup"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}