Merge pull request #2286 from nextcloud/ldap_password_pr-1

LDAP PR with tests
This commit is contained in:
Lukas Reschke 2016-11-23 20:54:31 +01:00 committed by GitHub
commit 38658da12b
15 changed files with 507 additions and 6 deletions

View File

@ -3,6 +3,11 @@
width: 85%; width: 85%;
} }
.inlinetable {
display: inline-table;
vertical-align: bottom;
}
.tablerow { .tablerow {
display: table-row; display: table-row;
white-space: nowrap; white-space: nowrap;

View File

@ -95,6 +95,10 @@ OCA = OCA || {};
$element: $('#ldap_paging_size'), $element: $('#ldap_paging_size'),
setMethod: 'setPagingSize' setMethod: 'setPagingSize'
}, },
ldap_turn_on_pwd_change: {
$element: $('#ldap_turn_on_pwd_change'),
setMethod: 'setPasswordChangeEnabled'
},
//Special Attributes //Special Attributes
ldap_quota_attr: { ldap_quota_attr: {
@ -289,6 +293,17 @@ OCA = OCA || {};
this.setElementValue(this.managedItems.ldap_paging_size.$element, size); this.setElementValue(this.managedItems.ldap_paging_size.$element, size);
}, },
/**
* sets whether the password changes per user should be enabled
*
* @param {string} doPasswordChange contains an int
*/
setPasswordChangeEnabled: function(doPasswordChange) {
this.setElementValue(
this.managedItems.ldap_turn_on_pwd_change.$element, doPasswordChange
);
},
/** /**
* sets the email attribute * sets the email attribute
* *

View File

@ -40,6 +40,8 @@
namespace OCA\User_LDAP; namespace OCA\User_LDAP;
use OC\HintException;
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
use OCA\User_LDAP\User\IUserTools; use OCA\User_LDAP\User\IUserTools;
use OCA\User_LDAP\User\Manager; use OCA\User_LDAP\User\Manager;
use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\OfflineUser;
@ -222,6 +224,33 @@ class Access extends LDAPUtility implements IUserTools {
return false; return false;
} }
/**
* Set password for an LDAP user identified by a DN
*
* @param string $userDN the user in question
* @param string $password the new password
* @return bool
* @throws HintException
* @throws \Exception
*/
public function setPassword($userDN, $password) {
if(intval($this->connection->turnOnPasswordChange) !== 1) {
throw new \Exception('LDAP password changes are disabled.');
}
$cr = $this->connection->getConnectionResource();
if(!$this->ldap->isResource($cr)) {
//LDAP not available
\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
return false;
}
try {
return $this->ldap->modReplace($cr, $userDN, $password);
} catch(ConstraintViolationException $e) {
throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
}
}
/** /**
* checks whether the given attributes value is probably a DN * checks whether the given attributes value is probably a DN
* @param string $attr the attribute in question * @param string $attr the attribute in question

View File

@ -11,6 +11,7 @@
* @author Lukas Reschke <lukas@statuscode.ch> * @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de> * @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <robin@mccorkell.me.uk> * @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roger Szabo <roger.szabo@web.de>
* *
* @license AGPL-3.0 * @license AGPL-3.0
* *
@ -90,6 +91,7 @@ class Configuration {
'lastJpegPhotoLookup' => null, 'lastJpegPhotoLookup' => null,
'ldapNestedGroups' => false, 'ldapNestedGroups' => false,
'ldapPagingSize' => null, 'ldapPagingSize' => null,
'turnOnPasswordChange' => false,
'ldapDynamicGroupMemberURL' => null, 'ldapDynamicGroupMemberURL' => null,
); );
@ -449,6 +451,7 @@ class Configuration {
'last_jpegPhoto_lookup' => 0, 'last_jpegPhoto_lookup' => 0,
'ldap_nested_groups' => 0, 'ldap_nested_groups' => 0,
'ldap_paging_size' => 500, 'ldap_paging_size' => 500,
'ldap_turn_on_pwd_change' => 0,
'ldap_experienced_admin' => 0, 'ldap_experienced_admin' => 0,
'ldap_dynamic_group_member_url' => '', 'ldap_dynamic_group_member_url' => '',
); );
@ -505,6 +508,7 @@ class Configuration {
'last_jpegPhoto_lookup' => 'lastJpegPhotoLookup', 'last_jpegPhoto_lookup' => 'lastJpegPhotoLookup',
'ldap_nested_groups' => 'ldapNestedGroups', 'ldap_nested_groups' => 'ldapNestedGroups',
'ldap_paging_size' => 'ldapPagingSize', 'ldap_paging_size' => 'ldapPagingSize',
'ldap_turn_on_pwd_change' => 'turnOnPasswordChange',
'ldap_experienced_admin' => 'ldapExperiencedAdmin', 'ldap_experienced_admin' => 'ldapExperiencedAdmin',
'ldap_dynamic_group_member_url' => 'ldapDynamicGroupMemberURL', 'ldap_dynamic_group_member_url' => 'ldapDynamicGroupMemberURL',
); );

View File

@ -42,6 +42,7 @@ use OC\ServerNotAvailableException;
* @property string ldapUserFilter * @property string ldapUserFilter
* @property string ldapUserDisplayName * @property string ldapUserDisplayName
* @property string ldapUserDisplayName2 * @property string ldapUserDisplayName2
* @property boolean turnOnPasswordChange
* @property boolean hasPagedResultSupport * @property boolean hasPagedResultSupport
* @property string[] ldapBaseUsers * @property string[] ldapBaseUsers
* @property int|string ldapPagingSize holds an integer * @property int|string ldapPagingSize holds an integer

View File

@ -0,0 +1,26 @@
<?php
/**
* @copyright Copyright (c) 2016 Roger Szabo <roger.szabo@web.de>
*
* @author Roger Szabo <roger.szabo@web.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_LDAP\Exceptions;
class ConstraintViolationException extends \Exception {}

View File

@ -164,6 +164,15 @@ interface ILDAPWrapper {
*/ */
public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0); public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0);
/**
* Replace the value of a userPassword by $password
* @param resource $link LDAP link resource
* @param string $userDN the DN of the user whose password is to be replaced
* @param string $password the new value for the userPassword
* @return bool true on success, false otherwise
*/
public function modReplace($link, $userDN, $password);
/** /**
* Sets the value of the specified option to be $value * Sets the value of the specified option to be $value
* @param resource $link LDAP link resource * @param resource $link LDAP link resource

View File

@ -9,6 +9,7 @@
* @author Lukas Reschke <lukas@statuscode.ch> * @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de> * @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <robin@mccorkell.me.uk> * @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roger Szabo <roger.szabo@web.de>
* *
* @license AGPL-3.0 * @license AGPL-3.0
* *
@ -29,6 +30,7 @@
namespace OCA\User_LDAP; namespace OCA\User_LDAP;
use OC\ServerNotAvailableException; use OC\ServerNotAvailableException;
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
class LDAP implements ILDAPWrapper { class LDAP implements ILDAPWrapper {
protected $curFunc = ''; protected $curFunc = '';
@ -192,6 +194,16 @@ class LDAP implements ILDAPWrapper {
return $this->invokeLDAPMethod('search', $link, $baseDN, $filter, $attr, $attrsOnly, $limit); return $this->invokeLDAPMethod('search', $link, $baseDN, $filter, $attr, $attrsOnly, $limit);
} }
/**
* @param LDAP $link
* @param string $userDN
* @param string $password
* @return bool
*/
public function modReplace($link, $userDN, $password) {
return $this->invokeLDAPMethod('mod_replace', $link, $userDN, array('userPassword' => $password));
}
/** /**
* @param LDAP $link * @param LDAP $link
* @param string $option * @param string $option
@ -248,7 +260,7 @@ class LDAP implements ILDAPWrapper {
/** /**
* @return mixed * @return mixed
*/ */
private function invokeLDAPMethod() { protected function invokeLDAPMethod() {
$arguments = func_get_args(); $arguments = func_get_args();
$func = 'ldap_' . array_shift($arguments); $func = 'ldap_' . array_shift($arguments);
if(function_exists($func)) { if(function_exists($func)) {
@ -288,6 +300,9 @@ class LDAP implements ILDAPWrapper {
throw new \Exception('LDAP authentication method rejected', $errorCode); throw new \Exception('LDAP authentication method rejected', $errorCode);
} else if ($errorCode === 1) { } else if ($errorCode === 1) {
throw new \Exception('LDAP Operations error', $errorCode); throw new \Exception('LDAP Operations error', $errorCode);
} else if ($errorCode === 19) {
ldap_get_option($this->curArgs[0], LDAP_OPT_ERROR_STRING, $extended_error);
throw new ConstraintViolationException(!empty($extended_error)?$extended_error:$errorMsg, $errorCode);
} else { } else {
\OCP\Util::writeLog('user_ldap', \OCP\Util::writeLog('user_ldap',
'LDAP error '.$errorMsg.' (' . 'LDAP error '.$errorMsg.' (' .

View File

@ -35,6 +35,7 @@
namespace OCA\User_LDAP; namespace OCA\User_LDAP;
use OC\User\Backend;
use OC\User\NoUserException; use OC\User\NoUserException;
use OCA\User_LDAP\Exceptions\NotOnLDAP; use OCA\User_LDAP\Exceptions\NotOnLDAP;
use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\OfflineUser;
@ -174,6 +175,26 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
return false; return false;
} }
/**
* Set password
* @param string $uid The username
* @param string $password The new password
* @return bool
*/
public function setPassword($uid, $password) {
$user = $this->access->userManager->get($uid);
if(!$user instanceof User) {
throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
'. Maybe the LDAP entry has no set display name attribute?');
}
if($user->getUsername() !== false) {
return $this->access->setPassword($user->getDN(), $password);
}
return false;
}
/** /**
* Get a list of all users * Get a list of all users
* *
@ -449,11 +470,12 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
* compared with OC_USER_BACKEND_CREATE_USER etc. * compared with OC_USER_BACKEND_CREATE_USER etc.
*/ */
public function implementsActions($actions) { public function implementsActions($actions) {
return (bool)((\OC\User\Backend::CHECK_PASSWORD return (bool)((Backend::CHECK_PASSWORD
| \OC\User\Backend::GET_HOME | Backend::GET_HOME
| \OC\User\Backend::GET_DISPLAYNAME | Backend::GET_DISPLAYNAME
| \OC\User\Backend::PROVIDE_AVATAR | Backend::PROVIDE_AVATAR
| \OC\User\Backend::COUNT_USERS) | Backend::COUNT_USERS
| ((intval($this->access->connection->turnOnPasswordChange) === 1)?(Backend::SET_PASSWORD):0))
& $actions); & $actions);
} }

View File

@ -263,6 +263,17 @@ class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface,
return $this->handleRequest($uid, 'deleteUser', array($uid)); return $this->handleRequest($uid, 'deleteUser', array($uid));
} }
/**
* Set password
* @param string $uid The username
* @param string $password The new password
* @return bool
*
*/
public function setPassword($uid, $password) {
return $this->handleRequest($uid, 'setPassword', array($uid, $password));
}
/** /**
* @return bool * @return bool
*/ */

View File

@ -101,6 +101,8 @@ style('user_ldap', 'settings');
<p><label for="ldap_dynamic_group_member_url"><?php p($l->t('Dynamic Group Member URL'));?></label><input type="text" id="ldap_dynamic_group_member_url" name="ldap_dynamic_group_member_url" title="<?php p($l->t('The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)'));?>" data-default="<?php p($_['ldap_dynamic_group_member_url_default']); ?>" /></p> <p><label for="ldap_dynamic_group_member_url"><?php p($l->t('Dynamic Group Member URL'));?></label><input type="text" id="ldap_dynamic_group_member_url" name="ldap_dynamic_group_member_url" title="<?php p($l->t('The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)'));?>" data-default="<?php p($_['ldap_dynamic_group_member_url_default']); ?>" /></p>
<p><label for="ldap_nested_groups"><?php p($l->t('Nested Groups'));?></label><input type="checkbox" id="ldap_nested_groups" name="ldap_nested_groups" value="1" data-default="<?php p($_['ldap_nested_groups_default']); ?>" title="<?php p($l->t('When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)'));?>" /></p> <p><label for="ldap_nested_groups"><?php p($l->t('Nested Groups'));?></label><input type="checkbox" id="ldap_nested_groups" name="ldap_nested_groups" value="1" data-default="<?php p($_['ldap_nested_groups_default']); ?>" title="<?php p($l->t('When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)'));?>" /></p>
<p><label for="ldap_paging_size"><?php p($l->t('Paging chunksize'));?></label><input type="number" id="ldap_paging_size" name="ldap_paging_size" title="<?php p($l->t('Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)'));?>" data-default="<?php p($_['ldap_paging_size_default']); ?>" /></p> <p><label for="ldap_paging_size"><?php p($l->t('Paging chunksize'));?></label><input type="number" id="ldap_paging_size" name="ldap_paging_size" title="<?php p($l->t('Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)'));?>" data-default="<?php p($_['ldap_paging_size_default']); ?>" /></p>
<p><label for="ldap_turn_on_pwd_change"><?php p($l->t('Enable LDAP password changes per user'));?></label><span class="inlinetable"><span class="tablerow left"><input type="checkbox" id="ldap_turn_on_pwd_change" name="ldap_turn_on_pwd_change" value="1" data-default="<?php p($_['ldap_turn_on_pwd_change_default']); ?>" title="<?php p($l->t('Allow LDAP users to change their password and allow Super Administrators and Group Administrators to change the password of their LDAP users. Only works when access control policies are configured accordingly on the LDAP server. As passwords are sent in plaintext to the LDAP server, transport encryption must be used and password hashing should be configured on the LDAP server.'));?>" /><span class="tablecell"><?php p($l->t('(New password is sent as plain text to LDAP)'));?></span></span>
</span><br/></p>
</div> </div>
<h3><?php p($l->t('Special Attributes'));?></h3> <h3><?php p($l->t('Special Attributes'));?></h3>
<div> <div>

View File

@ -1,12 +1,14 @@
<?php <?php
/** /**
* @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, ownCloud, Inc.
* @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
* *
* @author Andreas Fischer <bantu@owncloud.com> * @author Andreas Fischer <bantu@owncloud.com>
* @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com> * @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de> * @author Morris Jobke <hey@morrisjobke.de>
* @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Lukas Reschke <lukas@statuscode.ch>
* *
* @license AGPL-3.0 * @license AGPL-3.0
* *
@ -28,9 +30,11 @@ namespace OCA\User_LDAP\Tests;
use OCA\User_LDAP\Access; use OCA\User_LDAP\Access;
use OCA\User_LDAP\Connection; use OCA\User_LDAP\Connection;
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
use OCA\User_LDAP\FilesystemHelper; use OCA\User_LDAP\FilesystemHelper;
use OCA\User_LDAP\Helper; use OCA\User_LDAP\Helper;
use OCA\User_LDAP\ILDAPWrapper; use OCA\User_LDAP\ILDAPWrapper;
use OCA\User_LDAP\LDAP;
use OCA\User_LDAP\LogWrapper; use OCA\User_LDAP\LogWrapper;
use OCA\User_LDAP\User\Manager; use OCA\User_LDAP\User\Manager;
use OCP\IAvatarManager; use OCP\IAvatarManager;
@ -47,6 +51,31 @@ use OCP\IUserManager;
* @package OCA\User_LDAP\Tests * @package OCA\User_LDAP\Tests
*/ */
class AccessTest extends \Test\TestCase { class AccessTest extends \Test\TestCase {
/** @var Connection|\PHPUnit_Framework_MockObject_MockObject */
private $connection;
/** @var LDAP|\PHPUnit_Framework_MockObject_MockObject */
private $ldap;
/** @var Manager|\PHPUnit_Framework_MockObject_MockObject */
private $userManager;
/** @var Helper|\PHPUnit_Framework_MockObject_MockObject */
private $helper;
/** @var Access */
private $access;
public function setUp() {
$this->connection = $this->createMock(Connection::class);
$this->ldap = $this->createMock(LDAP::class);
$this->userManager = $this->createMock(Manager::class);
$this->helper = $this->createMock(Helper::class);
$this->access = new Access(
$this->connection,
$this->ldap,
$this->userManager,
$this->helper
);
}
private function getConnectorAndLdapMock() { private function getConnectorAndLdapMock() {
$lw = $this->createMock(ILDAPWrapper::class); $lw = $this->createMock(ILDAPWrapper::class);
$connector = $this->getMockBuilder(Connection::class) $connector = $this->getMockBuilder(Connection::class)
@ -317,4 +346,84 @@ class AccessTest extends \Test\TestCase {
$values = $access->readAttribute('uid=whoever,dc=example,dc=org', $attribute); $values = $access->readAttribute('uid=whoever,dc=example,dc=org', $attribute);
$this->assertSame($values[0], strtolower($dnFromServer)); $this->assertSame($values[0], strtolower($dnFromServer));
} }
/**
* @expectedException \Exception
* @expectedExceptionMessage LDAP password changes are disabled
*/
public function testSetPasswordWithDisabledChanges() {
$this->connection
->method('__get')
->willReturn(false);
$this->access->setPassword('CN=foo', 'MyPassword');
}
public function testSetPasswordWithLdapNotAvailable() {
$this->connection
->method('__get')
->willReturn(true);
$connection = $this->createMock(LDAP::class);
$this->connection
->expects($this->once())
->method('getConnectionResource')
->willReturn($connection);
$this->ldap
->expects($this->once())
->method('isResource')
->with($connection)
->willReturn(false);
$this->assertFalse($this->access->setPassword('CN=foo', 'MyPassword'));
}
/**
* @expectedException \OC\HintException
* @expectedExceptionMessage Password change rejected.
*/
public function testSetPasswordWithRejectedChange() {
$this->connection
->method('__get')
->willReturn(true);
$connection = $this->createMock(LDAP::class);
$this->connection
->expects($this->once())
->method('getConnectionResource')
->willReturn($connection);
$this->ldap
->expects($this->once())
->method('isResource')
->with($connection)
->willReturn(true);
$this->ldap
->expects($this->once())
->method('modReplace')
->with($connection, 'CN=foo', 'MyPassword')
->willThrowException(new ConstraintViolationException());
$this->access->setPassword('CN=foo', 'MyPassword');
}
public function testSetPassword() {
$this->connection
->method('__get')
->willReturn(true);
$connection = $this->createMock(LDAP::class);
$this->connection
->expects($this->once())
->method('getConnectionResource')
->willReturn($connection);
$this->ldap
->expects($this->once())
->method('isResource')
->with($connection)
->willReturn(true);
$this->ldap
->expects($this->once())
->method('modReplace')
->with($connection, 'CN=foo', 'MyPassword')
->willReturn(true);
$this->assertTrue($this->access->setPassword('CN=foo', 'MyPassword'));
}
} }

View File

@ -0,0 +1,50 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_LDAP\Tests;
use OCA\User_LDAP\LDAP;
use Test\TestCase;
class LDAPTest extends TestCase {
/** @var LDAP|\PHPUnit_Framework_MockObject_MockObject */
private $ldap;
public function setUp() {
parent::setUp();
$this->ldap = $this->getMockBuilder(LDAP::class)
->setMethods(['invokeLDAPMethod'])
->getMock();
}
public function testModReplace() {
$link = $this->createMock(LDAP::class);
$userDN = 'CN=user';
$password = 'MyPassword';
$this->ldap
->expects($this->once())
->method('invokeLDAPMethod')
->with('mod_replace', $link, $userDN, array('userPassword' => $password))
->willReturn(true);
$this->assertTrue($this->ldap->modReplace($link, $userDN, $password));
}
}

View File

@ -9,6 +9,7 @@
* @author Morris Jobke <hey@morrisjobke.de> * @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <robin@mccorkell.me.uk> * @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Roger Szabo <roger.szabo@web.de>
* *
* @license AGPL-3.0 * @license AGPL-3.0
* *
@ -36,8 +37,10 @@ use OCA\User_LDAP\ILDAPWrapper;
use OCA\User_LDAP\LogWrapper; use OCA\User_LDAP\LogWrapper;
use OCA\User_LDAP\User\Manager; use OCA\User_LDAP\User\Manager;
use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\OfflineUser;
use OC\HintException;
use OCA\User_LDAP\User\User; use OCA\User_LDAP\User\User;
use OCA\User_LDAP\User_LDAP as UserLDAP; use OCA\User_LDAP\User_LDAP as UserLDAP;
use OCA\User_LDAP\User_LDAP;
use OCP\IAvatarManager; use OCP\IAvatarManager;
use OCP\IConfig; use OCP\IConfig;
use OCP\IDBConnection; use OCP\IDBConnection;
@ -959,4 +962,143 @@ class User_LDAPTest extends TestCase {
$backend->loginName2UserName($loginName); $backend->loginName2UserName($loginName);
} }
/**
* Prepares the Access mock for setPassword tests
* @param \OCA\User_LDAP\Access|\PHPUnit_Framework_MockObject_MockObject $access mock
* @return void
*/
private function prepareAccessForSetPassword(&$access, $enablePasswordChange = true) {
$access->connection->expects($this->any())
->method('__get')
->will($this->returnCallback(function($name) use (&$enablePasswordChange) {
if($name === 'ldapLoginFilter') {
return '%uid';
}
if($name === 'turnOnPasswordChange') {
return $enablePasswordChange?1:0;
}
return null;
}));
$access->connection->expects($this->any())
->method('getFromCache')
->will($this->returnCallback(function($uid) {
if($uid === 'userExists'.'roland') {
return true;
}
return null;
}));
$access->expects($this->any())
->method('fetchListOfUsers')
->will($this->returnCallback(function($filter) {
if($filter === 'roland') {
return array(array('dn' => ['dnOfRoland,dc=test']));
}
return array();
}));
$access->expects($this->any())
->method('fetchUsersByLoginName')
->will($this->returnCallback(function($uid) {
if($uid === 'roland') {
return array(array('dn' => ['dnOfRoland,dc=test']));
}
return array();
}));
$access->expects($this->any())
->method('dn2username')
->with($this->equalTo('dnOfRoland,dc=test'))
->will($this->returnValue('roland'));
$access->expects($this->any())
->method('stringResemblesDN')
->with($this->equalTo('dnOfRoland,dc=test'))
->will($this->returnValue(true));
$access->expects($this->any())
->method('setPassword')
->will($this->returnCallback(function($uid, $password) {
if(strlen($password) <= 5) {
throw new HintException('Password fails quality checking policy', '', 19);
}
return true;
}));
}
/**
* @expectedException \OC\HintException
* @expectedExceptionMessage Password fails quality checking policy
*/
public function testSetPasswordInvalid() {
$access = $this->getAccessMock();
$this->prepareAccessForSetPassword($access);
$backend = new UserLDAP($access, $this->createMock(IConfig::class));
\OC_User::useBackend($backend);
$this->assertTrue(\OC_User::setPassword('roland', 'dt'));
}
public function testSetPasswordValid() {
$access = $this->getAccessMock();
$this->prepareAccessForSetPassword($access);
$backend = new UserLDAP($access, $this->createMock(IConfig::class));
\OC_User::useBackend($backend);
$this->assertTrue(\OC_User::setPassword('roland', 'dt12234$'));
}
public function testSetPasswordValidDisabled() {
$access = $this->getAccessMock();
$this->prepareAccessForSetPassword($access, false);
$backend = new UserLDAP($access, $this->createMock(IConfig::class));
\OC_User::useBackend($backend);
$this->assertFalse(\OC_User::setPassword('roland', 'dt12234$'));
}
/**
* @expectedException \Exception
* @expectedExceptionMessage LDAP setPassword: Could not get user object for uid NotExistingUser. Maybe the LDAP entry has no set display name attribute?
*/
public function testSetPasswordWithInvalidUser() {
$access = $this->createMock(Access::class);
$access->userManager = $this->createMock(IUserManager::class);
$access->userManager
->expects($this->once())
->method('get')
->with('NotExistingUser')
->willReturn(null);
$config = $this->createMock(IConfig::class);
$ldap = new User_LDAP(
$access,
$config
);
$ldap->setPassword('NotExistingUser', 'Password');
}
public function testSetPasswordWithUsernameFalse() {
$user = $this->createMock(User::class);
$user
->expects($this->once())
->method('getUsername')
->willReturn(false);
$access = $this->createMock(Access::class);
$access->userManager = $this->createMock(IUserManager::class);
$access->userManager
->expects($this->once())
->method('get')
->with('NotExistingUser')
->willReturn($user);
$config = $this->createMock(IConfig::class);
$ldap = new User_LDAP(
$access,
$config
);
$this->assertFalse($ldap->setPassword('NotExistingUser', 'Password'));
}
} }

View File

@ -0,0 +1,61 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_LDAP\Tests;
use OCA\User_LDAP\ILDAPWrapper;
use OCA\User_LDAP\User_Proxy;
use OCP\IConfig;
use Test\TestCase;
class User_ProxyTest extends TestCase {
/** @var ILDAPWrapper|\PHPUnit_Framework_MockObject_MockObject */
private $ldapWrapper;
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
private $config;
/** @var User_Proxy|\PHPUnit_Framework_MockObject_MockObject */
private $proxy;
public function setUp() {
parent::setUp();
$this->ldapWrapper = $this->createMock(ILDAPWrapper::class);
$this->config = $this->createMock(IConfig::class);
$this->proxy = $this->getMockBuilder(User_Proxy::class)
->setConstructorArgs([
[],
$this->ldapWrapper,
$this->config,
])
->setMethods(['handleRequest'])
->getMock();
}
public function testSetPassword() {
$this->proxy
->expects($this->once())
->method('handleRequest')
->with('MyUid', 'setPassword', ['MyUid', 'MyPassword'])
->willReturn(true);
$this->assertTrue($this->proxy->setPassword('MyUid', 'MyPassword'));
}
}