Synchronize users with local user model.

def sync_ldap_users(self, ldap_users):
        """Synchronize users with local user model."""
        model = get_user_model()

        #Load total User starts
        self.stats_user_total = len(ldap_users)

        list_profiles = []
        #Load extra profiles. This way we don't even need a callback. The same code is used to populate both auth_user and user_profile
        for list_profile in self.conf_LDAP_SYNC_USER_EXTRA_PROFILES:
            list_profiles.append((list_profile, apps.get_model(list_profile)))

        if not model._meta.get_field(self.conf_LDAP_SYNC_USERNAME_FIELD).unique:
            raise ImproperlyConfigured("Field '%s' must be unique" % self.conf_LDAP_SYNC_USERNAME_FIELD)

        actualProgress = 0

        for cname, attributes in ldap_users:
            defaults = {}
            actualProgress += 1
            if (self.conf_LDAP_SYNC_USER_SHOW_PROGRESS and ((100 * actualProgress // self.stats_user_total) > (100 * (actualProgress - 1) // self.stats_user_total))):
                logger.info("AD User Sync: Processed %d/%d users (%d" % (actualProgress, self.stats_user_total, (100 * actualProgress) // self.stats_user_total) + "%)")
            try:
                for name, attribute in attributes.items():
                    try:
                        if ((name.lower() == 'thumbnailphoto') or (name.lower() == 'jpegphoto') or (name.lower() == 'thumbnaillogo')):
                            defaults[self.conf_LDAP_SYNC_USER_ATTRIBUTES[name]] = attribute[0]
                        else:
                            defaults[self.conf_LDAP_SYNC_USER_ATTRIBUTES[name]] = attribute[0].decode('utf-8')
                    except KeyError:
                        pass
                    except UnicodeDecodeError:
                        raise ImproperlyConfigured('Error in attribute ' + name + ' ' + str(attribute))
            except AttributeError:
                # In some cases attributes is a list instead of a dict; skip these invalid users
                continue

            try:
                username = defaults[self.conf_LDAP_SYNC_USERNAME_FIELD].lower()
            except KeyError:
                logger.warning("User is missing a required attribute '%s'" % self.conf_LDAP_SYNC_USERNAME_FIELD)
                continue

            #Don't import users if they are in LDAP_SYNC_USER_EXEMPT_FROM_SYNC settings
            if (username in self.conf_LDAP_SYNC_USER_EXEMPT_FROM_SYNC):
                logger.debug("Skip importing user %s, it appears in LDAP_SYNC_USER_EXEMPT_FROM_SYNC list" % username)
                continue

            kwargs = {
                self.conf_LDAP_SYNC_USERNAME_FIELD + '__iexact': username,
                'defaults': defaults,
            }

            ### Users Disable
            #Check disable bit 
            user_account_control = int(attributes[self.ATTRIBUTE_DISABLED][0].decode('utf-8'))
            user_is_disabled = (user_account_control and ((user_account_control & self.FLAG_UF_ACCOUNT_DISABLE) == self.FLAG_UF_ACCOUNT_DISABLE))
            if user_is_disabled:
                try:
                    #If disabled, we need to check if the user already exists on Django
                    user = model.objects.get(**{self.conf_LDAP_SYNC_USERNAME_FIELD + '__iexact': username,})
                    if (not user):
                        #Ignore disabled users, we won't import it, only update it
                        continue
                except Exception as e:
                    #Ignore disabled users, we won't import it, only update it
                    continue
                else:
                    #If the user already exists on Django we'll run the callbacks
                    if (self.conf_LDAP_SYNC_REMOVED_USER_CALLBACKS):
                        self.stats_user_deleted += 1
                    for path in self.conf_LDAP_SYNC_REMOVED_USER_CALLBACKS:
                        logger.debug("Calling %s for user %s" % (path, username))
                        callback = import_string(path)
                        callback(user)
            ### User creation and sinchronization
            updated = False
            try:
                if user_is_disabled:
                    created = False
                    #reload it because it may be deleted
                    user = model.objects.get(**{self.conf_LDAP_SYNC_USERNAME_FIELD + '__iexact': username,})
                else:
                    user, created = model.objects.get_or_create(**kwargs)
            except (ObjectDoesNotExist) as e:
                if (user_is_disabled):
                    continue
                else:
                    raise e
            except (IntegrityError, DataError) as e:
                logger.error("Error creating user %s: %s" % (username, e))
                self.stats_user_errors += 1
            else:
                user_updated = False
                if created:
                    logger.debug("Created user %s" % username)
                    self.stats_user_added += 1
                    user.set_unusable_password()
                else:
                    for name, attr in defaults.items():
                        current_attr = getattr(user, name, None)
                        if current_attr != attr:
                            setattr(user, name, attr)
                            if (not user_updated):
                                user_updated = True
                    if user_updated:
                        logger.debug("Updated user %s" % username)
                        updated = True

                for path in self.conf_LDAP_SYNC_USER_CALLBACKS:
                    callback = import_string(path)
                    callback(user, attributes, created, user_updated)

                try:
                    if (created or user_updated):
                        user.save()
                except Exception as e:
                    logger.error("Error saving user %s: %s" % (username, e))
                    self.stats_user_errors += 1
                ### LDAP Sync Membership
                if (self.conf_LDAP_SYNC_GROUP_MEMBERSHIP):
                    membership_uri, ldap_membership = self.get_ldap_user_membership(attributes[self.conf_LDAP_SYNC_GROUP_MEMBERSHIP_DN_FIELD][0].decode('utf-8'))
                    if ldap_membership:
                        self.sync_ldap_user_membership(user, ldap_membership)
            #Profile creation and update.
            for name_profile, profile_model in list_profiles:
                try:
                    profile, created = profile_model.objects.get_or_create(user=user)  # ,  **kwargs )
                except (IntegrityError, DataError) as e:
                    logger.error("Error creating profile %s for user %s: %s" % (name_profile, username, e))
                    self.stats_user_errors += 1
                else:
                    profile_updated = False
                    if (created):
                        logger.debug("Created profile '%s' for user '%s'" % (name_profile, username))
                        #profile.save()
                    for unchanged_name, attr in attributes.items():
                        name = unchanged_name
                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                            name = unchanged_name.lower()
                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                            name = unchanged_name.upper()

                        try:
                            if ((name.lower() != 'thumbnailphoto') and (name.lower() != 'jpegphoto') and (name.lower() != 'thumbnaillogo') ):
                                current_attr = getattr(profile, name)
                                new_value = ''
                                if (isinstance(attr, list)):
                                    for val in attr:
                                        new_value += val.decode("utf8") + self.conf_LDAP_SYNC_MULTIVALUE_SEPARATOR
                                    if (new_value != ""):
                                        new_value = new_value[:-len(self.conf_LDAP_SYNC_MULTIVALUE_SEPARATOR)]
                                else:
                                    new_value = attr
                                if current_attr != new_value:
                                    setattr(profile, name, new_value)
                                    #logger.debug("Updated profile %s: Attribute %s from '%s' to '%s' - '%s'" % (username,name, current_attr, new_value, attr))
                                    profile_updated = True
                            else:
                                if (isinstance(attr, list)):
                                    newthumbPhoto = attr[0]
                                else:
                                    newthumbPhoto = attr
                                actualPhoto = None
                                try:
                                    if (name.lower() == 'thumbnailphoto'):
                                        if (not self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE):
                                            actualPhoto = profile.thumbnailPhoto.read()
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                                            actualPhoto = profile.thumbnailphoto.read()
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                                            actualPhoto = profile.THUMBNAILPHOTO.read()
                                    elif (name.lower() == 'thumbnaillogo'):
                                        if (not self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE):
                                            actualPhoto = profile.thumbnailLogo.read()
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                                            actualPhoto = profile.thumbnaillogo.read()
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                                            actualPhoto = profile.THUMBNAILLOGO.read()                                            
                                    else:
                                        if (not self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE):
                                            actualPhoto = profile.jpegPhoto.read()
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                                            actualPhoto = profile.jpegphoto.read()
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                                            actualPhoto = profile.JPEGPHOTO.read()
                                except Exception as e:
                                    pass
                                if (actualPhoto != newthumbPhoto):
                                    #Saving thumbnailphoto
                                    #logger.debug("Photo in "+username+" are different... ")
                                    photo_name = self.conf_LDAP_SYNC_USER_THUMBNAILPHOTO_NAME
                                    #we don't format because I don't know if username it's being used at all
                                    photo_name = photo_name.replace('{username}', username)
                                    photo_name = photo_name.replace('{uuid4}', str(uuid.uuid4()))
                                    photo_name = datetime.now().strftime(photo_name)
                                    if (name.lower() == 'thumbnailphoto'):
                                        if (not self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE):
                                            if (actualPhoto):
                                                profile.thumbnailPhoto.delete()
                                            profile.thumbnailPhoto.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                                            if (actualPhoto):
                                                profile.thumbnailphoto.delete()
                                            profile.thumbnailphoto.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                                            if (actualPhoto):
                                                profile.THUMBNAILPHOTO.delete()
                                            profile.THUMBNAILPHOTO.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                    elif (name.lower() == 'thumbnaillogo'):
                                        if (not self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE):
                                            if (actualPhoto):
                                                profile.thumbnailLogo.delete()
                                            profile.thumbnailLogo.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                                            if (actualPhoto):
                                                profile.thumbnaillogo.delete()
                                            profile.thumbnaillogo.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                                            if (actualPhoto):
                                                profile.THUMBNAILLOGO.delete()
                                            profile.THUMBNAILLOGO.save(name=photo_name, content=ContentFile(newthumbPhoto))                                            
                                    else:
                                        if (not self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE):
                                            if (actualPhoto):
                                                profile.jpegPhoto.delete()
                                            profile.jpegPhoto.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "lower"):
                                            if (actualPhoto):
                                                profile.jpegphoto.delete()
                                            profile.jpegphoto.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                        if (self.conf_LDAP_SYNC_USER_CHANGE_FIELDCASE == "upper"):
                                            if (actualPhoto):
                                                profile.JPEGPHOTO.delete()
                                            profile.JPEGPHOTO.save(name=photo_name, content=ContentFile(newthumbPhoto))
                                    profile_updated = True
                                else:
                                    pass
                                    #logger.debug("Photo "+username+" are equal")
                        except AttributeError:
                            pass
                            #logger.debug("Ignore Attribute %s on profile '%s'" % (name, name_profile))
                    if profile_updated:
                        logger.debug("Updated profile %s on user %s" % (name_profile, username))
                        updated = True
                    if (created or profile_updated):
                        try:
                            profile.save()
                        except Exception as e:
                            logger.error("Error saving profile %s for user %s: %s" % (name_profile, username, e))
                            self.stats_user_errors += 1
                    #profile.save()
                    #If either user record or any profile record is changed, we'll mark it as updated.
            if (updated):
                self.stats_user_updated += 1

        logger.info("Users are synchronized")