Unexpected behavior in ldap_user_user_presave on processing multiple users per request

Created on 2 February 2021, almost 4 years ago
Updated 3 November 2023, about 1 year ago

Problem/Motivation

When programmatically changing users in my module, if I change one user, everything works, but if I change multiple users, only the first user works, the second user it tries to combine information from the first and second user. If I skip execution of ldap_user_user_presave, then it updates all users without problem.

Steps to reproduce

In custom module execute code:

    $users = User::loadMultiple(\Drupal::entityQuery('user')->execute());
    foreach($users as $user){
      $drupalUnits=__get_user_units($user->getUsername());
      //Resulting Array Example (print_r output):
      //    Array
      //    (
      //        [0] => Array
      //            (
      //                [value] => Web Services
      //            )
      //        [1] => Array
      //            (
      //                [value] => Developers
      //            )
      //    )

      $user->get('field_unit')->setValue($drupalUnits);
      $user->save();
    }

Result is integrity violation as it tries to write a record that combines user 1 and user 2

Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '<<USER 1>>-en' for key 'user__name': INSERT INTO {users_field_data} (uid, langcode, preferred_langcode, preferred_admin_langcode, name, pass, mail, timezone, status, created, changed, access, login, init, default_langcode, ldap_user_puid_sid, ldap_user_puid, ldap_user_puid_property, ldap_user_current_dn, ldap_user_last_checked, ldap_user_ldap_exclude) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17, :db_insert_placeholder_18, :db_insert_placeholder_19, :db_insert_placeholder_20); Array ( [:db_insert_placeholder_0] => 70 [:db_insert_placeholder_1] => en [:db_insert_placeholder_2] => en [:db_insert_placeholder_3] => [:db_insert_placeholder_4] => <<USER 1>> [:db_insert_placeholder_5] => <<USER 2 PASSWORD HASH>> [:db_insert_placeholder_6] => <<USER 1 EMAIL>> [:db_insert_placeholder_7] => America/Detroit [:db_insert_placeholder_8] => 1 [:db_insert_placeholder_9] => 1590514193 [:db_insert_placeholder_10] => 1612294542 [:db_insert_placeholder_11] => 1607618808 [:db_insert_placeholder_12] => 1590514193 [:db_insert_placeholder_13] => <<USER 2 EMAIL>> [:db_insert_placeholder_14] => 1 [:db_insert_placeholder_15] => [:db_insert_placeholder_16] => [:db_insert_placeholder_17] => [:db_insert_placeholder_18] => <<USER 1 DN>> [:db_insert_placeholder_19] => 1612299055 [:db_insert_placeholder_20] => ) in Drupal\Core\Database\Connection->handleQueryException() (line 698 of /var/www/intranet8/web/core/lib/Drupal/Core/Database/Connection.php).

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

πŸ’¬ Support request
Status

Closed: outdated

Version

4.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States kwfinken Lansing, MI

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡΅πŸ‡ΉPortugal jrochate

    I also had this problem when using feeds to update users that were previously created/updated using LDAP Users module.

    Since this problem is set by the ldap_user_user_presave() I have made a patch to make an exception when using feeds JSON to update users.

     function ldap_user_user_presave($account) {
    +  // Exclude this hook when using feeds to update users
    +  $current_route = \Drupal::routeMatch()->getRouteName();
    +  if (!empty($current_route) && ($current_route !== "system.batch_page.json")) {
       /** @var \Drupal\ldap_user\Processor\DrupalUserProcessor $processor */
       $processor = \Drupal::service('ldap.drupal_user_processor');

    I hope this quick fix can help some else on the same situation.

  • πŸ‡ͺπŸ‡ΈSpain unstatu

    Thanks for the issue. It helped me a lot.

    I think this is an issue that should be fixed with a more generic solution, don't you? If this problem affects to each place in the codebase where several users are updated, it could be anywhere.

    I have implemented a solution that fixes the problem via an EventSubscriber. I'm not sure if this solution has any side effect. I don't understand why the \Drupal\ldap_user\Processor\DrupalUserProcessor reset is not managed directly in the `ldap_user_user_update` hook, for example. It could be done in purpose by the module maintainers because of any reason that I am not aware of or it may be a bug. If someone could clarify, would be great.

    In any case, here you can find how I fixed the problem in a more generic way.

    
    namespace Drupal\MY_MODULE\EventSubscriber;
    
    use Drupal\ldap_user\Event\LdapUserUpdatedEvent;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    /**
     * Event subscriber for the LDAP related events.
     */
    class LDAPSubscriber implements EventSubscriberInterface {
    
      /**
       * {@inheritDoc}
       */
      public static function getSubscribedEvents() {
        return [
          LdapUserUpdatedEvent::EVENT_NAME => 'userUpdated',
        ];
      }
    
      /**
       * Takes actions after the user is saved.
       * 
       * Fixes https://www.drupal.org/project/ldap/issues/3196170.
       *
       * @param \Drupal\ldap_user\Event\LdapUserUpdatedEvent $event
       *   The user updated event.
       */
      public function userUpdated(LdapUserUpdatedEvent $event) {
        \Drupal::service('ldap.drupal_user_processor')->reset();
      }
    
    }
    
    

    Don't forget to declare the services in the my_module.services.yml file:

    services:
      Drupal\MY_MODULE\EventSubscriber\LDAPSubscriber:
        tags:
          - { name: event_subscriber }
    
  • First commit to issue fork.
  • πŸ‡³πŸ‡ΏNew Zealand heathergaye

    Fork has a fix in the ldap user processing. Issue was that an account could be passed into the processor, but the ldapEntry wasn't reset.

  • Status changed to Postponed: needs info about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States bluegeek9

    I think this might be solved by πŸ› EntityStorageException: User Fixed . Can anyone confirm the issue occurs with 8.x-4.x?

  • πŸ‡ΊπŸ‡ΈUnited States bluegeek9
  • Status changed to Closed: outdated about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States bluegeek9
Production build 0.71.5 2024