ldap: Fix the syncing of user role via AUTH_LDAP_USER_FLAGS_BY_GROUP.

This was broken, due the mechanism simply using our
is_guest/is_realm_admin/etc. role setters, but failing to adjust system
group memberships - resulting in corrupted database state.
We need to ensure that change_user_role is called for setting user role.

There are two relevant codepaths that run the sync based on
AUTH_LDAP_USER_FLAGS_BY_GROUP and thus need to get this right:
1. manage.py sync_ldap_user_data
2. Just-in-time user creation when a user without a Zulip account logs
   in for the first using their ldap credentials. After
   get_or_build_user returns, django-auth-ldap sees that the user
   account has just been created, and proceeds to run ._populate_user().

Now that both user.save() and do_change_user_realm will be getting
called together, we need to ensure this always happens atomically.

This imposes the need to override _get_or_create_user to put it in a
transaction. The troublesome consequence is that this new
`atomic(savepoint=False)` causes the usual type of issue, where tests
testing error get their transaction rolled back and cannot continue
executing.

To get around that, we add a test helper
`artificial_transaction_savepoint` which allows these tests to wrap
their problematic blocks in an artificial transaction which provides a
savepoint, thus preventing the full test transaction rollback derailing
the rest of the test.
This commit is contained in:
Mateusz Mandera
2025-04-29 00:57:31 +02:00
committed by Tim Abbott
parent 03ebeb10ab
commit 6ea67a7df2
5 changed files with 164 additions and 28 deletions

View File

@@ -1485,10 +1485,7 @@ Output:
"invite_only": orjson.dumps(invite_only).decode(),
}
post_data.update(extra_post_data)
# We wrap the API call with a 'transaction.atomic' context
# manager as it helps us with NOT rolling back the entire
# test transaction due to error responses.
with transaction.atomic(savepoint=True):
with self.artificial_transaction_savepoint():
result = self.api_post(
user,
"/api/v1/users/me/subscriptions",
@@ -1965,6 +1962,16 @@ Output:
file_path = upload_message_attachment_from_request(UploadedFile(fp), user)[0]
return file_path
@contextmanager
def artificial_transaction_savepoint(self) -> Iterator[None]:
# Sometimes we need to wrap some test code, such as an API call with a
# 'transaction.atomic' context manager as it helps us with NOT rolling
# back the entire test transaction due to errors expected by the test.
# Otherwise, those errors can prevent the test from continuing, and throw
# TransactionManagementError instead.
with transaction.atomic(savepoint=True):
yield
class ZulipTestCase(ZulipTestCaseMixin, TestCase):
@contextmanager