Skip to content

Commit 1f6ecbe

Browse files
justinsisyphus-dev-ai
andcommitted
bugfix: 修复LDAP同步时code相同但username变更导致用户重建失败
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 0181cae commit 1f6ecbe

2 files changed

Lines changed: 70 additions & 0 deletions

File tree

src/api/bkuser_core/categories/plugins/ldap/helper.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ def db_profiles(self) -> Dict[str, Profile]:
139139
# 由于 bulk_update 需要从数据库查询完整的 Profile 信息, 为提高查询效率, 统一执行查询操作, 减轻数据库负担
140140
return {profile.username: profile for profile in Profile.objects.filter(category_id=self.category.pk).all()}
141141

142+
@cached_property
143+
def db_profiles_by_code(self) -> Dict[str, Profile]:
144+
return {
145+
profile.code: profile
146+
for profile in Profile.all_objects.filter(category_id=self.category.pk).exclude(code__isnull=True).exclude(code="")
147+
}
148+
142149
@cached_property
143150
def db_departments(self) -> Dict[str, Department]:
144151
# 由于 bulk_update 需要从数据库查询完整的 Department 信息, 为提高查询效率, 统一执行查询操作, 减轻数据库负担
@@ -182,13 +189,17 @@ def _load_base_info(self):
182189
"extras": info.extras,
183190
}
184191

192+
self._delete_profile_if_username_changed(info.code, info.username)
193+
185194
# 2. 更新或创建 Profile 对象
186195
if info.username in self.db_profiles:
187196
profile = self.db_profiles[info.username]
188197
for key, value in profile_params.items():
189198
setattr(profile, key, value)
190199
self.db_sync_manager.magic_add(profile, SyncOperation.UPDATE.value)
191200
profile_ids.add(profile.id)
201+
self.db_profiles[info.username] = profile
202+
self.db_profiles_by_code[info.code] = profile
192203
else:
193204
profile = Profile(**profile_params)
194205
if self.db_sync_manager.magic_exists(profile):
@@ -202,6 +213,8 @@ def _load_base_info(self):
202213
profile.id = self.db_sync_manager.register_id(LdapProfileMeta)
203214
self.db_sync_manager.magic_add(profile, SyncOperation.ADD.value)
204215
profile_ids.add(profile.id)
216+
self.db_profiles[info.username] = profile
217+
self.db_profiles_by_code[info.code] = profile
205218

206219
# 3. 维护关联关系
207220
for full_department_name_list in info.departments:
@@ -235,6 +248,24 @@ def _load_base_info(self):
235248
self.context.add_record(step=SyncStep.USERS, success=True, username=info.username)
236249
self.profile_ids = profile_ids
237250

251+
def _delete_profile_if_username_changed(self, code: str, username: str) -> None:
252+
profile = self.db_profiles_by_code.get(code)
253+
if not profile or profile.username == username:
254+
return
255+
256+
logger.info(
257+
"profile code<%s> exists but username changed from <%s> to <%s>, will hard delete old profile and recreate",
258+
code,
259+
profile.username,
260+
username,
261+
)
262+
263+
old_profile_id = profile.id
264+
Profile.all_objects.filter(id=old_profile_id).delete()
265+
self.db_profiles.pop(profile.username, None)
266+
self.db_profiles_by_code.pop(code, None)
267+
return
268+
238269
def _load_leader_info(self):
239270
# 加载上下级关系
240271
# WeOps改造,记录原有的上级关系

src/api/bkuser_core/tests/categories/plugins/ldap/test_syncer.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,45 @@ def test_sync_users(self, pre_created, test_ldap_syncer, users, expected_adding,
172172
in test_ldap_syncer.db_sync_manager._sets[ProfileMeta.target_model].updating_items
173173
)
174174

175+
def test_sync_users_recreate_when_code_exists_but_username_changes(self, test_ldap_syncer):
176+
make_simple_profile(
177+
"old_username",
178+
force_create_params={
179+
"category_id": test_ldap_syncer.category_id,
180+
"code": "same-code",
181+
},
182+
)
183+
184+
users = [
185+
{
186+
"raw_dn": b"CN=dddd aaaaa,OU=shenzhen,DC=center,DC=com",
187+
"dn": "CN=dddd aaaaa,OU=shenzhen,DC=center,DC=com",
188+
"raw_attributes": {
189+
"displayName": [b"dddd aaaaa"],
190+
"cn": [b"new_username"],
191+
"mail": [b"aaaa@asdf.com"],
192+
"telephonenumber": [b"123412341234"],
193+
"memberOf": [],
194+
},
195+
"attributes": {
196+
"displayName": "dddd aaaaa",
197+
"sAMAccountName": "new_username",
198+
"mail": "aaaa@asdf.com",
199+
"mobile": "123412341234",
200+
"memberOf": [],
201+
},
202+
"type": "searchResEntry",
203+
}
204+
]
205+
206+
with mock.patch.object(test_ldap_syncer.fetcher, "fetch") as fetch:
207+
fetch.return_value = [], [], users
208+
test_ldap_syncer._sync_profile()
209+
210+
assert not test_ldap_syncer.db_sync_manager.magic_get("old_username", ProfileMeta)
211+
new_profile = test_ldap_syncer.db_sync_manager.magic_get("new_username", ProfileMeta)
212+
assert new_profile in test_ldap_syncer.db_sync_manager._sets[ProfileMeta.target_model].adding_items
213+
175214
@pytest.mark.parametrize(
176215
"departments, expected, expected_count",
177216
[

0 commit comments

Comments
 (0)