* @param int $limit
* @param int $offset
* @return array with user ids
+ * @throws \Exception
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
if(!$this->enabled) {
//we got uids, need to get their DNs to 'translate' them to user names
$filter = $this->access->combineFilterWithAnd(array(
str_replace('%uid', trim($member), $this->access->connection->ldapLoginFilter),
- $this->access->getFilterPartForUserSearch($search)
+ $this->access->combineFilterWithAnd([
+ $this->access->getFilterPartForUserSearch($search),
+ $this->access->connection->ldapUserFilter
+ ])
));
$ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
if(count($ldap_users) < 1) {
$groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
} else {
//we got DNs, check if we need to filter by search or we can give back all of them
- if ($search !== '') {
- if(!$this->access->readAttribute($member,
+ $uid = $this->access->dn2username($member);
+ if(!$uid) {
+ continue;
+ }
+
+ $cacheKey = 'userExistsOnLDAP' . $uid;
+ $userExists = $this->access->connection->getFromCache($cacheKey);
+ if($userExists === false) {
+ continue;
+ }
+ if($userExists === null || $search !== '') {
+ if (!$this->access->readAttribute($member,
$this->access->connection->ldapUserDisplayName,
- $this->access->getFilterPartForUserSearch($search))) {
+ $this->access->combineFilterWithAnd([
+ $this->access->getFilterPartForUserSearch($search),
+ $this->access->connection->ldapUserFilter
+ ])))
+ {
+ if($search === '') {
+ $this->access->connection->writeToCache($cacheKey, false);
+ }
continue;
}
+ $this->access->connection->writeToCache($cacheKey, true);
}
- // dn2username will also check if the users belong to the allowed base
- if($ocname = $this->access->dn2username($member)) {
- $groupUsers[] = $ocname;
- }
+ $groupUsers[] = $uid;
}
}
}
}
+ /**
+ * marks a user as deleted
+ *
+ * @throws \OCP\PreConditionNotMetException
+ */
+ public function markUser() {
+ $curValue = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '0');
+ if($curValue === '1') {
+ // the user is already marked, do not write to DB again
+ return;
+ }
+ $this->config->setUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '1');
+ $this->config->setUserValue($this->getUsername(), 'user_ldap', 'foundDeleted', (string)time());
+ }
+
/**
* processes results from LDAP for attributes as returned by getAttributesToRead()
* @param array $ldapEntry the user entry as retrieved from LDAP
use OCA\User_LDAP\User\User;
use OCP\IConfig;
use OCP\ILogger;
-use OCP\IUser;
use OCP\IUserSession;
use OCP\Notification\IManager as INotificationManager;
use OCP\Util;
/** @var INotificationManager */
protected $notificationManager;
- /** @var string */
- protected $currentUserInDeletionProcess;
-
/** @var UserPluginManager */
protected $userPluginManager;
$this->ocConfig = $ocConfig;
$this->notificationManager = $notificationManager;
$this->userPluginManager = $userPluginManager;
- $this->registerHooks($userSession);
- }
-
- protected function registerHooks(IUserSession $userSession) {
- $userSession->listen('\OC\User', 'preDelete', [$this, 'preDeleteUser']);
- $userSession->listen('\OC\User', 'postDelete', [$this, 'postDeleteUser']);
- }
-
- public function preDeleteUser(IUser $user) {
- $this->currentUserInDeletionProcess = $user->getUID();
- }
-
- public function postDeleteUser() {
- $this->currentUserInDeletionProcess = null;
}
/**
if(is_null($user)) {
return false;
}
+ $uid = $user instanceof User ? $user->getUsername() : $user->getOCName();
+ $cacheKey = 'userExistsOnLDAP' . $uid;
+ $userExists = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($userExists)) {
+ return (bool)$userExists;
+ }
$dn = $user->getDN();
//check if user really still exists by reading its entry
try {
$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
if (!$uuid) {
+ $this->access->connection->writeToCache($cacheKey, false);
return false;
}
$newDn = $this->access->getUserDnByUuid($uuid);
//check if renamed user is still valid by reapplying the ldap filter
if ($newDn === $dn || !is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
+ $this->access->connection->writeToCache($cacheKey, false);
return false;
}
$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
+ $this->access->connection->writeToCache($cacheKey, true);
return true;
} catch (ServerNotAvailableException $e) {
throw $e;
} catch (\Exception $e) {
+ $this->access->connection->writeToCache($cacheKey, false);
return false;
}
}
$user->unmark();
}
+ $this->access->connection->writeToCache($cacheKey, true);
return true;
}
$this->access->connection->ldapHost, ILogger::DEBUG);
$this->access->connection->writeToCache('userExists'.$uid, false);
return false;
- } else if($user instanceof OfflineUser) {
- //express check for users marked as deleted. Returning true is
- //necessary for cleanup
- return true;
}
- $result = $this->userExistsOnLDAP($user);
- $this->access->connection->writeToCache('userExists'.$uid, $result);
- return $result;
+ $this->access->connection->writeToCache('userExists'.$uid, true);
+ return true;
}
/**
// early return path if it is a deleted user
$user = $this->access->userManager->get($uid);
- if($user instanceof OfflineUser) {
- if($this->currentUserInDeletionProcess !== null
- && $this->currentUserInDeletionProcess === $user->getOCName()
- ) {
- return $user->getHomePath();
- } else {
- throw new NoUserException($uid . ' is not a valid user anymore');
- }
- } else if ($user === null) {
+ if($user instanceof User || $user instanceof OfflineUser) {
+ $path = $user->getHomePath() ?: false;
+ } else {
throw new NoUserException($uid . ' is not a valid user anymore');
}
- $path = $user->getHomePath();
$this->access->cacheUserHome($uid, $path);
-
return $path;
}
use OCP\Notification\IManager as INotificationManager;
class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP {
- private $backends = array();
+ private $backends = [];
+ /** @var User_LDAP */
private $refBackend = null;
/**
* @param INotificationManager $notificationManager
* @param IUserSession $userSession
*/
- public function __construct(array $serverConfigPrefixes, ILDAPWrapper $ldap, IConfig $ocConfig,
- INotificationManager $notificationManager, IUserSession $userSession,
- UserPluginManager $userPluginManager) {
+ public function __construct(
+ array $serverConfigPrefixes,
+ ILDAPWrapper $ldap,
+ IConfig $ocConfig,
+ INotificationManager $notificationManager,
+ IUserSession $userSession,
+ UserPluginManager $userPluginManager
+ ) {
parent::__construct($ldap);
foreach($serverConfigPrefixes as $configPrefix) {
$this->backends[$configPrefix] =
&& method_exists($this->getAccess($prefix), $method)) {
$instance = $this->getAccess($prefix);
}
- $result = call_user_func_array(array($instance, $method), $parameters);
+ $result = call_user_func_array([$instance, $method], $parameters);
if($result === $passOnWhen) {
//not found here, reset cache to null if user vanished
//because sometimes methods return false with a reason
$userExists = call_user_func_array(
- array($this->backends[$prefix], 'userExists'),
- array($uid)
+ [$this->backends[$prefix], 'userExistsOnLDAP'],
+ [$uid]
);
if(!$userExists) {
$this->writeToCache($cacheKey, null);
* @return boolean
*/
public function userExists($uid) {
- return $this->handleRequest($uid, 'userExists', array($uid));
+ $existsOnLDAP = false;
+ $existsLocally = $this->handleRequest($uid, 'userExists', array($uid));
+ if($existsLocally) {
+ $existsOnLDAP = $this->userExistsOnLDAP($uid);
+ }
+ if($existsLocally && !$existsOnLDAP) {
+ try {
+ $user = $this->getLDAPAccess($uid)->userManager->get($uid);
+ if($user instanceof User) {
+ $user->markUser();
+ }
+ } catch (\Exception $e) {
+ // ignore
+ }
+ }
+ return $existsLocally;
}
/**
$offlineUser->expects($this->once())
->method('getHomePath')
->willReturn($home);
- $offlineUser->expects($this->once())
- ->method('getOCName')
- ->willReturn($uid);
$this->userManager->expects($this->atLeastOnce())
->method('get')
->willReturn($offlineUser);
$backend = new UserLDAP($this->access, $this->config, $this->notificationManager, $this->session, $this->pluginManager);
- /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())
- ->method('getUID')
- ->willReturn($uid);
-
- $backend->preDeleteUser($user);
$result = $backend->deleteUser($uid);
$this->assertTrue($result);
/** @noinspection PhpUnhandledExceptionInspection */
$this->prepareMockForUserExists();
$user = $this->createMock(User::class);
- $user->expects($this->any())
- ->method('getDN')
- ->willReturn('dnOfRoland,dc=test');
- $this->access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn) {
- if($dn === 'dnOfRoland,dc=test') {
- return array();
- }
- return false;
- }));
$this->userManager->expects($this->atLeastOnce())
->method('get')
->willReturn($user);
->with('dnOfFormerUser,dc=test')
->willReturn('45673458748');
- $this->access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn) {
- if($dn === 'dnOfRoland,dc=test') {
- return array();
- }
- return false;
- }));
$this->access->expects($this->any())
->method('getUserMapper')
->willReturn($mapper);
- $this->access->expects($this->once())
- ->method('getUserDnByUuid')
- ->willThrowException(new \Exception());
$user = $this->createMock(User::class);
- $user->expects($this->any())
- ->method('getDN')
- ->willReturn('dnOfFormerUser,dc=test');
$this->userManager->expects($this->atLeastOnce())
->method('get')
->willReturn($user);
- //test for deleted user
- $this->assertFalse($backend->userExists('formerUser'));
+ //test for deleted user – always returns true as long as we have the user in DB
+ $this->assertTrue($backend->userExists('formerUser'));
}
public function testUserExistsForNeverExisting() {
$this->assertTrue($result);
}
- public function testUserExistsPublicAPIForDeleted() {
- $backend = new UserLDAP($this->access, $this->config, $this->notificationManager, $this->session, $this->pluginManager);
- $this->prepareMockForUserExists();
- \OC_User::useBackend($backend);
-
- $mapper = $this->createMock(UserMapping::class);
- $mapper->expects($this->any())
- ->method('getUUIDByDN')
- ->with('dnOfFormerUser,dc=test')
- ->willReturn('45673458748');
-
- $this->access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn) {
- if($dn === 'dnOfRoland,dc=test') {
- return array();
- }
- return false;
- }));
- $this->access->expects($this->any())
- ->method('getUserMapper')
- ->willReturn($mapper);
- $this->access->expects($this->once())
- ->method('getUserDnByUuid')
- ->willThrowException(new \Exception());
-
- $user = $this->createMock(User::class);
- $user->expects($this->any())
- ->method('getDN')
- ->willReturn('dnOfFormerUser,dc=test');
-
- $this->userManager->expects($this->atLeastOnce())
- ->method('get')
- ->willReturn($user);
-
- //test for deleted user
- $this->assertFalse(\OC::$server->getUserManager()->userExists('formerUser'));
- }
-
- public function testUserExistsPublicAPIForNeverExisting() {
- $backend = new UserLDAP($this->access, $this->config, $this->notificationManager, $this->session, $this->pluginManager);
- $this->prepareMockForUserExists();
- \OC_User::useBackend($backend);
-
- $this->access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn) {
- if($dn === 'dnOfRoland,dc=test') {
- return array();
- }
- return false;
- }));
-
- //test for never-existing user
- $result = \OC::$server->getUserManager()->userExists('mallory');
- $this->assertFalse($result);
- }
-
public function testDeleteUserExisting() {
$backend = new UserLDAP($this->access, $this->config, $this->notificationManager, $this->session, $this->pluginManager);
$this->assertFalse($result);
}
- /**
- * @expectedException \OC\User\NoUserException
- */
public function testGetHomeDeletedUser() {
$uid = 'newyorker';
->will($this->returnValue(true));
$offlineUser = $this->createMock(OfflineUser::class);
- $offlineUser->expects($this->never())
- ->method('getHomePath');
+ $offlineUser->expects($this->atLeastOnce())
+ ->method('getHomePath')
+ ->willReturn('');
$this->userManager->expects($this->atLeastOnce())
->method('get')
->willReturn($offlineUser);
- $backend->getHome($uid);
+ $result = $backend->getHome($uid);
+ $this->assertFalse($result);
}
public function testGetHomeWithPlugin() {
->willReturn(42);
$this->assertEquals($this->backend->countUsers(),42);
- }
+ }
public function testLoginName2UserNameSuccess() {
$loginName = 'Alice';
$this->assertTrue(\OC_User::setPassword('roland', 'dt'));
}
-
+
public function testSetPasswordValid() {
$this->prepareAccessForSetPassword($this->access);