Suport mapping multi-value fields

Created on 2 May 2025, 12 days ago

Problem/Motivation

This module purports to map SAML attributes to user fields. However, there is no mention that these mappings can only be for single-value fields. Neither in the project documentation nor the UI. I was unpleasantly surprised when I uncovered this, and I desperately need this feature.

Steps to reproduce

Map a multi-value SAML field to a multi-value user field. You will see the first value populated, but no others. There's no warning in the UI when this occurs. Turn on extended logging and you'll see a brief mention of it from modules/samlauth_user_fields/src/EventSubscriber/UserFieldsEventSubscriber.php line 565:

SAML attribute %name has multiple values; we only support using the first one: @values.

Proposed resolution

Drupal Entity API supports multiple values, so there is no underlying reason why this module cannot sync multiple values. The limitation appears to be around the fact that UserFieldsEventSubscriber::getAttribute only returns one value. This fact is specifically called out in that method's comment documentation, but I'm not sure why:

* This is suitable for single-value attributes. For multi-value attributes,
* we log a debug message to make clear we're dropping data (because this
* indicates that the site owner may need to take care of getting more
* sophisticated path mapping code).

Another aspect of my confusion is why UserFieldsEventSubscriber attempts to link the user based on the mapped values. Unless I'm missing it, this is never mentioned in any documentation. Enabling samlauth_user_fields is supposed to "Maps custom SAML SSO attributes to user fields"

I see that UserFieldsEventSubscriber::getAttribute is used for both use sync and user link, and its use in user link is for building an entity query.

My proposed solution is to either build a separate getAttribute method that supports multiple values for use in user sync, or to update the existing one to support them everywhere it is used.

I would also like clarity on why samlauth_user_fields does user linking as well as syncing.

✨ Feature request
Status

Active

Version

3.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States pianomansam

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

Comments & Activities

  • Issue created by @pianomansam
  • πŸ‡³πŸ‡±Netherlands roderik Amsterdam,NL / Budapest,HU

    Multi-value fields are easily supportable in principle, as you indicate; it just needs some thought and testing. And you are the first one to bring it up in all these years, so that thought/testing hasn't been invested yet.

    The challenge for generic support is two fold:

    1. On the 'source' side (the SAML message), we get all values as arrays, so there is no way to distinguish between a single value and an array containing a single value. (Feel free to set me straight, if e.g. the attribute data in your SAML responses is somehow marked as "an actual array".)

    2. On the 'destination' side, not all fields actually support multi-value. (Case in point: user name, email, language.) And some may be configured/defined to be only single-value, even though the Drupal data model supports multiple. We don't want to just set a multi-value array into these fields, at least not without testing / logging a message.

    You're welcome to contribute or sponsor adding this functionality. Even a only-partly-generic patch or extra subscriber helps get it into the general module in time. Failing that: it should be fairly easy to roll your own event subscriber with just a few lines of actual logic. Something like

    $new = $event->getAttributes()[YOURATTRIBUTE];
    $old = $event->getAccount()->get(YOURFIELD);
    if ($new != $old) {
      $event->getAccount()->set(YOURFIELD, $new);
      $event->markAccountChanged();
    }
    
    I would also like clarity on why samlauth_user_fields does user linking as well as syncing.

    I'm not sure whether you're talking about the boolean map_users config value (which didn't initially exist at all / had its reasons for being introduced, but I'm not sure that's relevant here). Bottom line is: linking is optional.samlauth_user_fields.field_mapping is a per-field mapping, and no field is linked unless you explicitly set the optional 'link_user_order' sub-value for the specific field.

    (From just reviewing the config schema, I'm not sure from the top of my head if saving the samlauth_user_fields.field_mapping value always saves a 'link_user_order' with a default value of 0. It wasn't supposed to. If it does: that's a bug. If it doesn't: I don't see any issue with this.)

  • πŸ‡ΊπŸ‡ΈUnited States pianomansam

    @roderik thanks for the extensive reply. Unfortunately, `UserFieldsEventSubscriber` has proven to be obtuse and IMHO perhaps overengineered. So I'll most likely be writing a one-off data sync using the SamlauthEvents::USER_SYNC event for my purposes.

    I would also like clarity on why samlauth_user_fields does user linking as well as syncing.
    I'm not sure whether you're talking about the boolean map_users config value (which didn't initially exist at all / had its reasons for being introduced, but I'm not sure that's relevant here). Bottom line is: linking is optional.

    I'm referencing the SamlauthEvents::USER_LINK event within UserFieldsEventSubscriber:

      public static function getSubscribedEvents(): array {
        $events[SamlauthEvents::USER_LINK][] = ['onUserLink'];
        $events[SamlauthEvents::USER_SYNC][] = ['onUserSync'];
        return $events;
      }
    
      /**
       * Tries to link an existing user based on SAML attribute values.
       *
       * @param \Drupal\samlauth\Event\SamlauthUserLinkEvent $event
       *   The event being dispatched.
       */
      public function onUserLink(SamlauthUserLinkEvent $event) {
    
  • πŸ‡ΊπŸ‡ΈUnited States pianomansam

    A few more responses...

    1. On the 'source' side (the SAML message), we get all values as arrays, so there is no way to distinguish between a single value and an array containing a single value. (Feel free to set me straight, if e.g. the attribute data in your SAML responses is somehow marked as "an actual array".)

    I'm not sure why that matters, if we can solve for knowing whether or not the destination of the mapping handles arrays.

    2. On the 'destination' side, not all fields actually support multi-value. (Case in point: user name, email, language.) And some may be configured/defined to be only single-value, even though the Drupal data model supports multiple.

    This is true, but isn't it possible to know if the destination supports multiple values? If it's an entity property (like username, email, language, etc), shouldn't that be accessible from the entity definition? If it's a field on the user, shouldn't that be accessible from the field's configuration?

Production build 0.71.5 2024