@@ -116,6 +116,56 @@ public function getConnection() {
116116 return $ this ->connection ;
117117 }
118118
119+ /**
120+ * Reads several attributes for an LDAP record identified by a DN and a filter
121+ * No support for ranged attributes.
122+ *
123+ * @param string $dn the record in question
124+ * @param array $attrs the attributes that shall be retrieved
125+ * if empty, just check the record's existence
126+ * @param string $filter
127+ * @return array|false an array of values on success or an empty
128+ * array if $attr is empty, false otherwise
129+ * @throws ServerNotAvailableException
130+ */
131+ public function readAttributes (string $ dn , array $ attrs , string $ filter = 'objectClass=* ' ): array |false {
132+ if (!$ this ->checkConnection ()) {
133+ $ this ->logger ->warning (
134+ 'No LDAP Connector assigned, access impossible for readAttribute. ' ,
135+ ['app ' => 'user_ldap ' ]
136+ );
137+ return false ;
138+ }
139+ $ cr = $ this ->connection ->getConnectionResource ();
140+ $ attrs = array_map (
141+ fn (string $ attr ): string => mb_strtolower ($ attr , 'UTF-8 ' ),
142+ $ attrs ,
143+ );
144+
145+ $ values = [];
146+ $ record = $ this ->executeRead ($ dn , $ attrs , $ filter );
147+ if (is_bool ($ record )) {
148+ // when an exists request was run and it was successful, an empty
149+ // array must be returned
150+ return $ record ? [] : false ;
151+ }
152+
153+ $ result = [];
154+ foreach ($ attrs as $ attr ) {
155+ $ values = $ this ->extractAttributeValuesFromResult ($ record , $ attr );
156+ if (!empty ($ values )) {
157+ $ result [$ attr ] = $ values ;
158+ }
159+ }
160+
161+ if (!empty ($ result )) {
162+ return $ result ;
163+ }
164+
165+ $ this ->logger ->debug ('Requested attributes {attrs} not found for ' . $ dn , ['app ' => 'user_ldap ' , 'attrs ' => $ attrs ]);
166+ return false ;
167+ }
168+
119169 /**
120170 * reads a given attribute for an LDAP record identified by a DN
121171 *
@@ -191,9 +241,9 @@ public function readAttribute(string $dn, string $attr, string $filter = 'object
191241 * returned data on a successful usual operation
192242 * @throws ServerNotAvailableException
193243 */
194- public function executeRead (string $ dn , string $ attribute , string $ filter ) {
244+ public function executeRead (string $ dn , string | array $ attribute , string $ filter ) {
195245 $ dn = $ this ->helper ->DNasBaseParameter ($ dn );
196- $ rr = @$ this ->invokeLDAPMethod ('read ' , $ dn , $ filter , [$ attribute ]);
246+ $ rr = @$ this ->invokeLDAPMethod ('read ' , $ dn , $ filter , ( is_string ( $ attribute ) ? [$ attribute ] : $ attribute ) );
197247 if (!$ this ->ldap ->isResource ($ rr )) {
198248 if ($ attribute !== '' ) {
199249 //do not throw this message on userExists check, irritates
@@ -410,15 +460,15 @@ public function dn2groupname($fdn, $ldapName = null) {
410460 * @return string|false with with the name to use in Nextcloud
411461 * @throws \Exception
412462 */
413- public function dn2username ($ fdn, $ ldapName = null ) {
463+ public function dn2username ($ fdn ) {
414464 //To avoid bypassing the base DN settings under certain circumstances
415465 //with the group support, check whether the provided DN matches one of
416466 //the given Bases
417467 if (!$ this ->isDNPartOfBase ($ fdn , $ this ->connection ->ldapBaseUsers )) {
418468 return false ;
419469 }
420470
421- return $ this ->dn2ocname ($ fdn , $ ldapName , true );
471+ return $ this ->dn2ocname ($ fdn , null , true );
422472 }
423473
424474 /**
@@ -441,12 +491,8 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
441491 $ newlyMapped = false ;
442492 if ($ isUser ) {
443493 $ mapper = $ this ->getUserMapper ();
444- $ nameAttribute = $ this ->connection ->ldapUserDisplayName ;
445- $ filter = $ this ->connection ->ldapUserFilter ;
446494 } else {
447495 $ mapper = $ this ->getGroupMapper ();
448- $ nameAttribute = $ this ->connection ->ldapGroupDisplayName ;
449- $ filter = $ this ->connection ->ldapGroupFilter ;
450496 }
451497
452498 //let's try to retrieve the Nextcloud name from the mappings table
@@ -455,6 +501,36 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
455501 return $ ncName ;
456502 }
457503
504+ if ($ isUser ) {
505+ $ nameAttribute = strtolower ($ this ->connection ->ldapUserDisplayName );
506+ $ filter = $ this ->connection ->ldapUserFilter ;
507+ $ uuidAttr = 'ldapUuidUserAttribute ' ;
508+ $ uuidOverride = $ this ->connection ->ldapExpertUUIDUserAttr ;
509+ $ usernameAttribute = strtolower ($ this ->connection ->ldapExpertUsernameAttr );
510+ $ attributesToRead = [$ nameAttribute ,$ usernameAttribute ];
511+ // TODO fetch also display name attributes and cache them if the user is mapped
512+ } else {
513+ $ nameAttribute = strtolower ($ this ->connection ->ldapGroupDisplayName );
514+ $ filter = $ this ->connection ->ldapGroupFilter ;
515+ $ uuidAttr = 'ldapUuidGroupAttribute ' ;
516+ $ uuidOverride = $ this ->connection ->ldapExpertUUIDGroupAttr ;
517+ $ attributesToRead = [$ nameAttribute ];
518+ }
519+
520+ if ($ this ->detectUuidAttribute ($ fdn , $ isUser , false , $ record )) {
521+ $ attributesToRead [] = $ this ->connection ->$ uuidAttr ;
522+ }
523+
524+ if ($ record === null ) {
525+ /* No record was passed, fetch it */
526+ $ record = $ this ->readAttributes ($ fdn , $ attributesToRead , $ filter );
527+ if ($ record === false ) {
528+ $ this ->logger ->debug ('Cannot read attributes for ' . $ fdn . '. Skipping. ' , ['filter ' => $ filter ]);
529+ $ intermediates [($ isUser ? 'user- ' : 'group- ' ) . $ fdn ] = true ;
530+ return false ;
531+ }
532+ }
533+
458534 //second try: get the UUID and check if it is known. Then, update the DN and return the name.
459535 $ uuid = $ this ->getUUID ($ fdn , $ isUser , $ record );
460536 if (is_string ($ uuid )) {
@@ -469,20 +545,9 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
469545 return false ;
470546 }
471547
472- if (is_null ($ ldapName )) {
473- $ ldapName = $ this ->readAttribute ($ fdn , $ nameAttribute , $ filter );
474- if (!isset ($ ldapName [0 ]) || empty ($ ldapName [0 ])) {
475- $ this ->logger ->debug ('No or empty name for ' . $ fdn . ' with filter ' . $ filter . '. ' , ['app ' => 'user_ldap ' ]);
476- $ intermediates [($ isUser ? 'user- ' : 'group- ' ) . $ fdn ] = true ;
477- return false ;
478- }
479- $ ldapName = $ ldapName [0 ];
480- }
481-
482548 if ($ isUser ) {
483- $ usernameAttribute = (string )$ this ->connection ->ldapExpertUsernameAttr ;
484549 if ($ usernameAttribute !== '' ) {
485- $ username = $ this -> readAttribute ( $ fdn , $ usernameAttribute) ;
550+ $ username = $ record [ $ usernameAttribute] ;
486551 if (!isset ($ username [0 ]) || empty ($ username [0 ])) {
487552 $ this ->logger ->debug ('No or empty username ( ' . $ usernameAttribute . ') for ' . $ fdn . '. ' , ['app ' => 'user_ldap ' ]);
488553 return false ;
@@ -504,6 +569,15 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
504569 return false ;
505570 }
506571 } else {
572+ if (is_null ($ ldapName )) {
573+ $ ldapName = $ record [$ nameAttribute ];
574+ if (!isset ($ ldapName [0 ]) || empty ($ ldapName [0 ])) {
575+ $ this ->logger ->debug ('No or empty name for ' . $ fdn . ' with filter ' . $ filter . '. ' , ['app ' => 'user_ldap ' ]);
576+ $ intermediates ['group- ' . $ fdn ] = true ;
577+ return false ;
578+ }
579+ $ ldapName = $ ldapName [0 ];
580+ }
507581 $ intName = $ this ->sanitizeGroupIDCandidate ($ ldapName );
508582 }
509583
@@ -521,6 +595,7 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
521595 $ this ->connection ->setConfiguration (['ldapCacheTTL ' => $ originalTTL ]);
522596 $ newlyMapped = $ this ->mapAndAnnounceIfApplicable ($ mapper , $ fdn , $ intName , $ uuid , $ isUser );
523597 if ($ newlyMapped ) {
598+ $ this ->logger ->debug ('Mapped {fdn} as {name} ' , ['fdn ' => $ fdn ,'name ' => $ intName ]);
524599 return $ intName ;
525600 }
526601 }
@@ -535,7 +610,6 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
535610 'fdn ' => $ fdn ,
536611 'altName ' => $ altName ,
537612 'intName ' => $ intName ,
538- 'app ' => 'user_ldap ' ,
539613 ]
540614 );
541615 $ newlyMapped = true ;
@@ -660,6 +734,7 @@ public function cacheUserHome(string $ocName, $home): void {
660734 */
661735 public function cacheUserExists (string $ ocName ): void {
662736 $ this ->connection ->writeToCache ('userExists ' . $ ocName , true );
737+ $ this ->connection ->writeToCache ('userExistsOnLDAP ' . $ ocName , true );
663738 }
664739
665740 /**
0 commit comments