Completed
Pull Request — master (#3829)
by Maxence
12:57
created
apps/user_ldap/lib/Helper.php 1 patch
Indentation   +258 added lines, -258 removed lines patch added patch discarded remove patch
@@ -34,126 +34,126 @@  discard block
 block discarded – undo
34 34
 
35 35
 class Helper {
36 36
 
37
-	/** @var IConfig */
38
-	private $config;
39
-
40
-	/**
41
-	 * Helper constructor.
42
-	 *
43
-	 * @param IConfig $config
44
-	 */
45
-	public function __construct(IConfig $config) {
46
-		$this->config = $config;
47
-	}
48
-
49
-	/**
50
-	 * returns prefixes for each saved LDAP/AD server configuration.
51
-	 * @param bool $activeConfigurations optional, whether only active configuration shall be
52
-	 * retrieved, defaults to false
53
-	 * @return array with a list of the available prefixes
54
-	 *
55
-	 * Configuration prefixes are used to set up configurations for n LDAP or
56
-	 * AD servers. Since configuration is stored in the database, table
57
-	 * appconfig under appid user_ldap, the common identifiers in column
58
-	 * 'configkey' have a prefix. The prefix for the very first server
59
-	 * configuration is empty.
60
-	 * Configkey Examples:
61
-	 * Server 1: ldap_login_filter
62
-	 * Server 2: s1_ldap_login_filter
63
-	 * Server 3: s2_ldap_login_filter
64
-	 *
65
-	 * The prefix needs to be passed to the constructor of Connection class,
66
-	 * except the default (first) server shall be connected to.
67
-	 *
68
-	 */
69
-	public function getServerConfigurationPrefixes($activeConfigurations = false) {
70
-		$referenceConfigkey = 'ldap_configuration_active';
71
-
72
-		$keys = $this->getServersConfig($referenceConfigkey);
73
-
74
-		$prefixes = [];
75
-		foreach ($keys as $key) {
76
-			if ($activeConfigurations && $this->config->getAppValue('user_ldap', $key, '0') !== '1') {
77
-				continue;
78
-			}
79
-
80
-			$len = strlen($key) - strlen($referenceConfigkey);
81
-			$prefixes[] = substr($key, 0, $len);
82
-		}
83
-
84
-		return $prefixes;
85
-	}
86
-
87
-	/**
88
-	 *
89
-	 * determines the host for every configured connection
90
-	 * @return array an array with configprefix as keys
91
-	 *
92
-	 */
93
-	public function getServerConfigurationHosts() {
94
-		$referenceConfigkey = 'ldap_host';
95
-
96
-		$keys = $this->getServersConfig($referenceConfigkey);
97
-
98
-		$result = array();
99
-		foreach($keys as $key) {
100
-			$len = strlen($key) - strlen($referenceConfigkey);
101
-			$prefix = substr($key, 0, $len);
102
-			$result[$prefix] = $this->config->getAppValue('user_ldap', $key);
103
-		}
104
-
105
-		return $result;
106
-	}
107
-
108
-	/**
109
-	 * return the next available configuration prefix
110
-	 *
111
-	 * @return string
112
-	 */
113
-	public function getNextServerConfigurationPrefix() {
114
-		$serverConnections = $this->getServerConfigurationPrefixes();
115
-
116
-		if(count($serverConnections) === 0) {
117
-			return 's01';
118
-		}
119
-
120
-		sort($serverConnections);
121
-		$lastKey = array_pop($serverConnections);
122
-		$lastNumber = intval(str_replace('s', '', $lastKey));
123
-		$nextPrefix = 's' . str_pad($lastNumber + 1, 2, '0', STR_PAD_LEFT);
124
-		return $nextPrefix;
125
-	}
126
-
127
-	private function getServersConfig($value) {
128
-		$regex = '/' . $value . '$/S';
129
-
130
-		$keys = $this->config->getAppKeys('user_ldap');
131
-		$result = [];
132
-		foreach ($keys as $key) {
133
-			if (preg_match($regex, $key) === 1) {
134
-				$result[] = $key;
135
-			}
136
-		}
137
-
138
-		return $result;
139
-	}
140
-
141
-	/**
142
-	 * deletes a given saved LDAP/AD server configuration.
143
-	 * @param string $prefix the configuration prefix of the config to delete
144
-	 * @return bool true on success, false otherwise
145
-	 */
146
-	public function deleteServerConfiguration($prefix) {
147
-		if(!in_array($prefix, self::getServerConfigurationPrefixes())) {
148
-			return false;
149
-		}
150
-
151
-		$saveOtherConfigurations = '';
152
-		if(empty($prefix)) {
153
-			$saveOtherConfigurations = 'AND `configkey` NOT LIKE \'s%\'';
154
-		}
155
-
156
-		$query = \OCP\DB::prepare('
37
+    /** @var IConfig */
38
+    private $config;
39
+
40
+    /**
41
+     * Helper constructor.
42
+     *
43
+     * @param IConfig $config
44
+     */
45
+    public function __construct(IConfig $config) {
46
+        $this->config = $config;
47
+    }
48
+
49
+    /**
50
+     * returns prefixes for each saved LDAP/AD server configuration.
51
+     * @param bool $activeConfigurations optional, whether only active configuration shall be
52
+     * retrieved, defaults to false
53
+     * @return array with a list of the available prefixes
54
+     *
55
+     * Configuration prefixes are used to set up configurations for n LDAP or
56
+     * AD servers. Since configuration is stored in the database, table
57
+     * appconfig under appid user_ldap, the common identifiers in column
58
+     * 'configkey' have a prefix. The prefix for the very first server
59
+     * configuration is empty.
60
+     * Configkey Examples:
61
+     * Server 1: ldap_login_filter
62
+     * Server 2: s1_ldap_login_filter
63
+     * Server 3: s2_ldap_login_filter
64
+     *
65
+     * The prefix needs to be passed to the constructor of Connection class,
66
+     * except the default (first) server shall be connected to.
67
+     *
68
+     */
69
+    public function getServerConfigurationPrefixes($activeConfigurations = false) {
70
+        $referenceConfigkey = 'ldap_configuration_active';
71
+
72
+        $keys = $this->getServersConfig($referenceConfigkey);
73
+
74
+        $prefixes = [];
75
+        foreach ($keys as $key) {
76
+            if ($activeConfigurations && $this->config->getAppValue('user_ldap', $key, '0') !== '1') {
77
+                continue;
78
+            }
79
+
80
+            $len = strlen($key) - strlen($referenceConfigkey);
81
+            $prefixes[] = substr($key, 0, $len);
82
+        }
83
+
84
+        return $prefixes;
85
+    }
86
+
87
+    /**
88
+     *
89
+     * determines the host for every configured connection
90
+     * @return array an array with configprefix as keys
91
+     *
92
+     */
93
+    public function getServerConfigurationHosts() {
94
+        $referenceConfigkey = 'ldap_host';
95
+
96
+        $keys = $this->getServersConfig($referenceConfigkey);
97
+
98
+        $result = array();
99
+        foreach($keys as $key) {
100
+            $len = strlen($key) - strlen($referenceConfigkey);
101
+            $prefix = substr($key, 0, $len);
102
+            $result[$prefix] = $this->config->getAppValue('user_ldap', $key);
103
+        }
104
+
105
+        return $result;
106
+    }
107
+
108
+    /**
109
+     * return the next available configuration prefix
110
+     *
111
+     * @return string
112
+     */
113
+    public function getNextServerConfigurationPrefix() {
114
+        $serverConnections = $this->getServerConfigurationPrefixes();
115
+
116
+        if(count($serverConnections) === 0) {
117
+            return 's01';
118
+        }
119
+
120
+        sort($serverConnections);
121
+        $lastKey = array_pop($serverConnections);
122
+        $lastNumber = intval(str_replace('s', '', $lastKey));
123
+        $nextPrefix = 's' . str_pad($lastNumber + 1, 2, '0', STR_PAD_LEFT);
124
+        return $nextPrefix;
125
+    }
126
+
127
+    private function getServersConfig($value) {
128
+        $regex = '/' . $value . '$/S';
129
+
130
+        $keys = $this->config->getAppKeys('user_ldap');
131
+        $result = [];
132
+        foreach ($keys as $key) {
133
+            if (preg_match($regex, $key) === 1) {
134
+                $result[] = $key;
135
+            }
136
+        }
137
+
138
+        return $result;
139
+    }
140
+
141
+    /**
142
+     * deletes a given saved LDAP/AD server configuration.
143
+     * @param string $prefix the configuration prefix of the config to delete
144
+     * @return bool true on success, false otherwise
145
+     */
146
+    public function deleteServerConfiguration($prefix) {
147
+        if(!in_array($prefix, self::getServerConfigurationPrefixes())) {
148
+            return false;
149
+        }
150
+
151
+        $saveOtherConfigurations = '';
152
+        if(empty($prefix)) {
153
+            $saveOtherConfigurations = 'AND `configkey` NOT LIKE \'s%\'';
154
+        }
155
+
156
+        $query = \OCP\DB::prepare('
157 157
 			DELETE
158 158
 			FROM `*PREFIX*appconfig`
159 159
 			WHERE `configkey` LIKE ?
@@ -161,145 +161,145 @@  discard block
 block discarded – undo
161 161
 				AND `appid` = \'user_ldap\'
162 162
 				AND `configkey` NOT IN (\'enabled\', \'installed_version\', \'types\', \'bgjUpdateGroupsLastRun\')
163 163
 		');
164
-		$delRows = $query->execute(array($prefix.'%'));
165
-
166
-		if(\OCP\DB::isError($delRows)) {
167
-			return false;
168
-		}
169
-
170
-		if($delRows === 0) {
171
-			return false;
172
-		}
173
-
174
-		return true;
175
-	}
176
-
177
-	/**
178
-	 * checks whether there is one or more disabled LDAP configurations
179
-	 * @throws \Exception
180
-	 * @return bool
181
-	 */
182
-	public function haveDisabledConfigurations() {
183
-		$all = $this->getServerConfigurationPrefixes(false);
184
-		$active = $this->getServerConfigurationPrefixes(true);
185
-
186
-		if(!is_array($all) || !is_array($active)) {
187
-			throw new \Exception('Unexpected Return Value');
188
-		}
189
-
190
-		return count($all) !== count($active) || count($all) === 0;
191
-	}
192
-
193
-	/**
194
-	 * extracts the domain from a given URL
195
-	 * @param string $url the URL
196
-	 * @return string|false domain as string on success, false otherwise
197
-	 */
198
-	public function getDomainFromURL($url) {
199
-		$uinfo = parse_url($url);
200
-		if(!is_array($uinfo)) {
201
-			return false;
202
-		}
203
-
204
-		$domain = false;
205
-		if(isset($uinfo['host'])) {
206
-			$domain = $uinfo['host'];
207
-		} else if(isset($uinfo['path'])) {
208
-			$domain = $uinfo['path'];
209
-		}
210
-
211
-		return $domain;
212
-	}
164
+        $delRows = $query->execute(array($prefix.'%'));
165
+
166
+        if(\OCP\DB::isError($delRows)) {
167
+            return false;
168
+        }
169
+
170
+        if($delRows === 0) {
171
+            return false;
172
+        }
173
+
174
+        return true;
175
+    }
176
+
177
+    /**
178
+     * checks whether there is one or more disabled LDAP configurations
179
+     * @throws \Exception
180
+     * @return bool
181
+     */
182
+    public function haveDisabledConfigurations() {
183
+        $all = $this->getServerConfigurationPrefixes(false);
184
+        $active = $this->getServerConfigurationPrefixes(true);
185
+
186
+        if(!is_array($all) || !is_array($active)) {
187
+            throw new \Exception('Unexpected Return Value');
188
+        }
189
+
190
+        return count($all) !== count($active) || count($all) === 0;
191
+    }
192
+
193
+    /**
194
+     * extracts the domain from a given URL
195
+     * @param string $url the URL
196
+     * @return string|false domain as string on success, false otherwise
197
+     */
198
+    public function getDomainFromURL($url) {
199
+        $uinfo = parse_url($url);
200
+        if(!is_array($uinfo)) {
201
+            return false;
202
+        }
203
+
204
+        $domain = false;
205
+        if(isset($uinfo['host'])) {
206
+            $domain = $uinfo['host'];
207
+        } else if(isset($uinfo['path'])) {
208
+            $domain = $uinfo['path'];
209
+        }
210
+
211
+        return $domain;
212
+    }
213 213
 	
214
-	/**
215
-	 *
216
-	 * Set the LDAPProvider in the config
217
-	 *
218
-	 */
219
-	public function setLDAPProvider() {
220
-		$current = \OC::$server->getConfig()->getSystemValue('ldapProviderFactory', null);
221
-		if(is_null($current)) {
222
-			\OC::$server->getConfig()->setSystemValue('ldapProviderFactory', '\\OCA\\User_LDAP\\LDAPProviderFactory');
223
-		}
224
-	}
214
+    /**
215
+     *
216
+     * Set the LDAPProvider in the config
217
+     *
218
+     */
219
+    public function setLDAPProvider() {
220
+        $current = \OC::$server->getConfig()->getSystemValue('ldapProviderFactory', null);
221
+        if(is_null($current)) {
222
+            \OC::$server->getConfig()->setSystemValue('ldapProviderFactory', '\\OCA\\User_LDAP\\LDAPProviderFactory');
223
+        }
224
+    }
225 225
 	
226
-	/**
227
-	 * sanitizes a DN received from the LDAP server
228
-	 * @param array $dn the DN in question
229
-	 * @return array the sanitized DN
230
-	 */
231
-	public function sanitizeDN($dn) {
232
-		//treating multiple base DNs
233
-		if(is_array($dn)) {
234
-			$result = array();
235
-			foreach($dn as $singleDN) {
236
-				$result[] = $this->sanitizeDN($singleDN);
237
-			}
238
-			return $result;
239
-		}
240
-
241
-		//OID sometimes gives back DNs with whitespace after the comma
242
-		// a la "uid=foo, cn=bar, dn=..." We need to tackle this!
243
-		$dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
244
-
245
-		//make comparisons and everything work
246
-		$dn = mb_strtolower($dn, 'UTF-8');
247
-
248
-		//escape DN values according to RFC 2253 – this is already done by ldap_explode_dn
249
-		//to use the DN in search filters, \ needs to be escaped to \5c additionally
250
-		//to use them in bases, we convert them back to simple backslashes in readAttribute()
251
-		$replacements = array(
252
-			'\,' => '\5c2C',
253
-			'\=' => '\5c3D',
254
-			'\+' => '\5c2B',
255
-			'\<' => '\5c3C',
256
-			'\>' => '\5c3E',
257
-			'\;' => '\5c3B',
258
-			'\"' => '\5c22',
259
-			'\#' => '\5c23',
260
-			'('  => '\28',
261
-			')'  => '\29',
262
-			'*'  => '\2A',
263
-		);
264
-		$dn = str_replace(array_keys($replacements), array_values($replacements), $dn);
265
-
266
-		return $dn;
267
-	}
226
+    /**
227
+     * sanitizes a DN received from the LDAP server
228
+     * @param array $dn the DN in question
229
+     * @return array the sanitized DN
230
+     */
231
+    public function sanitizeDN($dn) {
232
+        //treating multiple base DNs
233
+        if(is_array($dn)) {
234
+            $result = array();
235
+            foreach($dn as $singleDN) {
236
+                $result[] = $this->sanitizeDN($singleDN);
237
+            }
238
+            return $result;
239
+        }
240
+
241
+        //OID sometimes gives back DNs with whitespace after the comma
242
+        // a la "uid=foo, cn=bar, dn=..." We need to tackle this!
243
+        $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
244
+
245
+        //make comparisons and everything work
246
+        $dn = mb_strtolower($dn, 'UTF-8');
247
+
248
+        //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn
249
+        //to use the DN in search filters, \ needs to be escaped to \5c additionally
250
+        //to use them in bases, we convert them back to simple backslashes in readAttribute()
251
+        $replacements = array(
252
+            '\,' => '\5c2C',
253
+            '\=' => '\5c3D',
254
+            '\+' => '\5c2B',
255
+            '\<' => '\5c3C',
256
+            '\>' => '\5c3E',
257
+            '\;' => '\5c3B',
258
+            '\"' => '\5c22',
259
+            '\#' => '\5c23',
260
+            '('  => '\28',
261
+            ')'  => '\29',
262
+            '*'  => '\2A',
263
+        );
264
+        $dn = str_replace(array_keys($replacements), array_values($replacements), $dn);
265
+
266
+        return $dn;
267
+    }
268 268
 	
269
-	/**
270
-	 * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
271
-	 * @param string $dn the DN
272
-	 * @return string
273
-	 */
274
-	public function DNasBaseParameter($dn) {
275
-		return str_ireplace('\\5c', '\\', $dn);
276
-	}
277
-
278
-	/**
279
-	 * listens to a hook thrown by server2server sharing and replaces the given
280
-	 * login name by a username, if it matches an LDAP user.
281
-	 *
282
-	 * @param array $param
283
-	 * @throws \Exception
284
-	 */
285
-	public static function loginName2UserName($param) {
286
-		if(!isset($param['uid'])) {
287
-			throw new \Exception('key uid is expected to be set in $param');
288
-		}
289
-
290
-		//ain't it ironic?
291
-		$helper = new Helper(\OC::$server->getConfig());
292
-
293
-		$configPrefixes = $helper->getServerConfigurationPrefixes(true);
294
-		$ldapWrapper = new LDAP();
295
-		$ocConfig = \OC::$server->getConfig();
296
-
297
-		$userBackend  = new User_Proxy(
298
-			$configPrefixes, $ldapWrapper, $ocConfig
299
-		);
300
-		$uid = $userBackend->loginName2UserName($param['uid'] );
301
-		if($uid !== false) {
302
-			$param['uid'] = $uid;
303
-		}
304
-	}
269
+    /**
270
+     * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
271
+     * @param string $dn the DN
272
+     * @return string
273
+     */
274
+    public function DNasBaseParameter($dn) {
275
+        return str_ireplace('\\5c', '\\', $dn);
276
+    }
277
+
278
+    /**
279
+     * listens to a hook thrown by server2server sharing and replaces the given
280
+     * login name by a username, if it matches an LDAP user.
281
+     *
282
+     * @param array $param
283
+     * @throws \Exception
284
+     */
285
+    public static function loginName2UserName($param) {
286
+        if(!isset($param['uid'])) {
287
+            throw new \Exception('key uid is expected to be set in $param');
288
+        }
289
+
290
+        //ain't it ironic?
291
+        $helper = new Helper(\OC::$server->getConfig());
292
+
293
+        $configPrefixes = $helper->getServerConfigurationPrefixes(true);
294
+        $ldapWrapper = new LDAP();
295
+        $ocConfig = \OC::$server->getConfig();
296
+
297
+        $userBackend  = new User_Proxy(
298
+            $configPrefixes, $ldapWrapper, $ocConfig
299
+        );
300
+        $uid = $userBackend->loginName2UserName($param['uid'] );
301
+        if($uid !== false) {
302
+            $param['uid'] = $uid;
303
+        }
304
+    }
305 305
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Proxy.php 1 patch
Indentation   +170 added lines, -170 removed lines patch added patch discarded remove patch
@@ -35,174 +35,174 @@
 block discarded – undo
35 35
 use OCA\User_LDAP\User\Manager;
36 36
 
37 37
 abstract class Proxy {
38
-	static private $accesses = array();
39
-	private $ldap = null;
40
-
41
-	/** @var \OCP\ICache|null */
42
-	private $cache;
43
-
44
-	/**
45
-	 * @param ILDAPWrapper $ldap
46
-	 */
47
-	public function __construct(ILDAPWrapper $ldap) {
48
-		$this->ldap = $ldap;
49
-		$memcache = \OC::$server->getMemCacheFactory();
50
-		if($memcache->isAvailable()) {
51
-			$this->cache = $memcache->create();
52
-		}
53
-	}
54
-
55
-	/**
56
-	 * @param string $configPrefix
57
-	 */
58
-	private function addAccess($configPrefix) {
59
-		static $ocConfig;
60
-		static $fs;
61
-		static $log;
62
-		static $avatarM;
63
-		static $userMap;
64
-		static $groupMap;
65
-		static $db;
66
-		static $coreUserManager;
67
-		if(is_null($fs)) {
68
-			$ocConfig = \OC::$server->getConfig();
69
-			$fs       = new FilesystemHelper();
70
-			$log      = new LogWrapper();
71
-			$avatarM  = \OC::$server->getAvatarManager();
72
-			$db       = \OC::$server->getDatabaseConnection();
73
-			$userMap  = new UserMapping($db);
74
-			$groupMap = new GroupMapping($db);
75
-			$coreUserManager = \OC::$server->getUserManager();
76
-		}
77
-		$userManager =
78
-			new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db, $coreUserManager);
79
-		$connector = new Connection($this->ldap, $configPrefix);
80
-		$access = new Access($connector, $this->ldap, $userManager, new Helper(\OC::$server->getConfig()));
81
-		$access->setUserMapper($userMap);
82
-		$access->setGroupMapper($groupMap);
83
-		self::$accesses[$configPrefix] = $access;
84
-	}
85
-
86
-	/**
87
-	 * @param string $configPrefix
88
-	 * @return mixed
89
-	 */
90
-	protected function getAccess($configPrefix) {
91
-		if(!isset(self::$accesses[$configPrefix])) {
92
-			$this->addAccess($configPrefix);
93
-		}
94
-		return self::$accesses[$configPrefix];
95
-	}
96
-
97
-	/**
98
-	 * @param string $uid
99
-	 * @return string
100
-	 */
101
-	protected function getUserCacheKey($uid) {
102
-		return 'user-'.$uid.'-lastSeenOn';
103
-	}
104
-
105
-	/**
106
-	 * @param string $gid
107
-	 * @return string
108
-	 */
109
-	protected function getGroupCacheKey($gid) {
110
-		return 'group-'.$gid.'-lastSeenOn';
111
-	}
112
-
113
-	/**
114
-	 * @param string $id
115
-	 * @param string $method
116
-	 * @param array $parameters
117
-	 * @param bool $passOnWhen
118
-	 * @return mixed
119
-	 */
120
-	abstract protected function callOnLastSeenOn($id, $method, $parameters, $passOnWhen);
121
-
122
-	/**
123
-	 * @param string $id
124
-	 * @param string $method
125
-	 * @param array $parameters
126
-	 * @return mixed
127
-	 */
128
-	abstract protected function walkBackends($id, $method, $parameters);
129
-
130
-	/**
131
-	 * @param string $id
132
-	 * @return Access
133
-	 */
134
-	abstract public function getLDAPAccess($id);
135
-
136
-	/**
137
-	 * Takes care of the request to the User backend
138
-	 * @param string $id
139
-	 * @param string $method string, the method of the user backend that shall be called
140
-	 * @param array $parameters an array of parameters to be passed
141
-	 * @param bool $passOnWhen
142
-	 * @return mixed, the result of the specified method
143
-	 */
144
-	protected function handleRequest($id, $method, $parameters, $passOnWhen = false) {
145
-		$result = $this->callOnLastSeenOn($id,  $method, $parameters, $passOnWhen);
146
-		if($result === $passOnWhen) {
147
-			$result = $this->walkBackends($id, $method, $parameters);
148
-		}
149
-		return $result;
150
-	}
151
-
152
-	/**
153
-	 * @param string|null $key
154
-	 * @return string
155
-	 */
156
-	private function getCacheKey($key) {
157
-		$prefix = 'LDAP-Proxy-';
158
-		if(is_null($key)) {
159
-			return $prefix;
160
-		}
161
-		return $prefix.md5($key);
162
-	}
163
-
164
-	/**
165
-	 * @param string $key
166
-	 * @return mixed|null
167
-	 */
168
-	public function getFromCache($key) {
169
-		if(is_null($this->cache) || !$this->isCached($key)) {
170
-			return null;
171
-		}
172
-		$key = $this->getCacheKey($key);
173
-
174
-		return json_decode(base64_decode($this->cache->get($key)));
175
-	}
176
-
177
-	/**
178
-	 * @param string $key
179
-	 * @return bool
180
-	 */
181
-	public function isCached($key) {
182
-		if(is_null($this->cache)) {
183
-			return false;
184
-		}
185
-		$key = $this->getCacheKey($key);
186
-		return $this->cache->hasKey($key);
187
-	}
188
-
189
-	/**
190
-	 * @param string $key
191
-	 * @param mixed $value
192
-	 */
193
-	public function writeToCache($key, $value) {
194
-		if(is_null($this->cache)) {
195
-			return;
196
-		}
197
-		$key   = $this->getCacheKey($key);
198
-		$value = base64_encode(json_encode($value));
199
-		$this->cache->set($key, $value, '2592000');
200
-	}
201
-
202
-	public function clearCache() {
203
-		if(is_null($this->cache)) {
204
-			return;
205
-		}
206
-		$this->cache->clear($this->getCacheKey(null));
207
-	}
38
+    static private $accesses = array();
39
+    private $ldap = null;
40
+
41
+    /** @var \OCP\ICache|null */
42
+    private $cache;
43
+
44
+    /**
45
+     * @param ILDAPWrapper $ldap
46
+     */
47
+    public function __construct(ILDAPWrapper $ldap) {
48
+        $this->ldap = $ldap;
49
+        $memcache = \OC::$server->getMemCacheFactory();
50
+        if($memcache->isAvailable()) {
51
+            $this->cache = $memcache->create();
52
+        }
53
+    }
54
+
55
+    /**
56
+     * @param string $configPrefix
57
+     */
58
+    private function addAccess($configPrefix) {
59
+        static $ocConfig;
60
+        static $fs;
61
+        static $log;
62
+        static $avatarM;
63
+        static $userMap;
64
+        static $groupMap;
65
+        static $db;
66
+        static $coreUserManager;
67
+        if(is_null($fs)) {
68
+            $ocConfig = \OC::$server->getConfig();
69
+            $fs       = new FilesystemHelper();
70
+            $log      = new LogWrapper();
71
+            $avatarM  = \OC::$server->getAvatarManager();
72
+            $db       = \OC::$server->getDatabaseConnection();
73
+            $userMap  = new UserMapping($db);
74
+            $groupMap = new GroupMapping($db);
75
+            $coreUserManager = \OC::$server->getUserManager();
76
+        }
77
+        $userManager =
78
+            new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db, $coreUserManager);
79
+        $connector = new Connection($this->ldap, $configPrefix);
80
+        $access = new Access($connector, $this->ldap, $userManager, new Helper(\OC::$server->getConfig()));
81
+        $access->setUserMapper($userMap);
82
+        $access->setGroupMapper($groupMap);
83
+        self::$accesses[$configPrefix] = $access;
84
+    }
85
+
86
+    /**
87
+     * @param string $configPrefix
88
+     * @return mixed
89
+     */
90
+    protected function getAccess($configPrefix) {
91
+        if(!isset(self::$accesses[$configPrefix])) {
92
+            $this->addAccess($configPrefix);
93
+        }
94
+        return self::$accesses[$configPrefix];
95
+    }
96
+
97
+    /**
98
+     * @param string $uid
99
+     * @return string
100
+     */
101
+    protected function getUserCacheKey($uid) {
102
+        return 'user-'.$uid.'-lastSeenOn';
103
+    }
104
+
105
+    /**
106
+     * @param string $gid
107
+     * @return string
108
+     */
109
+    protected function getGroupCacheKey($gid) {
110
+        return 'group-'.$gid.'-lastSeenOn';
111
+    }
112
+
113
+    /**
114
+     * @param string $id
115
+     * @param string $method
116
+     * @param array $parameters
117
+     * @param bool $passOnWhen
118
+     * @return mixed
119
+     */
120
+    abstract protected function callOnLastSeenOn($id, $method, $parameters, $passOnWhen);
121
+
122
+    /**
123
+     * @param string $id
124
+     * @param string $method
125
+     * @param array $parameters
126
+     * @return mixed
127
+     */
128
+    abstract protected function walkBackends($id, $method, $parameters);
129
+
130
+    /**
131
+     * @param string $id
132
+     * @return Access
133
+     */
134
+    abstract public function getLDAPAccess($id);
135
+
136
+    /**
137
+     * Takes care of the request to the User backend
138
+     * @param string $id
139
+     * @param string $method string, the method of the user backend that shall be called
140
+     * @param array $parameters an array of parameters to be passed
141
+     * @param bool $passOnWhen
142
+     * @return mixed, the result of the specified method
143
+     */
144
+    protected function handleRequest($id, $method, $parameters, $passOnWhen = false) {
145
+        $result = $this->callOnLastSeenOn($id,  $method, $parameters, $passOnWhen);
146
+        if($result === $passOnWhen) {
147
+            $result = $this->walkBackends($id, $method, $parameters);
148
+        }
149
+        return $result;
150
+    }
151
+
152
+    /**
153
+     * @param string|null $key
154
+     * @return string
155
+     */
156
+    private function getCacheKey($key) {
157
+        $prefix = 'LDAP-Proxy-';
158
+        if(is_null($key)) {
159
+            return $prefix;
160
+        }
161
+        return $prefix.md5($key);
162
+    }
163
+
164
+    /**
165
+     * @param string $key
166
+     * @return mixed|null
167
+     */
168
+    public function getFromCache($key) {
169
+        if(is_null($this->cache) || !$this->isCached($key)) {
170
+            return null;
171
+        }
172
+        $key = $this->getCacheKey($key);
173
+
174
+        return json_decode(base64_decode($this->cache->get($key)));
175
+    }
176
+
177
+    /**
178
+     * @param string $key
179
+     * @return bool
180
+     */
181
+    public function isCached($key) {
182
+        if(is_null($this->cache)) {
183
+            return false;
184
+        }
185
+        $key = $this->getCacheKey($key);
186
+        return $this->cache->hasKey($key);
187
+    }
188
+
189
+    /**
190
+     * @param string $key
191
+     * @param mixed $value
192
+     */
193
+    public function writeToCache($key, $value) {
194
+        if(is_null($this->cache)) {
195
+            return;
196
+        }
197
+        $key   = $this->getCacheKey($key);
198
+        $value = base64_encode(json_encode($value));
199
+        $this->cache->set($key, $value, '2592000');
200
+    }
201
+
202
+    public function clearCache() {
203
+        if(is_null($this->cache)) {
204
+            return;
205
+        }
206
+        $this->cache->clear($this->getCacheKey(null));
207
+    }
208 208
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Migration/UUIDFixInsert.php 1 patch
Indentation   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -32,70 +32,70 @@
 block discarded – undo
32 32
 
33 33
 class UUIDFixInsert implements IRepairStep {
34 34
 
35
-	/** @var IConfig */
36
-	protected $config;
35
+    /** @var IConfig */
36
+    protected $config;
37 37
 
38
-	/** @var UserMapping */
39
-	protected $userMapper;
38
+    /** @var UserMapping */
39
+    protected $userMapper;
40 40
 
41
-	/** @var GroupMapping */
42
-	protected $groupMapper;
41
+    /** @var GroupMapping */
42
+    protected $groupMapper;
43 43
 
44
-	/** @var IJobList */
45
-	protected $jobList;
44
+    /** @var IJobList */
45
+    protected $jobList;
46 46
 
47
-	public function __construct(IConfig $config, UserMapping $userMapper, GroupMapping $groupMapper, IJobList $jobList) {
48
-		$this->config = $config;
49
-		$this->userMapper = $userMapper;
50
-		$this->groupMapper = $groupMapper;
51
-		$this->jobList = $jobList;
52
-	}
47
+    public function __construct(IConfig $config, UserMapping $userMapper, GroupMapping $groupMapper, IJobList $jobList) {
48
+        $this->config = $config;
49
+        $this->userMapper = $userMapper;
50
+        $this->groupMapper = $groupMapper;
51
+        $this->jobList = $jobList;
52
+    }
53 53
 
54
-	/**
55
-	 * Returns the step's name
56
-	 *
57
-	 * @return string
58
-	 * @since 9.1.0
59
-	 */
60
-	public function getName() {
61
-		return 'Insert UUIDFix background job for user and group in batches';
62
-	}
54
+    /**
55
+     * Returns the step's name
56
+     *
57
+     * @return string
58
+     * @since 9.1.0
59
+     */
60
+    public function getName() {
61
+        return 'Insert UUIDFix background job for user and group in batches';
62
+    }
63 63
 
64
-	/**
65
-	 * Run repair step.
66
-	 * Must throw exception on error.
67
-	 *
68
-	 * @param IOutput $output
69
-	 * @throws \Exception in case of failure
70
-	 * @since 9.1.0
71
-	 */
72
-	public function run(IOutput $output) {
73
-		$installedVersion = $this->config->getAppValue('user_ldap', 'installed_version', '1.2.1');
74
-		if(version_compare($installedVersion, '1.2.1') !== -1) {
75
-			return;
76
-		}
64
+    /**
65
+     * Run repair step.
66
+     * Must throw exception on error.
67
+     *
68
+     * @param IOutput $output
69
+     * @throws \Exception in case of failure
70
+     * @since 9.1.0
71
+     */
72
+    public function run(IOutput $output) {
73
+        $installedVersion = $this->config->getAppValue('user_ldap', 'installed_version', '1.2.1');
74
+        if(version_compare($installedVersion, '1.2.1') !== -1) {
75
+            return;
76
+        }
77 77
 
78
-		foreach ([$this->userMapper, $this->groupMapper] as $mapper) {
79
-			$offset = 0;
80
-			$batchSize = 50;
81
-			$jobClass = $mapper instanceof UserMapping ? UUIDFixUser::class : UUIDFixGroup::class;
82
-			do {
83
-				$retry = false;
84
-				$records = $mapper->getList($offset, $batchSize);
85
-				if(count($records) === 0){
86
-					continue;
87
-				}
88
-				try {
89
-					$this->jobList->add($jobClass, ['records' => $records]);
90
-					$offset += $batchSize;
91
-				} catch (\InvalidArgumentException $e) {
92
-					if(strpos($e->getMessage(), 'Background job arguments can\'t exceed 4000') !== false) {
93
-						$batchSize = intval(floor(count($records) * 0.8));
94
-						$retry = true;
95
-					}
96
-				}
97
-			} while (count($records) === $batchSize || $retry);
98
-		}
78
+        foreach ([$this->userMapper, $this->groupMapper] as $mapper) {
79
+            $offset = 0;
80
+            $batchSize = 50;
81
+            $jobClass = $mapper instanceof UserMapping ? UUIDFixUser::class : UUIDFixGroup::class;
82
+            do {
83
+                $retry = false;
84
+                $records = $mapper->getList($offset, $batchSize);
85
+                if(count($records) === 0){
86
+                    continue;
87
+                }
88
+                try {
89
+                    $this->jobList->add($jobClass, ['records' => $records]);
90
+                    $offset += $batchSize;
91
+                } catch (\InvalidArgumentException $e) {
92
+                    if(strpos($e->getMessage(), 'Background job arguments can\'t exceed 4000') !== false) {
93
+                        $batchSize = intval(floor(count($records) * 0.8));
94
+                        $retry = true;
95
+                    }
96
+                }
97
+            } while (count($records) === $batchSize || $retry);
98
+        }
99 99
 
100
-	}
100
+    }
101 101
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Migration/UUIDFixGroup.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -30,8 +30,8 @@
 block discarded – undo
30 30
 use OCP\IConfig;
31 31
 
32 32
 class UUIDFixGroup extends UUIDFix {
33
-	public function __construct(GroupMapping $mapper, LDAP $ldap, IConfig $config, Helper $helper) {
34
-		$this->mapper = $mapper;
35
-		$this->proxy = new User_Proxy($helper->getServerConfigurationPrefixes(true), $ldap, $config);
36
-	}
33
+    public function __construct(GroupMapping $mapper, LDAP $ldap, IConfig $config, Helper $helper) {
34
+        $this->mapper = $mapper;
35
+        $this->proxy = new User_Proxy($helper->getServerConfigurationPrefixes(true), $ldap, $config);
36
+    }
37 37
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Migration/UUIDFixUser.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -30,8 +30,8 @@
 block discarded – undo
30 30
 use OCP\IConfig;
31 31
 
32 32
 class UUIDFixUser extends UUIDFix {
33
-	public function __construct(UserMapping $mapper, LDAP $ldap, IConfig $config, Helper $helper) {
34
-		$this->mapper = $mapper;
35
-		$this->proxy = new Group_Proxy($helper->getServerConfigurationPrefixes(true), $ldap, $config);
36
-	}
33
+    public function __construct(UserMapping $mapper, LDAP $ldap, IConfig $config, Helper $helper) {
34
+        $this->mapper = $mapper;
35
+        $this->proxy = new Group_Proxy($helper->getServerConfigurationPrefixes(true), $ldap, $config);
36
+    }
37 37
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Migration/UUIDFix.php 1 patch
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -30,31 +30,31 @@
 block discarded – undo
30 30
 use OCA\User_LDAP\User_Proxy;
31 31
 
32 32
 abstract class UUIDFix extends QueuedJob {
33
-	/** @var  AbstractMapping */
34
-	protected $mapper;
33
+    /** @var  AbstractMapping */
34
+    protected $mapper;
35 35
 
36
-	/** @var  Proxy */
37
-	protected $proxy;
36
+    /** @var  Proxy */
37
+    protected $proxy;
38 38
 
39
-	public function run($argument) {
40
-		$isUser = $this->proxy instanceof User_Proxy;
41
-		foreach($argument['records'] as $record) {
42
-			$access = $this->proxy->getLDAPAccess($record['name']);
43
-			$uuid = $access->getUUID($record['dn'], $isUser);
44
-			if($uuid === false) {
45
-				// record not found, no prob, continue with the next
46
-				continue;
47
-			}
48
-			if($uuid !== $record['uuid']) {
49
-				$this->mapper->setUUIDbyDN($uuid, $record['dn']);
50
-			}
51
-		}
52
-	}
39
+    public function run($argument) {
40
+        $isUser = $this->proxy instanceof User_Proxy;
41
+        foreach($argument['records'] as $record) {
42
+            $access = $this->proxy->getLDAPAccess($record['name']);
43
+            $uuid = $access->getUUID($record['dn'], $isUser);
44
+            if($uuid === false) {
45
+                // record not found, no prob, continue with the next
46
+                continue;
47
+            }
48
+            if($uuid !== $record['uuid']) {
49
+                $this->mapper->setUUIDbyDN($uuid, $record['dn']);
50
+            }
51
+        }
52
+    }
53 53
 
54
-	/**
55
-	 * @param Proxy $proxy
56
-	 */
57
-	public function overrideProxy(Proxy $proxy) {
58
-		$this->proxy = $proxy;
59
-	}
54
+    /**
55
+     * @param Proxy $proxy
56
+     */
57
+    public function overrideProxy(Proxy $proxy) {
58
+        $this->proxy = $proxy;
59
+    }
60 60
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Group_LDAP.php 1 patch
Indentation   +867 added lines, -867 removed lines patch added patch discarded remove patch
@@ -40,874 +40,874 @@
 block discarded – undo
40 40
 use OC\Cache\CappedMemoryCache;
41 41
 
42 42
 class Group_LDAP extends BackendUtility implements \OCP\GroupInterface {
43
-	protected $enabled = false;
44
-
45
-	/**
46
-	 * @var string[] $cachedGroupMembers array of users with gid as key
47
-	 */
48
-	protected $cachedGroupMembers;
49
-
50
-	/**
51
-	 * @var string[] $cachedGroupsByMember array of groups with uid as key
52
-	 */
53
-	protected $cachedGroupsByMember;
54
-
55
-	public function __construct(Access $access) {
56
-		parent::__construct($access);
57
-		$filter = $this->access->connection->ldapGroupFilter;
58
-		$gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
59
-		if(!empty($filter) && !empty($gassoc)) {
60
-			$this->enabled = true;
61
-		}
62
-
63
-		$this->cachedGroupMembers = new CappedMemoryCache();
64
-		$this->cachedGroupsByMember = new CappedMemoryCache();
65
-	}
66
-
67
-	/**
68
-	 * is user in group?
69
-	 * @param string $uid uid of the user
70
-	 * @param string $gid gid of the group
71
-	 * @return bool
72
-	 *
73
-	 * Checks whether the user is member of a group or not.
74
-	 */
75
-	public function inGroup($uid, $gid) {
76
-		if(!$this->enabled) {
77
-			return false;
78
-		}
79
-		$cacheKey = 'inGroup'.$uid.':'.$gid;
80
-		$inGroup = $this->access->connection->getFromCache($cacheKey);
81
-		if(!is_null($inGroup)) {
82
-			return (bool)$inGroup;
83
-		}
84
-
85
-		$userDN = $this->access->username2dn($uid);
86
-
87
-		if(isset($this->cachedGroupMembers[$gid])) {
88
-			$isInGroup = in_array($userDN, $this->cachedGroupMembers[$gid]);
89
-			return $isInGroup;
90
-		}
91
-
92
-		$cacheKeyMembers = 'inGroup-members:'.$gid;
93
-		$members = $this->access->connection->getFromCache($cacheKeyMembers);
94
-		if(!is_null($members)) {
95
-			$this->cachedGroupMembers[$gid] = $members;
96
-			$isInGroup = in_array($userDN, $members);
97
-			$this->access->connection->writeToCache($cacheKey, $isInGroup);
98
-			return $isInGroup;
99
-		}
100
-
101
-		$groupDN = $this->access->groupname2dn($gid);
102
-		// just in case
103
-		if(!$groupDN || !$userDN) {
104
-			$this->access->connection->writeToCache($cacheKey, false);
105
-			return false;
106
-		}
107
-
108
-		//check primary group first
109
-		if($gid === $this->getUserPrimaryGroup($userDN)) {
110
-			$this->access->connection->writeToCache($cacheKey, true);
111
-			return true;
112
-		}
113
-
114
-		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
115
-		$members = $this->_groupMembers($groupDN);
116
-		$members = array_keys($members); // uids are returned as keys
117
-		if(!is_array($members) || count($members) === 0) {
118
-			$this->access->connection->writeToCache($cacheKey, false);
119
-			return false;
120
-		}
121
-
122
-		//extra work if we don't get back user DNs
123
-		if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
124
-			$dns = array();
125
-			$filterParts = array();
126
-			$bytes = 0;
127
-			foreach($members as $mid) {
128
-				$filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
129
-				$filterParts[] = $filter;
130
-				$bytes += strlen($filter);
131
-				if($bytes >= 9000000) {
132
-					// AD has a default input buffer of 10 MB, we do not want
133
-					// to take even the chance to exceed it
134
-					$filter = $this->access->combineFilterWithOr($filterParts);
135
-					$bytes = 0;
136
-					$filterParts = array();
137
-					$users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
138
-					$dns = array_merge($dns, $users);
139
-				}
140
-			}
141
-			if(count($filterParts) > 0) {
142
-				$filter = $this->access->combineFilterWithOr($filterParts);
143
-				$users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
144
-				$dns = array_merge($dns, $users);
145
-			}
146
-			$members = $dns;
147
-		}
148
-
149
-		$isInGroup = in_array($userDN, $members);
150
-		$this->access->connection->writeToCache($cacheKey, $isInGroup);
151
-		$this->access->connection->writeToCache($cacheKeyMembers, $members);
152
-		$this->cachedGroupMembers[$gid] = $members;
153
-
154
-		return $isInGroup;
155
-	}
156
-
157
-	/**
158
-	 * @param string $dnGroup
159
-	 * @return array
160
-	 *
161
-	 * For a group that has user membership defined by an LDAP search url attribute returns the users
162
-	 * that match the search url otherwise returns an empty array.
163
-	 */
164
-	public function getDynamicGroupMembers($dnGroup) {
165
-		$dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
166
-
167
-		if (empty($dynamicGroupMemberURL)) {
168
-			return array();
169
-		}
170
-
171
-		$dynamicMembers = array();
172
-		$memberURLs = $this->access->readAttribute(
173
-			$dnGroup,
174
-			$dynamicGroupMemberURL,
175
-			$this->access->connection->ldapGroupFilter
176
-		);
177
-		if ($memberURLs !== false) {
178
-			// this group has the 'memberURL' attribute so this is a dynamic group
179
-			// example 1: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(o=HeadOffice)
180
-			// example 2: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(&(o=HeadOffice)(uidNumber>=500))
181
-			$pos = strpos($memberURLs[0], '(');
182
-			if ($pos !== false) {
183
-				$memberUrlFilter = substr($memberURLs[0], $pos);
184
-				$foundMembers = $this->access->searchUsers($memberUrlFilter,'dn');
185
-				$dynamicMembers = array();
186
-				foreach($foundMembers as $value) {
187
-					$dynamicMembers[$value['dn'][0]] = 1;
188
-				}
189
-			} else {
190
-				\OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
191
-					'of group ' . $dnGroup, \OCP\Util::DEBUG);
192
-			}
193
-		}
194
-		return $dynamicMembers;
195
-	}
196
-
197
-	/**
198
-	 * @param string $dnGroup
199
-	 * @param array|null &$seen
200
-	 * @return array|mixed|null
201
-	 */
202
-	private function _groupMembers($dnGroup, &$seen = null) {
203
-		if ($seen === null) {
204
-			$seen = array();
205
-		}
206
-		$allMembers = array();
207
-		if (array_key_exists($dnGroup, $seen)) {
208
-			// avoid loops
209
-			return array();
210
-		}
211
-		// used extensively in cron job, caching makes sense for nested groups
212
-		$cacheKey = '_groupMembers'.$dnGroup;
213
-		$groupMembers = $this->access->connection->getFromCache($cacheKey);
214
-		if(!is_null($groupMembers)) {
215
-			return $groupMembers;
216
-		}
217
-		$seen[$dnGroup] = 1;
218
-		$members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
219
-												$this->access->connection->ldapGroupFilter);
220
-		if (is_array($members)) {
221
-			foreach ($members as $memberDN) {
222
-				$allMembers[$memberDN] = 1;
223
-				$nestedGroups = $this->access->connection->ldapNestedGroups;
224
-				if (!empty($nestedGroups)) {
225
-					$subMembers = $this->_groupMembers($memberDN, $seen);
226
-					if ($subMembers) {
227
-						$allMembers = array_merge($allMembers, $subMembers);
228
-					}
229
-				}
230
-			}
231
-		}
43
+    protected $enabled = false;
44
+
45
+    /**
46
+     * @var string[] $cachedGroupMembers array of users with gid as key
47
+     */
48
+    protected $cachedGroupMembers;
49
+
50
+    /**
51
+     * @var string[] $cachedGroupsByMember array of groups with uid as key
52
+     */
53
+    protected $cachedGroupsByMember;
54
+
55
+    public function __construct(Access $access) {
56
+        parent::__construct($access);
57
+        $filter = $this->access->connection->ldapGroupFilter;
58
+        $gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
59
+        if(!empty($filter) && !empty($gassoc)) {
60
+            $this->enabled = true;
61
+        }
62
+
63
+        $this->cachedGroupMembers = new CappedMemoryCache();
64
+        $this->cachedGroupsByMember = new CappedMemoryCache();
65
+    }
66
+
67
+    /**
68
+     * is user in group?
69
+     * @param string $uid uid of the user
70
+     * @param string $gid gid of the group
71
+     * @return bool
72
+     *
73
+     * Checks whether the user is member of a group or not.
74
+     */
75
+    public function inGroup($uid, $gid) {
76
+        if(!$this->enabled) {
77
+            return false;
78
+        }
79
+        $cacheKey = 'inGroup'.$uid.':'.$gid;
80
+        $inGroup = $this->access->connection->getFromCache($cacheKey);
81
+        if(!is_null($inGroup)) {
82
+            return (bool)$inGroup;
83
+        }
84
+
85
+        $userDN = $this->access->username2dn($uid);
86
+
87
+        if(isset($this->cachedGroupMembers[$gid])) {
88
+            $isInGroup = in_array($userDN, $this->cachedGroupMembers[$gid]);
89
+            return $isInGroup;
90
+        }
91
+
92
+        $cacheKeyMembers = 'inGroup-members:'.$gid;
93
+        $members = $this->access->connection->getFromCache($cacheKeyMembers);
94
+        if(!is_null($members)) {
95
+            $this->cachedGroupMembers[$gid] = $members;
96
+            $isInGroup = in_array($userDN, $members);
97
+            $this->access->connection->writeToCache($cacheKey, $isInGroup);
98
+            return $isInGroup;
99
+        }
100
+
101
+        $groupDN = $this->access->groupname2dn($gid);
102
+        // just in case
103
+        if(!$groupDN || !$userDN) {
104
+            $this->access->connection->writeToCache($cacheKey, false);
105
+            return false;
106
+        }
107
+
108
+        //check primary group first
109
+        if($gid === $this->getUserPrimaryGroup($userDN)) {
110
+            $this->access->connection->writeToCache($cacheKey, true);
111
+            return true;
112
+        }
113
+
114
+        //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
115
+        $members = $this->_groupMembers($groupDN);
116
+        $members = array_keys($members); // uids are returned as keys
117
+        if(!is_array($members) || count($members) === 0) {
118
+            $this->access->connection->writeToCache($cacheKey, false);
119
+            return false;
120
+        }
121
+
122
+        //extra work if we don't get back user DNs
123
+        if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
124
+            $dns = array();
125
+            $filterParts = array();
126
+            $bytes = 0;
127
+            foreach($members as $mid) {
128
+                $filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
129
+                $filterParts[] = $filter;
130
+                $bytes += strlen($filter);
131
+                if($bytes >= 9000000) {
132
+                    // AD has a default input buffer of 10 MB, we do not want
133
+                    // to take even the chance to exceed it
134
+                    $filter = $this->access->combineFilterWithOr($filterParts);
135
+                    $bytes = 0;
136
+                    $filterParts = array();
137
+                    $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
138
+                    $dns = array_merge($dns, $users);
139
+                }
140
+            }
141
+            if(count($filterParts) > 0) {
142
+                $filter = $this->access->combineFilterWithOr($filterParts);
143
+                $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
144
+                $dns = array_merge($dns, $users);
145
+            }
146
+            $members = $dns;
147
+        }
148
+
149
+        $isInGroup = in_array($userDN, $members);
150
+        $this->access->connection->writeToCache($cacheKey, $isInGroup);
151
+        $this->access->connection->writeToCache($cacheKeyMembers, $members);
152
+        $this->cachedGroupMembers[$gid] = $members;
153
+
154
+        return $isInGroup;
155
+    }
156
+
157
+    /**
158
+     * @param string $dnGroup
159
+     * @return array
160
+     *
161
+     * For a group that has user membership defined by an LDAP search url attribute returns the users
162
+     * that match the search url otherwise returns an empty array.
163
+     */
164
+    public function getDynamicGroupMembers($dnGroup) {
165
+        $dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
166
+
167
+        if (empty($dynamicGroupMemberURL)) {
168
+            return array();
169
+        }
170
+
171
+        $dynamicMembers = array();
172
+        $memberURLs = $this->access->readAttribute(
173
+            $dnGroup,
174
+            $dynamicGroupMemberURL,
175
+            $this->access->connection->ldapGroupFilter
176
+        );
177
+        if ($memberURLs !== false) {
178
+            // this group has the 'memberURL' attribute so this is a dynamic group
179
+            // example 1: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(o=HeadOffice)
180
+            // example 2: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(&(o=HeadOffice)(uidNumber>=500))
181
+            $pos = strpos($memberURLs[0], '(');
182
+            if ($pos !== false) {
183
+                $memberUrlFilter = substr($memberURLs[0], $pos);
184
+                $foundMembers = $this->access->searchUsers($memberUrlFilter,'dn');
185
+                $dynamicMembers = array();
186
+                foreach($foundMembers as $value) {
187
+                    $dynamicMembers[$value['dn'][0]] = 1;
188
+                }
189
+            } else {
190
+                \OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
191
+                    'of group ' . $dnGroup, \OCP\Util::DEBUG);
192
+            }
193
+        }
194
+        return $dynamicMembers;
195
+    }
196
+
197
+    /**
198
+     * @param string $dnGroup
199
+     * @param array|null &$seen
200
+     * @return array|mixed|null
201
+     */
202
+    private function _groupMembers($dnGroup, &$seen = null) {
203
+        if ($seen === null) {
204
+            $seen = array();
205
+        }
206
+        $allMembers = array();
207
+        if (array_key_exists($dnGroup, $seen)) {
208
+            // avoid loops
209
+            return array();
210
+        }
211
+        // used extensively in cron job, caching makes sense for nested groups
212
+        $cacheKey = '_groupMembers'.$dnGroup;
213
+        $groupMembers = $this->access->connection->getFromCache($cacheKey);
214
+        if(!is_null($groupMembers)) {
215
+            return $groupMembers;
216
+        }
217
+        $seen[$dnGroup] = 1;
218
+        $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
219
+                                                $this->access->connection->ldapGroupFilter);
220
+        if (is_array($members)) {
221
+            foreach ($members as $memberDN) {
222
+                $allMembers[$memberDN] = 1;
223
+                $nestedGroups = $this->access->connection->ldapNestedGroups;
224
+                if (!empty($nestedGroups)) {
225
+                    $subMembers = $this->_groupMembers($memberDN, $seen);
226
+                    if ($subMembers) {
227
+                        $allMembers = array_merge($allMembers, $subMembers);
228
+                    }
229
+                }
230
+            }
231
+        }
232 232
 		
233
-		$allMembers = array_merge($allMembers, $this->getDynamicGroupMembers($dnGroup));
233
+        $allMembers = array_merge($allMembers, $this->getDynamicGroupMembers($dnGroup));
234 234
 		
235
-		$this->access->connection->writeToCache($cacheKey, $allMembers);
236
-		return $allMembers;
237
-	}
238
-
239
-	/**
240
-	 * @param string $DN
241
-	 * @param array|null &$seen
242
-	 * @return array
243
-	 */
244
-	private function _getGroupDNsFromMemberOf($DN, &$seen = null) {
245
-		if ($seen === null) {
246
-			$seen = array();
247
-		}
248
-		if (array_key_exists($DN, $seen)) {
249
-			// avoid loops
250
-			return array();
251
-		}
252
-		$seen[$DN] = 1;
253
-		$groups = $this->access->readAttribute($DN, 'memberOf');
254
-		if (!is_array($groups)) {
255
-			return array();
256
-		}
257
-		$groups = $this->access->groupsMatchFilter($groups);
258
-		$allGroups =  $groups;
259
-		$nestedGroups = $this->access->connection->ldapNestedGroups;
260
-		if (intval($nestedGroups) === 1) {
261
-			foreach ($groups as $group) {
262
-				$subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
263
-				$allGroups = array_merge($allGroups, $subGroups);
264
-			}
265
-		}
266
-		return $allGroups;	
267
-	}
268
-
269
-	/**
270
-	 * translates a primary group ID into an ownCloud internal name
271
-	 * @param string $gid as given by primaryGroupID on AD
272
-	 * @param string $dn a DN that belongs to the same domain as the group
273
-	 * @return string|bool
274
-	 */
275
-	public function primaryGroupID2Name($gid, $dn) {
276
-		$cacheKey = 'primaryGroupIDtoName';
277
-		$groupNames = $this->access->connection->getFromCache($cacheKey);
278
-		if(!is_null($groupNames) && isset($groupNames[$gid])) {
279
-			return $groupNames[$gid];
280
-		}
281
-
282
-		$domainObjectSid = $this->access->getSID($dn);
283
-		if($domainObjectSid === false) {
284
-			return false;
285
-		}
286
-
287
-		//we need to get the DN from LDAP
288
-		$filter = $this->access->combineFilterWithAnd(array(
289
-			$this->access->connection->ldapGroupFilter,
290
-			'objectsid=' . $domainObjectSid . '-' . $gid
291
-		));
292
-		$result = $this->access->searchGroups($filter, array('dn'), 1);
293
-		if(empty($result)) {
294
-			return false;
295
-		}
296
-		$dn = $result[0]['dn'][0];
297
-
298
-		//and now the group name
299
-		//NOTE once we have separate ownCloud group IDs and group names we can
300
-		//directly read the display name attribute instead of the DN
301
-		$name = $this->access->dn2groupname($dn);
302
-
303
-		$this->access->connection->writeToCache($cacheKey, $name);
304
-
305
-		return $name;
306
-	}
307
-
308
-	/**
309
-	 * returns the entry's primary group ID
310
-	 * @param string $dn
311
-	 * @param string $attribute
312
-	 * @return string|bool
313
-	 */
314
-	private function getEntryGroupID($dn, $attribute) {
315
-		$value = $this->access->readAttribute($dn, $attribute);
316
-		if(is_array($value) && !empty($value)) {
317
-			return $value[0];
318
-		}
319
-		return false;
320
-	}
321
-
322
-	/**
323
-	 * returns the group's primary ID
324
-	 * @param string $dn
325
-	 * @return string|bool
326
-	 */
327
-	public function getGroupPrimaryGroupID($dn) {
328
-		return $this->getEntryGroupID($dn, 'primaryGroupToken');
329
-	}
330
-
331
-	/**
332
-	 * returns the user's primary group ID
333
-	 * @param string $dn
334
-	 * @return string|bool
335
-	 */
336
-	public function getUserPrimaryGroupIDs($dn) {
337
-		$primaryGroupID = false;
338
-		if($this->access->connection->hasPrimaryGroups) {
339
-			$primaryGroupID = $this->getEntryGroupID($dn, 'primaryGroupID');
340
-			if($primaryGroupID === false) {
341
-				$this->access->connection->hasPrimaryGroups = false;
342
-			}
343
-		}
344
-		return $primaryGroupID;
345
-	}
346
-
347
-	/**
348
-	 * returns a filter for a "users in primary group" search or count operation
349
-	 *
350
-	 * @param string $groupDN
351
-	 * @param string $search
352
-	 * @return string
353
-	 * @throws \Exception
354
-	 */
355
-	private function prepareFilterForUsersInPrimaryGroup($groupDN, $search = '') {
356
-		$groupID = $this->getGroupPrimaryGroupID($groupDN);
357
-		if($groupID === false) {
358
-			throw new \Exception('Not a valid group');
359
-		}
360
-
361
-		$filterParts = [];
362
-		$filterParts[] = $this->access->getFilterForUserCount();
363
-		if ($search !== '') {
364
-			$filterParts[] = $this->access->getFilterPartForUserSearch($search);
365
-		}
366
-		$filterParts[] = 'primaryGroupID=' . $groupID;
367
-
368
-		$filter = $this->access->combineFilterWithAnd($filterParts);
369
-
370
-		return $filter;
371
-	}
372
-
373
-	/**
374
-	 * returns a list of users that have the given group as primary group
375
-	 *
376
-	 * @param string $groupDN
377
-	 * @param string $search
378
-	 * @param int $limit
379
-	 * @param int $offset
380
-	 * @return string[]
381
-	 */
382
-	public function getUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
383
-		try {
384
-			$filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
385
-			$users = $this->access->fetchListOfUsers(
386
-				$filter,
387
-				array($this->access->connection->ldapUserDisplayName, 'dn'),
388
-				$limit,
389
-				$offset
390
-			);
391
-			return $this->access->ownCloudUserNames($users);
392
-		} catch (\Exception $e) {
393
-			return array();
394
-		}
395
-	}
396
-
397
-	/**
398
-	 * returns the number of users that have the given group as primary group
399
-	 *
400
-	 * @param string $groupDN
401
-	 * @param string $search
402
-	 * @param int $limit
403
-	 * @param int $offset
404
-	 * @return int
405
-	 */
406
-	public function countUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
407
-		try {
408
-			$filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
409
-			$users = $this->access->countUsers($filter, array('dn'), $limit, $offset);
410
-			return (int)$users;
411
-		} catch (\Exception $e) {
412
-			return 0;
413
-		}
414
-	}
415
-
416
-	/**
417
-	 * gets the primary group of a user
418
-	 * @param string $dn
419
-	 * @return string
420
-	 */
421
-	public function getUserPrimaryGroup($dn) {
422
-		$groupID = $this->getUserPrimaryGroupIDs($dn);
423
-		if($groupID !== false) {
424
-			$groupName = $this->primaryGroupID2Name($groupID, $dn);
425
-			if($groupName !== false) {
426
-				return $groupName;
427
-			}
428
-		}
429
-
430
-		return false;
431
-	}
432
-
433
-	/**
434
-	 * Get all groups a user belongs to
435
-	 * @param string $uid Name of the user
436
-	 * @return array with group names
437
-	 *
438
-	 * This function fetches all groups a user belongs to. It does not check
439
-	 * if the user exists at all.
440
-	 *
441
-	 * This function includes groups based on dynamic group membership.
442
-	 */
443
-	public function getUserGroups($uid) {
444
-		if(!$this->enabled) {
445
-			return array();
446
-		}
447
-		$cacheKey = 'getUserGroups'.$uid;
448
-		$userGroups = $this->access->connection->getFromCache($cacheKey);
449
-		if(!is_null($userGroups)) {
450
-			return $userGroups;
451
-		}
452
-		$userDN = $this->access->username2dn($uid);
453
-		if(!$userDN) {
454
-			$this->access->connection->writeToCache($cacheKey, array());
455
-			return array();
456
-		}
457
-
458
-		$groups = [];
459
-		$primaryGroup = $this->getUserPrimaryGroup($userDN);
460
-
461
-		$dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
462
-
463
-		if (!empty($dynamicGroupMemberURL)) {
464
-			// look through dynamic groups to add them to the result array if needed
465
-			$groupsToMatch = $this->access->fetchListOfGroups(
466
-				$this->access->connection->ldapGroupFilter,array('dn',$dynamicGroupMemberURL));
467
-			foreach($groupsToMatch as $dynamicGroup) {
468
-				if (!array_key_exists($dynamicGroupMemberURL, $dynamicGroup)) {
469
-					continue;
470
-				}
471
-				$pos = strpos($dynamicGroup[$dynamicGroupMemberURL][0], '(');
472
-				if ($pos !== false) {
473
-					$memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0],$pos);
474
-					// apply filter via ldap search to see if this user is in this
475
-					// dynamic group
476
-					$userMatch = $this->access->readAttribute(
477
-						$userDN,
478
-						$this->access->connection->ldapUserDisplayName,
479
-						$memberUrlFilter
480
-					);
481
-					if ($userMatch !== false) {
482
-						// match found so this user is in this group
483
-						$groupName = $this->access->dn2groupname($dynamicGroup['dn'][0]);
484
-						if(is_string($groupName)) {
485
-							// be sure to never return false if the dn could not be
486
-							// resolved to a name, for whatever reason.
487
-							$groups[] = $groupName;
488
-						}
489
-					}
490
-				} else {
491
-					\OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
492
-						'of group ' . print_r($dynamicGroup, true), \OCP\Util::DEBUG);
493
-				}
494
-			}
495
-		}
496
-
497
-		// if possible, read out membership via memberOf. It's far faster than
498
-		// performing a search, which still is a fallback later.
499
-		if(intval($this->access->connection->hasMemberOfFilterSupport) === 1
500
-			&& intval($this->access->connection->useMemberOfToDetectMembership) === 1
501
-		) {
502
-			$groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
503
-			if (is_array($groupDNs)) {
504
-				foreach ($groupDNs as $dn) {
505
-					$groupName = $this->access->dn2groupname($dn);
506
-					if(is_string($groupName)) {
507
-						// be sure to never return false if the dn could not be
508
-						// resolved to a name, for whatever reason.
509
-						$groups[] = $groupName;
510
-					}
511
-				}
512
-			}
235
+        $this->access->connection->writeToCache($cacheKey, $allMembers);
236
+        return $allMembers;
237
+    }
238
+
239
+    /**
240
+     * @param string $DN
241
+     * @param array|null &$seen
242
+     * @return array
243
+     */
244
+    private function _getGroupDNsFromMemberOf($DN, &$seen = null) {
245
+        if ($seen === null) {
246
+            $seen = array();
247
+        }
248
+        if (array_key_exists($DN, $seen)) {
249
+            // avoid loops
250
+            return array();
251
+        }
252
+        $seen[$DN] = 1;
253
+        $groups = $this->access->readAttribute($DN, 'memberOf');
254
+        if (!is_array($groups)) {
255
+            return array();
256
+        }
257
+        $groups = $this->access->groupsMatchFilter($groups);
258
+        $allGroups =  $groups;
259
+        $nestedGroups = $this->access->connection->ldapNestedGroups;
260
+        if (intval($nestedGroups) === 1) {
261
+            foreach ($groups as $group) {
262
+                $subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
263
+                $allGroups = array_merge($allGroups, $subGroups);
264
+            }
265
+        }
266
+        return $allGroups;	
267
+    }
268
+
269
+    /**
270
+     * translates a primary group ID into an ownCloud internal name
271
+     * @param string $gid as given by primaryGroupID on AD
272
+     * @param string $dn a DN that belongs to the same domain as the group
273
+     * @return string|bool
274
+     */
275
+    public function primaryGroupID2Name($gid, $dn) {
276
+        $cacheKey = 'primaryGroupIDtoName';
277
+        $groupNames = $this->access->connection->getFromCache($cacheKey);
278
+        if(!is_null($groupNames) && isset($groupNames[$gid])) {
279
+            return $groupNames[$gid];
280
+        }
281
+
282
+        $domainObjectSid = $this->access->getSID($dn);
283
+        if($domainObjectSid === false) {
284
+            return false;
285
+        }
286
+
287
+        //we need to get the DN from LDAP
288
+        $filter = $this->access->combineFilterWithAnd(array(
289
+            $this->access->connection->ldapGroupFilter,
290
+            'objectsid=' . $domainObjectSid . '-' . $gid
291
+        ));
292
+        $result = $this->access->searchGroups($filter, array('dn'), 1);
293
+        if(empty($result)) {
294
+            return false;
295
+        }
296
+        $dn = $result[0]['dn'][0];
297
+
298
+        //and now the group name
299
+        //NOTE once we have separate ownCloud group IDs and group names we can
300
+        //directly read the display name attribute instead of the DN
301
+        $name = $this->access->dn2groupname($dn);
302
+
303
+        $this->access->connection->writeToCache($cacheKey, $name);
304
+
305
+        return $name;
306
+    }
307
+
308
+    /**
309
+     * returns the entry's primary group ID
310
+     * @param string $dn
311
+     * @param string $attribute
312
+     * @return string|bool
313
+     */
314
+    private function getEntryGroupID($dn, $attribute) {
315
+        $value = $this->access->readAttribute($dn, $attribute);
316
+        if(is_array($value) && !empty($value)) {
317
+            return $value[0];
318
+        }
319
+        return false;
320
+    }
321
+
322
+    /**
323
+     * returns the group's primary ID
324
+     * @param string $dn
325
+     * @return string|bool
326
+     */
327
+    public function getGroupPrimaryGroupID($dn) {
328
+        return $this->getEntryGroupID($dn, 'primaryGroupToken');
329
+    }
330
+
331
+    /**
332
+     * returns the user's primary group ID
333
+     * @param string $dn
334
+     * @return string|bool
335
+     */
336
+    public function getUserPrimaryGroupIDs($dn) {
337
+        $primaryGroupID = false;
338
+        if($this->access->connection->hasPrimaryGroups) {
339
+            $primaryGroupID = $this->getEntryGroupID($dn, 'primaryGroupID');
340
+            if($primaryGroupID === false) {
341
+                $this->access->connection->hasPrimaryGroups = false;
342
+            }
343
+        }
344
+        return $primaryGroupID;
345
+    }
346
+
347
+    /**
348
+     * returns a filter for a "users in primary group" search or count operation
349
+     *
350
+     * @param string $groupDN
351
+     * @param string $search
352
+     * @return string
353
+     * @throws \Exception
354
+     */
355
+    private function prepareFilterForUsersInPrimaryGroup($groupDN, $search = '') {
356
+        $groupID = $this->getGroupPrimaryGroupID($groupDN);
357
+        if($groupID === false) {
358
+            throw new \Exception('Not a valid group');
359
+        }
360
+
361
+        $filterParts = [];
362
+        $filterParts[] = $this->access->getFilterForUserCount();
363
+        if ($search !== '') {
364
+            $filterParts[] = $this->access->getFilterPartForUserSearch($search);
365
+        }
366
+        $filterParts[] = 'primaryGroupID=' . $groupID;
367
+
368
+        $filter = $this->access->combineFilterWithAnd($filterParts);
369
+
370
+        return $filter;
371
+    }
372
+
373
+    /**
374
+     * returns a list of users that have the given group as primary group
375
+     *
376
+     * @param string $groupDN
377
+     * @param string $search
378
+     * @param int $limit
379
+     * @param int $offset
380
+     * @return string[]
381
+     */
382
+    public function getUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
383
+        try {
384
+            $filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
385
+            $users = $this->access->fetchListOfUsers(
386
+                $filter,
387
+                array($this->access->connection->ldapUserDisplayName, 'dn'),
388
+                $limit,
389
+                $offset
390
+            );
391
+            return $this->access->ownCloudUserNames($users);
392
+        } catch (\Exception $e) {
393
+            return array();
394
+        }
395
+    }
396
+
397
+    /**
398
+     * returns the number of users that have the given group as primary group
399
+     *
400
+     * @param string $groupDN
401
+     * @param string $search
402
+     * @param int $limit
403
+     * @param int $offset
404
+     * @return int
405
+     */
406
+    public function countUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
407
+        try {
408
+            $filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
409
+            $users = $this->access->countUsers($filter, array('dn'), $limit, $offset);
410
+            return (int)$users;
411
+        } catch (\Exception $e) {
412
+            return 0;
413
+        }
414
+    }
415
+
416
+    /**
417
+     * gets the primary group of a user
418
+     * @param string $dn
419
+     * @return string
420
+     */
421
+    public function getUserPrimaryGroup($dn) {
422
+        $groupID = $this->getUserPrimaryGroupIDs($dn);
423
+        if($groupID !== false) {
424
+            $groupName = $this->primaryGroupID2Name($groupID, $dn);
425
+            if($groupName !== false) {
426
+                return $groupName;
427
+            }
428
+        }
429
+
430
+        return false;
431
+    }
432
+
433
+    /**
434
+     * Get all groups a user belongs to
435
+     * @param string $uid Name of the user
436
+     * @return array with group names
437
+     *
438
+     * This function fetches all groups a user belongs to. It does not check
439
+     * if the user exists at all.
440
+     *
441
+     * This function includes groups based on dynamic group membership.
442
+     */
443
+    public function getUserGroups($uid) {
444
+        if(!$this->enabled) {
445
+            return array();
446
+        }
447
+        $cacheKey = 'getUserGroups'.$uid;
448
+        $userGroups = $this->access->connection->getFromCache($cacheKey);
449
+        if(!is_null($userGroups)) {
450
+            return $userGroups;
451
+        }
452
+        $userDN = $this->access->username2dn($uid);
453
+        if(!$userDN) {
454
+            $this->access->connection->writeToCache($cacheKey, array());
455
+            return array();
456
+        }
457
+
458
+        $groups = [];
459
+        $primaryGroup = $this->getUserPrimaryGroup($userDN);
460
+
461
+        $dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
462
+
463
+        if (!empty($dynamicGroupMemberURL)) {
464
+            // look through dynamic groups to add them to the result array if needed
465
+            $groupsToMatch = $this->access->fetchListOfGroups(
466
+                $this->access->connection->ldapGroupFilter,array('dn',$dynamicGroupMemberURL));
467
+            foreach($groupsToMatch as $dynamicGroup) {
468
+                if (!array_key_exists($dynamicGroupMemberURL, $dynamicGroup)) {
469
+                    continue;
470
+                }
471
+                $pos = strpos($dynamicGroup[$dynamicGroupMemberURL][0], '(');
472
+                if ($pos !== false) {
473
+                    $memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0],$pos);
474
+                    // apply filter via ldap search to see if this user is in this
475
+                    // dynamic group
476
+                    $userMatch = $this->access->readAttribute(
477
+                        $userDN,
478
+                        $this->access->connection->ldapUserDisplayName,
479
+                        $memberUrlFilter
480
+                    );
481
+                    if ($userMatch !== false) {
482
+                        // match found so this user is in this group
483
+                        $groupName = $this->access->dn2groupname($dynamicGroup['dn'][0]);
484
+                        if(is_string($groupName)) {
485
+                            // be sure to never return false if the dn could not be
486
+                            // resolved to a name, for whatever reason.
487
+                            $groups[] = $groupName;
488
+                        }
489
+                    }
490
+                } else {
491
+                    \OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
492
+                        'of group ' . print_r($dynamicGroup, true), \OCP\Util::DEBUG);
493
+                }
494
+            }
495
+        }
496
+
497
+        // if possible, read out membership via memberOf. It's far faster than
498
+        // performing a search, which still is a fallback later.
499
+        if(intval($this->access->connection->hasMemberOfFilterSupport) === 1
500
+            && intval($this->access->connection->useMemberOfToDetectMembership) === 1
501
+        ) {
502
+            $groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
503
+            if (is_array($groupDNs)) {
504
+                foreach ($groupDNs as $dn) {
505
+                    $groupName = $this->access->dn2groupname($dn);
506
+                    if(is_string($groupName)) {
507
+                        // be sure to never return false if the dn could not be
508
+                        // resolved to a name, for whatever reason.
509
+                        $groups[] = $groupName;
510
+                    }
511
+                }
512
+            }
513 513
 			
514
-			if($primaryGroup !== false) {
515
-				$groups[] = $primaryGroup;
516
-			}
517
-			$this->access->connection->writeToCache($cacheKey, $groups);
518
-			return $groups;
519
-		}
520
-
521
-		//uniqueMember takes DN, memberuid the uid, so we need to distinguish
522
-		if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
523
-			|| (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
524
-		) {
525
-			$uid = $userDN;
526
-		} else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
527
-			$result = $this->access->readAttribute($userDN, 'uid');
528
-			if ($result === false) {
529
-				\OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '.
530
-					$this->access->connection->ldapHost, \OCP\Util::DEBUG);
531
-			}
532
-			$uid = $result[0];
533
-		} else {
534
-			// just in case
535
-			$uid = $userDN;
536
-		}
537
-
538
-		if(isset($this->cachedGroupsByMember[$uid])) {
539
-			$groups = array_merge($groups, $this->cachedGroupsByMember[$uid]);
540
-		} else {
541
-			$groupsByMember = array_values($this->getGroupsByMember($uid));
542
-			$groupsByMember = $this->access->ownCloudGroupNames($groupsByMember);
543
-			$this->cachedGroupsByMember[$uid] = $groupsByMember;
544
-			$groups = array_merge($groups, $groupsByMember);
545
-		}
546
-
547
-		if($primaryGroup !== false) {
548
-			$groups[] = $primaryGroup;
549
-		}
550
-
551
-		$groups = array_unique($groups, SORT_LOCALE_STRING);
552
-		$this->access->connection->writeToCache($cacheKey, $groups);
553
-
554
-		return $groups;
555
-	}
556
-
557
-	/**
558
-	 * @param string $dn
559
-	 * @param array|null &$seen
560
-	 * @return array
561
-	 */
562
-	private function getGroupsByMember($dn, &$seen = null) {
563
-		if ($seen === null) {
564
-			$seen = array();
565
-		}
566
-		$allGroups = array();
567
-		if (array_key_exists($dn, $seen)) {
568
-			// avoid loops
569
-			return array();
570
-		}
571
-		$seen[$dn] = true;
572
-		$filter = $this->access->combineFilterWithAnd(array(
573
-			$this->access->connection->ldapGroupFilter,
574
-			$this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
575
-		));
576
-		$groups = $this->access->fetchListOfGroups($filter,
577
-			array($this->access->connection->ldapGroupDisplayName, 'dn'));
578
-		if (is_array($groups)) {
579
-			foreach ($groups as $groupobj) {
580
-				$groupDN = $groupobj['dn'][0];
581
-				$allGroups[$groupDN] = $groupobj;
582
-				$nestedGroups = $this->access->connection->ldapNestedGroups;
583
-				if (!empty($nestedGroups)) {
584
-					$supergroups = $this->getGroupsByMember($groupDN, $seen);
585
-					if (is_array($supergroups) && (count($supergroups)>0)) {
586
-						$allGroups = array_merge($allGroups, $supergroups);
587
-					}
588
-				}
589
-			}
590
-		}
591
-		return $allGroups;
592
-	}
593
-
594
-	/**
595
-	 * get a list of all users in a group
596
-	 *
597
-	 * @param string $gid
598
-	 * @param string $search
599
-	 * @param int $limit
600
-	 * @param int $offset
601
-	 * @return array with user ids
602
-	 */
603
-	public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
604
-		if(!$this->enabled) {
605
-			return array();
606
-		}
607
-		if(!$this->groupExists($gid)) {
608
-			return array();
609
-		}
610
-		$search = $this->access->escapeFilterPart($search, true);
611
-		$cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
612
-		// check for cache of the exact query
613
-		$groupUsers = $this->access->connection->getFromCache($cacheKey);
614
-		if(!is_null($groupUsers)) {
615
-			return $groupUsers;
616
-		}
617
-
618
-		// check for cache of the query without limit and offset
619
-		$groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
620
-		if(!is_null($groupUsers)) {
621
-			$groupUsers = array_slice($groupUsers, $offset, $limit);
622
-			$this->access->connection->writeToCache($cacheKey, $groupUsers);
623
-			return $groupUsers;
624
-		}
625
-
626
-		if($limit === -1) {
627
-			$limit = null;
628
-		}
629
-		$groupDN = $this->access->groupname2dn($gid);
630
-		if(!$groupDN) {
631
-			// group couldn't be found, return empty resultset
632
-			$this->access->connection->writeToCache($cacheKey, array());
633
-			return array();
634
-		}
635
-
636
-		$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
637
-		$members = array_keys($this->_groupMembers($groupDN));
638
-		if(!$members && empty($primaryUsers)) {
639
-			//in case users could not be retrieved, return empty result set
640
-			$this->access->connection->writeToCache($cacheKey, array());
641
-			return array();
642
-		}
643
-
644
-		$groupUsers = array();
645
-		$isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
646
-		$attrs = $this->access->userManager->getAttributes(true);
647
-		foreach($members as $member) {
648
-			if($isMemberUid) {
649
-				//we got uids, need to get their DNs to 'translate' them to user names
650
-				$filter = $this->access->combineFilterWithAnd(array(
651
-					str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
652
-					$this->access->getFilterPartForUserSearch($search)
653
-				));
654
-				$ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
655
-				if(count($ldap_users) < 1) {
656
-					continue;
657
-				}
658
-				$groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
659
-			} else {
660
-				//we got DNs, check if we need to filter by search or we can give back all of them
661
-				if ($search !== '') {
662
-					if(!$this->access->readAttribute($member,
663
-						$this->access->connection->ldapUserDisplayName,
664
-						$this->access->getFilterPartForUserSearch($search))) {
665
-						continue;
666
-					}
667
-				}
668
-				// dn2username will also check if the users belong to the allowed base
669
-				if($ocname = $this->access->dn2username($member)) {
670
-					$groupUsers[] = $ocname;
671
-				}
672
-			}
673
-		}
674
-
675
-		$groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
676
-		natsort($groupUsers);
677
-		$this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
678
-		$groupUsers = array_slice($groupUsers, $offset, $limit);
679
-
680
-
681
-		$this->access->connection->writeToCache($cacheKey, $groupUsers);
682
-
683
-		return $groupUsers;
684
-	}
685
-
686
-	/**
687
-	 * returns the number of users in a group, who match the search term
688
-	 * @param string $gid the internal group name
689
-	 * @param string $search optional, a search string
690
-	 * @return int|bool
691
-	 */
692
-	public function countUsersInGroup($gid, $search = '') {
693
-		$cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
694
-		if(!$this->enabled || !$this->groupExists($gid)) {
695
-			return false;
696
-		}
697
-		$groupUsers = $this->access->connection->getFromCache($cacheKey);
698
-		if(!is_null($groupUsers)) {
699
-			return $groupUsers;
700
-		}
701
-
702
-		$groupDN = $this->access->groupname2dn($gid);
703
-		if(!$groupDN) {
704
-			// group couldn't be found, return empty result set
705
-			$this->access->connection->writeToCache($cacheKey, false);
706
-			return false;
707
-		}
708
-
709
-		$members = array_keys($this->_groupMembers($groupDN));
710
-		$primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
711
-		if(!$members && $primaryUserCount === 0) {
712
-			//in case users could not be retrieved, return empty result set
713
-			$this->access->connection->writeToCache($cacheKey, false);
714
-			return false;
715
-		}
716
-
717
-		if ($search === '') {
718
-			$groupUsers = count($members) + $primaryUserCount;
719
-			$this->access->connection->writeToCache($cacheKey, $groupUsers);
720
-			return $groupUsers;
721
-		}
722
-		$search = $this->access->escapeFilterPart($search, true);
723
-		$isMemberUid =
724
-			(strtolower($this->access->connection->ldapGroupMemberAssocAttr)
725
-			=== 'memberuid');
726
-
727
-		//we need to apply the search filter
728
-		//alternatives that need to be checked:
729
-		//a) get all users by search filter and array_intersect them
730
-		//b) a, but only when less than 1k 10k ?k users like it is
731
-		//c) put all DNs|uids in a LDAP filter, combine with the search string
732
-		//   and let it count.
733
-		//For now this is not important, because the only use of this method
734
-		//does not supply a search string
735
-		$groupUsers = array();
736
-		foreach($members as $member) {
737
-			if($isMemberUid) {
738
-				//we got uids, need to get their DNs to 'translate' them to user names
739
-				$filter = $this->access->combineFilterWithAnd(array(
740
-					str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
741
-					$this->access->getFilterPartForUserSearch($search)
742
-				));
743
-				$ldap_users = $this->access->fetchListOfUsers($filter, 'dn', 1);
744
-				if(count($ldap_users) < 1) {
745
-					continue;
746
-				}
747
-				$groupUsers[] = $this->access->dn2username($ldap_users[0]);
748
-			} else {
749
-				//we need to apply the search filter now
750
-				if(!$this->access->readAttribute($member,
751
-					$this->access->connection->ldapUserDisplayName,
752
-					$this->access->getFilterPartForUserSearch($search))) {
753
-					continue;
754
-				}
755
-				// dn2username will also check if the users belong to the allowed base
756
-				if($ocname = $this->access->dn2username($member)) {
757
-					$groupUsers[] = $ocname;
758
-				}
759
-			}
760
-		}
761
-
762
-		//and get users that have the group as primary
763
-		$primaryUsers = $this->countUsersInPrimaryGroup($groupDN, $search);
764
-
765
-		return count($groupUsers) + $primaryUsers;
766
-	}
767
-
768
-	/**
769
-	 * get a list of all groups
770
-	 *
771
-	 * @param string $search
772
-	 * @param $limit
773
-	 * @param int $offset
774
-	 * @return array with group names
775
-	 *
776
-	 * Returns a list with all groups (used by getGroups)
777
-	 */
778
-	protected function getGroupsChunk($search = '', $limit = -1, $offset = 0) {
779
-		if(!$this->enabled) {
780
-			return array();
781
-		}
782
-		$cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
783
-
784
-		//Check cache before driving unnecessary searches
785
-		\OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
786
-		$ldap_groups = $this->access->connection->getFromCache($cacheKey);
787
-		if(!is_null($ldap_groups)) {
788
-			return $ldap_groups;
789
-		}
790
-
791
-		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
792
-		// error. With a limit of 0, we get 0 results. So we pass null.
793
-		if($limit <= 0) {
794
-			$limit = null;
795
-		}
796
-		$filter = $this->access->combineFilterWithAnd(array(
797
-			$this->access->connection->ldapGroupFilter,
798
-			$this->access->getFilterPartForGroupSearch($search)
799
-		));
800
-		\OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
801
-		$ldap_groups = $this->access->fetchListOfGroups($filter,
802
-				array($this->access->connection->ldapGroupDisplayName, 'dn'),
803
-				$limit,
804
-				$offset);
805
-		$ldap_groups = $this->access->ownCloudGroupNames($ldap_groups);
806
-
807
-		$this->access->connection->writeToCache($cacheKey, $ldap_groups);
808
-		return $ldap_groups;
809
-	}
810
-
811
-	/**
812
-	 * get a list of all groups using a paged search
813
-	 *
814
-	 * @param string $search
815
-	 * @param int $limit
816
-	 * @param int $offset
817
-	 * @return array with group names
818
-	 *
819
-	 * Returns a list with all groups
820
-	 * Uses a paged search if available to override a
821
-	 * server side search limit.
822
-	 * (active directory has a limit of 1000 by default)
823
-	 */
824
-	public function getGroups($search = '', $limit = -1, $offset = 0) {
825
-		if(!$this->enabled) {
826
-			return array();
827
-		}
828
-		$search = $this->access->escapeFilterPart($search, true);
829
-		$pagingSize = intval($this->access->connection->ldapPagingSize);
830
-		if (!$this->access->connection->hasPagedResultSupport || $pagingSize <= 0) {
831
-			return $this->getGroupsChunk($search, $limit, $offset);
832
-		}
833
-		$maxGroups = 100000; // limit max results (just for safety reasons)
834
-		if ($limit > -1) {
835
-		   $overallLimit = min($limit + $offset, $maxGroups);
836
-		} else {
837
-		   $overallLimit = $maxGroups;
838
-		}
839
-		$chunkOffset = $offset;
840
-		$allGroups = array();
841
-		while ($chunkOffset < $overallLimit) {
842
-			$chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
843
-			$ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
844
-			$nread = count($ldapGroups);
845
-			\OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
846
-			if ($nread) {
847
-				$allGroups = array_merge($allGroups, $ldapGroups);
848
-				$chunkOffset += $nread;
849
-			}
850
-			if ($nread < $chunkLimit) {
851
-				break;
852
-			}
853
-		}
854
-		return $allGroups;
855
-	}
856
-
857
-	/**
858
-	 * @param string $group
859
-	 * @return bool
860
-	 */
861
-	public function groupMatchesFilter($group) {
862
-		return (strripos($group, $this->groupSearch) !== false);
863
-	}
864
-
865
-	/**
866
-	 * check if a group exists
867
-	 * @param string $gid
868
-	 * @return bool
869
-	 */
870
-	public function groupExists($gid) {
871
-		$groupExists = $this->access->connection->getFromCache('groupExists'.$gid);
872
-		if(!is_null($groupExists)) {
873
-			return (bool)$groupExists;
874
-		}
875
-
876
-		//getting dn, if false the group does not exist. If dn, it may be mapped
877
-		//only, requires more checking.
878
-		$dn = $this->access->groupname2dn($gid);
879
-		if(!$dn) {
880
-			$this->access->connection->writeToCache('groupExists'.$gid, false);
881
-			return false;
882
-		}
883
-
884
-		//if group really still exists, we will be able to read its objectclass
885
-		if(!is_array($this->access->readAttribute($dn, ''))) {
886
-			$this->access->connection->writeToCache('groupExists'.$gid, false);
887
-			return false;
888
-		}
889
-
890
-		$this->access->connection->writeToCache('groupExists'.$gid, true);
891
-		return true;
892
-	}
893
-
894
-	/**
895
-	* Check if backend implements actions
896
-	* @param int $actions bitwise-or'ed actions
897
-	* @return boolean
898
-	*
899
-	* Returns the supported actions as int to be
900
-	* compared with OC_USER_BACKEND_CREATE_USER etc.
901
-	*/
902
-	public function implementsActions($actions) {
903
-		return (bool)(\OC\Group\Backend::COUNT_USERS & $actions);
904
-	}
905
-
906
-	/**
907
-	 * Return access for LDAP interaction.
908
-	 * @return Access instance of Access for LDAP interaction
909
-	 */
910
-	public function getLDAPAccess() {
911
-		return $this->access;
912
-	}
514
+            if($primaryGroup !== false) {
515
+                $groups[] = $primaryGroup;
516
+            }
517
+            $this->access->connection->writeToCache($cacheKey, $groups);
518
+            return $groups;
519
+        }
520
+
521
+        //uniqueMember takes DN, memberuid the uid, so we need to distinguish
522
+        if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
523
+            || (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
524
+        ) {
525
+            $uid = $userDN;
526
+        } else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
527
+            $result = $this->access->readAttribute($userDN, 'uid');
528
+            if ($result === false) {
529
+                \OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '.
530
+                    $this->access->connection->ldapHost, \OCP\Util::DEBUG);
531
+            }
532
+            $uid = $result[0];
533
+        } else {
534
+            // just in case
535
+            $uid = $userDN;
536
+        }
537
+
538
+        if(isset($this->cachedGroupsByMember[$uid])) {
539
+            $groups = array_merge($groups, $this->cachedGroupsByMember[$uid]);
540
+        } else {
541
+            $groupsByMember = array_values($this->getGroupsByMember($uid));
542
+            $groupsByMember = $this->access->ownCloudGroupNames($groupsByMember);
543
+            $this->cachedGroupsByMember[$uid] = $groupsByMember;
544
+            $groups = array_merge($groups, $groupsByMember);
545
+        }
546
+
547
+        if($primaryGroup !== false) {
548
+            $groups[] = $primaryGroup;
549
+        }
550
+
551
+        $groups = array_unique($groups, SORT_LOCALE_STRING);
552
+        $this->access->connection->writeToCache($cacheKey, $groups);
553
+
554
+        return $groups;
555
+    }
556
+
557
+    /**
558
+     * @param string $dn
559
+     * @param array|null &$seen
560
+     * @return array
561
+     */
562
+    private function getGroupsByMember($dn, &$seen = null) {
563
+        if ($seen === null) {
564
+            $seen = array();
565
+        }
566
+        $allGroups = array();
567
+        if (array_key_exists($dn, $seen)) {
568
+            // avoid loops
569
+            return array();
570
+        }
571
+        $seen[$dn] = true;
572
+        $filter = $this->access->combineFilterWithAnd(array(
573
+            $this->access->connection->ldapGroupFilter,
574
+            $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
575
+        ));
576
+        $groups = $this->access->fetchListOfGroups($filter,
577
+            array($this->access->connection->ldapGroupDisplayName, 'dn'));
578
+        if (is_array($groups)) {
579
+            foreach ($groups as $groupobj) {
580
+                $groupDN = $groupobj['dn'][0];
581
+                $allGroups[$groupDN] = $groupobj;
582
+                $nestedGroups = $this->access->connection->ldapNestedGroups;
583
+                if (!empty($nestedGroups)) {
584
+                    $supergroups = $this->getGroupsByMember($groupDN, $seen);
585
+                    if (is_array($supergroups) && (count($supergroups)>0)) {
586
+                        $allGroups = array_merge($allGroups, $supergroups);
587
+                    }
588
+                }
589
+            }
590
+        }
591
+        return $allGroups;
592
+    }
593
+
594
+    /**
595
+     * get a list of all users in a group
596
+     *
597
+     * @param string $gid
598
+     * @param string $search
599
+     * @param int $limit
600
+     * @param int $offset
601
+     * @return array with user ids
602
+     */
603
+    public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
604
+        if(!$this->enabled) {
605
+            return array();
606
+        }
607
+        if(!$this->groupExists($gid)) {
608
+            return array();
609
+        }
610
+        $search = $this->access->escapeFilterPart($search, true);
611
+        $cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
612
+        // check for cache of the exact query
613
+        $groupUsers = $this->access->connection->getFromCache($cacheKey);
614
+        if(!is_null($groupUsers)) {
615
+            return $groupUsers;
616
+        }
617
+
618
+        // check for cache of the query without limit and offset
619
+        $groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
620
+        if(!is_null($groupUsers)) {
621
+            $groupUsers = array_slice($groupUsers, $offset, $limit);
622
+            $this->access->connection->writeToCache($cacheKey, $groupUsers);
623
+            return $groupUsers;
624
+        }
625
+
626
+        if($limit === -1) {
627
+            $limit = null;
628
+        }
629
+        $groupDN = $this->access->groupname2dn($gid);
630
+        if(!$groupDN) {
631
+            // group couldn't be found, return empty resultset
632
+            $this->access->connection->writeToCache($cacheKey, array());
633
+            return array();
634
+        }
635
+
636
+        $primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
637
+        $members = array_keys($this->_groupMembers($groupDN));
638
+        if(!$members && empty($primaryUsers)) {
639
+            //in case users could not be retrieved, return empty result set
640
+            $this->access->connection->writeToCache($cacheKey, array());
641
+            return array();
642
+        }
643
+
644
+        $groupUsers = array();
645
+        $isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
646
+        $attrs = $this->access->userManager->getAttributes(true);
647
+        foreach($members as $member) {
648
+            if($isMemberUid) {
649
+                //we got uids, need to get their DNs to 'translate' them to user names
650
+                $filter = $this->access->combineFilterWithAnd(array(
651
+                    str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
652
+                    $this->access->getFilterPartForUserSearch($search)
653
+                ));
654
+                $ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
655
+                if(count($ldap_users) < 1) {
656
+                    continue;
657
+                }
658
+                $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
659
+            } else {
660
+                //we got DNs, check if we need to filter by search or we can give back all of them
661
+                if ($search !== '') {
662
+                    if(!$this->access->readAttribute($member,
663
+                        $this->access->connection->ldapUserDisplayName,
664
+                        $this->access->getFilterPartForUserSearch($search))) {
665
+                        continue;
666
+                    }
667
+                }
668
+                // dn2username will also check if the users belong to the allowed base
669
+                if($ocname = $this->access->dn2username($member)) {
670
+                    $groupUsers[] = $ocname;
671
+                }
672
+            }
673
+        }
674
+
675
+        $groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
676
+        natsort($groupUsers);
677
+        $this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
678
+        $groupUsers = array_slice($groupUsers, $offset, $limit);
679
+
680
+
681
+        $this->access->connection->writeToCache($cacheKey, $groupUsers);
682
+
683
+        return $groupUsers;
684
+    }
685
+
686
+    /**
687
+     * returns the number of users in a group, who match the search term
688
+     * @param string $gid the internal group name
689
+     * @param string $search optional, a search string
690
+     * @return int|bool
691
+     */
692
+    public function countUsersInGroup($gid, $search = '') {
693
+        $cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
694
+        if(!$this->enabled || !$this->groupExists($gid)) {
695
+            return false;
696
+        }
697
+        $groupUsers = $this->access->connection->getFromCache($cacheKey);
698
+        if(!is_null($groupUsers)) {
699
+            return $groupUsers;
700
+        }
701
+
702
+        $groupDN = $this->access->groupname2dn($gid);
703
+        if(!$groupDN) {
704
+            // group couldn't be found, return empty result set
705
+            $this->access->connection->writeToCache($cacheKey, false);
706
+            return false;
707
+        }
708
+
709
+        $members = array_keys($this->_groupMembers($groupDN));
710
+        $primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
711
+        if(!$members && $primaryUserCount === 0) {
712
+            //in case users could not be retrieved, return empty result set
713
+            $this->access->connection->writeToCache($cacheKey, false);
714
+            return false;
715
+        }
716
+
717
+        if ($search === '') {
718
+            $groupUsers = count($members) + $primaryUserCount;
719
+            $this->access->connection->writeToCache($cacheKey, $groupUsers);
720
+            return $groupUsers;
721
+        }
722
+        $search = $this->access->escapeFilterPart($search, true);
723
+        $isMemberUid =
724
+            (strtolower($this->access->connection->ldapGroupMemberAssocAttr)
725
+            === 'memberuid');
726
+
727
+        //we need to apply the search filter
728
+        //alternatives that need to be checked:
729
+        //a) get all users by search filter and array_intersect them
730
+        //b) a, but only when less than 1k 10k ?k users like it is
731
+        //c) put all DNs|uids in a LDAP filter, combine with the search string
732
+        //   and let it count.
733
+        //For now this is not important, because the only use of this method
734
+        //does not supply a search string
735
+        $groupUsers = array();
736
+        foreach($members as $member) {
737
+            if($isMemberUid) {
738
+                //we got uids, need to get their DNs to 'translate' them to user names
739
+                $filter = $this->access->combineFilterWithAnd(array(
740
+                    str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
741
+                    $this->access->getFilterPartForUserSearch($search)
742
+                ));
743
+                $ldap_users = $this->access->fetchListOfUsers($filter, 'dn', 1);
744
+                if(count($ldap_users) < 1) {
745
+                    continue;
746
+                }
747
+                $groupUsers[] = $this->access->dn2username($ldap_users[0]);
748
+            } else {
749
+                //we need to apply the search filter now
750
+                if(!$this->access->readAttribute($member,
751
+                    $this->access->connection->ldapUserDisplayName,
752
+                    $this->access->getFilterPartForUserSearch($search))) {
753
+                    continue;
754
+                }
755
+                // dn2username will also check if the users belong to the allowed base
756
+                if($ocname = $this->access->dn2username($member)) {
757
+                    $groupUsers[] = $ocname;
758
+                }
759
+            }
760
+        }
761
+
762
+        //and get users that have the group as primary
763
+        $primaryUsers = $this->countUsersInPrimaryGroup($groupDN, $search);
764
+
765
+        return count($groupUsers) + $primaryUsers;
766
+    }
767
+
768
+    /**
769
+     * get a list of all groups
770
+     *
771
+     * @param string $search
772
+     * @param $limit
773
+     * @param int $offset
774
+     * @return array with group names
775
+     *
776
+     * Returns a list with all groups (used by getGroups)
777
+     */
778
+    protected function getGroupsChunk($search = '', $limit = -1, $offset = 0) {
779
+        if(!$this->enabled) {
780
+            return array();
781
+        }
782
+        $cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
783
+
784
+        //Check cache before driving unnecessary searches
785
+        \OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
786
+        $ldap_groups = $this->access->connection->getFromCache($cacheKey);
787
+        if(!is_null($ldap_groups)) {
788
+            return $ldap_groups;
789
+        }
790
+
791
+        // if we'd pass -1 to LDAP search, we'd end up in a Protocol
792
+        // error. With a limit of 0, we get 0 results. So we pass null.
793
+        if($limit <= 0) {
794
+            $limit = null;
795
+        }
796
+        $filter = $this->access->combineFilterWithAnd(array(
797
+            $this->access->connection->ldapGroupFilter,
798
+            $this->access->getFilterPartForGroupSearch($search)
799
+        ));
800
+        \OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
801
+        $ldap_groups = $this->access->fetchListOfGroups($filter,
802
+                array($this->access->connection->ldapGroupDisplayName, 'dn'),
803
+                $limit,
804
+                $offset);
805
+        $ldap_groups = $this->access->ownCloudGroupNames($ldap_groups);
806
+
807
+        $this->access->connection->writeToCache($cacheKey, $ldap_groups);
808
+        return $ldap_groups;
809
+    }
810
+
811
+    /**
812
+     * get a list of all groups using a paged search
813
+     *
814
+     * @param string $search
815
+     * @param int $limit
816
+     * @param int $offset
817
+     * @return array with group names
818
+     *
819
+     * Returns a list with all groups
820
+     * Uses a paged search if available to override a
821
+     * server side search limit.
822
+     * (active directory has a limit of 1000 by default)
823
+     */
824
+    public function getGroups($search = '', $limit = -1, $offset = 0) {
825
+        if(!$this->enabled) {
826
+            return array();
827
+        }
828
+        $search = $this->access->escapeFilterPart($search, true);
829
+        $pagingSize = intval($this->access->connection->ldapPagingSize);
830
+        if (!$this->access->connection->hasPagedResultSupport || $pagingSize <= 0) {
831
+            return $this->getGroupsChunk($search, $limit, $offset);
832
+        }
833
+        $maxGroups = 100000; // limit max results (just for safety reasons)
834
+        if ($limit > -1) {
835
+            $overallLimit = min($limit + $offset, $maxGroups);
836
+        } else {
837
+            $overallLimit = $maxGroups;
838
+        }
839
+        $chunkOffset = $offset;
840
+        $allGroups = array();
841
+        while ($chunkOffset < $overallLimit) {
842
+            $chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
843
+            $ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
844
+            $nread = count($ldapGroups);
845
+            \OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
846
+            if ($nread) {
847
+                $allGroups = array_merge($allGroups, $ldapGroups);
848
+                $chunkOffset += $nread;
849
+            }
850
+            if ($nread < $chunkLimit) {
851
+                break;
852
+            }
853
+        }
854
+        return $allGroups;
855
+    }
856
+
857
+    /**
858
+     * @param string $group
859
+     * @return bool
860
+     */
861
+    public function groupMatchesFilter($group) {
862
+        return (strripos($group, $this->groupSearch) !== false);
863
+    }
864
+
865
+    /**
866
+     * check if a group exists
867
+     * @param string $gid
868
+     * @return bool
869
+     */
870
+    public function groupExists($gid) {
871
+        $groupExists = $this->access->connection->getFromCache('groupExists'.$gid);
872
+        if(!is_null($groupExists)) {
873
+            return (bool)$groupExists;
874
+        }
875
+
876
+        //getting dn, if false the group does not exist. If dn, it may be mapped
877
+        //only, requires more checking.
878
+        $dn = $this->access->groupname2dn($gid);
879
+        if(!$dn) {
880
+            $this->access->connection->writeToCache('groupExists'.$gid, false);
881
+            return false;
882
+        }
883
+
884
+        //if group really still exists, we will be able to read its objectclass
885
+        if(!is_array($this->access->readAttribute($dn, ''))) {
886
+            $this->access->connection->writeToCache('groupExists'.$gid, false);
887
+            return false;
888
+        }
889
+
890
+        $this->access->connection->writeToCache('groupExists'.$gid, true);
891
+        return true;
892
+    }
893
+
894
+    /**
895
+     * Check if backend implements actions
896
+     * @param int $actions bitwise-or'ed actions
897
+     * @return boolean
898
+     *
899
+     * Returns the supported actions as int to be
900
+     * compared with OC_USER_BACKEND_CREATE_USER etc.
901
+     */
902
+    public function implementsActions($actions) {
903
+        return (bool)(\OC\Group\Backend::COUNT_USERS & $actions);
904
+    }
905
+
906
+    /**
907
+     * Return access for LDAP interaction.
908
+     * @return Access instance of Access for LDAP interaction
909
+     */
910
+    public function getLDAPAccess() {
911
+        return $this->access;
912
+    }
913 913
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Command/CheckUser.php 1 patch
Indentation   +96 added lines, -96 removed lines patch added patch discarded remove patch
@@ -36,101 +36,101 @@
 block discarded – undo
36 36
 use OCA\User_LDAP\User_Proxy;
37 37
 
38 38
 class CheckUser extends Command {
39
-	/** @var \OCA\User_LDAP\User_Proxy */
40
-	protected $backend;
41
-
42
-	/** @var \OCA\User_LDAP\Helper */
43
-	protected $helper;
44
-
45
-	/** @var \OCA\User_LDAP\User\DeletedUsersIndex */
46
-	protected $dui;
47
-
48
-	/** @var \OCA\User_LDAP\Mapping\UserMapping */
49
-	protected $mapping;
50
-
51
-	/**
52
-	 * @param User_Proxy $uBackend
53
-	 * @param LDAPHelper $helper
54
-	 * @param DeletedUsersIndex $dui
55
-	 * @param UserMapping $mapping
56
-	 */
57
-	public function __construct(User_Proxy $uBackend, LDAPHelper $helper, DeletedUsersIndex $dui, UserMapping $mapping) {
58
-		$this->backend = $uBackend;
59
-		$this->helper = $helper;
60
-		$this->dui = $dui;
61
-		$this->mapping = $mapping;
62
-		parent::__construct();
63
-	}
64
-
65
-	protected function configure() {
66
-		$this
67
-			->setName('ldap:check-user')
68
-			->setDescription('checks whether a user exists on LDAP.')
69
-			->addArgument(
70
-					'ocName',
71
-					InputArgument::REQUIRED,
72
-					'the user name as used in ownCloud'
73
-				     )
74
-			->addOption(
75
-					'force',
76
-					null,
77
-					InputOption::VALUE_NONE,
78
-					'ignores disabled LDAP configuration'
79
-				     )
80
-		;
81
-	}
82
-
83
-	protected function execute(InputInterface $input, OutputInterface $output) {
84
-		try {
85
-			$uid = $input->getArgument('ocName');
86
-			$this->isAllowed($input->getOption('force'));
87
-			$this->confirmUserIsMapped($uid);
88
-			$exists = $this->backend->userExistsOnLDAP($uid);
89
-			if($exists === true) {
90
-				$output->writeln('The user is still available on LDAP.');
91
-				return;
92
-			}
93
-
94
-			$this->dui->markUser($uid);
95
-			$output->writeln('The user does not exists on LDAP anymore.');
96
-			$output->writeln('Clean up the user\'s remnants by: ./occ user:delete "'
97
-				. $uid . '"');
98
-		} catch (\Exception $e) {
99
-			$output->writeln('<error>' . $e->getMessage(). '</error>');
100
-		}
101
-	}
102
-
103
-	/**
104
-	 * checks whether a user is actually mapped
105
-	 * @param string $ocName the username as used in ownCloud
106
-	 * @throws \Exception
107
-	 * @return true
108
-	 */
109
-	protected function confirmUserIsMapped($ocName) {
110
-		$dn = $this->mapping->getDNByName($ocName);
111
-		if ($dn === false) {
112
-			throw new \Exception('The given user is not a recognized LDAP user.');
113
-		}
114
-
115
-		return true;
116
-	}
117
-
118
-	/**
119
-	 * checks whether the setup allows reliable checking of LDAP user existence
120
-	 * @throws \Exception
121
-	 * @return true
122
-	 */
123
-	protected function isAllowed($force) {
124
-		if($this->helper->haveDisabledConfigurations() && !$force) {
125
-			throw new \Exception('Cannot check user existence, because '
126
-				. 'disabled LDAP configurations are present.');
127
-		}
128
-
129
-		// we don't check ldapUserCleanupInterval from config.php because this
130
-		// action is triggered manually, while the setting only controls the
131
-		// background job.
132
-
133
-		return true;
134
-	}
39
+    /** @var \OCA\User_LDAP\User_Proxy */
40
+    protected $backend;
41
+
42
+    /** @var \OCA\User_LDAP\Helper */
43
+    protected $helper;
44
+
45
+    /** @var \OCA\User_LDAP\User\DeletedUsersIndex */
46
+    protected $dui;
47
+
48
+    /** @var \OCA\User_LDAP\Mapping\UserMapping */
49
+    protected $mapping;
50
+
51
+    /**
52
+     * @param User_Proxy $uBackend
53
+     * @param LDAPHelper $helper
54
+     * @param DeletedUsersIndex $dui
55
+     * @param UserMapping $mapping
56
+     */
57
+    public function __construct(User_Proxy $uBackend, LDAPHelper $helper, DeletedUsersIndex $dui, UserMapping $mapping) {
58
+        $this->backend = $uBackend;
59
+        $this->helper = $helper;
60
+        $this->dui = $dui;
61
+        $this->mapping = $mapping;
62
+        parent::__construct();
63
+    }
64
+
65
+    protected function configure() {
66
+        $this
67
+            ->setName('ldap:check-user')
68
+            ->setDescription('checks whether a user exists on LDAP.')
69
+            ->addArgument(
70
+                    'ocName',
71
+                    InputArgument::REQUIRED,
72
+                    'the user name as used in ownCloud'
73
+                        )
74
+            ->addOption(
75
+                    'force',
76
+                    null,
77
+                    InputOption::VALUE_NONE,
78
+                    'ignores disabled LDAP configuration'
79
+                        )
80
+        ;
81
+    }
82
+
83
+    protected function execute(InputInterface $input, OutputInterface $output) {
84
+        try {
85
+            $uid = $input->getArgument('ocName');
86
+            $this->isAllowed($input->getOption('force'));
87
+            $this->confirmUserIsMapped($uid);
88
+            $exists = $this->backend->userExistsOnLDAP($uid);
89
+            if($exists === true) {
90
+                $output->writeln('The user is still available on LDAP.');
91
+                return;
92
+            }
93
+
94
+            $this->dui->markUser($uid);
95
+            $output->writeln('The user does not exists on LDAP anymore.');
96
+            $output->writeln('Clean up the user\'s remnants by: ./occ user:delete "'
97
+                . $uid . '"');
98
+        } catch (\Exception $e) {
99
+            $output->writeln('<error>' . $e->getMessage(). '</error>');
100
+        }
101
+    }
102
+
103
+    /**
104
+     * checks whether a user is actually mapped
105
+     * @param string $ocName the username as used in ownCloud
106
+     * @throws \Exception
107
+     * @return true
108
+     */
109
+    protected function confirmUserIsMapped($ocName) {
110
+        $dn = $this->mapping->getDNByName($ocName);
111
+        if ($dn === false) {
112
+            throw new \Exception('The given user is not a recognized LDAP user.');
113
+        }
114
+
115
+        return true;
116
+    }
117
+
118
+    /**
119
+     * checks whether the setup allows reliable checking of LDAP user existence
120
+     * @throws \Exception
121
+     * @return true
122
+     */
123
+    protected function isAllowed($force) {
124
+        if($this->helper->haveDisabledConfigurations() && !$force) {
125
+            throw new \Exception('Cannot check user existence, because '
126
+                . 'disabled LDAP configurations are present.');
127
+        }
128
+
129
+        // we don't check ldapUserCleanupInterval from config.php because this
130
+        // action is triggered manually, while the setting only controls the
131
+        // background job.
132
+
133
+        return true;
134
+    }
135 135
 
136 136
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Command/Search.php 1 patch
Indentation   +82 added lines, -82 removed lines patch added patch discarded remove patch
@@ -37,93 +37,93 @@
 block discarded – undo
37 37
 use OCP\IConfig;
38 38
 
39 39
 class Search extends Command {
40
-	/** @var \OCP\IConfig */
41
-	protected $ocConfig;
40
+    /** @var \OCP\IConfig */
41
+    protected $ocConfig;
42 42
 
43
-	/**
44
-	 * @param \OCP\IConfig $ocConfig
45
-	 */
46
-	public function __construct(IConfig $ocConfig) {
47
-		$this->ocConfig = $ocConfig;
48
-		parent::__construct();
49
-	}
43
+    /**
44
+     * @param \OCP\IConfig $ocConfig
45
+     */
46
+    public function __construct(IConfig $ocConfig) {
47
+        $this->ocConfig = $ocConfig;
48
+        parent::__construct();
49
+    }
50 50
 
51
-	protected function configure() {
52
-		$this
53
-			->setName('ldap:search')
54
-			->setDescription('executes a user or group search')
55
-			->addArgument(
56
-					'search',
57
-					InputArgument::REQUIRED,
58
-					'the search string (can be empty)'
59
-				     )
60
-			->addOption(
61
-					'group',
62
-					null,
63
-					InputOption::VALUE_NONE,
64
-					'searches groups instead of users'
65
-				     )
66
-			->addOption(
67
-					'offset',
68
-					null,
69
-					InputOption::VALUE_REQUIRED,
70
-					'The offset of the result set. Needs to be a multiple of limit. defaults to 0.',
71
-					0
72
-				     )
73
-			->addOption(
74
-					'limit',
75
-					null,
76
-					InputOption::VALUE_REQUIRED,
77
-					'limit the results. 0 means no limit, defaults to 15',
78
-					15
79
-				     )
80
-		;
81
-	}
51
+    protected function configure() {
52
+        $this
53
+            ->setName('ldap:search')
54
+            ->setDescription('executes a user or group search')
55
+            ->addArgument(
56
+                    'search',
57
+                    InputArgument::REQUIRED,
58
+                    'the search string (can be empty)'
59
+                        )
60
+            ->addOption(
61
+                    'group',
62
+                    null,
63
+                    InputOption::VALUE_NONE,
64
+                    'searches groups instead of users'
65
+                        )
66
+            ->addOption(
67
+                    'offset',
68
+                    null,
69
+                    InputOption::VALUE_REQUIRED,
70
+                    'The offset of the result set. Needs to be a multiple of limit. defaults to 0.',
71
+                    0
72
+                        )
73
+            ->addOption(
74
+                    'limit',
75
+                    null,
76
+                    InputOption::VALUE_REQUIRED,
77
+                    'limit the results. 0 means no limit, defaults to 15',
78
+                    15
79
+                        )
80
+        ;
81
+    }
82 82
 
83
-	/**
84
-	 * Tests whether the offset and limit options are valid
85
-	 * @param int $offset
86
-	 * @param int $limit
87
-	 * @throws \InvalidArgumentException
88
-	 */
89
-	protected function validateOffsetAndLimit($offset, $limit) {
90
-		if($limit < 0) {
91
-			throw new \InvalidArgumentException('limit must be  0 or greater');
92
-		}
93
-		if($offset  < 0) {
94
-			throw new \InvalidArgumentException('offset must be 0 or greater');
95
-		}
96
-		if($limit === 0 && $offset !== 0) {
97
-			throw new \InvalidArgumentException('offset must be 0 if limit is also set to 0');
98
-		}
99
-		if($offset > 0 && ($offset % $limit !== 0)) {
100
-			throw new \InvalidArgumentException('offset must be a multiple of limit');
101
-		}
102
-	}
83
+    /**
84
+     * Tests whether the offset and limit options are valid
85
+     * @param int $offset
86
+     * @param int $limit
87
+     * @throws \InvalidArgumentException
88
+     */
89
+    protected function validateOffsetAndLimit($offset, $limit) {
90
+        if($limit < 0) {
91
+            throw new \InvalidArgumentException('limit must be  0 or greater');
92
+        }
93
+        if($offset  < 0) {
94
+            throw new \InvalidArgumentException('offset must be 0 or greater');
95
+        }
96
+        if($limit === 0 && $offset !== 0) {
97
+            throw new \InvalidArgumentException('offset must be 0 if limit is also set to 0');
98
+        }
99
+        if($offset > 0 && ($offset % $limit !== 0)) {
100
+            throw new \InvalidArgumentException('offset must be a multiple of limit');
101
+        }
102
+    }
103 103
 
104
-	protected function execute(InputInterface $input, OutputInterface $output) {
105
-		$helper = new Helper($this->ocConfig);
106
-		$configPrefixes = $helper->getServerConfigurationPrefixes(true);
107
-		$ldapWrapper = new LDAP();
104
+    protected function execute(InputInterface $input, OutputInterface $output) {
105
+        $helper = new Helper($this->ocConfig);
106
+        $configPrefixes = $helper->getServerConfigurationPrefixes(true);
107
+        $ldapWrapper = new LDAP();
108 108
 
109
-		$offset = intval($input->getOption('offset'));
110
-		$limit = intval($input->getOption('limit'));
111
-		$this->validateOffsetAndLimit($offset, $limit);
109
+        $offset = intval($input->getOption('offset'));
110
+        $limit = intval($input->getOption('limit'));
111
+        $this->validateOffsetAndLimit($offset, $limit);
112 112
 
113
-		if($input->getOption('group')) {
114
-			$proxy = new Group_Proxy($configPrefixes, $ldapWrapper);
115
-			$getMethod = 'getGroups';
116
-			$printID = false;
117
-		} else {
118
-			$proxy = new User_Proxy($configPrefixes, $ldapWrapper, $this->ocConfig);
119
-			$getMethod = 'getDisplayNames';
120
-			$printID = true;
121
-		}
113
+        if($input->getOption('group')) {
114
+            $proxy = new Group_Proxy($configPrefixes, $ldapWrapper);
115
+            $getMethod = 'getGroups';
116
+            $printID = false;
117
+        } else {
118
+            $proxy = new User_Proxy($configPrefixes, $ldapWrapper, $this->ocConfig);
119
+            $getMethod = 'getDisplayNames';
120
+            $printID = true;
121
+        }
122 122
 
123
-		$result = $proxy->$getMethod($input->getArgument('search'), $limit, $offset);
124
-		foreach($result as $id => $name) {
125
-			$line = $name . ($printID ? ' ('.$id.')' : '');
126
-			$output->writeln($line);
127
-		}
128
-	}
123
+        $result = $proxy->$getMethod($input->getArgument('search'), $limit, $offset);
124
+        foreach($result as $id => $name) {
125
+            $line = $name . ($printID ? ' ('.$id.')' : '');
126
+            $output->writeln($line);
127
+        }
128
+    }
129 129
 }
Please login to merge, or discard this patch.