[Pkg-owncloud-commits] [owncloud] 54/70: support for AD primary groups

David Prévot taffit at moszumanska.debian.org
Mon Jul 14 17:38:08 UTC 2014


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository owncloud.

commit bc1ff4744b74aa3f4dede47c76927d5cff59e9cd
Author: Arthur Schiwon <blizzz at owncloud.com>
Date:   Tue Jul 1 22:02:41 2014 +0200

    support for AD primary groups
    
    support for primary groups
    
    actually the problem is only known on AD, it is only needed to take care of their attributes
    
    adjust to ADs special behaviour
    
    this change was not intended
    
    cache the SID value so it is not requested over and over again
    
    theres only one, use singular
    
    we are access
    
    add tests for new Access methods
    
    add tests for new Group methods
    
    address scrutinizer findings, mostly doc
    
    call ldap_explode_dn from ldap wrapper, enables tests without php5-ldap
    
    PHP Doc
    
    yo dawg, i heard you like backslashes … php doc fix
    
    PHPDoc updated and typos fixed while reviewing
---
 apps/user_ldap/group_ldap.php       | 235 ++++++++++++++++++++++++++++++------
 apps/user_ldap/lib/access.php       |  94 ++++++++++++++-
 apps/user_ldap/lib/connection.php   |   7 ++
 apps/user_ldap/lib/ildapwrapper.php |   9 ++
 apps/user_ldap/lib/ldap.php         |  11 ++
 apps/user_ldap/tests/access.php     |  52 +++++++-
 apps/user_ldap/tests/data/sid.dat   | Bin 0 -> 24 bytes
 apps/user_ldap/tests/group_ldap.php | 152 ++++++++++++++++++++++-
 8 files changed, 516 insertions(+), 44 deletions(-)

diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index 1a35691..0d3a705 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -50,20 +50,29 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		if(!$this->enabled) {
 			return false;
 		}
-		if($this->access->connection->isCached('inGroup'.$uid.':'.$gid)) {
-			return $this->access->connection->getFromCache('inGroup'.$uid.':'.$gid);
+		$cacheKey = 'inGroup'.$uid.':'.$gid;
+		if($this->access->connection->isCached($cacheKey)) {
+			return $this->access->connection->getFromCache($cacheKey);
 		}
-		$dn_user = $this->access->username2dn($uid);
-		$dn_group = $this->access->groupname2dn($gid);
+
+		$userDN = $this->access->username2dn($uid);
+		$groupDN = $this->access->groupname2dn($gid);
 		// just in case
-		if(!$dn_group || !$dn_user) {
-			$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
+		if(!$groupDN || !$userDN) {
+			$this->access->connection->writeToCache($cacheKey, false);
 			return false;
 		}
+
+		//check primary group first
+		if($gid === $this->getUserPrimaryGroup($userDN)) {
+			$this->access->connection->writeToCache($cacheKey, true);
+			return true;
+		}
+
 		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
-		$members = array_keys($this->_groupMembers($dn_group));
+		$members = array_keys($this->_groupMembers($groupDN));
 		if(!$members) {
-			$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
+			$this->access->connection->writeToCache($cacheKey, false);
 			return false;
 		}
 
@@ -82,8 +91,8 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 			$members = $dns;
 		}
 
-		$isInGroup = in_array($dn_user, $members);
-		$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup);
+		$isInGroup = in_array($userDN, $members);
+		$this->access->connection->writeToCache($cacheKey, $isInGroup);
 
 		return $isInGroup;
 	}
@@ -91,6 +100,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 	/**
 	 * @param string $dnGroup
 	 * @param array|null &$seen
+	 * @return array|mixed|null
 	 */
 	private function _groupMembers($dnGroup, &$seen = null) {
 		if ($seen === null) {
@@ -126,6 +136,125 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 	}
 
 	/**
+	 * translates a primary group ID into an ownCloud internal name
+	 * @param string $gid as given by primaryGroupID on AD
+	 * @param string $dn a DN that belongs to the same domain as the group
+	 * @return string|bool
+	 */
+	public function primaryGroupID2Name($gid, $dn) {
+		$cacheKey = 'primaryGroupIDtoName';
+		if($this->access->connection->isCached($cacheKey)) {
+			$groupNames = $this->access->connection->getFromCache($cacheKey);
+			if(isset($groupNames[$gid])) {
+				return $groupNames[$gid];
+			}
+		}
+
+		$domainObjectSid = $this->access->getSID($dn);
+		if($domainObjectSid === false) {
+			return false;
+		}
+
+		//we need to get the DN from LDAP
+		$filter = $this->access->combineFilterWithAnd(array(
+			$this->access->connection->ldapGroupFilter,
+			'objectsid=' . $domainObjectSid . '-' . $gid
+		));
+		$result = $this->access->searchGroups($filter, array('dn'), 1);
+		if(empty($result)) {
+			return false;
+		}
+		$dn = $result[0];
+
+		//and now the group name
+		//NOTE once we have separate ownCloud group IDs and group names we can
+		//directly read the display name attribute instead of the DN
+		$name = $this->access->dn2groupname($dn);
+
+		$this->access->connection->writeToCache($cacheKey, $name);
+
+		return $name;
+	}
+
+	/**
+	 * returns the entry's primary group ID
+	 * @param string $dn
+	 * @param string $attribute
+	 * @return string|bool
+	 */
+	private function getEntryGroupID($dn, $attribute) {
+		$value = $this->access->readAttribute($dn, $attribute);
+		if(is_array($value) && !empty($value)) {
+			return $value[0];
+		}
+		return false;
+	}
+
+	/**
+	 * returns the group's primary ID
+	 * @param string $dn
+	 * @return string|bool
+	 */
+	public function getGroupPrimaryGroupID($dn) {
+		return $this->getEntryGroupID($dn, 'primaryGroupToken');
+	}
+
+	/**
+	 * returns the user's primary group ID
+	 * @param string $dn
+	 * @return string|bool
+	 */
+	public function getUserPrimaryGroupIDs($dn) {
+		return $this->getEntryGroupID($dn, 'primaryGroupID');
+	}
+
+	/**
+	 * returns a list of users that have the given group as primary group
+	 *
+	 * @param string $groupDN
+	 * @param $limit
+	 * @param int $offset
+	 * @return string[]
+	 */
+	public function getUsersInPrimaryGroup($groupDN, $limit = -1, $offset = 0) {
+		$groupID = $this->getGroupPrimaryGroupID($groupDN);
+		if($groupID === false) {
+			return array();
+		}
+
+		$filter = $this->access->combineFilterWithAnd(array(
+			$this->access->connection->ldapUserFilter,
+			'primaryGroupID=' . $groupID
+		));
+
+		$users = $this->access->fetchListOfUsers(
+			$filter,
+			array($this->access->connection->ldapUserDisplayName, 'dn'),
+			$limit,
+			$offset
+		);
+
+		return $users;
+	}
+
+	/**
+	 * gets the primary group of a user
+	 * @param string $dn
+	 * @return string
+	 */
+	public function getUserPrimaryGroup($dn) {
+		$groupID = $this->getUserPrimaryGroupIDs($dn);
+		if($groupID !== false) {
+			$groupName = $this->primaryGroupID2Name($groupID, $dn);
+			if($groupName !== false) {
+				return $groupName;
+			}
+		}
+
+		return false;
+	}
+
+	/**
 	 * Get all groups a user belongs to
 	 * @param string $uid Name of the user
 	 * @return array with group names
@@ -161,7 +290,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		}
 
 		$groups = array_values($this->getGroupsByMember($uid));
-		$groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
+		$groups = $this->access->ownCloudGroupNames($groups);
+
+		$primaryGroup = $this->getUserPrimaryGroup($userDN);
+		if($primaryGroup !== false) {
+			$groups[] = $primaryGroup;
+		}
+
+		$groups = array_unique($groups, SORT_LOCALE_STRING);
 		$this->access->connection->writeToCache($cacheKey, $groups);
 
 		return $groups;
@@ -170,6 +306,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 	/**
 	 * @param string $dn
 	 * @param array|null &$seen
+	 * @return array
 	 */
 	private function getGroupsByMember($dn, &$seen = null) {
 		if ($seen === null) {
@@ -205,6 +342,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 
 	/**
 	 * get a list of all users in a group
+	 *
+	 * @param string $gid
+	 * @param string $search
+	 * @param int $limit
+	 * @param int $offset
 	 * @return array with user ids
 	 */
 	public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
@@ -214,9 +356,9 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		if(!$this->groupExists($gid)) {
 			return array();
 		}
-		$cachekey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
+		$cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
 		// check for cache of the exact query
-		$groupUsers = $this->access->connection->getFromCache($cachekey);
+		$groupUsers = $this->access->connection->getFromCache($cacheKey);
 		if(!is_null($groupUsers)) {
 			return $groupUsers;
 		}
@@ -225,7 +367,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		$groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
 		if(!is_null($groupUsers)) {
 			$groupUsers = array_slice($groupUsers, $offset, $limit);
-			$this->access->connection->writeToCache($cachekey, $groupUsers);
+			$this->access->connection->writeToCache($cacheKey, $groupUsers);
 			return $groupUsers;
 		}
 
@@ -235,14 +377,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		$groupDN = $this->access->groupname2dn($gid);
 		if(!$groupDN) {
 			// group couldn't be found, return empty resultset
-			$this->access->connection->writeToCache($cachekey, array());
+			$this->access->connection->writeToCache($cacheKey, array());
 			return array();
 		}
 
 		$members = array_keys($this->_groupMembers($groupDN));
 		if(!$members) {
-			//in case users could not be retrieved, return empty resultset
-			$this->access->connection->writeToCache($cachekey, array());
+			//in case users could not be retrieved, return empty result set
+			$this->access->connection->writeToCache($cacheKey, array());
 			return array();
 		}
 
@@ -250,7 +392,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		$isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
 		foreach($members as $member) {
 			if($isMemberUid) {
-				//we got uids, need to get their DNs to 'tranlsate' them to usernames
+				//we got uids, need to get their DNs to 'translate' them to user names
 				$filter = $this->access->combineFilterWithAnd(array(
 					\OCP\Util::mb_str_replace('%uid', $member,
 						$this->access->connection->ldapLoginFilter, 'UTF-8'),
@@ -276,10 +418,16 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 				}
 			}
 		}
+
 		natsort($groupUsers);
 		$this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
 		$groupUsers = array_slice($groupUsers, $offset, $limit);
-		$this->access->connection->writeToCache($cachekey, $groupUsers);
+
+		//and get users that have the group as primary
+		$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $limit, $offset);
+		$groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
+
+		$this->access->connection->writeToCache($cacheKey, $groupUsers);
 
 		return $groupUsers;
 	}
@@ -291,32 +439,32 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 	 * @return int|bool
 	 */
 	public function countUsersInGroup($gid, $search = '') {
-		$cachekey = 'countUsersInGroup-'.$gid.'-'.$search;
+		$cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
 		if(!$this->enabled || !$this->groupExists($gid)) {
 			return false;
 		}
-		$groupUsers = $this->access->connection->getFromCache($cachekey);
+		$groupUsers = $this->access->connection->getFromCache($cacheKey);
 		if(!is_null($groupUsers)) {
 			return $groupUsers;
 		}
 
 		$groupDN = $this->access->groupname2dn($gid);
 		if(!$groupDN) {
-			// group couldn't be found, return empty resultset
-			$this->access->connection->writeToCache($cachekey, false);
+			// group couldn't be found, return empty result set
+			$this->access->connection->writeToCache($cacheKey, false);
 			return false;
 		}
 
 		$members = array_keys($this->_groupMembers($groupDN));
 		if(!$members) {
-			//in case users could not be retrieved, return empty resultset
-			$this->access->connection->writeToCache($cachekey, false);
+			//in case users could not be retrieved, return empty result set
+			$this->access->connection->writeToCache($cacheKey, false);
 			return false;
 		}
 
 		if(empty($search)) {
 			$groupUsers = count($members);
-			$this->access->connection->writeToCache($cachekey, $groupUsers);
+			$this->access->connection->writeToCache($cacheKey, $groupUsers);
 			return $groupUsers;
 		}
 		$isMemberUid =
@@ -334,7 +482,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		$groupUsers = array();
 		foreach($members as $member) {
 			if($isMemberUid) {
-				//we got uids, need to get their DNs to 'tranlsate' them to usernames
+				//we got uids, need to get their DNs to 'translate' them to user names
 				$filter = $this->access->combineFilterWithAnd(array(
 					\OCP\Util::mb_str_replace('%uid', $member,
 						$this->access->connection->ldapLoginFilter, 'UTF-8'),
@@ -359,11 +507,19 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 			}
 		}
 
+		//and get users that have the group as primary
+		$primaryUsers = $this->getUsersInPrimaryGroup($groupDN);
+		$groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
+
 		return count($groupUsers);
 	}
 
 	/**
 	 * get a list of all groups
+	 *
+	 * @param string $search
+	 * @param $limit
+	 * @param int $offset
 	 * @return array with group names
 	 *
 	 * Returns a list with all groups (used by getGroups)
@@ -372,11 +528,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		if(!$this->enabled) {
 			return array();
 		}
-		$cachekey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
+		$cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
 
 		//Check cache before driving unnecessary searches
-		\OCP\Util::writeLog('user_ldap', 'getGroups '.$cachekey, \OCP\Util::DEBUG);
-		$ldap_groups = $this->access->connection->getFromCache($cachekey);
+		\OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
+		$ldap_groups = $this->access->connection->getFromCache($cacheKey);
 		if(!is_null($ldap_groups)) {
 			return $ldap_groups;
 		}
@@ -397,26 +553,30 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 				$offset);
 		$ldap_groups = $this->access->ownCloudGroupNames($ldap_groups);
 
-		$this->access->connection->writeToCache($cachekey, $ldap_groups);
+		$this->access->connection->writeToCache($cacheKey, $ldap_groups);
 		return $ldap_groups;
 	}
 
 	/**
 	 * get a list of all groups using a paged search
+	 *
+	 * @param string $search
+	 * @param int $limit
+	 * @param int $offset
 	 * @return array with group names
 	 *
 	 * Returns a list with all groups
-   	 * Uses a paged search if available to override a
-   	 * server side search limit.
-   	 * (active directory has a limit of 1000 by default)
+	 * Uses a paged search if available to override a
+	 * server side search limit.
+	 * (active directory has a limit of 1000 by default)
 	 */
 	public function getGroups($search = '', $limit = -1, $offset = 0) {
 		if(!$this->enabled) {
 			return array();
 		}
-		$pagingsize = $this->access->connection->ldapPagingSize;
+		$pagingSize = $this->access->connection->ldapPagingSize;
 		if ((! $this->access->connection->hasPagedResultSupport)
-		   	|| empty($pagingsize)) {
+		   	|| empty($pagingSize)) {
 			return $this->getGroupsChunk($search, $limit, $offset);
 		}
 		$maxGroups = 100000; // limit max results (just for safety reasons)
@@ -428,7 +588,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		$chunkOffset = $offset;
 		$allGroups = array();
 		while ($chunkOffset < $overallLimit) {
-			$chunkLimit = min($pagingsize, $overallLimit - $chunkOffset);
+			$chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
 			$ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
 			$nread = count($ldapGroups);
 			\OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
@@ -445,6 +605,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 
 	/**
 	 * @param string $group
+	 * @return bool
 	 */
 	public function groupMatchesFilter($group) {
 		return (strripos($group, $this->groupSearch) !== false);
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index ca5d138..e3b6566 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -28,6 +28,9 @@ namespace OCA\user_ldap\lib;
  * @package OCA\user_ldap\lib
  */
 class Access extends LDAPUtility implements user\IUserTools {
+	/**
+	 * @var \OCA\user_ldap\lib\Connection
+	 */
 	public $connection;
 	public $userManager;
 	//never ever check this var directly, always use getPagedSearchResultState
@@ -61,8 +64,8 @@ class Access extends LDAPUtility implements user\IUserTools {
 
 	/**
 	 * 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
+	 * @param string $dn the record in question
+	 * @param string $attr the attribute that shall be retrieved
 	 *        if empty, just check the record's existence
 	 * @param string $filter
 	 * @return array|false an array of values on success or an empty
@@ -181,6 +184,33 @@ class Access extends LDAPUtility implements user\IUserTools {
 	}
 
 	/**
+	 * returns a DN-string that is cleaned from not domain parts, e.g.
+	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
+	 * becomes dc=foobar,dc=server,dc=org
+	 * @param string $dn
+	 * @return string
+	 */
+	public function getDomainDNFromDN($dn) {
+		$allParts = $this->ldap->explodeDN($dn, 0);
+		if($allParts === false) {
+			//not a valid DN
+			return '';
+		}
+		$domainParts = array();
+		$dcFound = false;
+		foreach($allParts as $part) {
+			if(!$dcFound && strpos($part, 'dc=') === 0) {
+				$dcFound = true;
+			}
+			if($dcFound) {
+				$domainParts[] = $part;
+			}
+		}
+		$domainDN = implode(',', $domainParts);
+		return $domainDN;
+	}
+
+	/**
 	 * gives back the database table for the query
 	 * @param bool $isUser
 	 * @return string
@@ -534,7 +564,7 @@ class Access extends LDAPUtility implements user\IUserTools {
 			if(!\OC_Group::groupExists($altName)) {
 				return $altName;
 			}
-			$altName = $name . '_' . $lastNo + $attempts;
+			$altName = $name . '_' . ($lastNo + $attempts);
 			$attempts++;
 		}
 		return false;
@@ -581,6 +611,7 @@ class Access extends LDAPUtility implements user\IUserTools {
 
 	/**
 	 * @param boolean $isUsers
+	 * @return array
 	 */
 	private function mappedComponents($isUsers) {
 		$table = $this->getMapTable($isUsers);
@@ -834,7 +865,7 @@ class Access extends LDAPUtility implements user\IUserTools {
 	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
 		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
 
-		if(is_null($limit)) {
+		if(is_null($limit) || $limit <= 0) {
 			$limit = intval($this->connection->ldapPagingSize);
 		}
 
@@ -894,6 +925,10 @@ class Access extends LDAPUtility implements user\IUserTools {
 	 * @return array with the search result
 	 */
 	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
+		if($limit <= 0) {
+			//otherwise search will fail
+			$limit = null;
+		}
 		$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
 		if($search === false) {
 			return array();
@@ -908,7 +943,7 @@ class Access extends LDAPUtility implements user\IUserTools {
 			$this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
 											$offset, $pagedSearchOK,
 											$skipHandling);
-			return;
+			return array();
 		}
 
 		// Do the server-side sorting
@@ -1233,6 +1268,55 @@ class Access extends LDAPUtility implements user\IUserTools {
 	}
 
 	/**
+	 * gets a SID of the domain of the given dn
+	 * @param string $dn
+	 * @return string|bool
+	 */
+	public function getSID($dn) {
+		$domainDN = $this->getDomainDNFromDN($dn);
+		$cacheKey = 'getSID-'.$domainDN;
+		if($this->connection->isCached($cacheKey)) {
+			return $this->connection->getFromCache($cacheKey);
+		}
+
+		$objectSid = $this->readAttribute($domainDN, 'objectsid');
+		if(!is_array($objectSid) || empty($objectSid)) {
+			$this->connection->writeToCache($cacheKey, false);
+			return false;
+		}
+		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
+		$this->connection->writeToCache($cacheKey, $domainObjectSid);
+
+		return $domainObjectSid;
+	}
+
+	/**
+	 * converts a binary SID into a string representation
+	 * @param string $sid
+	 * @return string
+	 * @link http://blogs.freebsdish.org/tmclaugh/2010/07/21/finding-a-users-primary-group-in-ad/#comment-2855
+	 */
+	public function convertSID2Str($sid) {
+		try {
+			$srl = ord($sid[0]);
+			$numberSubID = ord($sid[1]);
+			$x = substr($sid, 2, 6);
+			$h = unpack('N', "\x0\x0" . substr($x,0,2));
+			$l = unpack('N', substr($x,2,6));
+			$iav = bcadd(bcmul($h[1], bcpow(2,32)), $l[1]);
+			$subIDs = array();
+			for ($i=0; $i<$numberSubID; $i++) {
+				$subID = unpack('V', substr($sid, 8+4*$i, 4));
+				$subIDs[] = $subID[1];
+			}
+		} catch (\Exception $e) {
+			return '';
+		}
+
+		return sprintf('S-%d-%d-%s', $srl, $iav, implode('-', $subIDs));
+	}
+
+	/**
 	 * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
 	 * @param string $dn the DN
 	 * @return string
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
index bafb0e0..336ea7b 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/connection.php
@@ -23,6 +23,13 @@
 
 namespace OCA\user_ldap\lib;
 
+//magic properties (incomplete)
+/**
+ * responsible for LDAP connections in context with the provided configuration
+ * @property string ldapUserFilter
+ * @property string ldapUserDisplayName
+ * @property boolean hasPagedResultSupport
+*/
 class Connection extends LDAPUtility {
 	private $ldapConnectionRes = null;
 	private $configPrefix;
diff --git a/apps/user_ldap/lib/ildapwrapper.php b/apps/user_ldap/lib/ildapwrapper.php
index 97ae081..590f6d7 100644
--- a/apps/user_ldap/lib/ildapwrapper.php
+++ b/apps/user_ldap/lib/ildapwrapper.php
@@ -90,6 +90,15 @@ interface ILDAPWrapper {
 	public function error($link);
 
 	/**
+	 * Splits DN into its component parts
+	 * @param string $dn
+	 * @param int @withAttrib
+	 * @return array|false
+	 * @link http://www.php.net/manual/en/function.ldap-explode-dn.php
+	 */
+	public function explodeDN($dn, $withAttrib);
+
+	/**
 	 * Return first result id
 	 * @param resource $link LDAP link resource
 	 * @param resource $result LDAP result resource
diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php
index 2b20b2a..967754d 100644
--- a/apps/user_ldap/lib/ldap.php
+++ b/apps/user_ldap/lib/ldap.php
@@ -99,6 +99,17 @@ class LDAP implements ILDAPWrapper {
 	}
 
 	/**
+	 * Splits DN into its component parts
+	 * @param string $dn
+	 * @param int @withAttrib
+	 * @return array|false
+	 * @link http://www.php.net/manual/en/function.ldap-explode-dn.php
+	 */
+	public function explodeDN($dn, $withAttrib) {
+		return $this->invokeLDAPMethod('ldap_explode_dn', $dn, $withAttrib);
+	}
+
+	/**
 	 * @param LDAP $link
 	 * @param LDAP $result
 	 * @return mixed
diff --git a/apps/user_ldap/tests/access.php b/apps/user_ldap/tests/access.php
index 8ead5d6..2ff7540 100644
--- a/apps/user_ldap/tests/access.php
+++ b/apps/user_ldap/tests/access.php
@@ -77,4 +77,54 @@ class Test_Access extends \PHPUnit_Framework_TestCase {
 		$expected = 'foo\\\\*bar';
 		$this->assertTrue($expected === $access->escapeFilterPart($input));
 	}
-}
\ No newline at end of file
+
+	public function testConvertSID2StrSuccess() {
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
+
+		$sidBinary = file_get_contents(__DIR__ . '/data/sid.dat');
+		$sidExpected = 'S-1-5-21-249921958-728525901-1594176202';
+
+		$this->assertSame($sidExpected, $access->convertSID2Str($sidBinary));
+	}
+
+	public function testConvertSID2StrInputError() {
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
+
+		$sidIllegal = 'foobar';
+		$sidExpected = '';
+
+		$this->assertSame($sidExpected, $access->convertSID2Str($sidIllegal));
+	}
+
+	public function testGetDomainDNFromDNSuccess() {
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
+
+		$inputDN = 'uid=zaphod,cn=foobar,dc=my,dc=server,dc=com';
+		$domainDN = 'dc=my,dc=server,dc=com';
+
+		$lw->expects($this->once())
+			->method('explodeDN')
+			->with($inputDN, 0)
+			->will($this->returnValue(explode(',', $inputDN)));
+
+		$this->assertSame($domainDN, $access->getDomainDNFromDN($inputDN));
+	}
+
+	public function testGetDomainDNFromDNError() {
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
+
+		$inputDN = 'foobar';
+		$expected = '';
+
+		$lw->expects($this->once())
+			->method('explodeDN')
+			->with($inputDN, 0)
+			->will($this->returnValue(false));
+
+		$this->assertSame($expected, $access->getDomainDNFromDN($inputDN));
+	}
+}
diff --git a/apps/user_ldap/tests/data/sid.dat b/apps/user_ldap/tests/data/sid.dat
new file mode 100644
index 0000000..3d500c6
Binary files /dev/null and b/apps/user_ldap/tests/data/sid.dat differ
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php
index 1184fe1..c4aed25 100644
--- a/apps/user_ldap/tests/group_ldap.php
+++ b/apps/user_ldap/tests/group_ldap.php
@@ -96,6 +96,10 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
 			->will($this->returnValue('cn=group,dc=foo,dc=bar'));
 
 		$access->expects($this->any())
+			->method('fetchListOfUsers')
+			->will($this->returnValue(array()));
+
+		$access->expects($this->any())
 			->method('readAttribute')
 			->will($this->returnCallback(function($name) {
 				//the search operation will call readAttribute, thus we need
@@ -111,7 +115,9 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
 
 		$access->expects($this->any())
 			->method('dn2username')
-			->will($this->returnValue('foobar'));
+			->will($this->returnCallback(function() {
+				return 'foobar' . \OCP\Util::generateRandomBytes(7);
+			}));
 
 		$groupBackend = new GroupLDAP($access);
 		$users = $groupBackend->countUsersInGroup('group', '3');
@@ -119,4 +125,148 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
 		$this->assertSame(2, $users);
 	}
 
+	public function testPrimaryGroupID2NameSuccess() {
+		$access = $this->getAccessMock();
+		$this->enableGroups($access);
+
+		$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
+
+		$access->expects($this->once())
+			->method('getSID')
+			->with($userDN)
+			->will($this->returnValue('S-1-5-21-249921958-728525901-1594176202'));
+
+		$access->expects($this->once())
+			->method('searchGroups')
+			->will($this->returnValue(array('cn=foo,dc=barfoo,dc=bar')));
+
+		$access->expects($this->once())
+			->method('dn2groupname')
+			->with('cn=foo,dc=barfoo,dc=bar')
+			->will($this->returnValue('MyGroup'));
+
+		$groupBackend = new GroupLDAP($access);
+
+		$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
+
+		$this->assertSame('MyGroup', $group);
+	}
+
+	public function testPrimaryGroupID2NameNoSID() {
+		$access = $this->getAccessMock();
+		$this->enableGroups($access);
+
+		$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
+
+		$access->expects($this->once())
+			->method('getSID')
+			->with($userDN)
+			->will($this->returnValue(false));
+
+		$access->expects($this->never())
+			->method('searchGroups');
+
+		$access->expects($this->never())
+			->method('dn2groupname');
+
+		$groupBackend = new GroupLDAP($access);
+
+		$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
+
+		$this->assertSame(false, $group);
+	}
+
+	public function testPrimaryGroupID2NameNoGroup() {
+		$access = $this->getAccessMock();
+		$this->enableGroups($access);
+
+		$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
+
+		$access->expects($this->once())
+			->method('getSID')
+			->with($userDN)
+			->will($this->returnValue('S-1-5-21-249921958-728525901-1594176202'));
+
+		$access->expects($this->once())
+			->method('searchGroups')
+			->will($this->returnValue(array()));
+
+		$access->expects($this->never())
+			->method('dn2groupname');
+
+		$groupBackend = new GroupLDAP($access);
+
+		$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
+
+		$this->assertSame(false, $group);
+	}
+
+	public function testPrimaryGroupID2NameNoName() {
+		$access = $this->getAccessMock();
+		$this->enableGroups($access);
+
+		$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
+
+		$access->expects($this->once())
+			->method('getSID')
+			->with($userDN)
+			->will($this->returnValue('S-1-5-21-249921958-728525901-1594176202'));
+
+		$access->expects($this->once())
+			->method('searchGroups')
+			->will($this->returnValue(array('cn=foo,dc=barfoo,dc=bar')));
+
+		$access->expects($this->once())
+			->method('dn2groupname')
+			->will($this->returnValue(false));
+
+		$groupBackend = new GroupLDAP($access);
+
+		$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
+
+		$this->assertSame(false, $group);
+	}
+
+	public function testGetEntryGroupIDValue() {
+		//tests getEntryGroupID via getGroupPrimaryGroupID
+		//which is basically identical to getUserPrimaryGroupIDs
+		$access = $this->getAccessMock();
+		$this->enableGroups($access);
+
+		$dn = 'cn=foobar,cn=foo,dc=barfoo,dc=bar';
+		$attr = 'primaryGroupToken';
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($dn, $attr)
+			->will($this->returnValue(array('3117')));
+
+		$groupBackend = new GroupLDAP($access);
+
+		$gid = $groupBackend->getGroupPrimaryGroupID($dn);
+
+		$this->assertSame('3117', $gid);
+	}
+
+	public function testGetEntryGroupIDNoValue() {
+		//tests getEntryGroupID via getGroupPrimaryGroupID
+		//which is basically identical to getUserPrimaryGroupIDs
+		$access = $this->getAccessMock();
+		$this->enableGroups($access);
+
+		$dn = 'cn=foobar,cn=foo,dc=barfoo,dc=bar';
+		$attr = 'primaryGroupToken';
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($dn, $attr)
+			->will($this->returnValue(false));
+
+		$groupBackend = new GroupLDAP($access);
+
+		$gid = $groupBackend->getGroupPrimaryGroupID($dn);
+
+		$this->assertSame(false, $gid);
+	}
+
 }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud.git



More information about the Pkg-owncloud-commits mailing list