@@ -84,7 +84,7 @@ public function __construct(IUserManager $userManager, IUserSession $userSession
84
84
* @return int bitwise-or'ed actions
85
85
*/
86
86
public function respondToActions () {
87
- $ setPassword = function_exists ( ' ldap_exop_passwd ' ) && !$ this ->ldapConnect ->hasPasswordPolicy ()
87
+ $ setPassword = $ this -> canSetPassword ( ) && !$ this ->ldapConnect ->hasPasswordPolicy ()
88
88
? Backend::SET_PASSWORD
89
89
: 0 ;
90
90
@@ -222,6 +222,10 @@ public function createUser($username, $password) {
222
222
$ displayNameAttribute = $ this ->ldapConnect ->getDisplayNameAttribute ();
223
223
}
224
224
225
+ if ($ connection === false ) {
226
+ throw new \Exception ('Could not bind to LDAP server ' );
227
+ }
228
+
225
229
[$ newUserDN , $ newUserEntry ] = $ this ->buildNewEntry ($ username , $ password , $ base );
226
230
$ newUserDN = $ this ->ldapProvider ->sanitizeDN ([$ newUserDN ])[0 ];
227
231
$ this ->ensureAttribute ($ newUserEntry , $ displayNameAttribute , $ username );
@@ -244,27 +248,8 @@ public function createUser($username, $password) {
244
248
throw new \Exception ('Cannot create user: ' . ldap_error ($ connection ), ldap_errno ($ connection ));
245
249
}
246
250
247
- // Set password through ldap password exop, if supported
248
251
if ($ this ->respondToActions () & Backend::SET_PASSWORD ) {
249
- try {
250
- $ ret = ldap_exop_passwd ($ connection , $ newUserDN , '' , $ password );
251
- if ($ ret === false ) {
252
- $ message = 'ldap_exop_passwd failed, falling back to ldap_mod_replace to to set password for new user ' ;
253
- $ this ->logger ->debug ($ message , ['app ' => Application::APP_ID ]);
254
-
255
- // Fallback to `userPassword` in case the server does not support exop_passwd
256
- $ ret = ldap_mod_replace ($ connection , $ newUserDN , ['userPassword ' => $ password ]);
257
- if ($ ret === false ) {
258
- $ message = 'Failed to set password for new user {dn} ' ;
259
- $ this ->logger ->error ($ message , [
260
- 'app ' => Application::APP_ID ,
261
- 'dn ' => $ newUserDN ,
262
- ]);
263
- }
264
- }
265
- } catch (\Exception $ e ) {
266
- $ this ->logger ->error ($ e ->getMessage (), ['exception ' => $ e , 'app ' => Application::APP_ID ]);
267
- }
252
+ $ this ->setPassword ($ username , $ password , $ connection );
268
253
}
269
254
return $ ret ? $ newUserDN : false ;
270
255
}
@@ -350,30 +335,75 @@ public function deleteUser($uid): bool {
350
335
return $ res ;
351
336
}
352
337
338
+ /**
339
+ * checks whether the user is allowed to change their password in Nextcloud
340
+ *
341
+ * @return boolean either the user can or cannot
342
+ */
343
+ public function canSetPassword (): bool {
344
+ return $ this ->configuration ->hasPasswordPermission ();
345
+ }
346
+
353
347
/**
354
348
* Set password
355
349
*
356
350
* @param string $uid The username
357
351
* @param string $password The new password
352
+ * @param ?\LDAP\Connection $connection LDAP connection or null to create one
358
353
* @return bool
359
354
*
360
355
* Change the password of a user
361
356
*/
362
- public function setPassword ($ uid , $ password ) {
363
- if (!function_exists ('ldap_exop_passwd ' )) {
364
- // since PHP 7.2 – respondToActions checked this already, this
365
- // method should not be called. Double check due to public scope.
366
- // This method can be removed when Nextcloud 16 compat is dropped.
367
- return false ;
357
+ public function setPassword ($ uid , $ password , $ connection = null ) {
358
+ if (is_null ($ connection )) {
359
+ $ connection = $ this ->ldapProvider ->getLDAPConnection ($ uid );
368
360
}
361
+
369
362
try {
370
- $ cr = $ this ->ldapProvider ->getLDAPConnection ($ uid );
371
363
$ userDN = $ this ->getUserDN ($ uid );
372
- return ldap_exop_passwd ($ cr , $ userDN , '' , $ password ) !== false ;
364
+ $ ret = false ;
365
+
366
+ // try ldap_exop_passwd first
367
+ if ($ this ->ldapConnect ->hasPasswdExopSupport ($ connection )) {
368
+ $ ret = ldap_exop_passwd ($ connection , $ userDN , '' , $ password );
369
+ if ($ ret === false ) {
370
+ $ message = 'Failed to set password for user {dn} using ldap_exop_passwd ' ;
371
+ $ this ->logger ->error ($ message , [
372
+ 'ldap_error ' => ldap_error ($ connection ),
373
+ 'app ' => Application::APP_ID ,
374
+ 'dn ' => $ userDN ,
375
+ ]);
376
+ } else {
377
+ // `ldap_exop_passwd` is either FALSE or the password, in the later case return TRUE
378
+ $ ret = true ;
379
+ }
380
+ } else {
381
+ // Fallback to `userPassword` in case the server does not support exop_passwd
382
+ $ entry = [];
383
+ if ($ this ->configuration ->useUnicodePassword ()) {
384
+ $ entry ['unicodePwd ' ] = iconv ('UTF-8 ' , 'UTF-16LE ' , '" ' . $ password . '" ' );
385
+ } else {
386
+ $ entry ['userPassword ' ] = $ password ;
387
+ }
388
+ $ ret = ldap_mod_replace ($ connection , $ userDN , $ entry );
389
+ if ($ ret === false ) {
390
+ $ message = 'Failed to set password for user {dn} using ldap_mod_replace ' ;
391
+ $ this ->logger ->error ($ message , [
392
+ 'ldap_error ' => ldap_error ($ connection ),
393
+ 'app ' => Application::APP_ID ,
394
+ 'dn ' => $ userDN ,
395
+ ]);
396
+ }
397
+ }
398
+ return $ ret ;
373
399
} catch (\Exception $ e ) {
374
- $ this ->logger ->error ($ e ->getMessage (), ['exception ' => $ e , 'app ' => Application::APP_ID ]);
400
+ $ this ->logger ->error ('Exception occured while setting the password of user {dn} ' , [
401
+ 'app ' => Application::APP_ID ,
402
+ 'exception ' => $ e ,
403
+ 'dn ' => $ uid
404
+ ]);
405
+ return false ;
375
406
}
376
- return false ;
377
407
}
378
408
379
409
/**
0 commit comments