mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-30 19:43:47 +00:00 
			
		
		
		
	zerver: Implement ldap group synchronization.
Fixes #9957. Co-authored-by: Mateusz Mandera <mateusz.mandera@zulip.com>
This commit is contained in:
		
				
					committed by
					
						 Tim Abbott
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							2bfbbf0035
						
					
				
				
					commit
					b6a25840a1
				
			| @@ -80,6 +80,10 @@ from zxcvbn import zxcvbn | ||||
|  | ||||
| from zerver.actions.create_user import do_create_user, do_reactivate_user | ||||
| from zerver.actions.custom_profile_fields import do_update_user_custom_profile_data_if_changed | ||||
| from zerver.actions.user_groups import ( | ||||
|     bulk_add_members_to_user_groups, | ||||
|     bulk_remove_members_from_user_groups, | ||||
| ) | ||||
| from zerver.actions.user_settings import do_regenerate_api_key | ||||
| from zerver.actions.users import do_deactivate_user | ||||
| from zerver.lib.avatar import avatar_url, is_avatar_new | ||||
| @@ -105,6 +109,8 @@ from zerver.models import ( | ||||
|     PreregistrationRealm, | ||||
|     PreregistrationUser, | ||||
|     Realm, | ||||
|     UserGroup, | ||||
|     UserGroupMembership, | ||||
|     UserProfile, | ||||
|     custom_profile_fields_for_realm, | ||||
|     get_realm, | ||||
| @@ -910,6 +916,78 @@ class ZulipLDAPAuthBackendBase(ZulipAuthMixin, LDAPBackend): | ||||
|         except SyncUserError as e: | ||||
|             raise ZulipLDAPError(str(e)) from e | ||||
|  | ||||
|     def sync_groups_from_ldap(self, user_profile: UserProfile, ldap_user: _LDAPUser) -> None: | ||||
|         """ | ||||
|         For the groups set up for syncing for the realm in LDAP_SYNCHRONIZED_GROUPS_BY_REALM: | ||||
|  | ||||
|         (1) Makes sure the user has membership in the Zulip UserGroups corresponding | ||||
|             to the LDAP groups ldap_user belongs to. | ||||
|         (2) Makes sure the user doesn't have membership in the Zulip UserGroups corresponding | ||||
|             to the LDAP groups ldap_user doesn't belong to. | ||||
|         """ | ||||
|  | ||||
|         if user_profile.realm.string_id not in settings.LDAP_SYNCHRONIZED_GROUPS_BY_REALM: | ||||
|             # no groups to sync for this realm | ||||
|             return | ||||
|  | ||||
|         configured_ldap_group_names_for_sync = set( | ||||
|             settings.LDAP_SYNCHRONIZED_GROUPS_BY_REALM[user_profile.realm.string_id] | ||||
|         ) | ||||
|  | ||||
|         try: | ||||
|             ldap_logger.debug("Syncing groups for user: %s", user_profile.id) | ||||
|             intended_group_name_set_for_user = set(ldap_user.group_names).intersection( | ||||
|                 configured_ldap_group_names_for_sync | ||||
|             ) | ||||
|  | ||||
|             existing_group_name_set_for_user = set( | ||||
|                 UserGroupMembership.objects.filter( | ||||
|                     user_group__realm=user_profile.realm, | ||||
|                     user_group__name__in=set( | ||||
|                         settings.LDAP_SYNCHRONIZED_GROUPS_BY_REALM[user_profile.realm.string_id] | ||||
|                     ), | ||||
|                     user_profile=user_profile, | ||||
|                 ).values_list("user_group__name", flat=True) | ||||
|             ) | ||||
|  | ||||
|             ldap_logger.debug( | ||||
|                 "intended groups: %s; zulip groups: %s", | ||||
|                 repr(intended_group_name_set_for_user), | ||||
|                 repr(existing_group_name_set_for_user), | ||||
|             ) | ||||
|  | ||||
|             new_groups = UserGroup.objects.filter( | ||||
|                 name__in=intended_group_name_set_for_user.difference( | ||||
|                     existing_group_name_set_for_user | ||||
|                 ), | ||||
|                 realm=user_profile.realm, | ||||
|             ) | ||||
|             if new_groups: | ||||
|                 ldap_logger.debug( | ||||
|                     "add %s to %s", user_profile.id, [group.name for group in new_groups] | ||||
|                 ) | ||||
|                 bulk_add_members_to_user_groups(new_groups, [user_profile.id], acting_user=None) | ||||
|  | ||||
|             group_names_for_membership_deletion = existing_group_name_set_for_user.difference( | ||||
|                 intended_group_name_set_for_user | ||||
|             ) | ||||
|             groups_for_membership_deletion = UserGroup.objects.filter( | ||||
|                 name__in=group_names_for_membership_deletion, realm=user_profile.realm | ||||
|             ) | ||||
|  | ||||
|             if group_names_for_membership_deletion: | ||||
|                 ldap_logger.debug( | ||||
|                     "removing groups %s from %s", | ||||
|                     group_names_for_membership_deletion, | ||||
|                     user_profile.id, | ||||
|                 ) | ||||
|                 bulk_remove_members_from_user_groups( | ||||
|                     groups_for_membership_deletion, [user_profile.id], acting_user=None | ||||
|                 ) | ||||
|  | ||||
|         except Exception as e: | ||||
|             raise ZulipLDAPError(str(e)) from e | ||||
|  | ||||
|  | ||||
| class ZulipLDAPAuthBackend(ZulipLDAPAuthBackendBase): | ||||
|     REALM_IS_NONE_ERROR = 1 | ||||
| @@ -1136,6 +1214,7 @@ class ZulipLDAPUserPopulator(ZulipLDAPAuthBackendBase): | ||||
|         self.sync_avatar_from_ldap(user, ldap_user) | ||||
|         self.sync_full_name_from_ldap(user, ldap_user) | ||||
|         self.sync_custom_profile_fields_from_ldap(user, ldap_user) | ||||
|         self.sync_groups_from_ldap(user, ldap_user) | ||||
|         return (user, built) | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user