Description
Symfony version(s) affected: 5.2.5
Description
The LDAP user provider will throw an InvalidArgumentException
when a valid user object is found, but one of the extra fields to pull in contains an empty value or multiple values.
This is a problem because many attributes are perfectly valid to have empty or multiple values. The schema in the LDAP directory backend ultimately has authority over which object keys can legally have empty/multiple values, and already performs stringent integrity constraints on its objects to ensure they're compliant with its schema.
I would therefore argue that in such cases all values should be brought in as an array, then it should be up to the application if it wants to work with all the values or just the first value. A good example would be the "mail" attribute, whereby users may happily specify multiple email addresses if they have secondary/backup mailboxes. (389-DS permits "mail" to be multi-value as standard whereas Active Directory does not. Symfony should be agnostic to this.)
This constraint is found in Symfony\Component\Ldap\Security\LdapUserProvider::getAttributeValue()
:
if (1 !== \count($values)) {
throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $attribute));
}
I would also argue that the following condition in the same function to throw exception when there isn't a value for the key isn't correct, as this would also be under schema constraint:
if (!$entry->hasAttribute($attribute)) {
throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $attribute, $entry->getDn()));
}
If it were checking for null
explicitly that may somewhat be an acceptable condition, though the key existing but without a value (empty array) should be up to the application to handle. For example a for new user that hasn't specified an email address in the "mail" attribute yet. (Or any other attribute unrelated to "mail".)
How to reproduce
This requires the LDAP user provider to be used and a directory available to work with.
Find any user object you like, choose a key that permits empty/multiple values, and give that user object empty/multiple values for the key.
Then configure extra fields to the key you've worked with.
Finally, login as that user in your Symfony app, and you'll have an InvalidArgumentException
exception.
Possible Solution
Working with Symfony\Component\Ldap\Security\LdapUserProvider::getAttributeValue()
,
Accept a defined key but no value by dropping the following condition/exception:
if (!$entry->hasAttribute($attribute)) {
throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $attribute, $entry->getDn()));
}
(Or modify it to a type strict null
check.)
Accept multiple values by dropping the following condition/exception:
if (1 !== \count($values)) {
throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $attribute));
}
The function should return either a single value or array according to what the object attribute contained:
return (null !== $values && 1 === \count($values) ? $values[0] : $values);
getAttributeValue()
doesn't have a docblock to show what its intention is, though its @return
should be "mixed|null".