Skip to content

Fix org member identity mapping for sync and SSO#404

Merged
yaojin3616 merged 2 commits intomainfrom
fix_org_member
Apr 14, 2026
Merged

Fix org member identity mapping for sync and SSO#404
yaojin3616 merged 2 commits intomainfrom
fix_org_member

Conversation

@yaojin3616
Copy link
Copy Markdown
Collaborator

Summary

  • require valid unionid during Feishu/DingTalk org sync
  • prevent syncing records where unionid == external_id
  • fix SSO identity linking to use real union_id/open_id/external_id
  • fix Feishu channel lazy shell creation so user_id is no longer written into unionid
  • add focused tests for org sync validation and identity id mapping

Verification

  • PYTHONPYCACHEPREFIX=/tmp/codex-pycache python3 -m py_compile app/services/sso_service.py app/services/channel_user_service.py app/services/registration_service.py app/api/auth.py app/api/feishu.py tests/test_identity_id_mapping.py tests/test_org_sync_adapter.py
  • pytest not run in this environment because the module is not installed

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d30a2e4897

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/app/api/auth.py Outdated
Comment on lines +974 to +975
lookup_provider_user_id = user_info.provider_union_id or user_info.provider_user_id
existing_user = await sso_service.check_duplicate_identity(db, provider, lookup_provider_user_id)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check bind conflicts against open_id fallback

The duplicate check in bind_identity now uses only provider_union_id (lookup_provider_user_id), but link_identity still matches existing OrgMember rows by open_id as well. For legacy rows that are keyed only by open_id (common from earlier bindings), check_duplicate_identity will miss the conflict and the later link_identity call can reassign member.user_id to the current account, effectively stealing an already-linked identity during bind.

Useful? React with 👍 / 👎.

Comment on lines +532 to +535
if self._provider_requires_unionid(provider) and not user.unionid:
raise ValueError(
f"unionid is required for {provider.provider_type} org sync user {user.external_id or user.name}"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent reconcile deletion on unionid validation failures

Raising on missing unionid here causes _upsert_member to fail for that user, but sync_org_structure continues and then runs reconciliation, which marks non-updated members as deleted. That means a transient provider response/permission issue that omits unionid can soft-delete previously valid Feishu/DingTalk members in bulk on a single sync run, which is a data-loss regression introduced by this strict validation path.

Useful? React with 👍 / 👎.

@yaojin3616 yaojin3616 merged commit af1b8b1 into main Apr 14, 2026
@yaojin3616 yaojin3616 deleted the fix_org_member branch April 14, 2026 14:45
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4101cfbd03

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +297 to +299
member = member_result.scalar_one_or_none()
if member:
return member
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Continue identity lookup after unlinked member matches

The new _find_identity_member helper returns on the first identifier hit, even when that OrgMember is just an unlinked shell (user_id is null). In resolve_user_identity/check_duplicate_identity, this short-circuit means a later open_id/external_id match that is already linked to another user is never checked, so bind can incorrectly pass conflict checks and then link_identity links the wrong record. Fresh evidence here is the new early return member in the lookup loop, which did not exist in the previous single-query path.

Useful? React with 👍 / 👎.

Comment on lines +585 to +591
if self._provider_requires_unionid(provider) and user.unionid:
fallback_query = fallback_query.where(
or_(
OrgMember.unionid.is_(None),
OrgMember.unionid == "",
OrgMember.unionid == user.unionid,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match legacy members when reconciling corrected unionid

The new fallback filter only allows external_id/open_id matches when existing unionid is null/empty or already equal to the incoming value. That blocks updates to legacy rows with incorrect non-empty unionid values (a known historical state), so _upsert_member creates a new row instead of updating the existing linked one; if email/mobile is absent, the replacement row loses user_id, and a later reconcile can delete the previously linked row. This introduces account-link breakage during normal sync migration.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant