diff options
author | Joas Schilling <nickvergessen@owncloud.com> | 2016-05-12 11:25:50 +0200 |
---|---|---|
committer | Joas Schilling <nickvergessen@owncloud.com> | 2016-05-25 16:04:56 +0200 |
commit | 3f5e76162d74701c35a31b5a611466dba93d9a04 (patch) | |
tree | 5dc6b119f48333057dc45e2c04ba5873c889dc67 /apps/user_ldap/lib/User | |
parent | b7fa5277915507622cc7043dc62998d849b8807d (diff) | |
download | nextcloud-server-3f5e76162d74701c35a31b5a611466dba93d9a04.tar.gz nextcloud-server-3f5e76162d74701c35a31b5a611466dba93d9a04.zip |
Move lib\user to PSR-4
Diffstat (limited to 'apps/user_ldap/lib/User')
-rw-r--r-- | apps/user_ldap/lib/User/DeletedUsersIndex.php | 113 | ||||
-rw-r--r-- | apps/user_ldap/lib/User/IUserTools.php | 40 | ||||
-rw-r--r-- | apps/user_ldap/lib/User/Manager.php | 238 | ||||
-rw-r--r-- | apps/user_ldap/lib/User/OfflineUser.php | 230 | ||||
-rw-r--r-- | apps/user_ldap/lib/User/User.php | 532 |
5 files changed, 1153 insertions, 0 deletions
diff --git a/apps/user_ldap/lib/User/DeletedUsersIndex.php b/apps/user_ldap/lib/User/DeletedUsersIndex.php new file mode 100644 index 00000000000..7a9823786dd --- /dev/null +++ b/apps/user_ldap/lib/User/DeletedUsersIndex.php @@ -0,0 +1,113 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\User_LDAP\User; + +use OCA\User_LDAP\Mapping\UserMapping; + +/** + * Class DeletedUsersIndex + * @package OCA\User_LDAP + */ +class DeletedUsersIndex { + /** + * @var \OCP\IConfig $config + */ + protected $config; + + /** + * @var \OCP\IDBConnection $db + */ + protected $db; + + /** + * @var \OCA\User_LDAP\Mapping\UserMapping $mapping + */ + protected $mapping; + + /** + * @var array $deletedUsers + */ + protected $deletedUsers; + + /** + * @param \OCP\IConfig $config + * @param \OCP\IDBConnection $db + * @param \OCA\User_LDAP\Mapping\UserMapping $mapping + */ + public function __construct(\OCP\IConfig $config, \OCP\IDBConnection $db, UserMapping $mapping) { + $this->config = $config; + $this->db = $db; + $this->mapping = $mapping; + } + + /** + * reads LDAP users marked as deleted from the database + * @return \OCA\User_LDAP\User\OfflineUser[] + */ + private function fetchDeletedUsers() { + $deletedUsers = $this->config->getUsersForUserValue( + 'user_ldap', 'isDeleted', '1'); + + $userObjects = array(); + foreach($deletedUsers as $user) { + $userObjects[] = new OfflineUser($user, $this->config, $this->db, $this->mapping); + } + $this->deletedUsers = $userObjects; + + return $this->deletedUsers; + } + + /** + * returns all LDAP users that are marked as deleted + * @return \OCA\User_LDAP\User\OfflineUser[] + */ + public function getUsers() { + if(is_array($this->deletedUsers)) { + return $this->deletedUsers; + } + return $this->fetchDeletedUsers(); + } + + /** + * whether at least one user was detected as deleted + * @return bool + */ + public function hasUsers() { + if($this->deletedUsers === false) { + $this->fetchDeletedUsers(); + } + if(is_array($this->deletedUsers) && count($this->deletedUsers) > 0) { + return true; + } + return false; + } + + /** + * marks a user as deleted + * @param string $ocName + */ + public function markUser($ocName) { + $this->config->setUserValue($ocName, 'user_ldap', 'isDeleted', '1'); + } +} diff --git a/apps/user_ldap/lib/User/IUserTools.php b/apps/user_ldap/lib/User/IUserTools.php new file mode 100644 index 00000000000..747b46ec68a --- /dev/null +++ b/apps/user_ldap/lib/User/IUserTools.php @@ -0,0 +1,40 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\User_LDAP\User; + +/** + * IUserTools + * + * defines methods that are required by User class for LDAP interaction + */ +interface IUserTools { + public function getConnection(); + + public function readAttribute($dn, $attr, $filter = 'objectClass=*'); + + public function stringResemblesDN($string); + + public function dn2username($dn, $ldapname = null); + + public function username2dn($name); +} diff --git a/apps/user_ldap/lib/User/Manager.php b/apps/user_ldap/lib/User/Manager.php new file mode 100644 index 00000000000..ca86970d477 --- /dev/null +++ b/apps/user_ldap/lib/User/Manager.php @@ -0,0 +1,238 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\User_LDAP\User; + +use OCA\user_ldap\lib\LogWrapper; +use OCA\user_ldap\lib\FilesystemHelper; +use OCP\IAvatarManager; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Image; +use OCP\IUserManager; + +/** + * Manager + * + * upon request, returns an LDAP user object either by creating or from run-time + * cache + */ +class Manager { + /** @var IUserTools */ + protected $access; + + /** @var IConfig */ + protected $ocConfig; + + /** @var IDBConnection */ + protected $db; + + /** @var FilesystemHelper */ + protected $ocFilesystem; + + /** @var LogWrapper */ + protected $ocLog; + + /** @var Image */ + protected $image; + + /** @param \OCP\IAvatarManager */ + protected $avatarManager; + + /** + * array['byDN'] \OCA\User_LDAP\User\User[] + * ['byUid'] \OCA\User_LDAP\User\User[] + * @var array $users + */ + protected $users = array( + 'byDN' => array(), + 'byUid' => array(), + ); + + /** + * @param IConfig $ocConfig + * @param \OCA\user_ldap\lib\FilesystemHelper $ocFilesystem object that + * gives access to necessary functions from the OC filesystem + * @param \OCA\user_ldap\lib\LogWrapper $ocLog + * @param IAvatarManager $avatarManager + * @param Image $image an empty image instance + * @param IDBConnection $db + * @throws \Exception when the methods mentioned above do not exist + */ + public function __construct(IConfig $ocConfig, + FilesystemHelper $ocFilesystem, LogWrapper $ocLog, + IAvatarManager $avatarManager, Image $image, + IDBConnection $db, IUserManager $userManager) { + + $this->ocConfig = $ocConfig; + $this->ocFilesystem = $ocFilesystem; + $this->ocLog = $ocLog; + $this->avatarManager = $avatarManager; + $this->image = $image; + $this->db = $db; + $this->userManager = $userManager; + } + + /** + * @brief binds manager to an instance of IUserTools (implemented by + * Access). It needs to be assigned first before the manager can be used. + * @param IUserTools + */ + public function setLdapAccess(IUserTools $access) { + $this->access = $access; + } + + /** + * @brief creates an instance of User and caches (just runtime) it in the + * property array + * @param string $dn the DN of the user + * @param string $uid the internal (owncloud) username + * @return \OCA\User_LDAP\User\User + */ + private function createAndCache($dn, $uid) { + $this->checkAccess(); + $user = new User($uid, $dn, $this->access, $this->ocConfig, + $this->ocFilesystem, clone $this->image, $this->ocLog, + $this->avatarManager, $this->userManager); + $this->users['byDN'][$dn] = $user; + $this->users['byUid'][$uid] = $user; + return $user; + } + + /** + * @brief checks whether the Access instance has been set + * @throws \Exception if Access has not been set + * @return null + */ + private function checkAccess() { + if(is_null($this->access)) { + throw new \Exception('LDAP Access instance must be set first'); + } + } + + /** + * returns a list of attributes that will be processed further, e.g. quota, + * email, displayname, or others. + * @param bool $minimal - optional, set to true to skip attributes with big + * payload + * @return string[] + */ + public function getAttributes($minimal = false) { + $attributes = array('dn', 'uid', 'samaccountname', 'memberof'); + $possible = array( + $this->access->getConnection()->ldapQuotaAttribute, + $this->access->getConnection()->ldapEmailAttribute, + $this->access->getConnection()->ldapUserDisplayName, + $this->access->getConnection()->ldapUserDisplayName2, + ); + foreach($possible as $attr) { + if(!is_null($attr)) { + $attributes[] = $attr; + } + } + + $homeRule = $this->access->getConnection()->homeFolderNamingRule; + if(strpos($homeRule, 'attr:') === 0) { + $attributes[] = substr($homeRule, strlen('attr:')); + } + + if(!$minimal) { + // attributes that are not really important but may come with big + // payload. + $attributes = array_merge($attributes, array( + 'jpegphoto', + 'thumbnailphoto' + )); + } + + return $attributes; + } + + /** + * Checks whether the specified user is marked as deleted + * @param string $id the ownCloud user name + * @return bool + */ + public function isDeletedUser($id) { + $isDeleted = $this->ocConfig->getUserValue( + $id, 'user_ldap', 'isDeleted', 0); + return intval($isDeleted) === 1; + } + + /** + * creates and returns an instance of OfflineUser for the specified user + * @param string $id + * @return \OCA\User_LDAP\User\OfflineUser + */ + public function getDeletedUser($id) { + return new OfflineUser( + $id, + $this->ocConfig, + $this->db, + $this->access->getUserMapper()); + } + + /** + * @brief returns a User object by it's ownCloud username + * @param string $id the DN or username of the user + * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null + */ + protected function createInstancyByUserName($id) { + //most likely a uid. Check whether it is a deleted user + if($this->isDeletedUser($id)) { + return $this->getDeletedUser($id); + } + $dn = $this->access->username2dn($id); + if($dn !== false) { + return $this->createAndCache($dn, $id); + } + return null; + } + + /** + * @brief returns a User object by it's DN or ownCloud username + * @param string $id the DN or username of the user + * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null + * @throws \Exception when connection could not be established + */ + public function get($id) { + $this->checkAccess(); + if(isset($this->users['byDN'][$id])) { + return $this->users['byDN'][$id]; + } else if(isset($this->users['byUid'][$id])) { + return $this->users['byUid'][$id]; + } + + if($this->access->stringResemblesDN($id) ) { + $uid = $this->access->dn2username($id); + if($uid !== false) { + return $this->createAndCache($id, $uid); + } + } + + return $this->createInstancyByUserName($id); + } + +} diff --git a/apps/user_ldap/lib/User/OfflineUser.php b/apps/user_ldap/lib/User/OfflineUser.php new file mode 100644 index 00000000000..a3a9995490d --- /dev/null +++ b/apps/user_ldap/lib/User/OfflineUser.php @@ -0,0 +1,230 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\User_LDAP\User; + +use OCA\User_LDAP\Mapping\UserMapping; + +class OfflineUser { + /** + * @var string $ocName + */ + protected $ocName; + /** + * @var string $dn + */ + protected $dn; + /** + * @var string $uid the UID as provided by LDAP + */ + protected $uid; + /** + * @var string $displayName + */ + protected $displayName; + /** + * @var string $homePath + */ + protected $homePath; + /** + * @var string $lastLogin the timestamp of the last login + */ + protected $lastLogin; + /** + * @var string $email + */ + protected $email; + /** + * @var bool $hasActiveShares + */ + protected $hasActiveShares; + /** + * @var \OCP\IConfig $config + */ + protected $config; + /** + * @var \OCP\IDBConnection $db + */ + protected $db; + /** + * @var \OCA\User_LDAP\Mapping\UserMapping + */ + protected $mapping; + + /** + * @param string $ocName + * @param \OCP\IConfig $config + * @param \OCP\IDBConnection $db + * @param \OCA\User_LDAP\Mapping\UserMapping $mapping + */ + public function __construct($ocName, \OCP\IConfig $config, \OCP\IDBConnection $db, UserMapping $mapping) { + $this->ocName = $ocName; + $this->config = $config; + $this->db = $db; + $this->mapping = $mapping; + $this->fetchDetails(); + } + + /** + * remove the Delete-flag from the user. + */ + public function unmark() { + $this->config->setUserValue($this->ocName, 'user_ldap', 'isDeleted', '0'); + } + + /** + * exports the user details in an assoc array + * @return array + */ + public function export() { + $data = array(); + $data['ocName'] = $this->getOCName(); + $data['dn'] = $this->getDN(); + $data['uid'] = $this->getUID(); + $data['displayName'] = $this->getDisplayName(); + $data['homePath'] = $this->getHomePath(); + $data['lastLogin'] = $this->getLastLogin(); + $data['email'] = $this->getEmail(); + $data['hasActiveShares'] = $this->getHasActiveShares(); + + return $data; + } + + /** + * getter for ownCloud internal name + * @return string + */ + public function getOCName() { + return $this->ocName; + } + + /** + * getter for LDAP uid + * @return string + */ + public function getUID() { + return $this->uid; + } + + /** + * getter for LDAP DN + * @return string + */ + public function getDN() { + return $this->dn; + } + + /** + * getter for display name + * @return string + */ + public function getDisplayName() { + return $this->displayName; + } + + /** + * getter for email + * @return string + */ + public function getEmail() { + return $this->email; + } + + /** + * getter for home directory path + * @return string + */ + public function getHomePath() { + return $this->homePath; + } + + /** + * getter for the last login timestamp + * @return int + */ + public function getLastLogin() { + return intval($this->lastLogin); + } + + /** + * getter for having active shares + * @return bool + */ + public function getHasActiveShares() { + return $this->hasActiveShares; + } + + /** + * reads the user details + */ + protected function fetchDetails() { + $properties = array ( + 'displayName' => 'user_ldap', + 'uid' => 'user_ldap', + 'homePath' => 'user_ldap', + 'email' => 'settings', + 'lastLogin' => 'login' + ); + foreach($properties as $property => $app) { + $this->$property = $this->config->getUserValue($this->ocName, $app, $property, ''); + } + + $dn = $this->mapping->getDNByName($this->ocName); + $this->dn = ($dn !== false) ? $dn : ''; + + $this->determineShares(); + } + + + /** + * finds out whether the user has active shares. The result is stored in + * $this->hasActiveShares + */ + protected function determineShares() { + $query = $this->db->prepare(' + SELECT COUNT(`uid_owner`) + FROM `*PREFIX*share` + WHERE `uid_owner` = ? + ', 1); + $query->execute(array($this->ocName)); + $sResult = $query->fetchColumn(0); + if(intval($sResult) === 1) { + $this->hasActiveShares = true; + return; + } + + $query = $this->db->prepare(' + SELECT COUNT(`owner`) + FROM `*PREFIX*share_external` + WHERE `owner` = ? + ', 1); + $query->execute(array($this->ocName)); + $sResult = $query->fetchColumn(0); + if(intval($sResult) === 1) { + $this->hasActiveShares = true; + return; + } + + $this->hasActiveShares = false; + } +} diff --git a/apps/user_ldap/lib/User/User.php b/apps/user_ldap/lib/User/User.php new file mode 100644 index 00000000000..4b9e3a3c9fc --- /dev/null +++ b/apps/user_ldap/lib/User/User.php @@ -0,0 +1,532 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\User_LDAP\User; + +use OCA\user_ldap\lib\Connection; +use OCA\user_ldap\lib\FilesystemHelper; +use OCA\user_ldap\lib\LogWrapper; +use OCP\IAvatarManager; +use OCP\IConfig; +use OCP\Image; +use OCP\IUserManager; + +/** + * User + * + * represents an LDAP user, gets and holds user-specific information from LDAP + */ +class User { + /** + * @var IUserTools + */ + protected $access; + /** + * @var Connection + */ + protected $connection; + /** + * @var IConfig + */ + protected $config; + /** + * @var FilesystemHelper + */ + protected $fs; + /** + * @var Image + */ + protected $image; + /** + * @var LogWrapper + */ + protected $log; + /** + * @var IAvatarManager + */ + protected $avatarManager; + /** + * @var IUserManager + */ + protected $userManager; + /** + * @var string + */ + protected $dn; + /** + * @var string + */ + protected $uid; + /** + * @var string[] + */ + protected $refreshedFeatures = array(); + /** + * @var string + */ + protected $avatarImage; + + /** + * DB config keys for user preferences + */ + const USER_PREFKEY_FIRSTLOGIN = 'firstLoginAccomplished'; + const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh'; + + /** + * @brief constructor, make sure the subclasses call this one! + * @param string $username the internal username + * @param string $dn the LDAP DN + * @param IUserTools $access an instance that implements IUserTools for + * LDAP interaction + * @param IConfig $config + * @param FilesystemHelper $fs + * @param Image $image any empty instance + * @param LogWrapper $log + * @param IAvatarManager $avatarManager + * @param IUserManager $userManager + */ + public function __construct($username, $dn, IUserTools $access, + IConfig $config, FilesystemHelper $fs, Image $image, + LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager) { + + $this->access = $access; + $this->connection = $access->getConnection(); + $this->config = $config; + $this->fs = $fs; + $this->dn = $dn; + $this->uid = $username; + $this->image = $image; + $this->log = $log; + $this->avatarManager = $avatarManager; + $this->userManager = $userManager; + } + + /** + * @brief updates properties like email, quota or avatar provided by LDAP + * @return null + */ + public function update() { + if(is_null($this->dn)) { + return null; + } + + $hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap', + self::USER_PREFKEY_FIRSTLOGIN, 0); + + if($this->needsRefresh()) { + $this->updateEmail(); + $this->updateQuota(); + if($hasLoggedIn !== 0) { + //we do not need to try it, when the user has not been logged in + //before, because the file system will not be ready. + $this->updateAvatar(); + //in order to get an avatar as soon as possible, mark the user + //as refreshed only when updating the avatar did happen + $this->markRefreshTime(); + } + } + } + + /** + * processes results from LDAP for attributes as returned by getAttributesToRead() + * @param array $ldapEntry the user entry as retrieved from LDAP + */ + public function processAttributes($ldapEntry) { + $this->markRefreshTime(); + //Quota + $attr = strtolower($this->connection->ldapQuotaAttribute); + if(isset($ldapEntry[$attr])) { + $this->updateQuota($ldapEntry[$attr][0]); + } + unset($attr); + + //Email + $attr = strtolower($this->connection->ldapEmailAttribute); + if(isset($ldapEntry[$attr])) { + $this->updateEmail($ldapEntry[$attr][0]); + } + unset($attr); + + //displayName + $displayName = $displayName2 = ''; + $attr = strtolower($this->connection->ldapUserDisplayName); + if(isset($ldapEntry[$attr])) { + $displayName = $ldapEntry[$attr][0]; + } + $attr = strtolower($this->connection->ldapUserDisplayName2); + if(isset($ldapEntry[$attr])) { + $displayName2 = $ldapEntry[$attr][0]; + } + if(!empty($displayName)) { + $this->composeAndStoreDisplayName($displayName); + $this->access->cacheUserDisplayName( + $this->getUsername(), + $displayName, + $displayName2 + ); + } + unset($attr); + + // LDAP Username, needed for s2s sharing + if(isset($ldapEntry['uid'])) { + $this->storeLDAPUserName($ldapEntry['uid'][0]); + } else if(isset($ldapEntry['samaccountname'])) { + $this->storeLDAPUserName($ldapEntry['samaccountname'][0]); + } + + //homePath + if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) { + $attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:'))); + if(isset($ldapEntry[$attr])) { + $this->access->cacheUserHome( + $this->getUsername(), $this->getHomePath($ldapEntry[$attr][0])); + } + } + + //memberOf groups + $cacheKey = 'getMemberOf'.$this->getUsername(); + $groups = false; + if(isset($ldapEntry['memberof'])) { + $groups = $ldapEntry['memberof']; + } + $this->connection->writeToCache($cacheKey, $groups); + + //Avatar + $attrs = array('jpegphoto', 'thumbnailphoto'); + foreach ($attrs as $attr) { + if(isset($ldapEntry[$attr])) { + $this->avatarImage = $ldapEntry[$attr][0]; + // the call to the method that saves the avatar in the file + // system must be postponed after the login. It is to ensure + // external mounts are mounted properly (e.g. with login + // credentials from the session). + \OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin'); + break; + } + } + } + + /** + * @brief returns the LDAP DN of the user + * @return string + */ + public function getDN() { + return $this->dn; + } + + /** + * @brief returns the ownCloud internal username of the user + * @return string + */ + public function getUsername() { + return $this->uid; + } + + /** + * returns the home directory of the user if specified by LDAP settings + * @param string $valueFromLDAP + * @return bool|string + * @throws \Exception + */ + public function getHomePath($valueFromLDAP = null) { + $path = $valueFromLDAP; + $attr = null; + + if( is_null($path) + && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0 + && $this->access->connection->homeFolderNamingRule !== 'attr:') + { + $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:')); + $homedir = $this->access->readAttribute( + $this->access->username2dn($this->getUsername()), $attr); + if ($homedir && isset($homedir[0])) { + $path = $homedir[0]; + } + } + + if(!empty($path)) { + //if attribute's value is an absolute path take this, otherwise append it to data dir + //check for / at the beginning or pattern c:\ resp. c:/ + if( '/' !== $path[0] + && !(3 < strlen($path) && ctype_alpha($path[0]) + && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2])) + ) { + $path = $this->config->getSystemValue('datadirectory', + \OC::$SERVERROOT.'/data' ) . '/' . $path; + } + //we need it to store it in the DB as well in case a user gets + //deleted so we can clean up afterwards + $this->config->setUserValue( + $this->getUsername(), 'user_ldap', 'homePath', $path + ); + return $path; + } + + if( !is_null($attr) + && $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true) + ) { + // a naming rule attribute is defined, but it doesn't exist for that LDAP user + throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername()); + } + + //false will apply default behaviour as defined and done by OC_User + $this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', ''); + return false; + } + + public function getMemberOfGroups() { + $cacheKey = 'getMemberOf'.$this->getUsername(); + $memberOfGroups = $this->connection->getFromCache($cacheKey); + if(!is_null($memberOfGroups)) { + return $memberOfGroups; + } + $groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf'); + $this->connection->writeToCache($cacheKey, $groupDNs); + return $groupDNs; + } + + /** + * @brief reads the image from LDAP that shall be used as Avatar + * @return string data (provided by LDAP) | false + */ + public function getAvatarImage() { + if(!is_null($this->avatarImage)) { + return $this->avatarImage; + } + + $this->avatarImage = false; + $attributes = array('jpegPhoto', 'thumbnailPhoto'); + foreach($attributes as $attribute) { + $result = $this->access->readAttribute($this->dn, $attribute); + if($result !== false && is_array($result) && isset($result[0])) { + $this->avatarImage = $result[0]; + break; + } + } + + return $this->avatarImage; + } + + /** + * @brief marks the user as having logged in at least once + * @return null + */ + public function markLogin() { + $this->config->setUserValue( + $this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1); + } + + /** + * @brief marks the time when user features like email have been updated + * @return null + */ + public function markRefreshTime() { + $this->config->setUserValue( + $this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time()); + } + + /** + * @brief checks whether user features needs to be updated again by + * comparing the difference of time of the last refresh to now with the + * desired interval + * @return bool + */ + private function needsRefresh() { + $lastChecked = $this->config->getUserValue($this->uid, 'user_ldap', + self::USER_PREFKEY_LASTREFRESH, 0); + + //TODO make interval configurable + if((time() - intval($lastChecked)) < 86400 ) { + return false; + } + return true; + } + + /** + * Stores a key-value pair in relation to this user + * + * @param string $key + * @param string $value + */ + private function store($key, $value) { + $this->config->setUserValue($this->uid, 'user_ldap', $key, $value); + } + + /** + * Composes the display name and stores it in the database. The final + * display name is returned. + * + * @param string $displayName + * @param string $displayName2 + * @returns string the effective display name + */ + public function composeAndStoreDisplayName($displayName, $displayName2 = '') { + if(!empty($displayName2)) { + $displayName .= ' (' . $displayName2 . ')'; + } + $this->store('displayName', $displayName); + return $displayName; + } + + /** + * Stores the LDAP Username in the Database + * @param string $userName + */ + public function storeLDAPUserName($userName) { + $this->store('uid', $userName); + } + + /** + * @brief checks whether an update method specified by feature was run + * already. If not, it will marked like this, because it is expected that + * the method will be run, when false is returned. + * @param string $feature email | quota | avatar (can be extended) + * @return bool + */ + private function wasRefreshed($feature) { + if(isset($this->refreshedFeatures[$feature])) { + return true; + } + $this->refreshedFeatures[$feature] = 1; + return false; + } + + /** + * fetches the email from LDAP and stores it as ownCloud user value + * @param string $valueFromLDAP if known, to save an LDAP read request + * @return null + */ + public function updateEmail($valueFromLDAP = null) { + if($this->wasRefreshed('email')) { + return; + } + $email = $valueFromLDAP; + if(is_null($valueFromLDAP)) { + $emailAttribute = $this->connection->ldapEmailAttribute; + if(!empty($emailAttribute)) { + $aEmail = $this->access->readAttribute($this->dn, $emailAttribute); + if(is_array($aEmail) && (count($aEmail) > 0)) { + $email = $aEmail[0]; + } + } + } + if(!is_null($email)) { + $user = $this->userManager->get($this->uid); + if (!is_null($user)) { + $user->setEMailAddress($email); + } + } + } + + /** + * fetches the quota from LDAP and stores it as ownCloud user value + * @param string $valueFromLDAP the quota attribute's value can be passed, + * to save the readAttribute request + * @return null + */ + public function updateQuota($valueFromLDAP = null) { + if($this->wasRefreshed('quota')) { + return; + } + //can be null + $quotaDefault = $this->connection->ldapQuotaDefault; + $quota = $quotaDefault !== '' ? $quotaDefault : null; + $quota = !is_null($valueFromLDAP) ? $valueFromLDAP : $quota; + + if(is_null($valueFromLDAP)) { + $quotaAttribute = $this->connection->ldapQuotaAttribute; + if(!empty($quotaAttribute)) { + $aQuota = $this->access->readAttribute($this->dn, $quotaAttribute); + if($aQuota && (count($aQuota) > 0)) { + $quota = $aQuota[0]; + } + } + } + if(!is_null($quota)) { + $user = $this->userManager->get($this->uid)->setQuota($quota); + } + } + + /** + * called by a post_login hook to save the avatar picture + * + * @param array $params + */ + public function updateAvatarPostLogin($params) { + if(isset($params['uid']) && $params['uid'] === $this->getUsername()) { + $this->updateAvatar(); + } + } + + /** + * @brief attempts to get an image from LDAP and sets it as ownCloud avatar + * @return null + */ + public function updateAvatar() { + if($this->wasRefreshed('avatar')) { + return; + } + $avatarImage = $this->getAvatarImage(); + if($avatarImage === false) { + //not set, nothing left to do; + return; + } + $this->image->loadFromBase64(base64_encode($avatarImage)); + $this->setOwnCloudAvatar(); + } + + /** + * @brief sets an image as ownCloud avatar + * @return null + */ + private function setOwnCloudAvatar() { + if(!$this->image->valid()) { + $this->log->log('user_ldap', 'jpegPhoto data invalid for '.$this->dn, + \OCP\Util::ERROR); + return; + } + //make sure it is a square and not bigger than 128x128 + $size = min(array($this->image->width(), $this->image->height(), 128)); + if(!$this->image->centerCrop($size)) { + $this->log->log('user_ldap', + 'croping image for avatar failed for '.$this->dn, + \OCP\Util::ERROR); + return; + } + + if(!$this->fs->isLoaded()) { + $this->fs->setup($this->uid); + } + + try { + $avatar = $this->avatarManager->getAvatar($this->uid); + $avatar->set($this->image); + } catch (\Exception $e) { + \OC::$server->getLogger()->notice( + 'Could not set avatar for ' . $this->dn . ', because: ' . $e->getMessage(), + ['app' => 'user_ldap']); + } + } + +} |