diff options
author | Bart Visscher <bartv@thisnet.nl> | 2012-07-25 17:51:36 +0200 |
---|---|---|
committer | Bart Visscher <bartv@thisnet.nl> | 2012-07-25 17:51:36 +0200 |
commit | d579defc668ff3354771c6bc346415d5ef6e2d73 (patch) | |
tree | 96b6f99717e1c8e14372d5116b5d359578465637 | |
parent | ac9dbd4b83852d137d35cb87911ebd6b21c494db (diff) | |
parent | d17eb2983f46b1a2e72f0c3c2d7cfce40f47f653 (diff) | |
download | nextcloud-server-d579defc668ff3354771c6bc346415d5ef6e2d73.tar.gz nextcloud-server-d579defc668ff3354771c6bc346415d5ef6e2d73.zip |
Merge branch 'master' into routing
-rw-r--r-- | apps/user_ldap/appinfo/app.php | 14 | ||||
-rw-r--r-- | apps/user_ldap/group_ldap.php | 94 | ||||
-rw-r--r-- | apps/user_ldap/lib/access.php | 593 | ||||
-rw-r--r-- | apps/user_ldap/lib/connection.php | 245 | ||||
-rw-r--r-- | apps/user_ldap/lib_ldap.php | 721 | ||||
-rw-r--r-- | apps/user_ldap/tests/group_ldap.php | 4 | ||||
-rw-r--r-- | apps/user_ldap/user_ldap.php | 72 | ||||
-rw-r--r-- | apps/user_openid/appinfo/app.php | 2 | ||||
-rw-r--r-- | lib/base.php | 3 | ||||
-rw-r--r-- | lib/group.php | 8 | ||||
-rw-r--r-- | lib/group/backend.php | 2 | ||||
-rw-r--r-- | lib/group/interface.php | 76 | ||||
-rw-r--r-- | lib/helper.php | 28 | ||||
-rw-r--r-- | lib/ocs.php | 85 | ||||
-rw-r--r-- | lib/public/groupinterface.php | 31 | ||||
-rw-r--r-- | lib/public/userinterface.php | 31 | ||||
-rw-r--r-- | lib/public/util.php | 11 | ||||
-rw-r--r-- | lib/user.php | 4 | ||||
-rw-r--r-- | lib/user/backend.php | 2 | ||||
-rw-r--r-- | lib/user/interface.php | 60 | ||||
-rw-r--r-- | webapps.php | 54 |
21 files changed, 1272 insertions, 868 deletions
diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 330574c1d42..3c6da47d71a 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -21,15 +21,17 @@ * */ -require_once('apps/user_ldap/lib_ldap.php'); -require_once('apps/user_ldap/user_ldap.php'); -require_once('apps/user_ldap/group_ldap.php'); +OCP\App::registerAdmin('user_ldap', 'settings'); -OCP\App::registerAdmin('user_ldap','settings'); +$connector = new OCA\user_ldap\lib\Connection('user_ldap'); +$userBackend = new OCA\user_ldap\USER_LDAP(); +$userBackend->setConnector($connector); +$groupBackend = new OCA\user_ldap\GROUP_LDAP(); +$groupBackend->setConnector($connector); // register user backend -OC_User::useBackend( 'LDAP' ); -OC_Group::useBackend( new OC_GROUP_LDAP() ); +OC_User::useBackend($userBackend); +OC_Group::useBackend($groupBackend); // add settings page to navigation $entry = array( diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index d438c7d84df..b9f4bdf1990 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -21,24 +21,22 @@ * */ -class OC_GROUP_LDAP extends OC_Group_Backend { -// //group specific settings - protected $ldapGroupFilter; - protected $ldapGroupMemberAssocAttr; - protected $configured = false; +namespace OCA\user_ldap; + +class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { + protected $enabled = false; protected $_group_user = array(); protected $_user_groups = array(); protected $_group_users = array(); protected $_groups = array(); - public function __construct() { - $this->ldapGroupFilter = OCP\Config::getAppValue('user_ldap', 'ldap_group_filter', '(objectClass=posixGroup)'); - $this->ldapGroupMemberAssocAttr = OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember'); - - if(!empty($this->ldapGroupFilter) && !empty($this->ldapGroupMemberAssocAttr)) { - $this->configured = true; + public function setConnector(lib\Connection &$connection) { + parent::setConnector($connection); + if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) { + $this->enabled = false; } + $this->enabled = true; } /** @@ -50,31 +48,31 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid) { - if(!$this->configured) { + if(!$this->enabled) { return false; } if(isset($this->_group_user[$gid][$uid])) { return $this->_group_user[$gid][$uid]; } - $dn_user = OC_LDAP::username2dn($uid); - $dn_group = OC_LDAP::groupname2dn($gid); + $dn_user = $this->username2dn($uid); + $dn_group = $this->groupname2dn($gid); // just in case if(!$dn_group || !$dn_user) { return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = OC_LDAP::readAttribute($dn_group, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { return false; } //extra work if we don't get back user DNs //TODO: this can be done with one LDAP query - if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { + if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { $dns = array(); foreach($members as $mid) { - $filter = str_replace('%uid', $mid, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } @@ -96,36 +94,37 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * if the user exists at all. */ public function getUserGroups($uid) { - if(!$this->configured) { + if(!$this->enabled) { return array(); } if(isset($this->_user_groups[$uid])) { return $this->_user_groups[$uid]; } - $userDN = OC_LDAP::username2dn($uid); + $userDN = $this->username2dn($uid); if(!$userDN) { $this->_user_groups[$uid] = array(); return array(); } //uniqueMember takes DN, memberuid the uid, so we need to distinguish - if((strtolower($this->ldapGroupMemberAssocAttr) == 'uniquemember') - || (strtolower($this->ldapGroupMemberAssocAttr) == 'member')) { + if((strtolower($this->connection->ldapGroupMemberAssocAttr) == 'uniquemember') + || (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'member') + ) { $uid = $userDN; - } else if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { - $result = OC_LDAP::readAttribute($userDN, 'uid'); + } else if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { + $result = $this->readAttribute($userDN, 'uid'); $uid = $result[0]; } else { // just in case $uid = $userDN; } - $filter = OC_LDAP::combineFilterWithAnd(array( - $this->ldapGroupFilter, - $this->ldapGroupMemberAssocAttr.'='.$uid + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapGroupFilter, + $this->connection->ldapGroupMemberAssocAttr.'='.$uid )); - $groups = OC_LDAP::fetchListOfGroups($filter, array(OC_LDAP::conf('ldapGroupDisplayName'),'dn')); - $this->_user_groups[$uid] = array_unique(OC_LDAP::ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn')); + $this->_user_groups[$uid] = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING); return $this->_user_groups[$uid]; } @@ -135,44 +134,44 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * @returns array with user ids */ public function usersInGroup($gid) { - if(!$this->configured) { + if(!$this->enabled) { return array(); } if(isset($this->_group_users[$gid])) { return $this->_group_users[$gid]; } - $groupDN = OC_LDAP::groupname2dn($gid); + $groupDN = $this->groupname2dn($gid); if(!$groupDN) { $this->_group_users[$gid] = array(); return array(); } - $members = OC_LDAP::readAttribute($groupDN, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { $this->_group_users[$gid] = array(); return array(); } $result = array(); - $isMemberUid = (strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid'); + $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid'); foreach($members as $member) { if($isMemberUid) { - $filter = OCP\Util::mb_str_replace('%uid', $member, OC_LDAP::conf('ldapLoginFilter'), 'UTF-8'); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $member, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } - $result[] = OC_LDAP::dn2username($ldap_users[0]); + $result[] = $this->dn2username($ldap_users[0]); continue; } else { - if($ocname = OC_LDAP::dn2username($member)){ + if($ocname = $this->dn2username($member)) { $result[] = $ocname; } } } if(!$isMemberUid) { - $result = array_intersect($result, OCP\User::getUsers()); + $result = array_intersect($result, \OCP\User::getUsers()); } $this->_group_users[$gid] = array_unique($result, SORT_LOCALE_STRING); return $this->_group_users[$gid]; @@ -185,12 +184,12 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * Returns a list with all groups */ public function getGroups() { - if(!$this->configured) { + if(!$this->enabled) { return array(); } if(empty($this->_groups)) { - $ldap_groups = OC_LDAP::fetchListOfGroups($this->ldapGroupFilter, array(OC_LDAP::conf('ldapGroupDisplayName'), 'dn')); - $this->_groups = OC_LDAP::ownCloudGroupNames($ldap_groups); + $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn')); + $this->_groups = $this->ownCloudGroupNames($ldap_groups); } return $this->_groups; } @@ -203,4 +202,17 @@ class OC_GROUP_LDAP extends OC_Group_Backend { public function groupExists($gid){ return in_array($gid, $this->getGroups()); } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + //always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now. + return false; + } }
\ No newline at end of file diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php new file mode 100644 index 00000000000..870f6330edd --- /dev/null +++ b/apps/user_ldap/lib/access.php @@ -0,0 +1,593 @@ +<?php + +/** + * ownCloud – LDAP Access + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +abstract class Access { + protected $connection; + + public function setConnector(Connection &$connection) { + $this->connection = $connection; + } + + private function checkConnection() { + return ($this->connection instanceof Connection); + } + + /** + * @brief reads a given attribute for an LDAP record identified by a DN + * @param $dn the record in question + * @param $attr the attribute that shall be retrieved + * @returns the values in an array on success, false otherwise + * + * Reads an attribute from an LDAP entry + */ + public function readAttribute($dn, $attr) { + if(!$this->checkConnection()) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN); + return false; + } + $cr = $this->connection->getConnectionResource(); + $rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr)); + if(!is_resource($rr)) { + \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG); + //in case an error occurs , e.g. object does not exist + return false; + } + $er = ldap_first_entry($cr, $rr); + //LDAP attributes are not case sensitive + $result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); + $attr = mb_strtolower($attr, 'UTF-8'); + + if(isset($result[$attr]) && $result[$attr]['count'] > 0) { + $values = array(); + for($i=0;$i<$result[$attr]['count'];$i++) { + $values[] = $this->resemblesDN($attr) ? $this->sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; + } + return $values; + } + return false; + } + + /** + * @brief checks wether the given attribute`s valua is probably a DN + * @param $attr the attribute in question + * @return if so true, otherwise false + */ + private function resemblesDN($attr) { + $resemblingAttributes = array( + 'dn', + 'uniquemember', + 'member' + ); + return in_array($attr, $resemblingAttributes); + } + + /** + * @brief sanitizes a DN received from the LDAP server + * @param $dn the DN in question + * @return the sanitized DN + */ + private function sanitizeDN($dn) { + //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! + $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); + + //make comparisons and everything work + $dn = mb_strtolower($dn, 'UTF-8'); + + return $dn; + } + + /** + * gives back the database table for the query + */ + private function getMapTable($isUser) { + if($isUser) { + return '*PREFIX*ldap_user_mapping'; + } else { + return '*PREFIX*ldap_group_mapping'; + } + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the group + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the group + */ + public function groupname2dn($name) { + return $this->ocname2dn($name, false); + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the user + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the user + */ + public function username2dn($name) { + $dn = $this->ocname2dn($name, true); + if($dn) { + return $dn; + } else { + //fallback: user is not mapped + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapUserFilter, + $this->connection->ldapUserDisplayName . '=' . $name, + )); + $result = $this->searchUsers($filter, 'dn'); + if(isset($result[0]['dn'])) { + $this->mapComponent($result[0], $name, true); + return $result[0]; + } + } + + return false; + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name + * @param $name the ownCloud name in question + * @param $isUser is it a user? otherwise group + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name + */ + private function ocname2dn($name, $isUser) { + $table = $this->getMapTable($isUser); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn + FROM '.$table.' + WHERE owncloud_name = ? + '); + + $record = $query->execute(array($name))->fetchOne(); + return $record; + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the group + * @param $dn the dn of the group object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud, false on DN outside of search DN + * + * returns the internal ownCloud name for the given LDAP DN of the group + */ + public function dn2groupname($dn, $ldapname = null) { + if(mb_strripos($dn, $this->connection->ldapBaseGroups, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->connection->ldapBaseGroups, 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, false); + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the user + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN + */ + public function dn2username($dn, $ldapname = null) { + if(mb_strripos($dn, $this->connection->ldapBaseUsers, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->connection->ldapBaseUsers, 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, true); + } + + /** + * @brief returns an internal ownCloud name for the given LDAP DN + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @param $isUser optional, wether it is a user object (otherwise group assumed) + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN + */ + public function dn2ocname($dn, $ldapname = null, $isUser = true) { + $dn = $this->sanitizeDN($dn); + $table = $this->getMapTable($isUser); + if($isUser) { + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE ldap_dn = ? + '); + + $component = $query->execute(array($dn))->fetchOne(); + if($component) { + return $component; + } + + if(is_null($ldapname)) { + $ldapname = $this->readAttribute($dn, $nameAttribute); + $ldapname = $ldapname[0]; + } + $ldapname = $this->sanitizeUsername($ldapname); + + //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. + if($this->mapComponent($dn, $ldapname, $isUser)) { + return $ldapname; + } + + //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. + $oc_name = $this->alternateOwnCloudName($ldapname, $dn); + if($this->mapComponent($dn, $oc_name, $isUser)) { + return $oc_name; + } + + //TODO: do not simple die away! + //and this of course should never been thrown :) + throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); + } + + /** + * @brief gives back the user names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) + * @returns an array with the user names to use in ownCloud + * + * gives back the user names as they are used ownClod internally + */ + public function ownCloudUserNames($ldapUsers) { + return $this->ldap2ownCloudNames($ldapUsers, true); + } + + /** + * @brief gives back the group names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) + * @returns an array with the group names to use in ownCloud + * + * gives back the group names as they are used ownClod internally + */ + public function ownCloudGroupNames($ldapGroups) { + return $this->ldap2ownCloudNames($ldapGroups, false); + } + + private function ldap2ownCloudNames($ldapObjects, $isUsers) { + if($isUsers) { + $knownObjects = $this->mappedUsers(); + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $knownObjects = $this->mappedGroups(); + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + $ownCloudNames = array(); + + foreach($ldapObjects as $ldapObject) { + $key = \OCP\Util::recursiveArraySearch($knownObjects, $ldapObject['dn']); + + //everything is fine when we know the group + if($key !== false) { + $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; + continue; + } + + //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. + $ocname = $this->sanitizeUsername($ldapObject[$nameAttribute]); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. + $ocname = $this->alternateOwnCloudName($ocname, $ldapObject['dn']); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //TODO: do not simple die away + //and this of course should never been thrown :) + throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); + } + return $ownCloudNames; + } + + /** + * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + * @param $name the display name of the object + * @param $dn the dn of the object + * @returns string with with the name to use in ownCloud + * + * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + */ + private function alternateOwnCloudName($name, $dn) { + $ufn = ldap_dn2ufn($dn); + $name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); + $name = $this->sanitizeUsername($name); + return $name; + } + + /** + * @brief retrieves all known groups from the mappings table + * @returns array with the results + * + * retrieves all known groups from the mappings table + */ + private function mappedGroups() { + return $this->mappedComponents(false); + } + + /** + * @brief retrieves all known users from the mappings table + * @returns array with the results + * + * retrieves all known users from the mappings table + */ + private function mappedUsers() { + return $this->mappedComponents(true); + } + + private function mappedComponents($isUsers) { + $table = $this->getMapTable($isUsers); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn, owncloud_name + FROM '. $table + ); + + return $query->execute()->fetchAll(); + } + + /** + * @brief inserts a new user or group into the mappings table + * @param $dn the record in question + * @param $ocname the name to use in ownCloud + * @param $isUser is it a user or a group? + * @returns true on success, false otherwise + * + * inserts a new user or group into the mappings table + */ + private function mapComponent($dn, $ocname, $isUser = true) { + $table = $this->getMapTable($isUser); + $dn = $this->sanitizeDN($dn); + + $sqlAdjustment = ''; + $dbtype = \OCP\Config::getSystemValue('dbtype'); + if($dbtype == 'mysql') { + $sqlAdjustment = 'FROM dual'; + } + + $insert = \OCP\DB::prepare(' + INSERT INTO '.$table.' (ldap_dn, owncloud_name) + SELECT ?,? + '.$sqlAdjustment.' + WHERE NOT EXISTS ( + SELECT 1 + FROM '.$table.' + WHERE ldap_dn = ? + OR owncloud_name = ? ) + '); + + $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); + + if(\OCP\DB::isError($res)) { + return false; + } + + $insRows = $res->numRows(); + + if($insRows == 0) { + return false; + } + + return true; + } + + public function fetchListOfUsers($filter, $attr) { + return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1)); + } + + public function fetchListOfGroups($filter, $attr) { + return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1)); + } + + private function fetchList($list, $manyAttributes) { + if(is_array($list)) { + if($manyAttributes) { + return $list; + } else { + return array_unique($list, SORT_LOCALE_STRING); + } + } + + //error cause actually, maybe throw an exception in future. + return array(); + } + + /** + * @brief executes an LDAP search, optimized for Users + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchUsers($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseUsers, $attr); + } + + /** + * @brief executes an LDAP search, optimized for Groups + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchGroups($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseGroups, $attr); + } + + /** + * @brief executes an LDAP search + * @param $filter the LDAP filter for the search + * @param $base the LDAP subtree that shall be searched + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + private function search($filter, $base, $attr = null) { + if(!is_null($attr) && !is_array($attr)) { + $attr = array(mb_strtolower($attr, 'UTF-8')); + } + + // See if we have a resource + $link_resource = $this->connection->getConnectionResource(); + if(is_resource($link_resource)) { + $sr = ldap_search($link_resource, $base, $filter, $attr); + $findings = ldap_get_entries($link_resource, $sr ); + + // if we're here, probably no connection resource is returned. + // to make ownCloud behave nicely, we simply give back an empty array. + if(is_null($findings)) { + return array(); + } + } else { + // Seems like we didn't find any resource. + // Return an empty array just like before. + \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG); + return array(); + } + + if(!is_null($attr)) { + $selection = array(); + $multiarray = false; + if(count($attr) > 1) { + $multiarray = true; + $i = 0; + } + foreach($findings as $item) { + if(!is_array($item)) { + continue; + } + $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8'); + + if($multiarray) { + foreach($attr as $key) { + $key = mb_strtolower($key, 'UTF-8'); + if(isset($item[$key])) { + if($key != 'dn') { + $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0]; + } else { + $selection[$i][$key] = $this->sanitizeDN($item[$key]); + } + } + + } + $i++; + } else { + //tribute to case insensitivity + $key = mb_strtolower($attr[0], 'UTF-8'); + + if(isset($item[$key])) { + if($this->resemblesDN($key)) { + $selection[] = $this->sanitizeDN($item[$key]); + } else { + $selection[] = $item[$key]; + } + } + } + } + return $selection; + } + return $findings; + } + + public function sanitizeUsername($name) { + if($this->connection->ldapIgnoreNamingRules) { + return $name; + } + + //REPLACEMENTS + $name = \OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8'); + + //every remaining unallowed characters will be removed + $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name); + + return $name; + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithAnd($filters) { + return $this->combineFilter($filters, '&'); + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithOr($filters) { + return $this->combineFilter($filters, '|'); + } + + /** + * @brief combines the input filters with given operator + * @param $filters array, the filters to connect + * @param $operator either & or | + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + private function combineFilter($filters, $operator) { + $combinedFilter = '('.$operator; + foreach($filters as $filter) { + if($filter[0] != '(') { + $filter = '('.$filter.')'; + } + $combinedFilter.=$filter; + } + $combinedFilter.=')'; + return $combinedFilter; + } + + public function areCredentialsValid($name, $password) { + $testConnection = clone $this->connection; + $credentials = array( + 'ldapAgentName' => $name, + 'ldapAgentPassword' => $password + ); + if(!$testConnection->setConfiguration($credentials)) { + return false; + } + return $testConnection->bind(); + } +}
\ No newline at end of file diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php new file mode 100644 index 00000000000..c8ba9dad70e --- /dev/null +++ b/apps/user_ldap/lib/connection.php @@ -0,0 +1,245 @@ +<?php + +/** + * ownCloud – LDAP Access + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +class Connection { + private $ldapConnectionRes = null; + private $configID; + private $configured = false; + + //cached settings + protected $config = array( + 'ldapHost' => null, + 'ldapPort' => null, + 'ldapBase' => null, + 'ldapBaseUsers' => null, + 'ldapBaseGroups' => null, + 'ldapAgentName' => null, + 'ldapAgentPassword' => null, + 'ldapTLS' => null, + 'ldapNoCase' => null, + 'ldapIgnoreNamingRules' => null, + 'ldapUserDisplayName' => null, + 'ldapUserFilter' => null, + 'ldapGroupFilter' => null, + 'ldapGroupDisplayName' => null, + 'ldapLoginFilter' => null, + 'ldapQuotaAttribute' => null, + 'ldapQuotaDefault' => null, + 'ldapEmailAttribute' => null, + ); + + public function __construct($configID = 'user_ldap') { + $this->configID = $configID; + } + + public function __destruct() { + @ldap_unbind($this->ldapConnectionRes); + } + + public function __get($name) { + if(!$this->configured) { + $this->readConfiguration(); + } + + if(isset($this->config[$name])) { + return $this->config[$name]; + } + } + + /** + * @brief initializes the LDAP backend + * @param $force read the config settings no matter what + * + * initializes the LDAP backend + */ + public function init($force = false) { + $this->readConfiguration($force); + $this->establishConnection(); + } + + /** + * Returns the LDAP handler + */ + public function getConnectionResource() { + if(!$this->ldapConnectionRes) { + $this->init(); + } + if(is_null($this->ldapConnectionRes)) { + \OCP\Util::writeLog('user_ldap', 'Connection could not be established', \OCP\Util::ERROR); + } + return $this->ldapConnectionRes; + } + + /** + * Caches the general LDAP configuration. + */ + private function readConfiguration($force = false) { + \OCP\Util::writeLog('user_ldap','Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG); + if((!$this->configured || $force) && !is_null($this->configID)) { + \OCP\Util::writeLog('user_ldap','Reading the configuration', \OCP\Util::DEBUG); + $this->config['ldapHost'] = \OCP\Config::getAppValue($this->configID, 'ldap_host', ''); + $this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389); + $this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn',''); + $this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password','')); + $this->config['ldapBase'] = \OCP\Config::getAppValue($this->configID, 'ldap_base', ''); + $this->config['ldapBaseUsers'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_users',$this->config['ldapBase']); + $this->config['ldapBaseGroups'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']); + $this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls',0); + $this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0); + $this->config['ldapUserDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8'); + $this->config['ldapUserFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter','objectClass=person'); + $this->config['ldapGroupFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter','(objectClass=posixGroup)'); + $this->config['ldapLoginFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)'); + $this->config['ldapGroupDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8'); + $this->config['ldapQuotaAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', ''); + $this->config['ldapQuotaDefault'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', ''); + $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', ''); + $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember'); + $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); + + $this->configured = $this->validateConfiguration(); + } + } + + /** + * @brief set LDAP configuration with values delivered by an array, not read from configuration + * @param $config array that holds the config parameters in an associated array + * @param &$setParameters optional; array where the set fields will be given to + * @return true if config validates, false otherwise. Check with $setParameters for detailed success on single parameters + */ + public function setConfiguration($config, &$setParameters = null) { + if(!is_array($config)) { + return false; + } + + foreach($config as $parameter => $value) { + if(isset($this->config[$parameter])) { + $this->config[$parameter] = $value; + if(is_array($setParameters)) { + $setParameters[] = $parameter; + } + } + } + + $this->configured = $this->validateConfiguration(); + + return $this->configured; + } + + /** + * @brief Validates the user specified configuration + * @returns true if configuration seems OK, false otherwise + */ + private function validateConfiguration() { + //first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message + if(empty($this->config['ldapBaseUsers'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseUsers'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapBaseGroups'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Groups is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseGroups'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) { + \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); + } + + //second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning. + $configurationOK = true; + if(empty($this->config['ldapHost'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP host given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapPort'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Port given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword'])) + || (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) { + \OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + //TODO: check if ldapAgentName is in DN form + if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) { + \OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapUserDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapGroupDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapLoginFilter'])) { + \OCP\Util::writeLog('user_ldap', 'No login filter specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) { + \OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG); + $configurationOK = false; + } + + return $configurationOK; + } + + /** + * Connects and Binds to LDAP + */ + private function establishConnection() { + if(!$this->configured) { + \OCP\Util::writeLog('user_ldap', 'Configuration is invalid, cannot connect', \OCP\Util::WARN); + return false; + } + if(!$this->ldapConnectionRes) { + $this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']); + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { + if($this->config['ldapTLS']) { + ldap_start_tls($this->ldapConnectionRes); + } + } + } + + return $this->bind(); + } + } + + /** + * Binds to LDAP + */ + public function bind() { + $ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']); + if(!$ldapLogin) { + \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR); + $this->ldapConnectionRes = null; + return false; + } + return true; + } + +}
\ No newline at end of file diff --git a/apps/user_ldap/lib_ldap.php b/apps/user_ldap/lib_ldap.php deleted file mode 100644 index 08b09304d78..00000000000 --- a/apps/user_ldap/lib_ldap.php +++ /dev/null @@ -1,721 +0,0 @@ -<?php - -/** - * ownCloud – LDAP lib - * - * @author Arthur Schiwon - * @copyright 2012 Arthur Schiwon blizzz@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -define('LDAP_GROUP_MEMBER_ASSOC_ATTR','uniqueMember'); -define('LDAP_GROUP_DISPLAY_NAME_ATTR','cn'); - -//needed to unbind, because we use OC_LDAP only statically -class OC_LDAP_DESTRUCTOR { - public function __destruct() { - OC_LDAP::destruct(); - } -} - -class OC_LDAP { - static protected $ldapConnectionRes = false; - static protected $configured = false; - - //cached settings - static protected $ldapHost; - static protected $ldapPort; - static protected $ldapBase; - static protected $ldapBaseUsers; - static protected $ldapBaseGroups; - static protected $ldapAgentName; - static protected $ldapAgentPassword; - static protected $ldapTLS; - static protected $ldapNoCase; - static protected $ldapIgnoreNamingRules; - // user and group settings, that are needed in both backends - static protected $ldapUserDisplayName; - static protected $ldapUserFilter; - static protected $ldapGroupDisplayName; - static protected $ldapLoginFilter; - - static protected $__d; - - /** - * @brief initializes the LDAP backend - * @param $force read the config settings no matter what - * - * initializes the LDAP backend - */ - static public function init($force = false) { - if(is_null(self::$__d)) { - self::$__d = new OC_LDAP_DESTRUCTOR(); - } - self::readConfiguration($force); - self::establishConnection(); - } - - static public function destruct() { - @ldap_unbind(self::$ldapConnectionRes); - } - - /** - * @brief returns a read-only configuration value - * @param $key the name of the configuration value - * @returns the value on success, otherwise null - * - * returns a read-only configuration values - * - * we cannot work with getters, because it is a static class - */ - static public function conf($key) { - if(!self::$configured) { - self::init(); - } - - $availableProperties = array( - 'ldapUserDisplayName', - 'ldapGroupDisplayName', - 'ldapLoginFilter' - ); - - if(in_array($key, $availableProperties)) { - return self::$$key; - } - - return null; - } - - /** - * gives back the database table for the query - */ - static private function getMapTable($isUser) { - if($isUser) { - return '*PREFIX*ldap_user_mapping'; - } else { - return '*PREFIX*ldap_group_mapping'; - } - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the group - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the group - */ - static public function groupname2dn($name) { - return self::ocname2dn($name, false); - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the user - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the user - */ - static public function username2dn($name) { - $dn = self::ocname2dn($name, true); - if($dn) { - return $dn; - } else { - //fallback: user is not mapped - self::init(); - $filter = self::combineFilterWithAnd(array( - self::$ldapUserFilter, - self::$ldapUserDisplayName . '=' . $name, - )); - $result = self::searchUsers($filter, 'dn'); - if(isset($result[0]['dn'])) { - self::mapUser($result[0], $name); - return $result[0]; - } - } - - return false; - } - - static private function ocname2dn($name, $isUser) { - $table = self::getMapTable($isUser); - - $query = OCP\DB::prepare(' - SELECT ldap_dn - FROM '.$table.' - WHERE owncloud_name = ? - '); - - $record = $query->execute(array($name))->fetchOne(); - return $record; - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the group - * @param $dn the dn of the group object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud, false on DN outside of search DN - * - * returns the internal ownCloud name for the given LDAP DN of the group - */ - static public function dn2groupname($dn, $ldapname = null) { - if(mb_strripos($dn, self::$ldapBaseGroups, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen(self::$ldapBaseGroups, 'UTF-8'))) { - return false; - } - return self::dn2ocname($dn, $ldapname, false); - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the user - * @param $dn the dn of the user object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud - * - * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN - */ - static public function dn2username($dn, $ldapname = null) { - if(mb_strripos($dn, self::$ldapBaseUsers, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen(self::$ldapBaseUsers, 'UTF-8'))) { - return false; - } - return self::dn2ocname($dn, $ldapname, true); - } - - static public function dn2ocname($dn, $ldapname = null, $isUser = true) { - $dn = self::sanitizeDN($dn); - $table = self::getMapTable($isUser); - if($isUser) { - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - - $query = OCP\DB::prepare(' - SELECT owncloud_name - FROM '.$table.' - WHERE ldap_dn = ? - '); - - $component = $query->execute(array($dn))->fetchOne(); - if($component) { - return $component; - } - - if(is_null($ldapname)) { - $ldapname = self::readAttribute($dn, $nameAttribute); - $ldapname = $ldapname[0]; - } - $ldapname = self::sanitizeUsername($ldapname); - - //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. - if(self::mapComponent($dn, $ldapname, $isUser)) { - return $ldapname; - } - - //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. - $oc_name = self::alternateOwnCloudName($ldapname, $dn); - if(self::mapComponent($dn, $oc_name, $isUser)) { - return $oc_name; - } - - //and this of course should never been thrown :) - throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); - } - - /** - * @brief gives back the user names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) - * @returns an array with the user names to use in ownCloud - * - * gives back the user names as they are used ownClod internally - */ - static public function ownCloudUserNames($ldapUsers) { - return self::ldap2ownCloudNames($ldapUsers, true); - } - - /** - * @brief gives back the group names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) - * @returns an array with the group names to use in ownCloud - * - * gives back the group names as they are used ownClod internally - */ - static public function ownCloudGroupNames($ldapGroups) { - return self::ldap2ownCloudNames($ldapGroups, false); - } - - static private function ldap2ownCloudNames($ldapObjects, $isUsers) { - if($isUsers) { - $knownObjects = self::mappedUsers(); - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $knownObjects = self::mappedGroups(); - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - $ownCloudNames = array(); - - foreach($ldapObjects as $ldapObject) { - $key = self::recursiveArraySearch($knownObjects, $ldapObject['dn']); - - //everything is fine when we know the group - if($key !== false) { - $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; - continue; - } - - //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. - $ocname = self::sanitizeUsername($ldapObject[$nameAttribute]); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. - $ocname = self::alternateOwnCloudName($ocname, $ldapObject['dn']); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //and this of course should never been thrown :) - throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); - } - return $ownCloudNames; - } - - /** - * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - * @param $name the display name of the object - * @param $dn the dn of the object - * @returns string with with the name to use in ownCloud - * - * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - */ - static private function alternateOwnCloudName($name, $dn) { - $ufn = ldap_dn2ufn($dn); - $name = $name . '@' . trim(OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); - $name = self::sanitizeUsername($name); - return $name; - } - - /** - * @brief retrieves all known groups from the mappings table - * @returns array with the results - * - * retrieves all known groups from the mappings table - */ - static private function mappedGroups() { - return self::mappedComponents(false); - } - - /** - * @brief retrieves all known users from the mappings table - * @returns array with the results - * - * retrieves all known users from the mappings table - */ - static private function mappedUsers() { - return self::mappedComponents(true); - } - - static private function mappedComponents($isUsers) { - $table = self::getMapTable($isUsers); - - $query = OCP\DB::prepare(' - SELECT ldap_dn, owncloud_name - FROM '. $table - ); - - return $query->execute()->fetchAll(); - } - - /** - * @brief inserts a new user or group into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @param $isUser is it a user or a group? - * @returns true on success, false otherwise - * - * inserts a new user or group into the mappings table - */ - static private function mapComponent($dn, $ocname, $isUser = true) { - $table = self::getMapTable($isUser); - $dn = self::sanitizeDN($dn); - - $sqlAdjustment = ''; - $dbtype = OCP\Config::getSystemValue('dbtype'); - if($dbtype == 'mysql') { - $sqlAdjustment = 'FROM dual'; - } - - $insert = OCP\DB::prepare(' - INSERT INTO '.$table.' (ldap_dn, owncloud_name) - SELECT ?,? - '.$sqlAdjustment.' - WHERE NOT EXISTS ( - SELECT 1 - FROM '.$table.' - WHERE ldap_dn = ? - OR owncloud_name = ? ) - '); - - $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); - - if(OCP\DB::isError($res)) { - return false; - } - - $insRows = $res->numRows(); - - if($insRows == 0) { - return false; - } - - return true; - } - - static public function fetchListOfUsers($filter, $attr) { - return self::fetchList(OC_LDAP::searchUsers($filter, $attr), (count($attr) > 1)); - } - - static public function fetchListOfGroups($filter, $attr) { - return self::fetchList(OC_LDAP::searchGroups($filter, $attr), (count($attr) > 1)); - } - - static private function fetchList($list, $manyAttributes) { - if(is_array($list)) { - if($manyAttributes) { - return $list; - } else { - return array_unique($list, SORT_LOCALE_STRING); - } - } - - //error cause actually, maybe throw an exception in future. - return array(); - } - - /** - * @brief reads a given attribute for an LDAP record identified by a DN - * @param $dn the record in question - * @param $attr the attribute that shall be retrieved - * @returns the values in an array on success, false otherwise - * - * Reads an attribute from an LDAP entry - */ - static public function readAttribute($dn, $attr) { - $cr = self::getConnectionResource(); - $rr = ldap_read($cr, $dn, 'objectClass=*', array($attr)); - $er = ldap_first_entry($cr, $rr); - //LDAP attributes are not case sensitive - $result = OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); - $attr = mb_strtolower($attr, 'UTF-8'); - - if(isset($result[$attr]) && $result[$attr]['count'] > 0){ - $values = array(); - for($i=0;$i<$result[$attr]['count'];$i++) { - $values[] = self::resemblesDN($attr) ? self::sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; - } - return $values; - } - return false; - } - - /** - * @brief executes an LDAP search, optimized for Users - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchUsers($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseUsers, $attr); - } - - /** - * @brief executes an LDAP search, optimized for Groups - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchGroups($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseGroups, $attr); - } - - /** - * @brief executes an LDAP search - * @param $filter the LDAP filter for the search - * @param $base the LDAP subtree that shall be searched - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static private function search($filter, $base, $attr = null) { - if(!is_null($attr) && !is_array($attr)) { - $attr = array(mb_strtolower($attr, 'UTF-8')); - } - - // See if we have a resource - $link_resource = self::getConnectionResource(); - if(is_resource($link_resource)) { - $sr = ldap_search($link_resource, $base, $filter, $attr); - $findings = ldap_get_entries($link_resource, $sr ); - - // if we're here, probably no connection resource is returned. - // to make ownCloud behave nicely, we simply give back an empty array. - if(is_null($findings)) { - return array(); - } - } else { - // Seems like we didn't find any resource. - // Return an empty array just like before. - return array(); - } - - if(!is_null($attr)) { - $selection = array(); - $multiarray = false; - if(count($attr) > 1) { - $multiarray = true; - $i = 0; - } - foreach($findings as $item) { - if(!is_array($item)) { - continue; - } - $item = OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8'); - - if($multiarray) { - foreach($attr as $key) { - $key = mb_strtolower($key, 'UTF-8'); - if(isset($item[$key])) { - if($key != 'dn'){ - $selection[$i][$key] = self::resemblesDN($key) ? self::sanitizeDN($item[$key][0]) : $item[$key][0]; - } else { - $selection[$i][$key] = self::sanitizeDN($item[$key]); - } - } - - } - $i++; - } else { - //tribute to case insensitivity - $key = mb_strtolower($attr[0], 'UTF-8'); - - if(isset($item[$key])) { - if(self::resemblesDN($key)) { - $selection[] = self::sanitizeDN($item[$key]); - } else { - $selection[] = $item[$key]; - } - } - } - - } - return $selection; - } - - return $findings; - } - - static private function resemblesDN($attr) { - $resemblingAttributes = array( - 'dn', - 'uniquemember', - 'member' - ); - return in_array($attr, $resemblingAttributes); - } - - static private function sanitizeDN($dn) { - //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! - $dn = preg_replace('/([^\\\]),(\s+)/u','\1,',$dn); - - //make comparisons and everything work - $dn = mb_strtolower($dn, 'UTF-8'); - - return $dn; - } - - static private function sanitizeUsername($name) { - if(self::$ldapIgnoreNamingRules) { - return $name; - } - - //REPLACEMENTS - $name = OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8'); - - //every remaining unallowed characters will be removed - $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name); - - return $name; - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithAnd($filters) { - return self::combineFilter($filters,'&'); - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithOr($filters) { - return self::combineFilter($filters,'|'); - } - - /** - * @brief combines the input filters with given operator - * @param $filters array, the filters to connect - * @param $operator either & or | - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static private function combineFilter($filters, $operator) { - $combinedFilter = '('.$operator; - foreach($filters as $filter) { - if($filter[0] != '(') { - $filter = '('.$filter.')'; - } - $combinedFilter.=$filter; - } - $combinedFilter.=')'; - return $combinedFilter; - } - - /** - * Returns the LDAP handler - */ - static private function getConnectionResource() { - if(!self::$ldapConnectionRes) { - self::init(); - } - if(is_null(self::$ldapConnectionRes)) { - OCP\Util::writeLog('ldap', 'Connection could not be established', OCP\Util::INFO); - } - return self::$ldapConnectionRes; - } - - /** - * Caches the general LDAP configuration. - */ - static private function readConfiguration($force = false) { - if(!self::$configured || $force) { - self::$ldapHost = OCP\Config::getAppValue('user_ldap', 'ldap_host', ''); - self::$ldapPort = OCP\Config::getAppValue('user_ldap', 'ldap_port', 389); - self::$ldapAgentName = OCP\Config::getAppValue('user_ldap', 'ldap_dn',''); - self::$ldapAgentPassword = base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password','')); - self::$ldapBase = OCP\Config::getAppValue('user_ldap', 'ldap_base', ''); - self::$ldapBaseUsers = OCP\Config::getAppValue('user_ldap', 'ldap_base_users',self::$ldapBase); - self::$ldapBaseGroups = OCP\Config::getAppValue('user_ldap', 'ldap_base_groups', self::$ldapBase); - self::$ldapTLS = OCP\Config::getAppValue('user_ldap', 'ldap_tls',0); - self::$ldapNoCase = OCP\Config::getAppValue('user_ldap', 'ldap_nocase', 0); - self::$ldapUserDisplayName = mb_strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid'), 'UTF-8'); - self::$ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter','objectClass=person'); - self::$ldapLoginFilter = OCP\Config::getAppValue('user_ldap', 'ldap_login_filter', '(uid=%uid)'); - self::$ldapGroupDisplayName = mb_strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', LDAP_GROUP_DISPLAY_NAME_ATTR), 'UTF-8'); - self::$ldapIgnoreNamingRules = OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); - - if(empty(self::$ldapBaseUsers)) { - OCP\Util::writeLog('ldap', 'Base for Users is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseUsers = self::$ldapBase; - } - if(empty(self::$ldapBaseGroups)) { - OCP\Util::writeLog('ldap', 'Base for Groups is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseGroups = self::$ldapBase; - } - - if( - !empty(self::$ldapHost) - && !empty(self::$ldapPort) - && ( - (!empty(self::$ldapAgentName) && !empty(self::$ldapAgentPassword)) - || ( empty(self::$ldapAgentName) && empty(self::$ldapAgentPassword)) - ) - && !empty(self::$ldapBase) - && !empty(self::$ldapUserDisplayName) - ) - { - self::$configured = true; - } - } - } - - /** - * Connects and Binds to LDAP - */ - static private function establishConnection() { - if(!self::$configured) { - OCP\Util::writeLog('ldap', 'Configuration is invalid, cannot connect', OCP\Util::INFO); - return false; - } - if(!self::$ldapConnectionRes) { - self::$ldapConnectionRes = ldap_connect(self::$ldapHost, self::$ldapPort); - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { - if(self::$ldapTLS) { - ldap_start_tls(self::$ldapConnectionRes); - } - } - } - - $ldapLogin = @ldap_bind(self::$ldapConnectionRes, self::$ldapAgentName, self::$ldapAgentPassword ); - if(!$ldapLogin) { - OCP\Util::writeLog('ldap', 'Bind failed: ' . ldap_errno(self::$ldapConnectionRes) . ': ' . ldap_error(self::$ldapConnectionRes), OCP\Util::ERROR); - self::$ldapConnectionRes = null; - return false; - } - } - } - - static public function areCredentialsValid($name, $password) { - return @ldap_bind(self::getConnectionResource(), $name, $password); - } - - /** - * taken from http://www.php.net/manual/en/function.array-search.php#97645 - * TODO: move somewhere, where its better placed since it is not LDAP specific. OC_Helper maybe? - */ - static public function recursiveArraySearch($haystack, $needle, $index = null) { - $aIt = new RecursiveArrayIterator($haystack); - $it = new RecursiveIteratorIterator($aIt); - - while($it->valid()) { - if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { - return $aIt->key(); - } - - $it->next(); - } - - return false; - } - - } diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php index 2be6b46fb23..106459580fa 100644 --- a/apps/user_ldap/tests/group_ldap.php +++ b/apps/user_ldap/tests/group_ldap.php @@ -26,8 +26,8 @@ class Test_Group_Ldap extends UnitTestCase { } function testSingleBackend(){ - OC_Group::useBackend(new OC_GROUP_LDAP()); - $group_ldap = new OC_GROUP_LDAP(); + OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP()); + $group_ldap = new OCA\user_ldap\GROUP_LDAP(); $this->assertIsA(OC_Group::getGroups(),gettype(array())); $this->assertIsA($group_ldap->getGroups(),gettype(array())); diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index b51d9a55cc7..a4a8921d08d 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -23,13 +23,9 @@ * */ -class OC_USER_LDAP extends OC_User_Backend { +namespace OCA\user_ldap; - // cached settings - protected $ldapUserFilter; - protected $ldapQuotaAttribute; - protected $ldapQuotaDefault; - protected $ldapEmailAttribute; +class USER_LDAP extends lib\Access implements \OCP\UserInterface { // will be retrieved from LDAP server protected $ldap_dc = false; @@ -37,39 +33,32 @@ class OC_USER_LDAP extends OC_User_Backend { // cache getUsers() protected $_users = null; - public function __construct() { - $this->ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter', '(objectClass=posixAccount)'); - $this->ldapQuotaAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_quota_attr', ''); - $this->ldapQuotaDefault = OCP\Config::getAppValue('user_ldap', 'ldap_quota_def', ''); - $this->ldapEmailAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_email_attr', ''); - } - private function updateQuota($dn) { $quota = null; - if(!empty($this->ldapQuotaDefault)) { - $quota = $this->ldapQuotaDefault; + if(!empty($this->connection->ldapQuotaDefault)) { + $quota = $this->connection->ldapQuotaDefault; } - if(!empty($this->ldapQuotaAttribute)) { - $aQuota = OC_LDAP::readAttribute($dn, $this->ldapQuotaAttribute); + if(!empty($this->connection->ldapQuotaAttribute)) { + $aQuota = $this->readAttribute($dn, $this->connection->ldapQuotaAttribute); if($aQuota && (count($aQuota) > 0)) { $quota = $aQuota[0]; } } if(!is_null($quota)) { - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'files', 'quota', OCP\Util::computerFileSize($quota)); + \OCP\Config::setUserValue($this->dn2username($dn), 'files', 'quota', \OCP\Util::computerFileSize($quota)); } } private function updateEmail($dn) { $email = null; - if(!empty($this->ldapEmailAttribute)) { - $aEmail = OC_LDAP::readAttribute($dn, $this->ldapEmailAttribute); + if(!empty($this->connection->ldapEmailAttribute)) { + $aEmail = $this->readAttribute($dn, $this->connection->ldapEmailAttribute); if($aEmail && (count($aEmail) > 0)) { $email = $aEmail[0]; } - if(!is_null($email)){ - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'settings', 'email', $email); + if(!is_null($email)) { + \OCP\Config::setUserValue($this->dn2username($dn), 'settings', 'email', $email); } } } @@ -84,15 +73,15 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function checkPassword($uid, $password){ //find out dn of the user name - $filter = OCP\Util::mb_str_replace('%uid', $uid, OC_LDAP::conf('ldapLoginFilter'), 'UTF-8'); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $uid, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { return false; } $dn = $ldap_users[0]; //are the credentials OK? - if(!OC_LDAP::areCredentialsValid($dn, $password)) { + if(!$this->areCredentialsValid($dn, $password)) { return false; } @@ -101,7 +90,7 @@ class OC_USER_LDAP extends OC_User_Backend { $this->updateEmail($dn); //give back the display name - return OC_LDAP::dn2username($dn); + return $this->dn2username($dn); } /** @@ -112,8 +101,8 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function getUsers(){ if(is_null($this->_users)) { - $ldap_users = OC_LDAP::fetchListOfUsers($this->ldapUserFilter, array(OC_LDAP::conf('ldapUserDisplayName'), 'dn')); - $this->_users = OC_LDAP::ownCloudUserNames($ldap_users); + $ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn')); + $this->_users = $this->ownCloudUserNames($ldap_users); } return $this->_users; } @@ -125,13 +114,13 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function userExists($uid){ //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking. - $dn = OC_LDAP::username2dn($uid); + $dn = $this->username2dn($uid); if(!$dn) { return false; } //if user really still exists, we will be able to read his cn - $cn = OC_LDAP::readAttribute($dn, 'cn'); + $cn = $this->readAttribute($dn, 'cn'); if(!$cn || empty($cn)) { return false; } @@ -139,4 +128,27 @@ class OC_USER_LDAP extends OC_User_Backend { return true; } + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid) { + return false; + } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + return (bool)(OC_USER_BACKEND_CHECK_PASSWORD & $actions); + } + }
\ No newline at end of file diff --git a/apps/user_openid/appinfo/app.php b/apps/user_openid/appinfo/app.php index a5fb6a0f45e..fe57b189fac 100644 --- a/apps/user_openid/appinfo/app.php +++ b/apps/user_openid/appinfo/app.php @@ -19,7 +19,7 @@ OCP\Util::addHeader('link',array('rel'=>'openid.delegate', 'href'=>OCP\Util::lin OCP\App::registerPersonal('user_openid','settings'); -require_once 'openid/user_openid.php'; +require_once 'apps/user_openid/user_openid.php'; //active the openid backend OC_User::useBackend('openid'); diff --git a/lib/base.php b/lib/base.php index fcca1e77d26..5041f43648e 100644 --- a/lib/base.php +++ b/lib/base.php @@ -83,6 +83,9 @@ class OC{ elseif(strpos($className,'OCP\\')===0){ require_once 'public/'.strtolower(str_replace('\\','/',substr($className,3)) . '.php'); } + elseif(strpos($className,'OCA\\')===0){ + require_once 'apps/'.strtolower(str_replace('\\','/',substr($className,3)) . '.php'); + } elseif(strpos($className,'Sabre_')===0) { require_once str_replace('_','/',$className) . '.php'; } diff --git a/lib/group.php b/lib/group.php index ceee5fa4edb..12e5f5ebb30 100644 --- a/lib/group.php +++ b/lib/group.php @@ -43,7 +43,7 @@ class OC_Group { * @returns true/false */ public static function useBackend( $backend ){ - if($backend instanceof OC_Group_Backend){ + if($backend instanceof OC_Group_Interface){ self::$_usedBackends[]=$backend; } } @@ -168,7 +168,7 @@ class OC_Group { if($run){ $succes=false; - + //add the user to the all backends that have the group foreach(self::$_usedBackends as $backend){ if(!$backend->implementsActions(OC_GROUP_BACKEND_ADD_TO_GROUP)) @@ -245,7 +245,7 @@ class OC_Group { asort($groups); return $groups; } - + /** * check if a group exists * @param string $gid @@ -259,7 +259,7 @@ class OC_Group { } return false; } - + /** * @brief get a list of all users in a group * @returns array with user ids diff --git a/lib/group/backend.php b/lib/group/backend.php index 24778afd1e5..ebc078f152a 100644 --- a/lib/group/backend.php +++ b/lib/group/backend.php @@ -37,7 +37,7 @@ define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00001000); /** * Abstract base class for user management */ -abstract class OC_Group_Backend { +abstract class OC_Group_Backend implements OC_Group_Interface { protected $possibleActions = array( OC_GROUP_BACKEND_CREATE_GROUP => 'createGroup', OC_GROUP_BACKEND_DELETE_GROUP => 'deleteGroup', diff --git a/lib/group/interface.php b/lib/group/interface.php new file mode 100644 index 00000000000..7cca6061e10 --- /dev/null +++ b/lib/group/interface.php @@ -0,0 +1,76 @@ +<?php + +/** + * ownCloud - group interface + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +interface OC_Group_Interface { + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_GROUP_BACKEND_CREATE_GROUP etc. + */ + public function implementsActions($actions); + + /** + * @brief is user in group? + * @param $uid uid of the user + * @param $gid gid of the group + * @returns true/false + * + * Checks whether the user is member of a group or not. + */ + public function inGroup($uid, $gid); + + /** + * @brief Get all groups a user belongs to + * @param $uid Name of the user + * @returns array with group names + * + * This function fetches all groups a user belongs to. It does not check + * if the user exists at all. + */ + public function getUserGroups($uid); + + /** + * @brief get a list of all groups + * @returns array with group names + * + * Returns a list with all groups + */ + public function getGroups(); + + /** + * check if a group exists + * @param string $gid + * @return bool + */ + public function groupExists($gid); + + /** + * @brief get a list of all users in a group + * @returns array with user ids + */ + public function usersInGroup($gid); + +}
\ No newline at end of file diff --git a/lib/helper.php b/lib/helper.php index f328c14ac77..666bc6badfc 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -360,7 +360,7 @@ class OC_Helper { }else{ $mimeType='application/octet-stream'; } - + if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ $info = @strtolower(finfo_file($finfo,$path)); if($info){ @@ -679,4 +679,30 @@ class OC_Helper { } return $subject; } + + /** + * @brief performs a search in a nested array + * @param haystack the array to be searched + * @param needle the search string + * @param $index optional, only search this key name + * @return the key of the matching field, otherwise false + * + * performs a search in a nested array + * + * taken from http://www.php.net/manual/en/function.array-search.php#97645 + */ + public static function recursiveArraySearch($haystack, $needle, $index = null) { + $aIt = new RecursiveArrayIterator($haystack); + $it = new RecursiveIteratorIterator($aIt); + + while($it->valid()) { + if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { + return $aIt->key(); + } + + $it->next(); + } + + return false; + } } diff --git a/lib/ocs.php b/lib/ocs.php index 18f01518bdb..570f5ac3e59 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -176,6 +176,10 @@ class OC_OCS { ->requirements(array('format'=>'xml|json')); // CLOUD + // systemWebApps + }elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-4]=='cloud') and ($ex[$paracount-3] == 'system') and ($ex[$paracount-2] == 'webapps')){ + OC_OCS::systemwebapps($format); + // quotaget $router->create('quota_get', '/cloud/user/{user}.{format}') @@ -199,6 +203,16 @@ class OC_OCS { }) ->requirements(array('format'=>'xml|json')); + // keygetpublic + }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'publickey')){ + $user=$ex[$paracount-3]; + OC_OCS::publicKeyGet($format,$user); + + // keygetprivate + }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'privatekey')){ + $user=$ex[$paracount-3]; + OC_OCS::privateKeyGet($format,$user); + // add more calls here // please document all the call in the draft spec @@ -581,6 +595,29 @@ class OC_OCS { // CLOUD API ############################################# /** + * get a list of installed web apps + * @param string $format + * @return string xml/json + */ + private static function systemWebApps($format) { + $login=OC_OCS::checkpassword(); + $apps=OC_App::getEnabledApps(); + $values=array(); + foreach($apps as $app) { + $info=OC_App::getAppInfo($app); + if(isset($info['standalone'])) { + $newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>''); + $values[]=$newvalue; + } + + } + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $values, 'cloud', '', 2, 0, 0); + echo($txt); + + } + + + /** * get the quota of a user * @param string $format * @param string $user @@ -608,10 +645,10 @@ class OC_OCS { $xml['used']=$used; $xml['relative']=$relative; - $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', 'full', 1, count($xml), 0); + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', '', 1, 0, 0); echo($txt); }else{ - echo self::generateXml('', 'fail', 300, 'User does not exist'); + echo self::generateXml('', 'fail', 300, 'User does not exist'); } }else{ echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); @@ -631,16 +668,56 @@ class OC_OCS { // todo // not yet implemented - // edit logic here + // add logic here error_log('OCS call: user:'.$user.' quota:'.$quota); $xml=array(); - $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', 'full', 1, count($xml), 0); + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', '', 1, 0, 0); echo($txt); }else{ echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); } } + /** + * get the public key of a user + * @param string $format + * @param string $user + * @return string xml/json + */ + private static function publicKeyGet($format,$user) { + $login=OC_OCS::checkpassword(); + + if(OC_User::userExists($user)){ + // calculate the disc space + $txt='this is the public key of '.$user; + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + } + + /** + * get the private key of a user + * @param string $format + * @param string $user + * @return string xml/json + */ + private static function privateKeyGet($format,$user) { + $login=OC_OCS::checkpassword(); + if(OC_Group::inGroup($login, 'admin') or ($login==$user)) { + + if(OC_User::userExists($user)){ + // calculate the disc space + $txt='this is the private key of '.$user; + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + }else{ + echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); + } + } + } diff --git a/lib/public/groupinterface.php b/lib/public/groupinterface.php new file mode 100644 index 00000000000..97833028118 --- /dev/null +++ b/lib/public/groupinterface.php @@ -0,0 +1,31 @@ +<?php +/** +* ownCloud +* +* @author Arthur Schiwon +* @copyright 2012 Arthur Schiwon blizzz@owncloud.org +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * Public interface of ownCloud for apps to use. + * Group Class. + * + */ + +namespace OCP; + +interface GroupInterface extends \OC_Group_Interface {}
\ No newline at end of file diff --git a/lib/public/userinterface.php b/lib/public/userinterface.php new file mode 100644 index 00000000000..b73a8f8d8b0 --- /dev/null +++ b/lib/public/userinterface.php @@ -0,0 +1,31 @@ +<?php +/** +* ownCloud +* +* @author Arthur Schiwon +* @copyright 2012 Arthur Schiwon blizzz@owncloud.org +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * Public interface of ownCloud for apps to use. + * User Class. + * + */ + +namespace OCP; + +interface UserInterface extends \OC_User_Interface {}
\ No newline at end of file diff --git a/lib/public/util.php b/lib/public/util.php index 43f9e3cee5d..75ca29f7129 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -320,4 +320,15 @@ class Util { public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { return(\OC_Helper::mb_str_replace($search, $replace, $subject, $encoding, $count)); } + + /** + * @brief performs a search in a nested array + * @param haystack the array to be searched + * @param needle the search string + * @param $index optional, only search this key name + * @return the key of the matching field, otherwise false + */ + public static function recursiveArraySearch($haystack, $needle, $index = null) { + return(\OC_Helper::recursiveArraySearch($haystack, $needle, $index)); + } } diff --git a/lib/user.php b/lib/user.php index 549319b3a77..e3c9c23effa 100644 --- a/lib/user.php +++ b/lib/user.php @@ -21,7 +21,7 @@ */ /** - * This class provides wrapper methods for user management. Multiple backends are + * This class provides wrapper methods for user management. Multiple backends are * supported. User management operations are delegated to the configured backend for * execution. * @@ -83,7 +83,7 @@ class OC_User { * Set the User Authentication Module */ public static function useBackend( $backend = 'database' ){ - if($backend instanceof OC_User_Backend){ + if($backend instanceof OC_User_Interface){ self::$_usedBackends[get_class($backend)]=$backend; }else{ // You'll never know what happens diff --git a/lib/user/backend.php b/lib/user/backend.php index be068a63ce0..daa942d261c 100644 --- a/lib/user/backend.php +++ b/lib/user/backend.php @@ -42,7 +42,7 @@ define('OC_USER_BACKEND_CHECK_PASSWORD', 0x000100); * * Subclass this for your own backends, and see OC_User_Example for descriptions */ -abstract class OC_User_Backend { +abstract class OC_User_Backend implements OC_User_Interface { protected $possibleActions = array( OC_USER_BACKEND_CREATE_USER => 'createUser', diff --git a/lib/user/interface.php b/lib/user/interface.php new file mode 100644 index 00000000000..dc3685dc20d --- /dev/null +++ b/lib/user/interface.php @@ -0,0 +1,60 @@ +<?php + +/** + * ownCloud - user interface + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +interface OC_User_Interface { + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions); + + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid); + + /** + * @brief Get a list of all users + * @returns array with all uids + * + * Get a list of all users. + */ + public function getUsers(); + + /** + * @brief check if a user exists + * @param string $uid the username + * @return boolean + */ + public function userExists($uid); + +}
\ No newline at end of file diff --git a/webapps.php b/webapps.php deleted file mode 100644 index 82e677a51c7..00000000000 --- a/webapps.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** -* ownCloud status page. usefull if you want to check from the outside if an owncloud installation exists -* -* @author Frank Karlitschek -* @copyright 2012 Frank Karlitschek frank@owncloud.org -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE -* License as published by the Free Software Foundation; either -* version 3 of the License, or any later version. -* -* This library 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 library. If not, see <http://www.gnu.org/licenses/>. -* -*/ - -$RUNTIME_NOAPPS = TRUE; //no apps, yet - -require_once('lib/base.php'); - - -//valid user account -if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser=''; -if(isset($_SERVER['PHP_AUTH_PW'])) $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw=''; - -if(!OC_User::login($authuser,$authpw)){ - header('WWW-Authenticate: Basic realm="your valid user account"'); - header('HTTP/1.0 401 Unauthorized'); - exit; -}else{ - - $apps=OC_App::getEnabledApps(); - $values=array(); - foreach($apps as $app) { - $info=OC_App::getAppInfo($app); - if(isset($info['standalone'])) { - $newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>''); - $values[]=$newvalue; - } - - } - - echo(json_encode($values)); - exit; - - -} |