Passed
Push — master ( e80e0d...c605ef )
by Blizzz
15:00 queued 15s
created
apps/user_ldap/lib/Jobs/CleanUp.php 2 patches
Indentation   +171 added lines, -171 removed lines patch added patch discarded remove patch
@@ -40,175 +40,175 @@
 block discarded – undo
40 40
  * @package OCA\User_LDAP\Jobs;
41 41
  */
42 42
 class CleanUp extends TimedJob {
43
-	/** @var ?int $limit amount of users that should be checked per run */
44
-	protected $limit;
45
-
46
-	/** @var int $defaultIntervalMin default interval in minutes */
47
-	protected $defaultIntervalMin = 51;
48
-
49
-	/** @var User_LDAP|User_Proxy $userBackend */
50
-	protected $userBackend;
51
-
52
-	/** @var \OCP\IConfig $ocConfig */
53
-	protected $ocConfig;
54
-
55
-	/** @var \OCP\IDBConnection $db */
56
-	protected $db;
57
-
58
-	/** @var Helper $ldapHelper */
59
-	protected $ldapHelper;
60
-
61
-	/** @var UserMapping */
62
-	protected $mapping;
63
-
64
-	/** @var DeletedUsersIndex */
65
-	protected $dui;
66
-
67
-	public function __construct(User_Proxy $userBackend, DeletedUsersIndex $dui) {
68
-		$minutes = \OC::$server->getConfig()->getSystemValue(
69
-			'ldapUserCleanupInterval', (string)$this->defaultIntervalMin);
70
-		$this->setInterval((int)$minutes * 60);
71
-		$this->userBackend = $userBackend;
72
-		$this->dui = $dui;
73
-	}
74
-
75
-	/**
76
-	 * assigns the instances passed to run() to the class properties
77
-	 * @param array $arguments
78
-	 */
79
-	public function setArguments($arguments): void {
80
-		//Dependency Injection is not possible, because the constructor will
81
-		//only get values that are serialized to JSON. I.e. whatever we would
82
-		//pass in app.php we do add here, except something else is passed e.g.
83
-		//in tests.
84
-
85
-		if (isset($arguments['helper'])) {
86
-			$this->ldapHelper = $arguments['helper'];
87
-		} else {
88
-			$this->ldapHelper = new Helper(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection());
89
-		}
90
-
91
-		if (isset($arguments['ocConfig'])) {
92
-			$this->ocConfig = $arguments['ocConfig'];
93
-		} else {
94
-			$this->ocConfig = \OC::$server->getConfig();
95
-		}
96
-
97
-		if (isset($arguments['userBackend'])) {
98
-			$this->userBackend = $arguments['userBackend'];
99
-		}
100
-
101
-		if (isset($arguments['db'])) {
102
-			$this->db = $arguments['db'];
103
-		} else {
104
-			$this->db = \OC::$server->getDatabaseConnection();
105
-		}
106
-
107
-		if (isset($arguments['mapping'])) {
108
-			$this->mapping = $arguments['mapping'];
109
-		} else {
110
-			$this->mapping = new UserMapping($this->db);
111
-		}
112
-
113
-		if (isset($arguments['deletedUsersIndex'])) {
114
-			$this->dui = $arguments['deletedUsersIndex'];
115
-		}
116
-	}
117
-
118
-	/**
119
-	 * makes the background job do its work
120
-	 * @param array $argument
121
-	 */
122
-	public function run($argument): void {
123
-		$this->setArguments($argument);
124
-
125
-		if (!$this->isCleanUpAllowed()) {
126
-			return;
127
-		}
128
-		$users = $this->mapping->getList($this->getOffset(), $this->getChunkSize());
129
-		$resetOffset = $this->isOffsetResetNecessary(count($users));
130
-		$this->checkUsers($users);
131
-		$this->setOffset($resetOffset);
132
-	}
133
-
134
-	/**
135
-	 * checks whether next run should start at 0 again
136
-	 */
137
-	public function isOffsetResetNecessary(int $resultCount): bool {
138
-		return $resultCount < $this->getChunkSize();
139
-	}
140
-
141
-	/**
142
-	 * checks whether cleaning up LDAP users is allowed
143
-	 */
144
-	public function isCleanUpAllowed(): bool {
145
-		try {
146
-			if ($this->ldapHelper->haveDisabledConfigurations()) {
147
-				return false;
148
-			}
149
-		} catch (\Exception $e) {
150
-			return false;
151
-		}
152
-
153
-		return $this->isCleanUpEnabled();
154
-	}
155
-
156
-	/**
157
-	 * checks whether clean up is enabled by configuration
158
-	 */
159
-	private function isCleanUpEnabled(): bool {
160
-		return (bool)$this->ocConfig->getSystemValue(
161
-			'ldapUserCleanupInterval', (string)$this->defaultIntervalMin);
162
-	}
163
-
164
-	/**
165
-	 * checks users whether they are still existing
166
-	 * @param array $users result from getMappedUsers()
167
-	 */
168
-	private function checkUsers(array $users): void {
169
-		foreach ($users as $user) {
170
-			$this->checkUser($user);
171
-		}
172
-	}
173
-
174
-	/**
175
-	 * checks whether a user is still existing in LDAP
176
-	 * @param string[] $user
177
-	 */
178
-	private function checkUser(array $user): void {
179
-		if ($this->userBackend->userExistsOnLDAP($user['name'])) {
180
-			//still available, all good
181
-
182
-			return;
183
-		}
184
-
185
-		$this->dui->markUser($user['name']);
186
-	}
187
-
188
-	/**
189
-	 * gets the offset to fetch users from the mappings table
190
-	 */
191
-	private function getOffset(): int {
192
-		return (int)$this->ocConfig->getAppValue('user_ldap', 'cleanUpJobOffset', '0');
193
-	}
194
-
195
-	/**
196
-	 * sets the new offset for the next run
197
-	 * @param bool $reset whether the offset should be set to 0
198
-	 */
199
-	public function setOffset(bool $reset = false): void {
200
-		$newOffset = $reset ? 0 :
201
-			$this->getOffset() + $this->getChunkSize();
202
-		$this->ocConfig->setAppValue('user_ldap', 'cleanUpJobOffset', (string)$newOffset);
203
-	}
204
-
205
-	/**
206
-	 * returns the chunk size (limit in DB speak)
207
-	 */
208
-	public function getChunkSize(): int {
209
-		if ($this->limit === null) {
210
-			$this->limit = (int)$this->ocConfig->getAppValue('user_ldap', 'cleanUpJobChunkSize', '50');
211
-		}
212
-		return $this->limit;
213
-	}
43
+    /** @var ?int $limit amount of users that should be checked per run */
44
+    protected $limit;
45
+
46
+    /** @var int $defaultIntervalMin default interval in minutes */
47
+    protected $defaultIntervalMin = 51;
48
+
49
+    /** @var User_LDAP|User_Proxy $userBackend */
50
+    protected $userBackend;
51
+
52
+    /** @var \OCP\IConfig $ocConfig */
53
+    protected $ocConfig;
54
+
55
+    /** @var \OCP\IDBConnection $db */
56
+    protected $db;
57
+
58
+    /** @var Helper $ldapHelper */
59
+    protected $ldapHelper;
60
+
61
+    /** @var UserMapping */
62
+    protected $mapping;
63
+
64
+    /** @var DeletedUsersIndex */
65
+    protected $dui;
66
+
67
+    public function __construct(User_Proxy $userBackend, DeletedUsersIndex $dui) {
68
+        $minutes = \OC::$server->getConfig()->getSystemValue(
69
+            'ldapUserCleanupInterval', (string)$this->defaultIntervalMin);
70
+        $this->setInterval((int)$minutes * 60);
71
+        $this->userBackend = $userBackend;
72
+        $this->dui = $dui;
73
+    }
74
+
75
+    /**
76
+     * assigns the instances passed to run() to the class properties
77
+     * @param array $arguments
78
+     */
79
+    public function setArguments($arguments): void {
80
+        //Dependency Injection is not possible, because the constructor will
81
+        //only get values that are serialized to JSON. I.e. whatever we would
82
+        //pass in app.php we do add here, except something else is passed e.g.
83
+        //in tests.
84
+
85
+        if (isset($arguments['helper'])) {
86
+            $this->ldapHelper = $arguments['helper'];
87
+        } else {
88
+            $this->ldapHelper = new Helper(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection());
89
+        }
90
+
91
+        if (isset($arguments['ocConfig'])) {
92
+            $this->ocConfig = $arguments['ocConfig'];
93
+        } else {
94
+            $this->ocConfig = \OC::$server->getConfig();
95
+        }
96
+
97
+        if (isset($arguments['userBackend'])) {
98
+            $this->userBackend = $arguments['userBackend'];
99
+        }
100
+
101
+        if (isset($arguments['db'])) {
102
+            $this->db = $arguments['db'];
103
+        } else {
104
+            $this->db = \OC::$server->getDatabaseConnection();
105
+        }
106
+
107
+        if (isset($arguments['mapping'])) {
108
+            $this->mapping = $arguments['mapping'];
109
+        } else {
110
+            $this->mapping = new UserMapping($this->db);
111
+        }
112
+
113
+        if (isset($arguments['deletedUsersIndex'])) {
114
+            $this->dui = $arguments['deletedUsersIndex'];
115
+        }
116
+    }
117
+
118
+    /**
119
+     * makes the background job do its work
120
+     * @param array $argument
121
+     */
122
+    public function run($argument): void {
123
+        $this->setArguments($argument);
124
+
125
+        if (!$this->isCleanUpAllowed()) {
126
+            return;
127
+        }
128
+        $users = $this->mapping->getList($this->getOffset(), $this->getChunkSize());
129
+        $resetOffset = $this->isOffsetResetNecessary(count($users));
130
+        $this->checkUsers($users);
131
+        $this->setOffset($resetOffset);
132
+    }
133
+
134
+    /**
135
+     * checks whether next run should start at 0 again
136
+     */
137
+    public function isOffsetResetNecessary(int $resultCount): bool {
138
+        return $resultCount < $this->getChunkSize();
139
+    }
140
+
141
+    /**
142
+     * checks whether cleaning up LDAP users is allowed
143
+     */
144
+    public function isCleanUpAllowed(): bool {
145
+        try {
146
+            if ($this->ldapHelper->haveDisabledConfigurations()) {
147
+                return false;
148
+            }
149
+        } catch (\Exception $e) {
150
+            return false;
151
+        }
152
+
153
+        return $this->isCleanUpEnabled();
154
+    }
155
+
156
+    /**
157
+     * checks whether clean up is enabled by configuration
158
+     */
159
+    private function isCleanUpEnabled(): bool {
160
+        return (bool)$this->ocConfig->getSystemValue(
161
+            'ldapUserCleanupInterval', (string)$this->defaultIntervalMin);
162
+    }
163
+
164
+    /**
165
+     * checks users whether they are still existing
166
+     * @param array $users result from getMappedUsers()
167
+     */
168
+    private function checkUsers(array $users): void {
169
+        foreach ($users as $user) {
170
+            $this->checkUser($user);
171
+        }
172
+    }
173
+
174
+    /**
175
+     * checks whether a user is still existing in LDAP
176
+     * @param string[] $user
177
+     */
178
+    private function checkUser(array $user): void {
179
+        if ($this->userBackend->userExistsOnLDAP($user['name'])) {
180
+            //still available, all good
181
+
182
+            return;
183
+        }
184
+
185
+        $this->dui->markUser($user['name']);
186
+    }
187
+
188
+    /**
189
+     * gets the offset to fetch users from the mappings table
190
+     */
191
+    private function getOffset(): int {
192
+        return (int)$this->ocConfig->getAppValue('user_ldap', 'cleanUpJobOffset', '0');
193
+    }
194
+
195
+    /**
196
+     * sets the new offset for the next run
197
+     * @param bool $reset whether the offset should be set to 0
198
+     */
199
+    public function setOffset(bool $reset = false): void {
200
+        $newOffset = $reset ? 0 :
201
+            $this->getOffset() + $this->getChunkSize();
202
+        $this->ocConfig->setAppValue('user_ldap', 'cleanUpJobOffset', (string)$newOffset);
203
+    }
204
+
205
+    /**
206
+     * returns the chunk size (limit in DB speak)
207
+     */
208
+    public function getChunkSize(): int {
209
+        if ($this->limit === null) {
210
+            $this->limit = (int)$this->ocConfig->getAppValue('user_ldap', 'cleanUpJobChunkSize', '50');
211
+        }
212
+        return $this->limit;
213
+    }
214 214
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -9 removed lines patch added patch discarded remove patch
@@ -66,8 +66,8 @@  discard block
 block discarded – undo
66 66
 
67 67
 	public function __construct(User_Proxy $userBackend, DeletedUsersIndex $dui) {
68 68
 		$minutes = \OC::$server->getConfig()->getSystemValue(
69
-			'ldapUserCleanupInterval', (string)$this->defaultIntervalMin);
70
-		$this->setInterval((int)$minutes * 60);
69
+			'ldapUserCleanupInterval', (string) $this->defaultIntervalMin);
70
+		$this->setInterval((int) $minutes * 60);
71 71
 		$this->userBackend = $userBackend;
72 72
 		$this->dui = $dui;
73 73
 	}
@@ -157,8 +157,8 @@  discard block
 block discarded – undo
157 157
 	 * checks whether clean up is enabled by configuration
158 158
 	 */
159 159
 	private function isCleanUpEnabled(): bool {
160
-		return (bool)$this->ocConfig->getSystemValue(
161
-			'ldapUserCleanupInterval', (string)$this->defaultIntervalMin);
160
+		return (bool) $this->ocConfig->getSystemValue(
161
+			'ldapUserCleanupInterval', (string) $this->defaultIntervalMin);
162 162
 	}
163 163
 
164 164
 	/**
@@ -189,7 +189,7 @@  discard block
 block discarded – undo
189 189
 	 * gets the offset to fetch users from the mappings table
190 190
 	 */
191 191
 	private function getOffset(): int {
192
-		return (int)$this->ocConfig->getAppValue('user_ldap', 'cleanUpJobOffset', '0');
192
+		return (int) $this->ocConfig->getAppValue('user_ldap', 'cleanUpJobOffset', '0');
193 193
 	}
194 194
 
195 195
 	/**
@@ -197,9 +197,8 @@  discard block
 block discarded – undo
197 197
 	 * @param bool $reset whether the offset should be set to 0
198 198
 	 */
199 199
 	public function setOffset(bool $reset = false): void {
200
-		$newOffset = $reset ? 0 :
201
-			$this->getOffset() + $this->getChunkSize();
202
-		$this->ocConfig->setAppValue('user_ldap', 'cleanUpJobOffset', (string)$newOffset);
200
+		$newOffset = $reset ? 0 : $this->getOffset() + $this->getChunkSize();
201
+		$this->ocConfig->setAppValue('user_ldap', 'cleanUpJobOffset', (string) $newOffset);
203 202
 	}
204 203
 
205 204
 	/**
@@ -207,7 +206,7 @@  discard block
 block discarded – undo
207 206
 	 */
208 207
 	public function getChunkSize(): int {
209 208
 		if ($this->limit === null) {
210
-			$this->limit = (int)$this->ocConfig->getAppValue('user_ldap', 'cleanUpJobChunkSize', '50');
209
+			$this->limit = (int) $this->ocConfig->getAppValue('user_ldap', 'cleanUpJobChunkSize', '50');
211 210
 		}
212 211
 		return $this->limit;
213 212
 	}
Please login to merge, or discard this patch.
apps/user_ldap/lib/Access.php 1 patch
Indentation   +2020 added lines, -2020 removed lines patch added patch discarded remove patch
@@ -68,1806 +68,1806 @@  discard block
 block discarded – undo
68 68
  * @package OCA\User_LDAP
69 69
  */
70 70
 class Access extends LDAPUtility {
71
-	public const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
72
-
73
-	/** @var \OCA\User_LDAP\Connection */
74
-	public $connection;
75
-	/** @var Manager */
76
-	public $userManager;
77
-	//never ever check this var directly, always use getPagedSearchResultState
78
-	protected $pagedSearchedSuccessful;
79
-
80
-	/**
81
-	 * @var UserMapping $userMapper
82
-	 */
83
-	protected $userMapper;
84
-
85
-	/**
86
-	 * @var AbstractMapping $userMapper
87
-	 */
88
-	protected $groupMapper;
89
-
90
-	/**
91
-	 * @var \OCA\User_LDAP\Helper
92
-	 */
93
-	private $helper;
94
-	/** @var IConfig */
95
-	private $config;
96
-	/** @var IUserManager */
97
-	private $ncUserManager;
98
-	/** @var LoggerInterface */
99
-	private $logger;
100
-	/** @var string */
101
-	private $lastCookie = '';
102
-
103
-	public function __construct(
104
-		Connection $connection,
105
-		ILDAPWrapper $ldap,
106
-		Manager $userManager,
107
-		Helper $helper,
108
-		IConfig $config,
109
-		IUserManager $ncUserManager,
110
-		LoggerInterface $logger
111
-	) {
112
-		parent::__construct($ldap);
113
-		$this->connection = $connection;
114
-		$this->userManager = $userManager;
115
-		$this->userManager->setLdapAccess($this);
116
-		$this->helper = $helper;
117
-		$this->config = $config;
118
-		$this->ncUserManager = $ncUserManager;
119
-		$this->logger = $logger;
120
-	}
121
-
122
-	/**
123
-	 * sets the User Mapper
124
-	 *
125
-	 * @param AbstractMapping $mapper
126
-	 */
127
-	public function setUserMapper(AbstractMapping $mapper) {
128
-		$this->userMapper = $mapper;
129
-	}
130
-
131
-	/**
132
-	 * @throws \Exception
133
-	 */
134
-	public function getUserMapper(): UserMapping {
135
-		if (is_null($this->userMapper)) {
136
-			throw new \Exception('UserMapper was not assigned to this Access instance.');
137
-		}
138
-		return $this->userMapper;
139
-	}
140
-
141
-	/**
142
-	 * sets the Group Mapper
143
-	 *
144
-	 * @param AbstractMapping $mapper
145
-	 */
146
-	public function setGroupMapper(AbstractMapping $mapper) {
147
-		$this->groupMapper = $mapper;
148
-	}
149
-
150
-	/**
151
-	 * returns the Group Mapper
152
-	 *
153
-	 * @return AbstractMapping
154
-	 * @throws \Exception
155
-	 */
156
-	public function getGroupMapper() {
157
-		if (is_null($this->groupMapper)) {
158
-			throw new \Exception('GroupMapper was not assigned to this Access instance.');
159
-		}
160
-		return $this->groupMapper;
161
-	}
162
-
163
-	/**
164
-	 * @return bool
165
-	 */
166
-	private function checkConnection() {
167
-		return ($this->connection instanceof Connection);
168
-	}
169
-
170
-	/**
171
-	 * returns the Connection instance
172
-	 *
173
-	 * @return \OCA\User_LDAP\Connection
174
-	 */
175
-	public function getConnection() {
176
-		return $this->connection;
177
-	}
178
-
179
-	/**
180
-	 * reads a given attribute for an LDAP record identified by a DN
181
-	 *
182
-	 * @param string $dn the record in question
183
-	 * @param string $attr the attribute that shall be retrieved
184
-	 *        if empty, just check the record's existence
185
-	 * @param string $filter
186
-	 * @return array|false an array of values on success or an empty
187
-	 *          array if $attr is empty, false otherwise
188
-	 * @throws ServerNotAvailableException
189
-	 */
190
-	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
191
-		if (!$this->checkConnection()) {
192
-			$this->logger->warning(
193
-				'No LDAP Connector assigned, access impossible for readAttribute.',
194
-				['app' => 'user_ldap']
195
-			);
196
-			return false;
197
-		}
198
-		$cr = $this->connection->getConnectionResource();
199
-		if (!$this->ldap->isResource($cr)) {
200
-			//LDAP not available
201
-			$this->logger->debug('LDAP resource not available.', ['app' => 'user_ldap']);
202
-			return false;
203
-		}
204
-		//Cancel possibly running Paged Results operation, otherwise we run in
205
-		//LDAP protocol errors
206
-		$this->abandonPagedSearch();
207
-		// openLDAP requires that we init a new Paged Search. Not needed by AD,
208
-		// but does not hurt either.
209
-		$pagingSize = (int)$this->connection->ldapPagingSize;
210
-		// 0 won't result in replies, small numbers may leave out groups
211
-		// (cf. #12306), 500 is default for paging and should work everywhere.
212
-		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
213
-		$attr = mb_strtolower($attr, 'UTF-8');
214
-		// the actual read attribute later may contain parameters on a ranged
215
-		// request, e.g. member;range=99-199. Depends on server reply.
216
-		$attrToRead = $attr;
217
-
218
-		$values = [];
219
-		$isRangeRequest = false;
220
-		do {
221
-			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
222
-			if (is_bool($result)) {
223
-				// when an exists request was run and it was successful, an empty
224
-				// array must be returned
225
-				return $result ? [] : false;
226
-			}
227
-
228
-			if (!$isRangeRequest) {
229
-				$values = $this->extractAttributeValuesFromResult($result, $attr);
230
-				if (!empty($values)) {
231
-					return $values;
232
-				}
233
-			}
234
-
235
-			$isRangeRequest = false;
236
-			$result = $this->extractRangeData($result, $attr);
237
-			if (!empty($result)) {
238
-				$normalizedResult = $this->extractAttributeValuesFromResult(
239
-					[$attr => $result['values']],
240
-					$attr
241
-				);
242
-				$values = array_merge($values, $normalizedResult);
243
-
244
-				if ($result['rangeHigh'] === '*') {
245
-					// when server replies with * as high range value, there are
246
-					// no more results left
247
-					return $values;
248
-				} else {
249
-					$low = $result['rangeHigh'] + 1;
250
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
251
-					$isRangeRequest = true;
252
-				}
253
-			}
254
-		} while ($isRangeRequest);
255
-
256
-		$this->logger->debug('Requested attribute ' . $attr . ' not found for ' . $dn, ['app' => 'user_ldap']);
257
-		return false;
258
-	}
259
-
260
-	/**
261
-	 * Runs an read operation against LDAP
262
-	 *
263
-	 * @param resource|\LDAP\Connection $cr the LDAP connection
264
-	 * @param string $dn
265
-	 * @param string $attribute
266
-	 * @param string $filter
267
-	 * @param int $maxResults
268
-	 * @return array|bool false if there was any error, true if an exists check
269
-	 *                    was performed and the requested DN found, array with the
270
-	 *                    returned data on a successful usual operation
271
-	 * @throws ServerNotAvailableException
272
-	 */
273
-	public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
274
-		try {
275
-			$this->initPagedSearch($filter, $dn, [$attribute], $maxResults, 0);
276
-		} catch (NoMoreResults $e) {
277
-			// does not happen, no pagination here since offset is 0, but the
278
-			// previous call is needed for a potential reset of the state.
279
-			// Tools would still point out a possible NoMoreResults exception.
280
-			return false;
281
-		}
282
-		$dn = $this->helper->DNasBaseParameter($dn);
283
-		$rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, [$attribute]);
284
-		if (!$this->ldap->isResource($rr)) {
285
-			if ($attribute !== '') {
286
-				//do not throw this message on userExists check, irritates
287
-				$this->logger->debug('readAttribute failed for DN ' . $dn, ['app' => 'user_ldap']);
288
-			}
289
-			//in case an error occurs , e.g. object does not exist
290
-			return false;
291
-		}
292
-		if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
293
-			$this->logger->debug('readAttribute: ' . $dn . ' found', ['app' => 'user_ldap']);
294
-			return true;
295
-		}
296
-		$er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
297
-		if (!$this->ldap->isResource($er)) {
298
-			//did not match the filter, return false
299
-			return false;
300
-		}
301
-		//LDAP attributes are not case sensitive
302
-		$result = \OCP\Util::mb_array_change_key_case(
303
-			$this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
304
-
305
-		return $result;
306
-	}
307
-
308
-	/**
309
-	 * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
310
-	 * data if present.
311
-	 *
312
-	 * @param array $result from ILDAPWrapper::getAttributes()
313
-	 * @param string $attribute the attribute name that was read
314
-	 * @return string[]
315
-	 */
316
-	public function extractAttributeValuesFromResult($result, $attribute) {
317
-		$values = [];
318
-		if (isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
319
-			$lowercaseAttribute = strtolower($attribute);
320
-			for ($i = 0; $i < $result[$attribute]['count']; $i++) {
321
-				if ($this->resemblesDN($attribute)) {
322
-					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
323
-				} elseif ($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
324
-					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
325
-				} else {
326
-					$values[] = $result[$attribute][$i];
327
-				}
328
-			}
329
-		}
330
-		return $values;
331
-	}
332
-
333
-	/**
334
-	 * Attempts to find ranged data in a getAttribute results and extracts the
335
-	 * returned values as well as information on the range and full attribute
336
-	 * name for further processing.
337
-	 *
338
-	 * @param array $result from ILDAPWrapper::getAttributes()
339
-	 * @param string $attribute the attribute name that was read. Without ";range=…"
340
-	 * @return array If a range was detected with keys 'values', 'attributeName',
341
-	 *               'attributeFull' and 'rangeHigh', otherwise empty.
342
-	 */
343
-	public function extractRangeData($result, $attribute) {
344
-		$keys = array_keys($result);
345
-		foreach ($keys as $key) {
346
-			if ($key !== $attribute && strpos($key, $attribute) === 0) {
347
-				$queryData = explode(';', $key);
348
-				if (strpos($queryData[1], 'range=') === 0) {
349
-					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
350
-					$data = [
351
-						'values' => $result[$key],
352
-						'attributeName' => $queryData[0],
353
-						'attributeFull' => $key,
354
-						'rangeHigh' => $high,
355
-					];
356
-					return $data;
357
-				}
358
-			}
359
-		}
360
-		return [];
361
-	}
362
-
363
-	/**
364
-	 * Set password for an LDAP user identified by a DN
365
-	 *
366
-	 * @param string $userDN the user in question
367
-	 * @param string $password the new password
368
-	 * @return bool
369
-	 * @throws HintException
370
-	 * @throws \Exception
371
-	 */
372
-	public function setPassword($userDN, $password) {
373
-		if ((int)$this->connection->turnOnPasswordChange !== 1) {
374
-			throw new \Exception('LDAP password changes are disabled.');
375
-		}
376
-		$cr = $this->connection->getConnectionResource();
377
-		if (!$this->ldap->isResource($cr)) {
378
-			//LDAP not available
379
-			$this->logger->debug('LDAP resource not available.', ['app' => 'user_ldap']);
380
-			return false;
381
-		}
382
-		try {
383
-			// try PASSWD extended operation first
384
-			return @$this->invokeLDAPMethod('exopPasswd', $cr, $userDN, '', $password) ||
385
-				@$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
386
-		} catch (ConstraintViolationException $e) {
387
-			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ') . $e->getMessage(), $e->getCode());
388
-		}
389
-	}
390
-
391
-	/**
392
-	 * checks whether the given attributes value is probably a DN
393
-	 *
394
-	 * @param string $attr the attribute in question
395
-	 * @return boolean if so true, otherwise false
396
-	 */
397
-	private function resemblesDN($attr) {
398
-		$resemblingAttributes = [
399
-			'dn',
400
-			'uniquemember',
401
-			'member',
402
-			// memberOf is an "operational" attribute, without a definition in any RFC
403
-			'memberof'
404
-		];
405
-		return in_array($attr, $resemblingAttributes);
406
-	}
407
-
408
-	/**
409
-	 * checks whether the given string is probably a DN
410
-	 *
411
-	 * @param string $string
412
-	 * @return boolean
413
-	 */
414
-	public function stringResemblesDN($string) {
415
-		$r = $this->ldap->explodeDN($string, 0);
416
-		// if exploding a DN succeeds and does not end up in
417
-		// an empty array except for $r[count] being 0.
418
-		return (is_array($r) && count($r) > 1);
419
-	}
420
-
421
-	/**
422
-	 * returns a DN-string that is cleaned from not domain parts, e.g.
423
-	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
424
-	 * becomes dc=foobar,dc=server,dc=org
425
-	 *
426
-	 * @param string $dn
427
-	 * @return string
428
-	 */
429
-	public function getDomainDNFromDN($dn) {
430
-		$allParts = $this->ldap->explodeDN($dn, 0);
431
-		if ($allParts === false) {
432
-			//not a valid DN
433
-			return '';
434
-		}
435
-		$domainParts = [];
436
-		$dcFound = false;
437
-		foreach ($allParts as $part) {
438
-			if (!$dcFound && strpos($part, 'dc=') === 0) {
439
-				$dcFound = true;
440
-			}
441
-			if ($dcFound) {
442
-				$domainParts[] = $part;
443
-			}
444
-		}
445
-		return implode(',', $domainParts);
446
-	}
447
-
448
-	/**
449
-	 * returns the LDAP DN for the given internal Nextcloud name of the group
450
-	 *
451
-	 * @param string $name the Nextcloud name in question
452
-	 * @return string|false LDAP DN on success, otherwise false
453
-	 */
454
-	public function groupname2dn($name) {
455
-		return $this->groupMapper->getDNByName($name);
456
-	}
457
-
458
-	/**
459
-	 * returns the LDAP DN for the given internal Nextcloud name of the user
460
-	 *
461
-	 * @param string $name the Nextcloud name in question
462
-	 * @return string|false with the LDAP DN on success, otherwise false
463
-	 */
464
-	public function username2dn($name) {
465
-		$fdn = $this->userMapper->getDNByName($name);
466
-
467
-		//Check whether the DN belongs to the Base, to avoid issues on multi-
468
-		//server setups
469
-		if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
470
-			return $fdn;
471
-		}
472
-
473
-		return false;
474
-	}
475
-
476
-	/**
477
-	 * returns the internal Nextcloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
478
-	 *
479
-	 * @param string $fdn the dn of the group object
480
-	 * @param string $ldapName optional, the display name of the object
481
-	 * @return string|false with the name to use in Nextcloud, false on DN outside of search DN
482
-	 * @throws \Exception
483
-	 */
484
-	public function dn2groupname($fdn, $ldapName = null) {
485
-		//To avoid bypassing the base DN settings under certain circumstances
486
-		//with the group support, check whether the provided DN matches one of
487
-		//the given Bases
488
-		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
489
-			return false;
490
-		}
491
-
492
-		return $this->dn2ocname($fdn, $ldapName, false);
493
-	}
494
-
495
-	/**
496
-	 * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
497
-	 *
498
-	 * @param string $dn the dn of the user object
499
-	 * @param string $ldapName optional, the display name of the object
500
-	 * @return string|false with with the name to use in Nextcloud
501
-	 * @throws \Exception
502
-	 */
503
-	public function dn2username($fdn, $ldapName = null) {
504
-		//To avoid bypassing the base DN settings under certain circumstances
505
-		//with the group support, check whether the provided DN matches one of
506
-		//the given Bases
507
-		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
508
-			return false;
509
-		}
510
-
511
-		return $this->dn2ocname($fdn, $ldapName, true);
512
-	}
513
-
514
-	/**
515
-	 * returns an internal Nextcloud name for the given LDAP DN, false on DN outside of search DN
516
-	 *
517
-	 * @param string $fdn the dn of the user object
518
-	 * @param string|null $ldapName optional, the display name of the object
519
-	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
520
-	 * @param bool|null $newlyMapped
521
-	 * @param array|null $record
522
-	 * @return false|string with with the name to use in Nextcloud
523
-	 * @throws \Exception
524
-	 */
525
-	public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
526
-		$newlyMapped = false;
527
-		if ($isUser) {
528
-			$mapper = $this->getUserMapper();
529
-			$nameAttribute = $this->connection->ldapUserDisplayName;
530
-			$filter = $this->connection->ldapUserFilter;
531
-		} else {
532
-			$mapper = $this->getGroupMapper();
533
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
534
-			$filter = $this->connection->ldapGroupFilter;
535
-		}
536
-
537
-		//let's try to retrieve the Nextcloud name from the mappings table
538
-		$ncName = $mapper->getNameByDN($fdn);
539
-		if (is_string($ncName)) {
540
-			return $ncName;
541
-		}
542
-
543
-		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
544
-		$uuid = $this->getUUID($fdn, $isUser, $record);
545
-		if (is_string($uuid)) {
546
-			$ncName = $mapper->getNameByUUID($uuid);
547
-			if (is_string($ncName)) {
548
-				$mapper->setDNbyUUID($fdn, $uuid);
549
-				return $ncName;
550
-			}
551
-		} else {
552
-			//If the UUID can't be detected something is foul.
553
-			$this->logger->debug('Cannot determine UUID for ' . $fdn . '. Skipping.', ['app' => 'user_ldap']);
554
-			return false;
555
-		}
556
-
557
-		if (is_null($ldapName)) {
558
-			$ldapName = $this->readAttribute($fdn, $nameAttribute, $filter);
559
-			if (!isset($ldapName[0]) || empty($ldapName[0])) {
560
-				$this->logger->debug('No or empty name for ' . $fdn . ' with filter ' . $filter . '.', ['app' => 'user_ldap']);
561
-				return false;
562
-			}
563
-			$ldapName = $ldapName[0];
564
-		}
565
-
566
-		if ($isUser) {
567
-			$usernameAttribute = (string)$this->connection->ldapExpertUsernameAttr;
568
-			if ($usernameAttribute !== '') {
569
-				$username = $this->readAttribute($fdn, $usernameAttribute);
570
-				if (!isset($username[0]) || empty($username[0])) {
571
-					$this->logger->debug('No or empty username (' . $usernameAttribute . ') for ' . $fdn . '.', ['app' => 'user_ldap']);
572
-					return false;
573
-				}
574
-				$username = $username[0];
575
-			} else {
576
-				$username = $uuid;
577
-			}
578
-			try {
579
-				$intName = $this->sanitizeUsername($username);
580
-			} catch (\InvalidArgumentException $e) {
581
-				$this->logger->warning('Error sanitizing username: ' . $e->getMessage(), [
582
-					'exception' => $e,
583
-				]);
584
-				// we don't attempt to set a username here. We can go for
585
-				// for an alternative 4 digit random number as we would append
586
-				// otherwise, however it's likely not enough space in bigger
587
-				// setups, and most importantly: this is not intended.
588
-				return false;
589
-			}
590
-		} else {
591
-			$intName = $this->sanitizeGroupIDCandidate($ldapName);
592
-		}
593
-
594
-		//a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
595
-		//disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
596
-		//NOTE: mind, disabling cache affects only this instance! Using it
597
-		// outside of core user management will still cache the user as non-existing.
598
-		$originalTTL = $this->connection->ldapCacheTTL;
599
-		$this->connection->setConfiguration(['ldapCacheTTL' => 0]);
600
-		if ($intName !== ''
601
-			&& (($isUser && !$this->ncUserManager->userExists($intName))
602
-				|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))
603
-			)
604
-		) {
605
-			$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
606
-			$newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
607
-			if ($newlyMapped) {
608
-				return $intName;
609
-			}
610
-		}
611
-
612
-		$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
613
-		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
614
-		if (is_string($altName)) {
615
-			if ($this->mapAndAnnounceIfApplicable($mapper, $fdn, $altName, $uuid, $isUser)) {
616
-				$newlyMapped = true;
617
-				return $altName;
618
-			}
619
-		}
620
-
621
-		//if everything else did not help..
622
-		$this->logger->info('Could not create unique name for ' . $fdn . '.', ['app' => 'user_ldap']);
623
-		return false;
624
-	}
625
-
626
-	public function mapAndAnnounceIfApplicable(
627
-		AbstractMapping $mapper,
628
-		string $fdn,
629
-		string $name,
630
-		string $uuid,
631
-		bool $isUser
632
-	): bool {
633
-		if ($mapper->map($fdn, $name, $uuid)) {
634
-			if ($this->ncUserManager instanceof PublicEmitter && $isUser) {
635
-				$this->cacheUserExists($name);
636
-				$this->ncUserManager->emit('\OC\User', 'assignedUserId', [$name]);
637
-			} elseif (!$isUser) {
638
-				$this->cacheGroupExists($name);
639
-			}
640
-			return true;
641
-		}
642
-		return false;
643
-	}
644
-
645
-	/**
646
-	 * gives back the user names as they are used ownClod internally
647
-	 *
648
-	 * @param array $ldapUsers as returned by fetchList()
649
-	 * @return array an array with the user names to use in Nextcloud
650
-	 *
651
-	 * gives back the user names as they are used ownClod internally
652
-	 * @throws \Exception
653
-	 */
654
-	public function nextcloudUserNames($ldapUsers) {
655
-		return $this->ldap2NextcloudNames($ldapUsers, true);
656
-	}
657
-
658
-	/**
659
-	 * gives back the group names as they are used ownClod internally
660
-	 *
661
-	 * @param array $ldapGroups as returned by fetchList()
662
-	 * @return array an array with the group names to use in Nextcloud
663
-	 *
664
-	 * gives back the group names as they are used ownClod internally
665
-	 * @throws \Exception
666
-	 */
667
-	public function nextcloudGroupNames($ldapGroups) {
668
-		return $this->ldap2NextcloudNames($ldapGroups, false);
669
-	}
670
-
671
-	/**
672
-	 * @param array $ldapObjects as returned by fetchList()
673
-	 * @param bool $isUsers
674
-	 * @return array
675
-	 * @throws \Exception
676
-	 */
677
-	private function ldap2NextcloudNames($ldapObjects, $isUsers) {
678
-		if ($isUsers) {
679
-			$nameAttribute = $this->connection->ldapUserDisplayName;
680
-			$sndAttribute = $this->connection->ldapUserDisplayName2;
681
-		} else {
682
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
683
-		}
684
-		$nextcloudNames = [];
685
-
686
-		foreach ($ldapObjects as $ldapObject) {
687
-			$nameByLDAP = null;
688
-			if (isset($ldapObject[$nameAttribute])
689
-				&& is_array($ldapObject[$nameAttribute])
690
-				&& isset($ldapObject[$nameAttribute][0])
691
-			) {
692
-				// might be set, but not necessarily. if so, we use it.
693
-				$nameByLDAP = $ldapObject[$nameAttribute][0];
694
-			}
695
-
696
-			$ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
697
-			if ($ncName) {
698
-				$nextcloudNames[] = $ncName;
699
-				if ($isUsers) {
700
-					$this->updateUserState($ncName);
701
-					//cache the user names so it does not need to be retrieved
702
-					//again later (e.g. sharing dialogue).
703
-					if (is_null($nameByLDAP)) {
704
-						continue;
705
-					}
706
-					$sndName = isset($ldapObject[$sndAttribute][0])
707
-						? $ldapObject[$sndAttribute][0] : '';
708
-					$this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
709
-				} elseif ($nameByLDAP !== null) {
710
-					$this->cacheGroupDisplayName($ncName, $nameByLDAP);
711
-				}
712
-			}
713
-		}
714
-		return $nextcloudNames;
715
-	}
716
-
717
-	/**
718
-	 * removes the deleted-flag of a user if it was set
719
-	 *
720
-	 * @param string $ncname
721
-	 * @throws \Exception
722
-	 */
723
-	public function updateUserState($ncname) {
724
-		$user = $this->userManager->get($ncname);
725
-		if ($user instanceof OfflineUser) {
726
-			$user->unmark();
727
-		}
728
-	}
729
-
730
-	/**
731
-	 * caches the user display name
732
-	 *
733
-	 * @param string $ocName the internal Nextcloud username
734
-	 * @param string|false $home the home directory path
735
-	 */
736
-	public function cacheUserHome($ocName, $home) {
737
-		$cacheKey = 'getHome' . $ocName;
738
-		$this->connection->writeToCache($cacheKey, $home);
739
-	}
740
-
741
-	/**
742
-	 * caches a user as existing
743
-	 *
744
-	 * @param string $ocName the internal Nextcloud username
745
-	 */
746
-	public function cacheUserExists($ocName) {
747
-		$this->connection->writeToCache('userExists' . $ocName, true);
748
-	}
749
-
750
-	/**
751
-	 * caches a group as existing
752
-	 */
753
-	public function cacheGroupExists(string $gid): void {
754
-		$this->connection->writeToCache('groupExists' . $gid, true);
755
-	}
756
-
757
-	/**
758
-	 * caches the user display name
759
-	 *
760
-	 * @param string $ocName the internal Nextcloud username
761
-	 * @param string $displayName the display name
762
-	 * @param string $displayName2 the second display name
763
-	 * @throws \Exception
764
-	 */
765
-	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
766
-		$user = $this->userManager->get($ocName);
767
-		if ($user === null) {
768
-			return;
769
-		}
770
-		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
771
-		$cacheKeyTrunk = 'getDisplayName';
772
-		$this->connection->writeToCache($cacheKeyTrunk . $ocName, $displayName);
773
-	}
774
-
775
-	public function cacheGroupDisplayName(string $ncName, string $displayName): void {
776
-		$cacheKey = 'group_getDisplayName' . $ncName;
777
-		$this->connection->writeToCache($cacheKey, $displayName);
778
-	}
779
-
780
-	/**
781
-	 * creates a unique name for internal Nextcloud use for users. Don't call it directly.
782
-	 *
783
-	 * @param string $name the display name of the object
784
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful
785
-	 *
786
-	 * Instead of using this method directly, call
787
-	 * createAltInternalOwnCloudName($name, true)
788
-	 */
789
-	private function _createAltInternalOwnCloudNameForUsers($name) {
790
-		$attempts = 0;
791
-		//while loop is just a precaution. If a name is not generated within
792
-		//20 attempts, something else is very wrong. Avoids infinite loop.
793
-		while ($attempts < 20) {
794
-			$altName = $name . '_' . rand(1000, 9999);
795
-			if (!$this->ncUserManager->userExists($altName)) {
796
-				return $altName;
797
-			}
798
-			$attempts++;
799
-		}
800
-		return false;
801
-	}
802
-
803
-	/**
804
-	 * creates a unique name for internal Nextcloud use for groups. Don't call it directly.
805
-	 *
806
-	 * @param string $name the display name of the object
807
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful.
808
-	 *
809
-	 * Instead of using this method directly, call
810
-	 * createAltInternalOwnCloudName($name, false)
811
-	 *
812
-	 * Group names are also used as display names, so we do a sequential
813
-	 * numbering, e.g. Developers_42 when there are 41 other groups called
814
-	 * "Developers"
815
-	 */
816
-	private function _createAltInternalOwnCloudNameForGroups($name) {
817
-		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
818
-		if (!$usedNames || count($usedNames) === 0) {
819
-			$lastNo = 1; //will become name_2
820
-		} else {
821
-			natsort($usedNames);
822
-			$lastName = array_pop($usedNames);
823
-			$lastNo = (int)substr($lastName, strrpos($lastName, '_') + 1);
824
-		}
825
-		$altName = $name . '_' . (string)($lastNo + 1);
826
-		unset($usedNames);
827
-
828
-		$attempts = 1;
829
-		while ($attempts < 21) {
830
-			// Check to be really sure it is unique
831
-			// while loop is just a precaution. If a name is not generated within
832
-			// 20 attempts, something else is very wrong. Avoids infinite loop.
833
-			if (!\OC::$server->getGroupManager()->groupExists($altName)) {
834
-				return $altName;
835
-			}
836
-			$altName = $name . '_' . ($lastNo + $attempts);
837
-			$attempts++;
838
-		}
839
-		return false;
840
-	}
841
-
842
-	/**
843
-	 * creates a unique name for internal Nextcloud use.
844
-	 *
845
-	 * @param string $name the display name of the object
846
-	 * @param boolean $isUser whether name should be created for a user (true) or a group (false)
847
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful
848
-	 */
849
-	private function createAltInternalOwnCloudName($name, $isUser) {
850
-		// ensure there is space for the "_1234" suffix
851
-		if (strlen($name) > 59) {
852
-			$name = substr($name, 0, 59);
853
-		}
854
-
855
-		$originalTTL = $this->connection->ldapCacheTTL;
856
-		$this->connection->setConfiguration(['ldapCacheTTL' => 0]);
857
-		if ($isUser) {
858
-			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
859
-		} else {
860
-			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
861
-		}
862
-		$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
863
-
864
-		return $altName;
865
-	}
866
-
867
-	/**
868
-	 * fetches a list of users according to a provided loginName and utilizing
869
-	 * the login filter.
870
-	 */
871
-	public function fetchUsersByLoginName(string $loginName, array $attributes = ['dn']): array {
872
-		$loginName = $this->escapeFilterPart($loginName);
873
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
874
-		return $this->fetchListOfUsers($filter, $attributes);
875
-	}
876
-
877
-	/**
878
-	 * counts the number of users according to a provided loginName and
879
-	 * utilizing the login filter.
880
-	 *
881
-	 * @param string $loginName
882
-	 * @return int
883
-	 */
884
-	public function countUsersByLoginName($loginName) {
885
-		$loginName = $this->escapeFilterPart($loginName);
886
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
887
-		return $this->countUsers($filter);
888
-	}
889
-
890
-	/**
891
-	 * @throws \Exception
892
-	 */
893
-	public function fetchListOfUsers(string $filter, array $attr, int $limit = null, int $offset = null, bool $forceApplyAttributes = false): array {
894
-		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
895
-		$recordsToUpdate = $ldapRecords;
896
-		if (!$forceApplyAttributes) {
897
-			$isBackgroundJobModeAjax = $this->config
898
-					->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
899
-			$listOfDNs = array_reduce($ldapRecords, function ($listOfDNs, $entry) {
900
-				$listOfDNs[] = $entry['dn'][0];
901
-				return $listOfDNs;
902
-			}, []);
903
-			$idsByDn = $this->userMapper->getListOfIdsByDn($listOfDNs);
904
-			$recordsToUpdate = array_filter($ldapRecords, function ($record) use ($isBackgroundJobModeAjax, $idsByDn) {
905
-				$newlyMapped = false;
906
-				$uid = $idsByDn[$record['dn'][0]] ?? null;
907
-				if ($uid === null) {
908
-					$uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
909
-				}
910
-				if (is_string($uid)) {
911
-					$this->cacheUserExists($uid);
912
-				}
913
-				return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
914
-			});
915
-		}
916
-		$this->batchApplyUserAttributes($recordsToUpdate);
917
-		return $this->fetchList($ldapRecords, $this->manyAttributes($attr));
918
-	}
919
-
920
-	/**
921
-	 * provided with an array of LDAP user records the method will fetch the
922
-	 * user object and requests it to process the freshly fetched attributes and
923
-	 * and their values
924
-	 *
925
-	 * @param array $ldapRecords
926
-	 * @throws \Exception
927
-	 */
928
-	public function batchApplyUserAttributes(array $ldapRecords) {
929
-		$displayNameAttribute = strtolower((string)$this->connection->ldapUserDisplayName);
930
-		foreach ($ldapRecords as $userRecord) {
931
-			if (!isset($userRecord[$displayNameAttribute])) {
932
-				// displayName is obligatory
933
-				continue;
934
-			}
935
-			$ocName = $this->dn2ocname($userRecord['dn'][0], null, true);
936
-			if ($ocName === false) {
937
-				continue;
938
-			}
939
-			$this->updateUserState($ocName);
940
-			$user = $this->userManager->get($ocName);
941
-			if ($user !== null) {
942
-				$user->processAttributes($userRecord);
943
-			} else {
944
-				$this->logger->debug(
945
-					"The ldap user manager returned null for $ocName",
946
-					['app' => 'user_ldap']
947
-				);
948
-			}
949
-		}
950
-	}
951
-
952
-	/**
953
-	 * @param string $filter
954
-	 * @param string|string[] $attr
955
-	 * @param int $limit
956
-	 * @param int $offset
957
-	 * @return array
958
-	 */
959
-	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
960
-		$groupRecords = $this->searchGroups($filter, $attr, $limit, $offset);
961
-
962
-		$listOfDNs = array_reduce($groupRecords, function ($listOfDNs, $entry) {
963
-			$listOfDNs[] = $entry['dn'][0];
964
-			return $listOfDNs;
965
-		}, []);
966
-		$idsByDn = $this->groupMapper->getListOfIdsByDn($listOfDNs);
967
-
968
-		array_walk($groupRecords, function ($record) use ($idsByDn) {
969
-			$newlyMapped = false;
970
-			$gid = $idsByDn[$record['dn'][0]] ?? null;
971
-			if ($gid === null) {
972
-				$gid = $this->dn2ocname($record['dn'][0], null, false, $newlyMapped, $record);
973
-			}
974
-			if (!$newlyMapped && is_string($gid)) {
975
-				$this->cacheGroupExists($gid);
976
-			}
977
-		});
978
-		return $this->fetchList($groupRecords, $this->manyAttributes($attr));
979
-	}
980
-
981
-	/**
982
-	 * @param array $list
983
-	 * @param bool $manyAttributes
984
-	 * @return array
985
-	 */
986
-	private function fetchList($list, $manyAttributes) {
987
-		if (is_array($list)) {
988
-			if ($manyAttributes) {
989
-				return $list;
990
-			} else {
991
-				$list = array_reduce($list, function ($carry, $item) {
992
-					$attribute = array_keys($item)[0];
993
-					$carry[] = $item[$attribute][0];
994
-					return $carry;
995
-				}, []);
996
-				return array_unique($list, SORT_LOCALE_STRING);
997
-			}
998
-		}
999
-
1000
-		//error cause actually, maybe throw an exception in future.
1001
-		return [];
1002
-	}
1003
-
1004
-	/**
1005
-	 * @throws ServerNotAvailableException
1006
-	 */
1007
-	public function searchUsers(string $filter, array $attr = null, int $limit = null, int $offset = null): array {
1008
-		$result = [];
1009
-		foreach ($this->connection->ldapBaseUsers as $base) {
1010
-			$result = array_merge($result, $this->search($filter, $base, $attr, $limit, $offset));
1011
-		}
1012
-		return $result;
1013
-	}
1014
-
1015
-	/**
1016
-	 * @param string $filter
1017
-	 * @param string|string[] $attr
1018
-	 * @param int $limit
1019
-	 * @param int $offset
1020
-	 * @return false|int
1021
-	 * @throws ServerNotAvailableException
1022
-	 */
1023
-	public function countUsers($filter, $attr = ['dn'], $limit = null, $offset = null) {
1024
-		$result = false;
1025
-		foreach ($this->connection->ldapBaseUsers as $base) {
1026
-			$count = $this->count($filter, [$base], $attr, $limit, $offset);
1027
-			$result = is_int($count) ? (int)$result + $count : $result;
1028
-		}
1029
-		return $result;
1030
-	}
1031
-
1032
-	/**
1033
-	 * executes an LDAP search, optimized for Groups
1034
-	 *
1035
-	 * @param string $filter the LDAP filter for the search
1036
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
1037
-	 * @param integer $limit
1038
-	 * @param integer $offset
1039
-	 * @return array with the search result
1040
-	 *
1041
-	 * Executes an LDAP search
1042
-	 * @throws ServerNotAvailableException
1043
-	 */
1044
-	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
1045
-		$result = [];
1046
-		foreach ($this->connection->ldapBaseGroups as $base) {
1047
-			$result = array_merge($result, $this->search($filter, $base, $attr, $limit, $offset));
1048
-		}
1049
-		return $result;
1050
-	}
1051
-
1052
-	/**
1053
-	 * returns the number of available groups
1054
-	 *
1055
-	 * @param string $filter the LDAP search filter
1056
-	 * @param string[] $attr optional
1057
-	 * @param int|null $limit
1058
-	 * @param int|null $offset
1059
-	 * @return int|bool
1060
-	 * @throws ServerNotAvailableException
1061
-	 */
1062
-	public function countGroups($filter, $attr = ['dn'], $limit = null, $offset = null) {
1063
-		$result = false;
1064
-		foreach ($this->connection->ldapBaseGroups as $base) {
1065
-			$count = $this->count($filter, [$base], $attr, $limit, $offset);
1066
-			$result = is_int($count) ? (int)$result + $count : $result;
1067
-		}
1068
-		return $result;
1069
-	}
1070
-
1071
-	/**
1072
-	 * returns the number of available objects on the base DN
1073
-	 *
1074
-	 * @param int|null $limit
1075
-	 * @param int|null $offset
1076
-	 * @return int|bool
1077
-	 * @throws ServerNotAvailableException
1078
-	 */
1079
-	public function countObjects($limit = null, $offset = null) {
1080
-		$result = false;
1081
-		foreach ($this->connection->ldapBase as $base) {
1082
-			$count = $this->count('objectclass=*', [$base], ['dn'], $limit, $offset);
1083
-			$result = is_int($count) ? (int)$result + $count : $result;
1084
-		}
1085
-		return $result;
1086
-	}
1087
-
1088
-	/**
1089
-	 * Returns the LDAP handler
1090
-	 *
1091
-	 * @throws \OC\ServerNotAvailableException
1092
-	 */
1093
-
1094
-	/**
1095
-	 * @return mixed
1096
-	 * @throws \OC\ServerNotAvailableException
1097
-	 */
1098
-	private function invokeLDAPMethod() {
1099
-		$arguments = func_get_args();
1100
-		$command = array_shift($arguments);
1101
-		$cr = array_shift($arguments);
1102
-		if (!method_exists($this->ldap, $command)) {
1103
-			return null;
1104
-		}
1105
-		array_unshift($arguments, $cr);
1106
-		// php no longer supports call-time pass-by-reference
1107
-		// thus cannot support controlPagedResultResponse as the third argument
1108
-		// is a reference
1109
-		$doMethod = function () use ($command, &$arguments) {
1110
-			if ($command == 'controlPagedResultResponse') {
1111
-				throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1112
-			} else {
1113
-				return call_user_func_array([$this->ldap, $command], $arguments);
1114
-			}
1115
-		};
1116
-		try {
1117
-			$ret = $doMethod();
1118
-		} catch (ServerNotAvailableException $e) {
1119
-			/* Server connection lost, attempt to reestablish it
71
+    public const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
72
+
73
+    /** @var \OCA\User_LDAP\Connection */
74
+    public $connection;
75
+    /** @var Manager */
76
+    public $userManager;
77
+    //never ever check this var directly, always use getPagedSearchResultState
78
+    protected $pagedSearchedSuccessful;
79
+
80
+    /**
81
+     * @var UserMapping $userMapper
82
+     */
83
+    protected $userMapper;
84
+
85
+    /**
86
+     * @var AbstractMapping $userMapper
87
+     */
88
+    protected $groupMapper;
89
+
90
+    /**
91
+     * @var \OCA\User_LDAP\Helper
92
+     */
93
+    private $helper;
94
+    /** @var IConfig */
95
+    private $config;
96
+    /** @var IUserManager */
97
+    private $ncUserManager;
98
+    /** @var LoggerInterface */
99
+    private $logger;
100
+    /** @var string */
101
+    private $lastCookie = '';
102
+
103
+    public function __construct(
104
+        Connection $connection,
105
+        ILDAPWrapper $ldap,
106
+        Manager $userManager,
107
+        Helper $helper,
108
+        IConfig $config,
109
+        IUserManager $ncUserManager,
110
+        LoggerInterface $logger
111
+    ) {
112
+        parent::__construct($ldap);
113
+        $this->connection = $connection;
114
+        $this->userManager = $userManager;
115
+        $this->userManager->setLdapAccess($this);
116
+        $this->helper = $helper;
117
+        $this->config = $config;
118
+        $this->ncUserManager = $ncUserManager;
119
+        $this->logger = $logger;
120
+    }
121
+
122
+    /**
123
+     * sets the User Mapper
124
+     *
125
+     * @param AbstractMapping $mapper
126
+     */
127
+    public function setUserMapper(AbstractMapping $mapper) {
128
+        $this->userMapper = $mapper;
129
+    }
130
+
131
+    /**
132
+     * @throws \Exception
133
+     */
134
+    public function getUserMapper(): UserMapping {
135
+        if (is_null($this->userMapper)) {
136
+            throw new \Exception('UserMapper was not assigned to this Access instance.');
137
+        }
138
+        return $this->userMapper;
139
+    }
140
+
141
+    /**
142
+     * sets the Group Mapper
143
+     *
144
+     * @param AbstractMapping $mapper
145
+     */
146
+    public function setGroupMapper(AbstractMapping $mapper) {
147
+        $this->groupMapper = $mapper;
148
+    }
149
+
150
+    /**
151
+     * returns the Group Mapper
152
+     *
153
+     * @return AbstractMapping
154
+     * @throws \Exception
155
+     */
156
+    public function getGroupMapper() {
157
+        if (is_null($this->groupMapper)) {
158
+            throw new \Exception('GroupMapper was not assigned to this Access instance.');
159
+        }
160
+        return $this->groupMapper;
161
+    }
162
+
163
+    /**
164
+     * @return bool
165
+     */
166
+    private function checkConnection() {
167
+        return ($this->connection instanceof Connection);
168
+    }
169
+
170
+    /**
171
+     * returns the Connection instance
172
+     *
173
+     * @return \OCA\User_LDAP\Connection
174
+     */
175
+    public function getConnection() {
176
+        return $this->connection;
177
+    }
178
+
179
+    /**
180
+     * reads a given attribute for an LDAP record identified by a DN
181
+     *
182
+     * @param string $dn the record in question
183
+     * @param string $attr the attribute that shall be retrieved
184
+     *        if empty, just check the record's existence
185
+     * @param string $filter
186
+     * @return array|false an array of values on success or an empty
187
+     *          array if $attr is empty, false otherwise
188
+     * @throws ServerNotAvailableException
189
+     */
190
+    public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
191
+        if (!$this->checkConnection()) {
192
+            $this->logger->warning(
193
+                'No LDAP Connector assigned, access impossible for readAttribute.',
194
+                ['app' => 'user_ldap']
195
+            );
196
+            return false;
197
+        }
198
+        $cr = $this->connection->getConnectionResource();
199
+        if (!$this->ldap->isResource($cr)) {
200
+            //LDAP not available
201
+            $this->logger->debug('LDAP resource not available.', ['app' => 'user_ldap']);
202
+            return false;
203
+        }
204
+        //Cancel possibly running Paged Results operation, otherwise we run in
205
+        //LDAP protocol errors
206
+        $this->abandonPagedSearch();
207
+        // openLDAP requires that we init a new Paged Search. Not needed by AD,
208
+        // but does not hurt either.
209
+        $pagingSize = (int)$this->connection->ldapPagingSize;
210
+        // 0 won't result in replies, small numbers may leave out groups
211
+        // (cf. #12306), 500 is default for paging and should work everywhere.
212
+        $maxResults = $pagingSize > 20 ? $pagingSize : 500;
213
+        $attr = mb_strtolower($attr, 'UTF-8');
214
+        // the actual read attribute later may contain parameters on a ranged
215
+        // request, e.g. member;range=99-199. Depends on server reply.
216
+        $attrToRead = $attr;
217
+
218
+        $values = [];
219
+        $isRangeRequest = false;
220
+        do {
221
+            $result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
222
+            if (is_bool($result)) {
223
+                // when an exists request was run and it was successful, an empty
224
+                // array must be returned
225
+                return $result ? [] : false;
226
+            }
227
+
228
+            if (!$isRangeRequest) {
229
+                $values = $this->extractAttributeValuesFromResult($result, $attr);
230
+                if (!empty($values)) {
231
+                    return $values;
232
+                }
233
+            }
234
+
235
+            $isRangeRequest = false;
236
+            $result = $this->extractRangeData($result, $attr);
237
+            if (!empty($result)) {
238
+                $normalizedResult = $this->extractAttributeValuesFromResult(
239
+                    [$attr => $result['values']],
240
+                    $attr
241
+                );
242
+                $values = array_merge($values, $normalizedResult);
243
+
244
+                if ($result['rangeHigh'] === '*') {
245
+                    // when server replies with * as high range value, there are
246
+                    // no more results left
247
+                    return $values;
248
+                } else {
249
+                    $low = $result['rangeHigh'] + 1;
250
+                    $attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
251
+                    $isRangeRequest = true;
252
+                }
253
+            }
254
+        } while ($isRangeRequest);
255
+
256
+        $this->logger->debug('Requested attribute ' . $attr . ' not found for ' . $dn, ['app' => 'user_ldap']);
257
+        return false;
258
+    }
259
+
260
+    /**
261
+     * Runs an read operation against LDAP
262
+     *
263
+     * @param resource|\LDAP\Connection $cr the LDAP connection
264
+     * @param string $dn
265
+     * @param string $attribute
266
+     * @param string $filter
267
+     * @param int $maxResults
268
+     * @return array|bool false if there was any error, true if an exists check
269
+     *                    was performed and the requested DN found, array with the
270
+     *                    returned data on a successful usual operation
271
+     * @throws ServerNotAvailableException
272
+     */
273
+    public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
274
+        try {
275
+            $this->initPagedSearch($filter, $dn, [$attribute], $maxResults, 0);
276
+        } catch (NoMoreResults $e) {
277
+            // does not happen, no pagination here since offset is 0, but the
278
+            // previous call is needed for a potential reset of the state.
279
+            // Tools would still point out a possible NoMoreResults exception.
280
+            return false;
281
+        }
282
+        $dn = $this->helper->DNasBaseParameter($dn);
283
+        $rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, [$attribute]);
284
+        if (!$this->ldap->isResource($rr)) {
285
+            if ($attribute !== '') {
286
+                //do not throw this message on userExists check, irritates
287
+                $this->logger->debug('readAttribute failed for DN ' . $dn, ['app' => 'user_ldap']);
288
+            }
289
+            //in case an error occurs , e.g. object does not exist
290
+            return false;
291
+        }
292
+        if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
293
+            $this->logger->debug('readAttribute: ' . $dn . ' found', ['app' => 'user_ldap']);
294
+            return true;
295
+        }
296
+        $er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
297
+        if (!$this->ldap->isResource($er)) {
298
+            //did not match the filter, return false
299
+            return false;
300
+        }
301
+        //LDAP attributes are not case sensitive
302
+        $result = \OCP\Util::mb_array_change_key_case(
303
+            $this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
304
+
305
+        return $result;
306
+    }
307
+
308
+    /**
309
+     * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
310
+     * data if present.
311
+     *
312
+     * @param array $result from ILDAPWrapper::getAttributes()
313
+     * @param string $attribute the attribute name that was read
314
+     * @return string[]
315
+     */
316
+    public function extractAttributeValuesFromResult($result, $attribute) {
317
+        $values = [];
318
+        if (isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
319
+            $lowercaseAttribute = strtolower($attribute);
320
+            for ($i = 0; $i < $result[$attribute]['count']; $i++) {
321
+                if ($this->resemblesDN($attribute)) {
322
+                    $values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
323
+                } elseif ($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
324
+                    $values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
325
+                } else {
326
+                    $values[] = $result[$attribute][$i];
327
+                }
328
+            }
329
+        }
330
+        return $values;
331
+    }
332
+
333
+    /**
334
+     * Attempts to find ranged data in a getAttribute results and extracts the
335
+     * returned values as well as information on the range and full attribute
336
+     * name for further processing.
337
+     *
338
+     * @param array $result from ILDAPWrapper::getAttributes()
339
+     * @param string $attribute the attribute name that was read. Without ";range=…"
340
+     * @return array If a range was detected with keys 'values', 'attributeName',
341
+     *               'attributeFull' and 'rangeHigh', otherwise empty.
342
+     */
343
+    public function extractRangeData($result, $attribute) {
344
+        $keys = array_keys($result);
345
+        foreach ($keys as $key) {
346
+            if ($key !== $attribute && strpos($key, $attribute) === 0) {
347
+                $queryData = explode(';', $key);
348
+                if (strpos($queryData[1], 'range=') === 0) {
349
+                    $high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
350
+                    $data = [
351
+                        'values' => $result[$key],
352
+                        'attributeName' => $queryData[0],
353
+                        'attributeFull' => $key,
354
+                        'rangeHigh' => $high,
355
+                    ];
356
+                    return $data;
357
+                }
358
+            }
359
+        }
360
+        return [];
361
+    }
362
+
363
+    /**
364
+     * Set password for an LDAP user identified by a DN
365
+     *
366
+     * @param string $userDN the user in question
367
+     * @param string $password the new password
368
+     * @return bool
369
+     * @throws HintException
370
+     * @throws \Exception
371
+     */
372
+    public function setPassword($userDN, $password) {
373
+        if ((int)$this->connection->turnOnPasswordChange !== 1) {
374
+            throw new \Exception('LDAP password changes are disabled.');
375
+        }
376
+        $cr = $this->connection->getConnectionResource();
377
+        if (!$this->ldap->isResource($cr)) {
378
+            //LDAP not available
379
+            $this->logger->debug('LDAP resource not available.', ['app' => 'user_ldap']);
380
+            return false;
381
+        }
382
+        try {
383
+            // try PASSWD extended operation first
384
+            return @$this->invokeLDAPMethod('exopPasswd', $cr, $userDN, '', $password) ||
385
+                @$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
386
+        } catch (ConstraintViolationException $e) {
387
+            throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ') . $e->getMessage(), $e->getCode());
388
+        }
389
+    }
390
+
391
+    /**
392
+     * checks whether the given attributes value is probably a DN
393
+     *
394
+     * @param string $attr the attribute in question
395
+     * @return boolean if so true, otherwise false
396
+     */
397
+    private function resemblesDN($attr) {
398
+        $resemblingAttributes = [
399
+            'dn',
400
+            'uniquemember',
401
+            'member',
402
+            // memberOf is an "operational" attribute, without a definition in any RFC
403
+            'memberof'
404
+        ];
405
+        return in_array($attr, $resemblingAttributes);
406
+    }
407
+
408
+    /**
409
+     * checks whether the given string is probably a DN
410
+     *
411
+     * @param string $string
412
+     * @return boolean
413
+     */
414
+    public function stringResemblesDN($string) {
415
+        $r = $this->ldap->explodeDN($string, 0);
416
+        // if exploding a DN succeeds and does not end up in
417
+        // an empty array except for $r[count] being 0.
418
+        return (is_array($r) && count($r) > 1);
419
+    }
420
+
421
+    /**
422
+     * returns a DN-string that is cleaned from not domain parts, e.g.
423
+     * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
424
+     * becomes dc=foobar,dc=server,dc=org
425
+     *
426
+     * @param string $dn
427
+     * @return string
428
+     */
429
+    public function getDomainDNFromDN($dn) {
430
+        $allParts = $this->ldap->explodeDN($dn, 0);
431
+        if ($allParts === false) {
432
+            //not a valid DN
433
+            return '';
434
+        }
435
+        $domainParts = [];
436
+        $dcFound = false;
437
+        foreach ($allParts as $part) {
438
+            if (!$dcFound && strpos($part, 'dc=') === 0) {
439
+                $dcFound = true;
440
+            }
441
+            if ($dcFound) {
442
+                $domainParts[] = $part;
443
+            }
444
+        }
445
+        return implode(',', $domainParts);
446
+    }
447
+
448
+    /**
449
+     * returns the LDAP DN for the given internal Nextcloud name of the group
450
+     *
451
+     * @param string $name the Nextcloud name in question
452
+     * @return string|false LDAP DN on success, otherwise false
453
+     */
454
+    public function groupname2dn($name) {
455
+        return $this->groupMapper->getDNByName($name);
456
+    }
457
+
458
+    /**
459
+     * returns the LDAP DN for the given internal Nextcloud name of the user
460
+     *
461
+     * @param string $name the Nextcloud name in question
462
+     * @return string|false with the LDAP DN on success, otherwise false
463
+     */
464
+    public function username2dn($name) {
465
+        $fdn = $this->userMapper->getDNByName($name);
466
+
467
+        //Check whether the DN belongs to the Base, to avoid issues on multi-
468
+        //server setups
469
+        if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
470
+            return $fdn;
471
+        }
472
+
473
+        return false;
474
+    }
475
+
476
+    /**
477
+     * returns the internal Nextcloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
478
+     *
479
+     * @param string $fdn the dn of the group object
480
+     * @param string $ldapName optional, the display name of the object
481
+     * @return string|false with the name to use in Nextcloud, false on DN outside of search DN
482
+     * @throws \Exception
483
+     */
484
+    public function dn2groupname($fdn, $ldapName = null) {
485
+        //To avoid bypassing the base DN settings under certain circumstances
486
+        //with the group support, check whether the provided DN matches one of
487
+        //the given Bases
488
+        if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
489
+            return false;
490
+        }
491
+
492
+        return $this->dn2ocname($fdn, $ldapName, false);
493
+    }
494
+
495
+    /**
496
+     * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
497
+     *
498
+     * @param string $dn the dn of the user object
499
+     * @param string $ldapName optional, the display name of the object
500
+     * @return string|false with with the name to use in Nextcloud
501
+     * @throws \Exception
502
+     */
503
+    public function dn2username($fdn, $ldapName = null) {
504
+        //To avoid bypassing the base DN settings under certain circumstances
505
+        //with the group support, check whether the provided DN matches one of
506
+        //the given Bases
507
+        if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
508
+            return false;
509
+        }
510
+
511
+        return $this->dn2ocname($fdn, $ldapName, true);
512
+    }
513
+
514
+    /**
515
+     * returns an internal Nextcloud name for the given LDAP DN, false on DN outside of search DN
516
+     *
517
+     * @param string $fdn the dn of the user object
518
+     * @param string|null $ldapName optional, the display name of the object
519
+     * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
520
+     * @param bool|null $newlyMapped
521
+     * @param array|null $record
522
+     * @return false|string with with the name to use in Nextcloud
523
+     * @throws \Exception
524
+     */
525
+    public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
526
+        $newlyMapped = false;
527
+        if ($isUser) {
528
+            $mapper = $this->getUserMapper();
529
+            $nameAttribute = $this->connection->ldapUserDisplayName;
530
+            $filter = $this->connection->ldapUserFilter;
531
+        } else {
532
+            $mapper = $this->getGroupMapper();
533
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
534
+            $filter = $this->connection->ldapGroupFilter;
535
+        }
536
+
537
+        //let's try to retrieve the Nextcloud name from the mappings table
538
+        $ncName = $mapper->getNameByDN($fdn);
539
+        if (is_string($ncName)) {
540
+            return $ncName;
541
+        }
542
+
543
+        //second try: get the UUID and check if it is known. Then, update the DN and return the name.
544
+        $uuid = $this->getUUID($fdn, $isUser, $record);
545
+        if (is_string($uuid)) {
546
+            $ncName = $mapper->getNameByUUID($uuid);
547
+            if (is_string($ncName)) {
548
+                $mapper->setDNbyUUID($fdn, $uuid);
549
+                return $ncName;
550
+            }
551
+        } else {
552
+            //If the UUID can't be detected something is foul.
553
+            $this->logger->debug('Cannot determine UUID for ' . $fdn . '. Skipping.', ['app' => 'user_ldap']);
554
+            return false;
555
+        }
556
+
557
+        if (is_null($ldapName)) {
558
+            $ldapName = $this->readAttribute($fdn, $nameAttribute, $filter);
559
+            if (!isset($ldapName[0]) || empty($ldapName[0])) {
560
+                $this->logger->debug('No or empty name for ' . $fdn . ' with filter ' . $filter . '.', ['app' => 'user_ldap']);
561
+                return false;
562
+            }
563
+            $ldapName = $ldapName[0];
564
+        }
565
+
566
+        if ($isUser) {
567
+            $usernameAttribute = (string)$this->connection->ldapExpertUsernameAttr;
568
+            if ($usernameAttribute !== '') {
569
+                $username = $this->readAttribute($fdn, $usernameAttribute);
570
+                if (!isset($username[0]) || empty($username[0])) {
571
+                    $this->logger->debug('No or empty username (' . $usernameAttribute . ') for ' . $fdn . '.', ['app' => 'user_ldap']);
572
+                    return false;
573
+                }
574
+                $username = $username[0];
575
+            } else {
576
+                $username = $uuid;
577
+            }
578
+            try {
579
+                $intName = $this->sanitizeUsername($username);
580
+            } catch (\InvalidArgumentException $e) {
581
+                $this->logger->warning('Error sanitizing username: ' . $e->getMessage(), [
582
+                    'exception' => $e,
583
+                ]);
584
+                // we don't attempt to set a username here. We can go for
585
+                // for an alternative 4 digit random number as we would append
586
+                // otherwise, however it's likely not enough space in bigger
587
+                // setups, and most importantly: this is not intended.
588
+                return false;
589
+            }
590
+        } else {
591
+            $intName = $this->sanitizeGroupIDCandidate($ldapName);
592
+        }
593
+
594
+        //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
595
+        //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
596
+        //NOTE: mind, disabling cache affects only this instance! Using it
597
+        // outside of core user management will still cache the user as non-existing.
598
+        $originalTTL = $this->connection->ldapCacheTTL;
599
+        $this->connection->setConfiguration(['ldapCacheTTL' => 0]);
600
+        if ($intName !== ''
601
+            && (($isUser && !$this->ncUserManager->userExists($intName))
602
+                || (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))
603
+            )
604
+        ) {
605
+            $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
606
+            $newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
607
+            if ($newlyMapped) {
608
+                return $intName;
609
+            }
610
+        }
611
+
612
+        $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
613
+        $altName = $this->createAltInternalOwnCloudName($intName, $isUser);
614
+        if (is_string($altName)) {
615
+            if ($this->mapAndAnnounceIfApplicable($mapper, $fdn, $altName, $uuid, $isUser)) {
616
+                $newlyMapped = true;
617
+                return $altName;
618
+            }
619
+        }
620
+
621
+        //if everything else did not help..
622
+        $this->logger->info('Could not create unique name for ' . $fdn . '.', ['app' => 'user_ldap']);
623
+        return false;
624
+    }
625
+
626
+    public function mapAndAnnounceIfApplicable(
627
+        AbstractMapping $mapper,
628
+        string $fdn,
629
+        string $name,
630
+        string $uuid,
631
+        bool $isUser
632
+    ): bool {
633
+        if ($mapper->map($fdn, $name, $uuid)) {
634
+            if ($this->ncUserManager instanceof PublicEmitter && $isUser) {
635
+                $this->cacheUserExists($name);
636
+                $this->ncUserManager->emit('\OC\User', 'assignedUserId', [$name]);
637
+            } elseif (!$isUser) {
638
+                $this->cacheGroupExists($name);
639
+            }
640
+            return true;
641
+        }
642
+        return false;
643
+    }
644
+
645
+    /**
646
+     * gives back the user names as they are used ownClod internally
647
+     *
648
+     * @param array $ldapUsers as returned by fetchList()
649
+     * @return array an array with the user names to use in Nextcloud
650
+     *
651
+     * gives back the user names as they are used ownClod internally
652
+     * @throws \Exception
653
+     */
654
+    public function nextcloudUserNames($ldapUsers) {
655
+        return $this->ldap2NextcloudNames($ldapUsers, true);
656
+    }
657
+
658
+    /**
659
+     * gives back the group names as they are used ownClod internally
660
+     *
661
+     * @param array $ldapGroups as returned by fetchList()
662
+     * @return array an array with the group names to use in Nextcloud
663
+     *
664
+     * gives back the group names as they are used ownClod internally
665
+     * @throws \Exception
666
+     */
667
+    public function nextcloudGroupNames($ldapGroups) {
668
+        return $this->ldap2NextcloudNames($ldapGroups, false);
669
+    }
670
+
671
+    /**
672
+     * @param array $ldapObjects as returned by fetchList()
673
+     * @param bool $isUsers
674
+     * @return array
675
+     * @throws \Exception
676
+     */
677
+    private function ldap2NextcloudNames($ldapObjects, $isUsers) {
678
+        if ($isUsers) {
679
+            $nameAttribute = $this->connection->ldapUserDisplayName;
680
+            $sndAttribute = $this->connection->ldapUserDisplayName2;
681
+        } else {
682
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
683
+        }
684
+        $nextcloudNames = [];
685
+
686
+        foreach ($ldapObjects as $ldapObject) {
687
+            $nameByLDAP = null;
688
+            if (isset($ldapObject[$nameAttribute])
689
+                && is_array($ldapObject[$nameAttribute])
690
+                && isset($ldapObject[$nameAttribute][0])
691
+            ) {
692
+                // might be set, but not necessarily. if so, we use it.
693
+                $nameByLDAP = $ldapObject[$nameAttribute][0];
694
+            }
695
+
696
+            $ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
697
+            if ($ncName) {
698
+                $nextcloudNames[] = $ncName;
699
+                if ($isUsers) {
700
+                    $this->updateUserState($ncName);
701
+                    //cache the user names so it does not need to be retrieved
702
+                    //again later (e.g. sharing dialogue).
703
+                    if (is_null($nameByLDAP)) {
704
+                        continue;
705
+                    }
706
+                    $sndName = isset($ldapObject[$sndAttribute][0])
707
+                        ? $ldapObject[$sndAttribute][0] : '';
708
+                    $this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
709
+                } elseif ($nameByLDAP !== null) {
710
+                    $this->cacheGroupDisplayName($ncName, $nameByLDAP);
711
+                }
712
+            }
713
+        }
714
+        return $nextcloudNames;
715
+    }
716
+
717
+    /**
718
+     * removes the deleted-flag of a user if it was set
719
+     *
720
+     * @param string $ncname
721
+     * @throws \Exception
722
+     */
723
+    public function updateUserState($ncname) {
724
+        $user = $this->userManager->get($ncname);
725
+        if ($user instanceof OfflineUser) {
726
+            $user->unmark();
727
+        }
728
+    }
729
+
730
+    /**
731
+     * caches the user display name
732
+     *
733
+     * @param string $ocName the internal Nextcloud username
734
+     * @param string|false $home the home directory path
735
+     */
736
+    public function cacheUserHome($ocName, $home) {
737
+        $cacheKey = 'getHome' . $ocName;
738
+        $this->connection->writeToCache($cacheKey, $home);
739
+    }
740
+
741
+    /**
742
+     * caches a user as existing
743
+     *
744
+     * @param string $ocName the internal Nextcloud username
745
+     */
746
+    public function cacheUserExists($ocName) {
747
+        $this->connection->writeToCache('userExists' . $ocName, true);
748
+    }
749
+
750
+    /**
751
+     * caches a group as existing
752
+     */
753
+    public function cacheGroupExists(string $gid): void {
754
+        $this->connection->writeToCache('groupExists' . $gid, true);
755
+    }
756
+
757
+    /**
758
+     * caches the user display name
759
+     *
760
+     * @param string $ocName the internal Nextcloud username
761
+     * @param string $displayName the display name
762
+     * @param string $displayName2 the second display name
763
+     * @throws \Exception
764
+     */
765
+    public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
766
+        $user = $this->userManager->get($ocName);
767
+        if ($user === null) {
768
+            return;
769
+        }
770
+        $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
771
+        $cacheKeyTrunk = 'getDisplayName';
772
+        $this->connection->writeToCache($cacheKeyTrunk . $ocName, $displayName);
773
+    }
774
+
775
+    public function cacheGroupDisplayName(string $ncName, string $displayName): void {
776
+        $cacheKey = 'group_getDisplayName' . $ncName;
777
+        $this->connection->writeToCache($cacheKey, $displayName);
778
+    }
779
+
780
+    /**
781
+     * creates a unique name for internal Nextcloud use for users. Don't call it directly.
782
+     *
783
+     * @param string $name the display name of the object
784
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful
785
+     *
786
+     * Instead of using this method directly, call
787
+     * createAltInternalOwnCloudName($name, true)
788
+     */
789
+    private function _createAltInternalOwnCloudNameForUsers($name) {
790
+        $attempts = 0;
791
+        //while loop is just a precaution. If a name is not generated within
792
+        //20 attempts, something else is very wrong. Avoids infinite loop.
793
+        while ($attempts < 20) {
794
+            $altName = $name . '_' . rand(1000, 9999);
795
+            if (!$this->ncUserManager->userExists($altName)) {
796
+                return $altName;
797
+            }
798
+            $attempts++;
799
+        }
800
+        return false;
801
+    }
802
+
803
+    /**
804
+     * creates a unique name for internal Nextcloud use for groups. Don't call it directly.
805
+     *
806
+     * @param string $name the display name of the object
807
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful.
808
+     *
809
+     * Instead of using this method directly, call
810
+     * createAltInternalOwnCloudName($name, false)
811
+     *
812
+     * Group names are also used as display names, so we do a sequential
813
+     * numbering, e.g. Developers_42 when there are 41 other groups called
814
+     * "Developers"
815
+     */
816
+    private function _createAltInternalOwnCloudNameForGroups($name) {
817
+        $usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
818
+        if (!$usedNames || count($usedNames) === 0) {
819
+            $lastNo = 1; //will become name_2
820
+        } else {
821
+            natsort($usedNames);
822
+            $lastName = array_pop($usedNames);
823
+            $lastNo = (int)substr($lastName, strrpos($lastName, '_') + 1);
824
+        }
825
+        $altName = $name . '_' . (string)($lastNo + 1);
826
+        unset($usedNames);
827
+
828
+        $attempts = 1;
829
+        while ($attempts < 21) {
830
+            // Check to be really sure it is unique
831
+            // while loop is just a precaution. If a name is not generated within
832
+            // 20 attempts, something else is very wrong. Avoids infinite loop.
833
+            if (!\OC::$server->getGroupManager()->groupExists($altName)) {
834
+                return $altName;
835
+            }
836
+            $altName = $name . '_' . ($lastNo + $attempts);
837
+            $attempts++;
838
+        }
839
+        return false;
840
+    }
841
+
842
+    /**
843
+     * creates a unique name for internal Nextcloud use.
844
+     *
845
+     * @param string $name the display name of the object
846
+     * @param boolean $isUser whether name should be created for a user (true) or a group (false)
847
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful
848
+     */
849
+    private function createAltInternalOwnCloudName($name, $isUser) {
850
+        // ensure there is space for the "_1234" suffix
851
+        if (strlen($name) > 59) {
852
+            $name = substr($name, 0, 59);
853
+        }
854
+
855
+        $originalTTL = $this->connection->ldapCacheTTL;
856
+        $this->connection->setConfiguration(['ldapCacheTTL' => 0]);
857
+        if ($isUser) {
858
+            $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
859
+        } else {
860
+            $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
861
+        }
862
+        $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
863
+
864
+        return $altName;
865
+    }
866
+
867
+    /**
868
+     * fetches a list of users according to a provided loginName and utilizing
869
+     * the login filter.
870
+     */
871
+    public function fetchUsersByLoginName(string $loginName, array $attributes = ['dn']): array {
872
+        $loginName = $this->escapeFilterPart($loginName);
873
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
874
+        return $this->fetchListOfUsers($filter, $attributes);
875
+    }
876
+
877
+    /**
878
+     * counts the number of users according to a provided loginName and
879
+     * utilizing the login filter.
880
+     *
881
+     * @param string $loginName
882
+     * @return int
883
+     */
884
+    public function countUsersByLoginName($loginName) {
885
+        $loginName = $this->escapeFilterPart($loginName);
886
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
887
+        return $this->countUsers($filter);
888
+    }
889
+
890
+    /**
891
+     * @throws \Exception
892
+     */
893
+    public function fetchListOfUsers(string $filter, array $attr, int $limit = null, int $offset = null, bool $forceApplyAttributes = false): array {
894
+        $ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
895
+        $recordsToUpdate = $ldapRecords;
896
+        if (!$forceApplyAttributes) {
897
+            $isBackgroundJobModeAjax = $this->config
898
+                    ->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
899
+            $listOfDNs = array_reduce($ldapRecords, function ($listOfDNs, $entry) {
900
+                $listOfDNs[] = $entry['dn'][0];
901
+                return $listOfDNs;
902
+            }, []);
903
+            $idsByDn = $this->userMapper->getListOfIdsByDn($listOfDNs);
904
+            $recordsToUpdate = array_filter($ldapRecords, function ($record) use ($isBackgroundJobModeAjax, $idsByDn) {
905
+                $newlyMapped = false;
906
+                $uid = $idsByDn[$record['dn'][0]] ?? null;
907
+                if ($uid === null) {
908
+                    $uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
909
+                }
910
+                if (is_string($uid)) {
911
+                    $this->cacheUserExists($uid);
912
+                }
913
+                return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
914
+            });
915
+        }
916
+        $this->batchApplyUserAttributes($recordsToUpdate);
917
+        return $this->fetchList($ldapRecords, $this->manyAttributes($attr));
918
+    }
919
+
920
+    /**
921
+     * provided with an array of LDAP user records the method will fetch the
922
+     * user object and requests it to process the freshly fetched attributes and
923
+     * and their values
924
+     *
925
+     * @param array $ldapRecords
926
+     * @throws \Exception
927
+     */
928
+    public function batchApplyUserAttributes(array $ldapRecords) {
929
+        $displayNameAttribute = strtolower((string)$this->connection->ldapUserDisplayName);
930
+        foreach ($ldapRecords as $userRecord) {
931
+            if (!isset($userRecord[$displayNameAttribute])) {
932
+                // displayName is obligatory
933
+                continue;
934
+            }
935
+            $ocName = $this->dn2ocname($userRecord['dn'][0], null, true);
936
+            if ($ocName === false) {
937
+                continue;
938
+            }
939
+            $this->updateUserState($ocName);
940
+            $user = $this->userManager->get($ocName);
941
+            if ($user !== null) {
942
+                $user->processAttributes($userRecord);
943
+            } else {
944
+                $this->logger->debug(
945
+                    "The ldap user manager returned null for $ocName",
946
+                    ['app' => 'user_ldap']
947
+                );
948
+            }
949
+        }
950
+    }
951
+
952
+    /**
953
+     * @param string $filter
954
+     * @param string|string[] $attr
955
+     * @param int $limit
956
+     * @param int $offset
957
+     * @return array
958
+     */
959
+    public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
960
+        $groupRecords = $this->searchGroups($filter, $attr, $limit, $offset);
961
+
962
+        $listOfDNs = array_reduce($groupRecords, function ($listOfDNs, $entry) {
963
+            $listOfDNs[] = $entry['dn'][0];
964
+            return $listOfDNs;
965
+        }, []);
966
+        $idsByDn = $this->groupMapper->getListOfIdsByDn($listOfDNs);
967
+
968
+        array_walk($groupRecords, function ($record) use ($idsByDn) {
969
+            $newlyMapped = false;
970
+            $gid = $idsByDn[$record['dn'][0]] ?? null;
971
+            if ($gid === null) {
972
+                $gid = $this->dn2ocname($record['dn'][0], null, false, $newlyMapped, $record);
973
+            }
974
+            if (!$newlyMapped && is_string($gid)) {
975
+                $this->cacheGroupExists($gid);
976
+            }
977
+        });
978
+        return $this->fetchList($groupRecords, $this->manyAttributes($attr));
979
+    }
980
+
981
+    /**
982
+     * @param array $list
983
+     * @param bool $manyAttributes
984
+     * @return array
985
+     */
986
+    private function fetchList($list, $manyAttributes) {
987
+        if (is_array($list)) {
988
+            if ($manyAttributes) {
989
+                return $list;
990
+            } else {
991
+                $list = array_reduce($list, function ($carry, $item) {
992
+                    $attribute = array_keys($item)[0];
993
+                    $carry[] = $item[$attribute][0];
994
+                    return $carry;
995
+                }, []);
996
+                return array_unique($list, SORT_LOCALE_STRING);
997
+            }
998
+        }
999
+
1000
+        //error cause actually, maybe throw an exception in future.
1001
+        return [];
1002
+    }
1003
+
1004
+    /**
1005
+     * @throws ServerNotAvailableException
1006
+     */
1007
+    public function searchUsers(string $filter, array $attr = null, int $limit = null, int $offset = null): array {
1008
+        $result = [];
1009
+        foreach ($this->connection->ldapBaseUsers as $base) {
1010
+            $result = array_merge($result, $this->search($filter, $base, $attr, $limit, $offset));
1011
+        }
1012
+        return $result;
1013
+    }
1014
+
1015
+    /**
1016
+     * @param string $filter
1017
+     * @param string|string[] $attr
1018
+     * @param int $limit
1019
+     * @param int $offset
1020
+     * @return false|int
1021
+     * @throws ServerNotAvailableException
1022
+     */
1023
+    public function countUsers($filter, $attr = ['dn'], $limit = null, $offset = null) {
1024
+        $result = false;
1025
+        foreach ($this->connection->ldapBaseUsers as $base) {
1026
+            $count = $this->count($filter, [$base], $attr, $limit, $offset);
1027
+            $result = is_int($count) ? (int)$result + $count : $result;
1028
+        }
1029
+        return $result;
1030
+    }
1031
+
1032
+    /**
1033
+     * executes an LDAP search, optimized for Groups
1034
+     *
1035
+     * @param string $filter the LDAP filter for the search
1036
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
1037
+     * @param integer $limit
1038
+     * @param integer $offset
1039
+     * @return array with the search result
1040
+     *
1041
+     * Executes an LDAP search
1042
+     * @throws ServerNotAvailableException
1043
+     */
1044
+    public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
1045
+        $result = [];
1046
+        foreach ($this->connection->ldapBaseGroups as $base) {
1047
+            $result = array_merge($result, $this->search($filter, $base, $attr, $limit, $offset));
1048
+        }
1049
+        return $result;
1050
+    }
1051
+
1052
+    /**
1053
+     * returns the number of available groups
1054
+     *
1055
+     * @param string $filter the LDAP search filter
1056
+     * @param string[] $attr optional
1057
+     * @param int|null $limit
1058
+     * @param int|null $offset
1059
+     * @return int|bool
1060
+     * @throws ServerNotAvailableException
1061
+     */
1062
+    public function countGroups($filter, $attr = ['dn'], $limit = null, $offset = null) {
1063
+        $result = false;
1064
+        foreach ($this->connection->ldapBaseGroups as $base) {
1065
+            $count = $this->count($filter, [$base], $attr, $limit, $offset);
1066
+            $result = is_int($count) ? (int)$result + $count : $result;
1067
+        }
1068
+        return $result;
1069
+    }
1070
+
1071
+    /**
1072
+     * returns the number of available objects on the base DN
1073
+     *
1074
+     * @param int|null $limit
1075
+     * @param int|null $offset
1076
+     * @return int|bool
1077
+     * @throws ServerNotAvailableException
1078
+     */
1079
+    public function countObjects($limit = null, $offset = null) {
1080
+        $result = false;
1081
+        foreach ($this->connection->ldapBase as $base) {
1082
+            $count = $this->count('objectclass=*', [$base], ['dn'], $limit, $offset);
1083
+            $result = is_int($count) ? (int)$result + $count : $result;
1084
+        }
1085
+        return $result;
1086
+    }
1087
+
1088
+    /**
1089
+     * Returns the LDAP handler
1090
+     *
1091
+     * @throws \OC\ServerNotAvailableException
1092
+     */
1093
+
1094
+    /**
1095
+     * @return mixed
1096
+     * @throws \OC\ServerNotAvailableException
1097
+     */
1098
+    private function invokeLDAPMethod() {
1099
+        $arguments = func_get_args();
1100
+        $command = array_shift($arguments);
1101
+        $cr = array_shift($arguments);
1102
+        if (!method_exists($this->ldap, $command)) {
1103
+            return null;
1104
+        }
1105
+        array_unshift($arguments, $cr);
1106
+        // php no longer supports call-time pass-by-reference
1107
+        // thus cannot support controlPagedResultResponse as the third argument
1108
+        // is a reference
1109
+        $doMethod = function () use ($command, &$arguments) {
1110
+            if ($command == 'controlPagedResultResponse') {
1111
+                throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1112
+            } else {
1113
+                return call_user_func_array([$this->ldap, $command], $arguments);
1114
+            }
1115
+        };
1116
+        try {
1117
+            $ret = $doMethod();
1118
+        } catch (ServerNotAvailableException $e) {
1119
+            /* Server connection lost, attempt to reestablish it
1120 1120
 			 * Maybe implement exponential backoff?
1121 1121
 			 * This was enough to get solr indexer working which has large delays between LDAP fetches.
1122 1122
 			 */
1123
-			$this->logger->debug("Connection lost on $command, attempting to reestablish.", ['app' => 'user_ldap']);
1124
-			$this->connection->resetConnectionResource();
1125
-			$cr = $this->connection->getConnectionResource();
1126
-
1127
-			if (!$this->ldap->isResource($cr)) {
1128
-				// Seems like we didn't find any resource.
1129
-				$this->logger->debug("Could not $command, because resource is missing.", ['app' => 'user_ldap']);
1130
-				throw $e;
1131
-			}
1132
-
1133
-			$arguments[0] = $cr;
1134
-			$ret = $doMethod();
1135
-		}
1136
-		return $ret;
1137
-	}
1138
-
1139
-	/**
1140
-	 * retrieved. Results will according to the order in the array.
1141
-	 *
1142
-	 * @param string $filter
1143
-	 * @param string $base
1144
-	 * @param string[] $attr
1145
-	 * @param int|null $limit optional, maximum results to be counted
1146
-	 * @param int|null $offset optional, a starting point
1147
-	 * @return array|false array with the search result as first value and pagedSearchOK as
1148
-	 * second | false if not successful
1149
-	 * @throws ServerNotAvailableException
1150
-	 */
1151
-	private function executeSearch(
1152
-		string $filter,
1153
-		string $base,
1154
-		?array &$attr,
1155
-		?int $limit,
1156
-		?int $offset
1157
-	) {
1158
-		// See if we have a resource, in case not cancel with message
1159
-		$cr = $this->connection->getConnectionResource();
1160
-		if (!$this->ldap->isResource($cr)) {
1161
-			// Seems like we didn't find any resource.
1162
-			// Return an empty array just like before.
1163
-			$this->logger->debug('Could not search, because resource is missing.', ['app' => 'user_ldap']);
1164
-			return false;
1165
-		}
1166
-
1167
-		//check whether paged search should be attempted
1168
-		try {
1169
-			$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int)$limit, (int)$offset);
1170
-		} catch (NoMoreResults $e) {
1171
-			// beyond last results page
1172
-			return false;
1173
-		}
1174
-
1175
-		$sr = $this->invokeLDAPMethod('search', $cr, $base, $filter, $attr);
1176
-		// cannot use $cr anymore, might have changed in the previous call!
1177
-		$error = $this->ldap->errno($this->connection->getConnectionResource());
1178
-		if (!$this->ldap->isResource($sr) || $error !== 0) {
1179
-			$this->logger->error('Attempt for Paging?  ' . print_r($pagedSearchOK, true), ['app' => 'user_ldap']);
1180
-			return false;
1181
-		}
1182
-
1183
-		return [$sr, $pagedSearchOK];
1184
-	}
1185
-
1186
-	/**
1187
-	 * processes an LDAP paged search operation
1188
-	 *
1189
-	 * @param resource|\LDAP\Result|resource[]|\LDAP\Result[] $sr the array containing the LDAP search resources
1190
-	 * @param int $foundItems number of results in the single search operation
1191
-	 * @param int $limit maximum results to be counted
1192
-	 * @param bool $pagedSearchOK whether a paged search has been executed
1193
-	 * @param bool $skipHandling required for paged search when cookies to
1194
-	 * prior results need to be gained
1195
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
1196
-	 * @throws ServerNotAvailableException
1197
-	 */
1198
-	private function processPagedSearchStatus(
1199
-		$sr,
1200
-		int $foundItems,
1201
-		int $limit,
1202
-		bool $pagedSearchOK,
1203
-		bool $skipHandling
1204
-	): bool {
1205
-		$cookie = null;
1206
-		if ($pagedSearchOK) {
1207
-			$cr = $this->connection->getConnectionResource();
1208
-			if ($this->ldap->controlPagedResultResponse($cr, $sr, $cookie)) {
1209
-				$this->lastCookie = $cookie;
1210
-			}
1211
-
1212
-			//browsing through prior pages to get the cookie for the new one
1213
-			if ($skipHandling) {
1214
-				return false;
1215
-			}
1216
-			// if count is bigger, then the server does not support
1217
-			// paged search. Instead, he did a normal search. We set a
1218
-			// flag here, so the callee knows how to deal with it.
1219
-			if ($foundItems <= $limit) {
1220
-				$this->pagedSearchedSuccessful = true;
1221
-			}
1222
-		} else {
1223
-			if (!is_null($limit) && (int)$this->connection->ldapPagingSize !== 0) {
1224
-				$this->logger->debug(
1225
-					'Paged search was not available',
1226
-					['app' => 'user_ldap']
1227
-				);
1228
-			}
1229
-		}
1230
-		/* ++ Fixing RHDS searches with pages with zero results ++
1123
+            $this->logger->debug("Connection lost on $command, attempting to reestablish.", ['app' => 'user_ldap']);
1124
+            $this->connection->resetConnectionResource();
1125
+            $cr = $this->connection->getConnectionResource();
1126
+
1127
+            if (!$this->ldap->isResource($cr)) {
1128
+                // Seems like we didn't find any resource.
1129
+                $this->logger->debug("Could not $command, because resource is missing.", ['app' => 'user_ldap']);
1130
+                throw $e;
1131
+            }
1132
+
1133
+            $arguments[0] = $cr;
1134
+            $ret = $doMethod();
1135
+        }
1136
+        return $ret;
1137
+    }
1138
+
1139
+    /**
1140
+     * retrieved. Results will according to the order in the array.
1141
+     *
1142
+     * @param string $filter
1143
+     * @param string $base
1144
+     * @param string[] $attr
1145
+     * @param int|null $limit optional, maximum results to be counted
1146
+     * @param int|null $offset optional, a starting point
1147
+     * @return array|false array with the search result as first value and pagedSearchOK as
1148
+     * second | false if not successful
1149
+     * @throws ServerNotAvailableException
1150
+     */
1151
+    private function executeSearch(
1152
+        string $filter,
1153
+        string $base,
1154
+        ?array &$attr,
1155
+        ?int $limit,
1156
+        ?int $offset
1157
+    ) {
1158
+        // See if we have a resource, in case not cancel with message
1159
+        $cr = $this->connection->getConnectionResource();
1160
+        if (!$this->ldap->isResource($cr)) {
1161
+            // Seems like we didn't find any resource.
1162
+            // Return an empty array just like before.
1163
+            $this->logger->debug('Could not search, because resource is missing.', ['app' => 'user_ldap']);
1164
+            return false;
1165
+        }
1166
+
1167
+        //check whether paged search should be attempted
1168
+        try {
1169
+            $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int)$limit, (int)$offset);
1170
+        } catch (NoMoreResults $e) {
1171
+            // beyond last results page
1172
+            return false;
1173
+        }
1174
+
1175
+        $sr = $this->invokeLDAPMethod('search', $cr, $base, $filter, $attr);
1176
+        // cannot use $cr anymore, might have changed in the previous call!
1177
+        $error = $this->ldap->errno($this->connection->getConnectionResource());
1178
+        if (!$this->ldap->isResource($sr) || $error !== 0) {
1179
+            $this->logger->error('Attempt for Paging?  ' . print_r($pagedSearchOK, true), ['app' => 'user_ldap']);
1180
+            return false;
1181
+        }
1182
+
1183
+        return [$sr, $pagedSearchOK];
1184
+    }
1185
+
1186
+    /**
1187
+     * processes an LDAP paged search operation
1188
+     *
1189
+     * @param resource|\LDAP\Result|resource[]|\LDAP\Result[] $sr the array containing the LDAP search resources
1190
+     * @param int $foundItems number of results in the single search operation
1191
+     * @param int $limit maximum results to be counted
1192
+     * @param bool $pagedSearchOK whether a paged search has been executed
1193
+     * @param bool $skipHandling required for paged search when cookies to
1194
+     * prior results need to be gained
1195
+     * @return bool cookie validity, true if we have more pages, false otherwise.
1196
+     * @throws ServerNotAvailableException
1197
+     */
1198
+    private function processPagedSearchStatus(
1199
+        $sr,
1200
+        int $foundItems,
1201
+        int $limit,
1202
+        bool $pagedSearchOK,
1203
+        bool $skipHandling
1204
+    ): bool {
1205
+        $cookie = null;
1206
+        if ($pagedSearchOK) {
1207
+            $cr = $this->connection->getConnectionResource();
1208
+            if ($this->ldap->controlPagedResultResponse($cr, $sr, $cookie)) {
1209
+                $this->lastCookie = $cookie;
1210
+            }
1211
+
1212
+            //browsing through prior pages to get the cookie for the new one
1213
+            if ($skipHandling) {
1214
+                return false;
1215
+            }
1216
+            // if count is bigger, then the server does not support
1217
+            // paged search. Instead, he did a normal search. We set a
1218
+            // flag here, so the callee knows how to deal with it.
1219
+            if ($foundItems <= $limit) {
1220
+                $this->pagedSearchedSuccessful = true;
1221
+            }
1222
+        } else {
1223
+            if (!is_null($limit) && (int)$this->connection->ldapPagingSize !== 0) {
1224
+                $this->logger->debug(
1225
+                    'Paged search was not available',
1226
+                    ['app' => 'user_ldap']
1227
+                );
1228
+            }
1229
+        }
1230
+        /* ++ Fixing RHDS searches with pages with zero results ++
1231 1231
 		 * Return cookie status. If we don't have more pages, with RHDS
1232 1232
 		 * cookie is null, with openldap cookie is an empty string and
1233 1233
 		 * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0
1234 1234
 		 */
1235
-		return !empty($cookie) || $cookie === '0';
1236
-	}
1237
-
1238
-	/**
1239
-	 * executes an LDAP search, but counts the results only
1240
-	 *
1241
-	 * @param string $filter the LDAP filter for the search
1242
-	 * @param array $bases an array containing the LDAP subtree(s) that shall be searched
1243
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1244
-	 * retrieved. Results will according to the order in the array.
1245
-	 * @param int $limit optional, maximum results to be counted
1246
-	 * @param int $offset optional, a starting point
1247
-	 * @param bool $skipHandling indicates whether the pages search operation is
1248
-	 * completed
1249
-	 * @return int|false Integer or false if the search could not be initialized
1250
-	 * @throws ServerNotAvailableException
1251
-	 */
1252
-	private function count(
1253
-		string $filter,
1254
-		array $bases,
1255
-		$attr = null,
1256
-		?int $limit = null,
1257
-		?int $offset = null,
1258
-		bool $skipHandling = false
1259
-	) {
1260
-		$this->logger->debug('Count filter: {filter}', [
1261
-			'app' => 'user_ldap',
1262
-			'filter' => $filter
1263
-		]);
1264
-
1265
-		if (!is_null($attr) && !is_array($attr)) {
1266
-			$attr = [mb_strtolower($attr, 'UTF-8')];
1267
-		}
1268
-
1269
-		$limitPerPage = (int)$this->connection->ldapPagingSize;
1270
-		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1271
-			$limitPerPage = $limit;
1272
-		}
1273
-
1274
-		$counter = 0;
1275
-		$count = null;
1276
-		$this->connection->getConnectionResource();
1277
-
1278
-		foreach ($bases as $base) {
1279
-			do {
1280
-				$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1281
-				if ($search === false) {
1282
-					return $counter > 0 ? $counter : false;
1283
-				}
1284
-				[$sr, $pagedSearchOK] = $search;
1285
-
1286
-				/* ++ Fixing RHDS searches with pages with zero results ++
1235
+        return !empty($cookie) || $cookie === '0';
1236
+    }
1237
+
1238
+    /**
1239
+     * executes an LDAP search, but counts the results only
1240
+     *
1241
+     * @param string $filter the LDAP filter for the search
1242
+     * @param array $bases an array containing the LDAP subtree(s) that shall be searched
1243
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1244
+     * retrieved. Results will according to the order in the array.
1245
+     * @param int $limit optional, maximum results to be counted
1246
+     * @param int $offset optional, a starting point
1247
+     * @param bool $skipHandling indicates whether the pages search operation is
1248
+     * completed
1249
+     * @return int|false Integer or false if the search could not be initialized
1250
+     * @throws ServerNotAvailableException
1251
+     */
1252
+    private function count(
1253
+        string $filter,
1254
+        array $bases,
1255
+        $attr = null,
1256
+        ?int $limit = null,
1257
+        ?int $offset = null,
1258
+        bool $skipHandling = false
1259
+    ) {
1260
+        $this->logger->debug('Count filter: {filter}', [
1261
+            'app' => 'user_ldap',
1262
+            'filter' => $filter
1263
+        ]);
1264
+
1265
+        if (!is_null($attr) && !is_array($attr)) {
1266
+            $attr = [mb_strtolower($attr, 'UTF-8')];
1267
+        }
1268
+
1269
+        $limitPerPage = (int)$this->connection->ldapPagingSize;
1270
+        if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1271
+            $limitPerPage = $limit;
1272
+        }
1273
+
1274
+        $counter = 0;
1275
+        $count = null;
1276
+        $this->connection->getConnectionResource();
1277
+
1278
+        foreach ($bases as $base) {
1279
+            do {
1280
+                $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1281
+                if ($search === false) {
1282
+                    return $counter > 0 ? $counter : false;
1283
+                }
1284
+                [$sr, $pagedSearchOK] = $search;
1285
+
1286
+                /* ++ Fixing RHDS searches with pages with zero results ++
1287 1287
 				 * countEntriesInSearchResults() method signature changed
1288 1288
 				 * by removing $limit and &$hasHitLimit parameters
1289 1289
 				 */
1290
-				$count = $this->countEntriesInSearchResults($sr);
1291
-				$counter += $count;
1290
+                $count = $this->countEntriesInSearchResults($sr);
1291
+                $counter += $count;
1292 1292
 
1293
-				$hasMorePages = $this->processPagedSearchStatus($sr, $count, $limitPerPage, $pagedSearchOK, $skipHandling);
1294
-				$offset += $limitPerPage;
1295
-				/* ++ Fixing RHDS searches with pages with zero results ++
1293
+                $hasMorePages = $this->processPagedSearchStatus($sr, $count, $limitPerPage, $pagedSearchOK, $skipHandling);
1294
+                $offset += $limitPerPage;
1295
+                /* ++ Fixing RHDS searches with pages with zero results ++
1296 1296
 				 * Continue now depends on $hasMorePages value
1297 1297
 				 */
1298
-				$continue = $pagedSearchOK && $hasMorePages;
1299
-			} while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1300
-		}
1301
-
1302
-		return $counter;
1303
-	}
1304
-
1305
-	/**
1306
-	 * @param resource|\LDAP\Result|resource[]|\LDAP\Result[] $sr
1307
-	 * @return int
1308
-	 * @throws ServerNotAvailableException
1309
-	 */
1310
-	private function countEntriesInSearchResults($sr): int {
1311
-		return (int)$this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $sr);
1312
-	}
1313
-
1314
-	/**
1315
-	 * Executes an LDAP search
1316
-	 *
1317
-	 * @throws ServerNotAvailableException
1318
-	 */
1319
-	public function search(
1320
-		string $filter,
1321
-		string $base,
1322
-		?array $attr = null,
1323
-		?int $limit = null,
1324
-		?int $offset = null,
1325
-		bool $skipHandling = false
1326
-	): array {
1327
-		$limitPerPage = (int)$this->connection->ldapPagingSize;
1328
-		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1329
-			$limitPerPage = $limit;
1330
-		}
1331
-
1332
-		if (!is_null($attr) && !is_array($attr)) {
1333
-			$attr = [mb_strtolower($attr, 'UTF-8')];
1334
-		}
1335
-
1336
-		/* ++ Fixing RHDS searches with pages with zero results ++
1298
+                $continue = $pagedSearchOK && $hasMorePages;
1299
+            } while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1300
+        }
1301
+
1302
+        return $counter;
1303
+    }
1304
+
1305
+    /**
1306
+     * @param resource|\LDAP\Result|resource[]|\LDAP\Result[] $sr
1307
+     * @return int
1308
+     * @throws ServerNotAvailableException
1309
+     */
1310
+    private function countEntriesInSearchResults($sr): int {
1311
+        return (int)$this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $sr);
1312
+    }
1313
+
1314
+    /**
1315
+     * Executes an LDAP search
1316
+     *
1317
+     * @throws ServerNotAvailableException
1318
+     */
1319
+    public function search(
1320
+        string $filter,
1321
+        string $base,
1322
+        ?array $attr = null,
1323
+        ?int $limit = null,
1324
+        ?int $offset = null,
1325
+        bool $skipHandling = false
1326
+    ): array {
1327
+        $limitPerPage = (int)$this->connection->ldapPagingSize;
1328
+        if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1329
+            $limitPerPage = $limit;
1330
+        }
1331
+
1332
+        if (!is_null($attr) && !is_array($attr)) {
1333
+            $attr = [mb_strtolower($attr, 'UTF-8')];
1334
+        }
1335
+
1336
+        /* ++ Fixing RHDS searches with pages with zero results ++
1337 1337
 		 * As we can have pages with zero results and/or pages with less
1338 1338
 		 * than $limit results but with a still valid server 'cookie',
1339 1339
 		 * loops through until we get $continue equals true and
1340 1340
 		 * $findings['count'] < $limit
1341 1341
 		 */
1342
-		$findings = [];
1343
-		$savedoffset = $offset;
1344
-		$iFoundItems = 0;
1345
-
1346
-		do {
1347
-			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1348
-			if ($search === false) {
1349
-				return [];
1350
-			}
1351
-			[$sr, $pagedSearchOK] = $search;
1352
-			$cr = $this->connection->getConnectionResource();
1353
-
1354
-			if ($skipHandling) {
1355
-				//i.e. result do not need to be fetched, we just need the cookie
1356
-				//thus pass 1 or any other value as $iFoundItems because it is not
1357
-				//used
1358
-				$this->processPagedSearchStatus($sr, 1, $limitPerPage, $pagedSearchOK, $skipHandling);
1359
-				return [];
1360
-			}
1361
-
1362
-			$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $sr));
1363
-			$iFoundItems = max($iFoundItems, $findings['count']);
1364
-			unset($findings['count']);
1365
-
1366
-			$continue = $this->processPagedSearchStatus($sr, $iFoundItems, $limitPerPage, $pagedSearchOK, $skipHandling);
1367
-			$offset += $limitPerPage;
1368
-		} while ($continue && $pagedSearchOK && ($limit === null || count($findings) < $limit));
1369
-
1370
-		// resetting offset
1371
-		$offset = $savedoffset;
1372
-
1373
-		// if we're here, probably no connection resource is returned.
1374
-		// to make Nextcloud behave nicely, we simply give back an empty array.
1375
-		if (is_null($findings)) {
1376
-			return [];
1377
-		}
1378
-
1379
-		if (!is_null($attr)) {
1380
-			$selection = [];
1381
-			$i = 0;
1382
-			foreach ($findings as $item) {
1383
-				if (!is_array($item)) {
1384
-					continue;
1385
-				}
1386
-				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1387
-				foreach ($attr as $key) {
1388
-					if (isset($item[$key])) {
1389
-						if (is_array($item[$key]) && isset($item[$key]['count'])) {
1390
-							unset($item[$key]['count']);
1391
-						}
1392
-						if ($key !== 'dn') {
1393
-							if ($this->resemblesDN($key)) {
1394
-								$selection[$i][$key] = $this->helper->sanitizeDN($item[$key]);
1395
-							} elseif ($key === 'objectguid' || $key === 'guid') {
1396
-								$selection[$i][$key] = [$this->convertObjectGUID2Str($item[$key][0])];
1397
-							} else {
1398
-								$selection[$i][$key] = $item[$key];
1399
-							}
1400
-						} else {
1401
-							$selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1402
-						}
1403
-					}
1404
-				}
1405
-				$i++;
1406
-			}
1407
-			$findings = $selection;
1408
-		}
1409
-		//we slice the findings, when
1410
-		//a) paged search unsuccessful, though attempted
1411
-		//b) no paged search, but limit set
1412
-		if ((!$this->getPagedSearchResultState()
1413
-				&& $pagedSearchOK)
1414
-			|| (
1415
-				!$pagedSearchOK
1416
-				&& !is_null($limit)
1417
-			)
1418
-		) {
1419
-			$findings = array_slice($findings, (int)$offset, $limit);
1420
-		}
1421
-		return $findings;
1422
-	}
1423
-
1424
-	/**
1425
-	 * @param string $name
1426
-	 * @return string
1427
-	 * @throws \InvalidArgumentException
1428
-	 */
1429
-	public function sanitizeUsername($name) {
1430
-		$name = trim($name);
1431
-
1432
-		if ($this->connection->ldapIgnoreNamingRules) {
1433
-			return $name;
1434
-		}
1435
-
1436
-		// Use htmlentities to get rid of accents
1437
-		$name = htmlentities($name, ENT_NOQUOTES, 'UTF-8');
1438
-
1439
-		// Remove accents
1440
-		$name = preg_replace('#&([A-Za-z])(?:acute|cedil|caron|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $name);
1441
-		// Remove ligatures
1442
-		$name = preg_replace('#&([A-Za-z]{2})(?:lig);#', '\1', $name);
1443
-		// Remove unknown leftover entities
1444
-		$name = preg_replace('#&[^;]+;#', '', $name);
1445
-
1446
-		// Replacements
1447
-		$name = str_replace(' ', '_', $name);
1448
-
1449
-		// Every remaining disallowed characters will be removed
1450
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1451
-
1452
-		if (strlen($name) > 64) {
1453
-			$name = (string)hash('sha256', $name, false);
1454
-		}
1455
-
1456
-		if ($name === '') {
1457
-			throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1458
-		}
1459
-
1460
-		return $name;
1461
-	}
1462
-
1463
-	public function sanitizeGroupIDCandidate(string $candidate): string {
1464
-		$candidate = trim($candidate);
1465
-		if (strlen($candidate) > 64) {
1466
-			$candidate = (string)hash('sha256', $candidate, false);
1467
-		}
1468
-		if ($candidate === '') {
1469
-			throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1470
-		}
1471
-
1472
-		return $candidate;
1473
-	}
1474
-
1475
-	/**
1476
-	 * escapes (user provided) parts for LDAP filter
1477
-	 *
1478
-	 * @param string $input , the provided value
1479
-	 * @param bool $allowAsterisk whether in * at the beginning should be preserved
1480
-	 * @return string the escaped string
1481
-	 */
1482
-	public function escapeFilterPart($input, $allowAsterisk = false): string {
1483
-		$asterisk = '';
1484
-		if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1485
-			$asterisk = '*';
1486
-			$input = mb_substr($input, 1, null, 'UTF-8');
1487
-		}
1488
-		$search = ['*', '\\', '(', ')'];
1489
-		$replace = ['\\*', '\\\\', '\\(', '\\)'];
1490
-		return $asterisk . str_replace($search, $replace, $input);
1491
-	}
1492
-
1493
-	/**
1494
-	 * combines the input filters with AND
1495
-	 *
1496
-	 * @param string[] $filters the filters to connect
1497
-	 * @return string the combined filter
1498
-	 */
1499
-	public function combineFilterWithAnd($filters): string {
1500
-		return $this->combineFilter($filters, '&');
1501
-	}
1502
-
1503
-	/**
1504
-	 * combines the input filters with OR
1505
-	 *
1506
-	 * @param string[] $filters the filters to connect
1507
-	 * @return string the combined filter
1508
-	 * Combines Filter arguments with OR
1509
-	 */
1510
-	public function combineFilterWithOr($filters) {
1511
-		return $this->combineFilter($filters, '|');
1512
-	}
1513
-
1514
-	/**
1515
-	 * combines the input filters with given operator
1516
-	 *
1517
-	 * @param string[] $filters the filters to connect
1518
-	 * @param string $operator either & or |
1519
-	 * @return string the combined filter
1520
-	 */
1521
-	private function combineFilter($filters, $operator) {
1522
-		$combinedFilter = '(' . $operator;
1523
-		foreach ($filters as $filter) {
1524
-			if ($filter !== '' && $filter[0] !== '(') {
1525
-				$filter = '(' . $filter . ')';
1526
-			}
1527
-			$combinedFilter .= $filter;
1528
-		}
1529
-		$combinedFilter .= ')';
1530
-		return $combinedFilter;
1531
-	}
1532
-
1533
-	/**
1534
-	 * creates a filter part for to perform search for users
1535
-	 *
1536
-	 * @param string $search the search term
1537
-	 * @return string the final filter part to use in LDAP searches
1538
-	 */
1539
-	public function getFilterPartForUserSearch($search) {
1540
-		return $this->getFilterPartForSearch($search,
1541
-			$this->connection->ldapAttributesForUserSearch,
1542
-			$this->connection->ldapUserDisplayName);
1543
-	}
1544
-
1545
-	/**
1546
-	 * creates a filter part for to perform search for groups
1547
-	 *
1548
-	 * @param string $search the search term
1549
-	 * @return string the final filter part to use in LDAP searches
1550
-	 */
1551
-	public function getFilterPartForGroupSearch($search) {
1552
-		return $this->getFilterPartForSearch($search,
1553
-			$this->connection->ldapAttributesForGroupSearch,
1554
-			$this->connection->ldapGroupDisplayName);
1555
-	}
1556
-
1557
-	/**
1558
-	 * creates a filter part for searches by splitting up the given search
1559
-	 * string into single words
1560
-	 *
1561
-	 * @param string $search the search term
1562
-	 * @param string[] $searchAttributes needs to have at least two attributes,
1563
-	 * otherwise it does not make sense :)
1564
-	 * @return string the final filter part to use in LDAP searches
1565
-	 * @throws DomainException
1566
-	 */
1567
-	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1568
-		if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1569
-			throw new DomainException('searchAttributes must be an array with at least two string');
1570
-		}
1571
-		$searchWords = explode(' ', trim($search));
1572
-		$wordFilters = [];
1573
-		foreach ($searchWords as $word) {
1574
-			$word = $this->prepareSearchTerm($word);
1575
-			//every word needs to appear at least once
1576
-			$wordMatchOneAttrFilters = [];
1577
-			foreach ($searchAttributes as $attr) {
1578
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1579
-			}
1580
-			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1581
-		}
1582
-		return $this->combineFilterWithAnd($wordFilters);
1583
-	}
1584
-
1585
-	/**
1586
-	 * creates a filter part for searches
1587
-	 *
1588
-	 * @param string $search the search term
1589
-	 * @param string[]|null $searchAttributes
1590
-	 * @param string $fallbackAttribute a fallback attribute in case the user
1591
-	 * did not define search attributes. Typically the display name attribute.
1592
-	 * @return string the final filter part to use in LDAP searches
1593
-	 */
1594
-	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1595
-		$filter = [];
1596
-		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1597
-		if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1598
-			try {
1599
-				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1600
-			} catch (DomainException $e) {
1601
-				// Creating advanced filter for search failed, falling back to simple method. Edge case, but valid.
1602
-			}
1603
-		}
1604
-
1605
-		$search = $this->prepareSearchTerm($search);
1606
-		if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1607
-			if ($fallbackAttribute === '') {
1608
-				return '';
1609
-			}
1610
-			$filter[] = $fallbackAttribute . '=' . $search;
1611
-		} else {
1612
-			foreach ($searchAttributes as $attribute) {
1613
-				$filter[] = $attribute . '=' . $search;
1614
-			}
1615
-		}
1616
-		if (count($filter) === 1) {
1617
-			return '(' . $filter[0] . ')';
1618
-		}
1619
-		return $this->combineFilterWithOr($filter);
1620
-	}
1621
-
1622
-	/**
1623
-	 * returns the search term depending on whether we are allowed
1624
-	 * list users found by ldap with the current input appended by
1625
-	 * a *
1626
-	 *
1627
-	 * @return string
1628
-	 */
1629
-	private function prepareSearchTerm($term) {
1630
-		$config = \OC::$server->getConfig();
1631
-
1632
-		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1633
-
1634
-		$result = $term;
1635
-		if ($term === '') {
1636
-			$result = '*';
1637
-		} elseif ($allowEnum !== 'no') {
1638
-			$result = $term . '*';
1639
-		}
1640
-		return $result;
1641
-	}
1642
-
1643
-	/**
1644
-	 * returns the filter used for counting users
1645
-	 *
1646
-	 * @return string
1647
-	 */
1648
-	public function getFilterForUserCount() {
1649
-		$filter = $this->combineFilterWithAnd([
1650
-			$this->connection->ldapUserFilter,
1651
-			$this->connection->ldapUserDisplayName . '=*'
1652
-		]);
1653
-
1654
-		return $filter;
1655
-	}
1656
-
1657
-	/**
1658
-	 * @param string $name
1659
-	 * @param string $password
1660
-	 * @return bool
1661
-	 */
1662
-	public function areCredentialsValid($name, $password) {
1663
-		$name = $this->helper->DNasBaseParameter($name);
1664
-		$testConnection = clone $this->connection;
1665
-		$credentials = [
1666
-			'ldapAgentName' => $name,
1667
-			'ldapAgentPassword' => $password
1668
-		];
1669
-		if (!$testConnection->setConfiguration($credentials)) {
1670
-			return false;
1671
-		}
1672
-		return $testConnection->bind();
1673
-	}
1674
-
1675
-	/**
1676
-	 * reverse lookup of a DN given a known UUID
1677
-	 *
1678
-	 * @param string $uuid
1679
-	 * @return string
1680
-	 * @throws \Exception
1681
-	 */
1682
-	public function getUserDnByUuid($uuid) {
1683
-		$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1684
-		$filter = $this->connection->ldapUserFilter;
1685
-		$bases = $this->connection->ldapBaseUsers;
1686
-
1687
-		if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1688
-			// Sacrebleu! The UUID attribute is unknown :( We need first an
1689
-			// existing DN to be able to reliably detect it.
1690
-			foreach ($bases as $base) {
1691
-				$result = $this->search($filter, $base, ['dn'], 1);
1692
-				if (!isset($result[0]) || !isset($result[0]['dn'])) {
1693
-					continue;
1694
-				}
1695
-				$dn = $result[0]['dn'][0];
1696
-				if ($hasFound = $this->detectUuidAttribute($dn, true)) {
1697
-					break;
1698
-				}
1699
-			}
1700
-			if (!isset($hasFound) || !$hasFound) {
1701
-				throw new \Exception('Cannot determine UUID attribute');
1702
-			}
1703
-		} else {
1704
-			// The UUID attribute is either known or an override is given.
1705
-			// By calling this method we ensure that $this->connection->$uuidAttr
1706
-			// is definitely set
1707
-			if (!$this->detectUuidAttribute('', true)) {
1708
-				throw new \Exception('Cannot determine UUID attribute');
1709
-			}
1710
-		}
1711
-
1712
-		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1713
-		if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1714
-			$uuid = $this->formatGuid2ForFilterUser($uuid);
1715
-		}
1716
-
1717
-		$filter = $uuidAttr . '=' . $uuid;
1718
-		$result = $this->searchUsers($filter, ['dn'], 2);
1719
-		if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1720
-			// we put the count into account to make sure that this is
1721
-			// really unique
1722
-			return $result[0]['dn'][0];
1723
-		}
1724
-
1725
-		throw new \Exception('Cannot determine UUID attribute');
1726
-	}
1727
-
1728
-	/**
1729
-	 * auto-detects the directory's UUID attribute
1730
-	 *
1731
-	 * @param string $dn a known DN used to check against
1732
-	 * @param bool $isUser
1733
-	 * @param bool $force the detection should be run, even if it is not set to auto
1734
-	 * @param array|null $ldapRecord
1735
-	 * @return bool true on success, false otherwise
1736
-	 * @throws ServerNotAvailableException
1737
-	 */
1738
-	private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1739
-		if ($isUser) {
1740
-			$uuidAttr = 'ldapUuidUserAttribute';
1741
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1742
-		} else {
1743
-			$uuidAttr = 'ldapUuidGroupAttribute';
1744
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1745
-		}
1746
-
1747
-		if (!$force) {
1748
-			if ($this->connection->$uuidAttr !== 'auto') {
1749
-				return true;
1750
-			} elseif (is_string($uuidOverride) && trim($uuidOverride) !== '') {
1751
-				$this->connection->$uuidAttr = $uuidOverride;
1752
-				return true;
1753
-			}
1754
-
1755
-			$attribute = $this->connection->getFromCache($uuidAttr);
1756
-			if ($attribute !== null) {
1757
-				$this->connection->$uuidAttr = $attribute;
1758
-				return true;
1759
-			}
1760
-		}
1761
-
1762
-		foreach (self::UUID_ATTRIBUTES as $attribute) {
1763
-			if ($ldapRecord !== null) {
1764
-				// we have the info from LDAP already, we don't need to talk to the server again
1765
-				if (isset($ldapRecord[$attribute])) {
1766
-					$this->connection->$uuidAttr = $attribute;
1767
-					return true;
1768
-				}
1769
-			}
1770
-
1771
-			$value = $this->readAttribute($dn, $attribute);
1772
-			if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1773
-				$this->logger->debug(
1774
-					'Setting {attribute} as {subject}',
1775
-					[
1776
-						'app' => 'user_ldap',
1777
-						'attribute' => $attribute,
1778
-						'subject' => $uuidAttr
1779
-					]
1780
-				);
1781
-				$this->connection->$uuidAttr = $attribute;
1782
-				$this->connection->writeToCache($uuidAttr, $attribute);
1783
-				return true;
1784
-			}
1785
-		}
1786
-		$this->logger->debug('Could not autodetect the UUID attribute', ['app' => 'user_ldap']);
1787
-
1788
-		return false;
1789
-	}
1790
-
1791
-	/**
1792
-	 * @param string $dn
1793
-	 * @param bool $isUser
1794
-	 * @param null $ldapRecord
1795
-	 * @return false|string
1796
-	 * @throws ServerNotAvailableException
1797
-	 */
1798
-	public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1799
-		if ($isUser) {
1800
-			$uuidAttr = 'ldapUuidUserAttribute';
1801
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1802
-		} else {
1803
-			$uuidAttr = 'ldapUuidGroupAttribute';
1804
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1805
-		}
1806
-
1807
-		$uuid = false;
1808
-		if ($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1809
-			$attr = $this->connection->$uuidAttr;
1810
-			$uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1811
-			if (!is_array($uuid)
1812
-				&& $uuidOverride !== ''
1813
-				&& $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord)) {
1814
-				$uuid = isset($ldapRecord[$this->connection->$uuidAttr])
1815
-					? $ldapRecord[$this->connection->$uuidAttr]
1816
-					: $this->readAttribute($dn, $this->connection->$uuidAttr);
1817
-			}
1818
-			if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1819
-				$uuid = $uuid[0];
1820
-			}
1821
-		}
1822
-
1823
-		return $uuid;
1824
-	}
1825
-
1826
-	/**
1827
-	 * converts a binary ObjectGUID into a string representation
1828
-	 *
1829
-	 * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1830
-	 * @return string
1831
-	 * @link https://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1832
-	 */
1833
-	private function convertObjectGUID2Str($oguid) {
1834
-		$hex_guid = bin2hex($oguid);
1835
-		$hex_guid_to_guid_str = '';
1836
-		for ($k = 1; $k <= 4; ++$k) {
1837
-			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1838
-		}
1839
-		$hex_guid_to_guid_str .= '-';
1840
-		for ($k = 1; $k <= 2; ++$k) {
1841
-			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1842
-		}
1843
-		$hex_guid_to_guid_str .= '-';
1844
-		for ($k = 1; $k <= 2; ++$k) {
1845
-			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1846
-		}
1847
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1848
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1849
-
1850
-		return strtoupper($hex_guid_to_guid_str);
1851
-	}
1852
-
1853
-	/**
1854
-	 * the first three blocks of the string-converted GUID happen to be in
1855
-	 * reverse order. In order to use it in a filter, this needs to be
1856
-	 * corrected. Furthermore the dashes need to be replaced and \\ preprended
1857
-	 * to every two hax figures.
1858
-	 *
1859
-	 * If an invalid string is passed, it will be returned without change.
1860
-	 *
1861
-	 * @param string $guid
1862
-	 * @return string
1863
-	 */
1864
-	public function formatGuid2ForFilterUser($guid) {
1865
-		if (!is_string($guid)) {
1866
-			throw new \InvalidArgumentException('String expected');
1867
-		}
1868
-		$blocks = explode('-', $guid);
1869
-		if (count($blocks) !== 5) {
1870
-			/*
1342
+        $findings = [];
1343
+        $savedoffset = $offset;
1344
+        $iFoundItems = 0;
1345
+
1346
+        do {
1347
+            $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1348
+            if ($search === false) {
1349
+                return [];
1350
+            }
1351
+            [$sr, $pagedSearchOK] = $search;
1352
+            $cr = $this->connection->getConnectionResource();
1353
+
1354
+            if ($skipHandling) {
1355
+                //i.e. result do not need to be fetched, we just need the cookie
1356
+                //thus pass 1 or any other value as $iFoundItems because it is not
1357
+                //used
1358
+                $this->processPagedSearchStatus($sr, 1, $limitPerPage, $pagedSearchOK, $skipHandling);
1359
+                return [];
1360
+            }
1361
+
1362
+            $findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $sr));
1363
+            $iFoundItems = max($iFoundItems, $findings['count']);
1364
+            unset($findings['count']);
1365
+
1366
+            $continue = $this->processPagedSearchStatus($sr, $iFoundItems, $limitPerPage, $pagedSearchOK, $skipHandling);
1367
+            $offset += $limitPerPage;
1368
+        } while ($continue && $pagedSearchOK && ($limit === null || count($findings) < $limit));
1369
+
1370
+        // resetting offset
1371
+        $offset = $savedoffset;
1372
+
1373
+        // if we're here, probably no connection resource is returned.
1374
+        // to make Nextcloud behave nicely, we simply give back an empty array.
1375
+        if (is_null($findings)) {
1376
+            return [];
1377
+        }
1378
+
1379
+        if (!is_null($attr)) {
1380
+            $selection = [];
1381
+            $i = 0;
1382
+            foreach ($findings as $item) {
1383
+                if (!is_array($item)) {
1384
+                    continue;
1385
+                }
1386
+                $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1387
+                foreach ($attr as $key) {
1388
+                    if (isset($item[$key])) {
1389
+                        if (is_array($item[$key]) && isset($item[$key]['count'])) {
1390
+                            unset($item[$key]['count']);
1391
+                        }
1392
+                        if ($key !== 'dn') {
1393
+                            if ($this->resemblesDN($key)) {
1394
+                                $selection[$i][$key] = $this->helper->sanitizeDN($item[$key]);
1395
+                            } elseif ($key === 'objectguid' || $key === 'guid') {
1396
+                                $selection[$i][$key] = [$this->convertObjectGUID2Str($item[$key][0])];
1397
+                            } else {
1398
+                                $selection[$i][$key] = $item[$key];
1399
+                            }
1400
+                        } else {
1401
+                            $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1402
+                        }
1403
+                    }
1404
+                }
1405
+                $i++;
1406
+            }
1407
+            $findings = $selection;
1408
+        }
1409
+        //we slice the findings, when
1410
+        //a) paged search unsuccessful, though attempted
1411
+        //b) no paged search, but limit set
1412
+        if ((!$this->getPagedSearchResultState()
1413
+                && $pagedSearchOK)
1414
+            || (
1415
+                !$pagedSearchOK
1416
+                && !is_null($limit)
1417
+            )
1418
+        ) {
1419
+            $findings = array_slice($findings, (int)$offset, $limit);
1420
+        }
1421
+        return $findings;
1422
+    }
1423
+
1424
+    /**
1425
+     * @param string $name
1426
+     * @return string
1427
+     * @throws \InvalidArgumentException
1428
+     */
1429
+    public function sanitizeUsername($name) {
1430
+        $name = trim($name);
1431
+
1432
+        if ($this->connection->ldapIgnoreNamingRules) {
1433
+            return $name;
1434
+        }
1435
+
1436
+        // Use htmlentities to get rid of accents
1437
+        $name = htmlentities($name, ENT_NOQUOTES, 'UTF-8');
1438
+
1439
+        // Remove accents
1440
+        $name = preg_replace('#&([A-Za-z])(?:acute|cedil|caron|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $name);
1441
+        // Remove ligatures
1442
+        $name = preg_replace('#&([A-Za-z]{2})(?:lig);#', '\1', $name);
1443
+        // Remove unknown leftover entities
1444
+        $name = preg_replace('#&[^;]+;#', '', $name);
1445
+
1446
+        // Replacements
1447
+        $name = str_replace(' ', '_', $name);
1448
+
1449
+        // Every remaining disallowed characters will be removed
1450
+        $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1451
+
1452
+        if (strlen($name) > 64) {
1453
+            $name = (string)hash('sha256', $name, false);
1454
+        }
1455
+
1456
+        if ($name === '') {
1457
+            throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1458
+        }
1459
+
1460
+        return $name;
1461
+    }
1462
+
1463
+    public function sanitizeGroupIDCandidate(string $candidate): string {
1464
+        $candidate = trim($candidate);
1465
+        if (strlen($candidate) > 64) {
1466
+            $candidate = (string)hash('sha256', $candidate, false);
1467
+        }
1468
+        if ($candidate === '') {
1469
+            throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1470
+        }
1471
+
1472
+        return $candidate;
1473
+    }
1474
+
1475
+    /**
1476
+     * escapes (user provided) parts for LDAP filter
1477
+     *
1478
+     * @param string $input , the provided value
1479
+     * @param bool $allowAsterisk whether in * at the beginning should be preserved
1480
+     * @return string the escaped string
1481
+     */
1482
+    public function escapeFilterPart($input, $allowAsterisk = false): string {
1483
+        $asterisk = '';
1484
+        if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1485
+            $asterisk = '*';
1486
+            $input = mb_substr($input, 1, null, 'UTF-8');
1487
+        }
1488
+        $search = ['*', '\\', '(', ')'];
1489
+        $replace = ['\\*', '\\\\', '\\(', '\\)'];
1490
+        return $asterisk . str_replace($search, $replace, $input);
1491
+    }
1492
+
1493
+    /**
1494
+     * combines the input filters with AND
1495
+     *
1496
+     * @param string[] $filters the filters to connect
1497
+     * @return string the combined filter
1498
+     */
1499
+    public function combineFilterWithAnd($filters): string {
1500
+        return $this->combineFilter($filters, '&');
1501
+    }
1502
+
1503
+    /**
1504
+     * combines the input filters with OR
1505
+     *
1506
+     * @param string[] $filters the filters to connect
1507
+     * @return string the combined filter
1508
+     * Combines Filter arguments with OR
1509
+     */
1510
+    public function combineFilterWithOr($filters) {
1511
+        return $this->combineFilter($filters, '|');
1512
+    }
1513
+
1514
+    /**
1515
+     * combines the input filters with given operator
1516
+     *
1517
+     * @param string[] $filters the filters to connect
1518
+     * @param string $operator either & or |
1519
+     * @return string the combined filter
1520
+     */
1521
+    private function combineFilter($filters, $operator) {
1522
+        $combinedFilter = '(' . $operator;
1523
+        foreach ($filters as $filter) {
1524
+            if ($filter !== '' && $filter[0] !== '(') {
1525
+                $filter = '(' . $filter . ')';
1526
+            }
1527
+            $combinedFilter .= $filter;
1528
+        }
1529
+        $combinedFilter .= ')';
1530
+        return $combinedFilter;
1531
+    }
1532
+
1533
+    /**
1534
+     * creates a filter part for to perform search for users
1535
+     *
1536
+     * @param string $search the search term
1537
+     * @return string the final filter part to use in LDAP searches
1538
+     */
1539
+    public function getFilterPartForUserSearch($search) {
1540
+        return $this->getFilterPartForSearch($search,
1541
+            $this->connection->ldapAttributesForUserSearch,
1542
+            $this->connection->ldapUserDisplayName);
1543
+    }
1544
+
1545
+    /**
1546
+     * creates a filter part for to perform search for groups
1547
+     *
1548
+     * @param string $search the search term
1549
+     * @return string the final filter part to use in LDAP searches
1550
+     */
1551
+    public function getFilterPartForGroupSearch($search) {
1552
+        return $this->getFilterPartForSearch($search,
1553
+            $this->connection->ldapAttributesForGroupSearch,
1554
+            $this->connection->ldapGroupDisplayName);
1555
+    }
1556
+
1557
+    /**
1558
+     * creates a filter part for searches by splitting up the given search
1559
+     * string into single words
1560
+     *
1561
+     * @param string $search the search term
1562
+     * @param string[] $searchAttributes needs to have at least two attributes,
1563
+     * otherwise it does not make sense :)
1564
+     * @return string the final filter part to use in LDAP searches
1565
+     * @throws DomainException
1566
+     */
1567
+    private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1568
+        if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1569
+            throw new DomainException('searchAttributes must be an array with at least two string');
1570
+        }
1571
+        $searchWords = explode(' ', trim($search));
1572
+        $wordFilters = [];
1573
+        foreach ($searchWords as $word) {
1574
+            $word = $this->prepareSearchTerm($word);
1575
+            //every word needs to appear at least once
1576
+            $wordMatchOneAttrFilters = [];
1577
+            foreach ($searchAttributes as $attr) {
1578
+                $wordMatchOneAttrFilters[] = $attr . '=' . $word;
1579
+            }
1580
+            $wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1581
+        }
1582
+        return $this->combineFilterWithAnd($wordFilters);
1583
+    }
1584
+
1585
+    /**
1586
+     * creates a filter part for searches
1587
+     *
1588
+     * @param string $search the search term
1589
+     * @param string[]|null $searchAttributes
1590
+     * @param string $fallbackAttribute a fallback attribute in case the user
1591
+     * did not define search attributes. Typically the display name attribute.
1592
+     * @return string the final filter part to use in LDAP searches
1593
+     */
1594
+    private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1595
+        $filter = [];
1596
+        $haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1597
+        if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1598
+            try {
1599
+                return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1600
+            } catch (DomainException $e) {
1601
+                // Creating advanced filter for search failed, falling back to simple method. Edge case, but valid.
1602
+            }
1603
+        }
1604
+
1605
+        $search = $this->prepareSearchTerm($search);
1606
+        if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1607
+            if ($fallbackAttribute === '') {
1608
+                return '';
1609
+            }
1610
+            $filter[] = $fallbackAttribute . '=' . $search;
1611
+        } else {
1612
+            foreach ($searchAttributes as $attribute) {
1613
+                $filter[] = $attribute . '=' . $search;
1614
+            }
1615
+        }
1616
+        if (count($filter) === 1) {
1617
+            return '(' . $filter[0] . ')';
1618
+        }
1619
+        return $this->combineFilterWithOr($filter);
1620
+    }
1621
+
1622
+    /**
1623
+     * returns the search term depending on whether we are allowed
1624
+     * list users found by ldap with the current input appended by
1625
+     * a *
1626
+     *
1627
+     * @return string
1628
+     */
1629
+    private function prepareSearchTerm($term) {
1630
+        $config = \OC::$server->getConfig();
1631
+
1632
+        $allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1633
+
1634
+        $result = $term;
1635
+        if ($term === '') {
1636
+            $result = '*';
1637
+        } elseif ($allowEnum !== 'no') {
1638
+            $result = $term . '*';
1639
+        }
1640
+        return $result;
1641
+    }
1642
+
1643
+    /**
1644
+     * returns the filter used for counting users
1645
+     *
1646
+     * @return string
1647
+     */
1648
+    public function getFilterForUserCount() {
1649
+        $filter = $this->combineFilterWithAnd([
1650
+            $this->connection->ldapUserFilter,
1651
+            $this->connection->ldapUserDisplayName . '=*'
1652
+        ]);
1653
+
1654
+        return $filter;
1655
+    }
1656
+
1657
+    /**
1658
+     * @param string $name
1659
+     * @param string $password
1660
+     * @return bool
1661
+     */
1662
+    public function areCredentialsValid($name, $password) {
1663
+        $name = $this->helper->DNasBaseParameter($name);
1664
+        $testConnection = clone $this->connection;
1665
+        $credentials = [
1666
+            'ldapAgentName' => $name,
1667
+            'ldapAgentPassword' => $password
1668
+        ];
1669
+        if (!$testConnection->setConfiguration($credentials)) {
1670
+            return false;
1671
+        }
1672
+        return $testConnection->bind();
1673
+    }
1674
+
1675
+    /**
1676
+     * reverse lookup of a DN given a known UUID
1677
+     *
1678
+     * @param string $uuid
1679
+     * @return string
1680
+     * @throws \Exception
1681
+     */
1682
+    public function getUserDnByUuid($uuid) {
1683
+        $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1684
+        $filter = $this->connection->ldapUserFilter;
1685
+        $bases = $this->connection->ldapBaseUsers;
1686
+
1687
+        if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1688
+            // Sacrebleu! The UUID attribute is unknown :( We need first an
1689
+            // existing DN to be able to reliably detect it.
1690
+            foreach ($bases as $base) {
1691
+                $result = $this->search($filter, $base, ['dn'], 1);
1692
+                if (!isset($result[0]) || !isset($result[0]['dn'])) {
1693
+                    continue;
1694
+                }
1695
+                $dn = $result[0]['dn'][0];
1696
+                if ($hasFound = $this->detectUuidAttribute($dn, true)) {
1697
+                    break;
1698
+                }
1699
+            }
1700
+            if (!isset($hasFound) || !$hasFound) {
1701
+                throw new \Exception('Cannot determine UUID attribute');
1702
+            }
1703
+        } else {
1704
+            // The UUID attribute is either known or an override is given.
1705
+            // By calling this method we ensure that $this->connection->$uuidAttr
1706
+            // is definitely set
1707
+            if (!$this->detectUuidAttribute('', true)) {
1708
+                throw new \Exception('Cannot determine UUID attribute');
1709
+            }
1710
+        }
1711
+
1712
+        $uuidAttr = $this->connection->ldapUuidUserAttribute;
1713
+        if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1714
+            $uuid = $this->formatGuid2ForFilterUser($uuid);
1715
+        }
1716
+
1717
+        $filter = $uuidAttr . '=' . $uuid;
1718
+        $result = $this->searchUsers($filter, ['dn'], 2);
1719
+        if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1720
+            // we put the count into account to make sure that this is
1721
+            // really unique
1722
+            return $result[0]['dn'][0];
1723
+        }
1724
+
1725
+        throw new \Exception('Cannot determine UUID attribute');
1726
+    }
1727
+
1728
+    /**
1729
+     * auto-detects the directory's UUID attribute
1730
+     *
1731
+     * @param string $dn a known DN used to check against
1732
+     * @param bool $isUser
1733
+     * @param bool $force the detection should be run, even if it is not set to auto
1734
+     * @param array|null $ldapRecord
1735
+     * @return bool true on success, false otherwise
1736
+     * @throws ServerNotAvailableException
1737
+     */
1738
+    private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1739
+        if ($isUser) {
1740
+            $uuidAttr = 'ldapUuidUserAttribute';
1741
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1742
+        } else {
1743
+            $uuidAttr = 'ldapUuidGroupAttribute';
1744
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1745
+        }
1746
+
1747
+        if (!$force) {
1748
+            if ($this->connection->$uuidAttr !== 'auto') {
1749
+                return true;
1750
+            } elseif (is_string($uuidOverride) && trim($uuidOverride) !== '') {
1751
+                $this->connection->$uuidAttr = $uuidOverride;
1752
+                return true;
1753
+            }
1754
+
1755
+            $attribute = $this->connection->getFromCache($uuidAttr);
1756
+            if ($attribute !== null) {
1757
+                $this->connection->$uuidAttr = $attribute;
1758
+                return true;
1759
+            }
1760
+        }
1761
+
1762
+        foreach (self::UUID_ATTRIBUTES as $attribute) {
1763
+            if ($ldapRecord !== null) {
1764
+                // we have the info from LDAP already, we don't need to talk to the server again
1765
+                if (isset($ldapRecord[$attribute])) {
1766
+                    $this->connection->$uuidAttr = $attribute;
1767
+                    return true;
1768
+                }
1769
+            }
1770
+
1771
+            $value = $this->readAttribute($dn, $attribute);
1772
+            if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1773
+                $this->logger->debug(
1774
+                    'Setting {attribute} as {subject}',
1775
+                    [
1776
+                        'app' => 'user_ldap',
1777
+                        'attribute' => $attribute,
1778
+                        'subject' => $uuidAttr
1779
+                    ]
1780
+                );
1781
+                $this->connection->$uuidAttr = $attribute;
1782
+                $this->connection->writeToCache($uuidAttr, $attribute);
1783
+                return true;
1784
+            }
1785
+        }
1786
+        $this->logger->debug('Could not autodetect the UUID attribute', ['app' => 'user_ldap']);
1787
+
1788
+        return false;
1789
+    }
1790
+
1791
+    /**
1792
+     * @param string $dn
1793
+     * @param bool $isUser
1794
+     * @param null $ldapRecord
1795
+     * @return false|string
1796
+     * @throws ServerNotAvailableException
1797
+     */
1798
+    public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1799
+        if ($isUser) {
1800
+            $uuidAttr = 'ldapUuidUserAttribute';
1801
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1802
+        } else {
1803
+            $uuidAttr = 'ldapUuidGroupAttribute';
1804
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1805
+        }
1806
+
1807
+        $uuid = false;
1808
+        if ($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1809
+            $attr = $this->connection->$uuidAttr;
1810
+            $uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1811
+            if (!is_array($uuid)
1812
+                && $uuidOverride !== ''
1813
+                && $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord)) {
1814
+                $uuid = isset($ldapRecord[$this->connection->$uuidAttr])
1815
+                    ? $ldapRecord[$this->connection->$uuidAttr]
1816
+                    : $this->readAttribute($dn, $this->connection->$uuidAttr);
1817
+            }
1818
+            if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1819
+                $uuid = $uuid[0];
1820
+            }
1821
+        }
1822
+
1823
+        return $uuid;
1824
+    }
1825
+
1826
+    /**
1827
+     * converts a binary ObjectGUID into a string representation
1828
+     *
1829
+     * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1830
+     * @return string
1831
+     * @link https://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1832
+     */
1833
+    private function convertObjectGUID2Str($oguid) {
1834
+        $hex_guid = bin2hex($oguid);
1835
+        $hex_guid_to_guid_str = '';
1836
+        for ($k = 1; $k <= 4; ++$k) {
1837
+            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1838
+        }
1839
+        $hex_guid_to_guid_str .= '-';
1840
+        for ($k = 1; $k <= 2; ++$k) {
1841
+            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1842
+        }
1843
+        $hex_guid_to_guid_str .= '-';
1844
+        for ($k = 1; $k <= 2; ++$k) {
1845
+            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1846
+        }
1847
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1848
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1849
+
1850
+        return strtoupper($hex_guid_to_guid_str);
1851
+    }
1852
+
1853
+    /**
1854
+     * the first three blocks of the string-converted GUID happen to be in
1855
+     * reverse order. In order to use it in a filter, this needs to be
1856
+     * corrected. Furthermore the dashes need to be replaced and \\ preprended
1857
+     * to every two hax figures.
1858
+     *
1859
+     * If an invalid string is passed, it will be returned without change.
1860
+     *
1861
+     * @param string $guid
1862
+     * @return string
1863
+     */
1864
+    public function formatGuid2ForFilterUser($guid) {
1865
+        if (!is_string($guid)) {
1866
+            throw new \InvalidArgumentException('String expected');
1867
+        }
1868
+        $blocks = explode('-', $guid);
1869
+        if (count($blocks) !== 5) {
1870
+            /*
1871 1871
 			 * Why not throw an Exception instead? This method is a utility
1872 1872
 			 * called only when trying to figure out whether a "missing" known
1873 1873
 			 * LDAP user was or was not renamed on the LDAP server. And this
@@ -1878,247 +1878,247 @@  discard block
 block discarded – undo
1878 1878
 			 * an exception here would kill the experience for a valid, acting
1879 1879
 			 * user. Instead we write a log message.
1880 1880
 			 */
1881
-			$this->logger->info(
1882
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1883
-				'({uuid}) probably does not match UUID configuration.',
1884
-				['app' => 'user_ldap', 'uuid' => $guid]
1885
-			);
1886
-			return $guid;
1887
-		}
1888
-		for ($i = 0; $i < 3; $i++) {
1889
-			$pairs = str_split($blocks[$i], 2);
1890
-			$pairs = array_reverse($pairs);
1891
-			$blocks[$i] = implode('', $pairs);
1892
-		}
1893
-		for ($i = 0; $i < 5; $i++) {
1894
-			$pairs = str_split($blocks[$i], 2);
1895
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1896
-		}
1897
-		return implode('', $blocks);
1898
-	}
1899
-
1900
-	/**
1901
-	 * gets a SID of the domain of the given dn
1902
-	 *
1903
-	 * @param string $dn
1904
-	 * @return string|bool
1905
-	 * @throws ServerNotAvailableException
1906
-	 */
1907
-	public function getSID($dn) {
1908
-		$domainDN = $this->getDomainDNFromDN($dn);
1909
-		$cacheKey = 'getSID-' . $domainDN;
1910
-		$sid = $this->connection->getFromCache($cacheKey);
1911
-		if (!is_null($sid)) {
1912
-			return $sid;
1913
-		}
1914
-
1915
-		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1916
-		if (!is_array($objectSid) || empty($objectSid)) {
1917
-			$this->connection->writeToCache($cacheKey, false);
1918
-			return false;
1919
-		}
1920
-		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
1921
-		$this->connection->writeToCache($cacheKey, $domainObjectSid);
1922
-
1923
-		return $domainObjectSid;
1924
-	}
1925
-
1926
-	/**
1927
-	 * converts a binary SID into a string representation
1928
-	 *
1929
-	 * @param string $sid
1930
-	 * @return string
1931
-	 */
1932
-	public function convertSID2Str($sid) {
1933
-		// The format of a SID binary string is as follows:
1934
-		// 1 byte for the revision level
1935
-		// 1 byte for the number n of variable sub-ids
1936
-		// 6 bytes for identifier authority value
1937
-		// n*4 bytes for n sub-ids
1938
-		//
1939
-		// Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1940
-		//  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1941
-		$revision = ord($sid[0]);
1942
-		$numberSubID = ord($sid[1]);
1943
-
1944
-		$subIdStart = 8; // 1 + 1 + 6
1945
-		$subIdLength = 4;
1946
-		if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1947
-			// Incorrect number of bytes present.
1948
-			return '';
1949
-		}
1950
-
1951
-		// 6 bytes = 48 bits can be represented using floats without loss of
1952
-		// precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1953
-		$iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1954
-
1955
-		$subIDs = [];
1956
-		for ($i = 0; $i < $numberSubID; $i++) {
1957
-			$subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1958
-			$subIDs[] = sprintf('%u', $subID[1]);
1959
-		}
1960
-
1961
-		// Result for example above: S-1-5-21-249921958-728525901-1594176202
1962
-		return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1963
-	}
1964
-
1965
-	/**
1966
-	 * checks if the given DN is part of the given base DN(s)
1967
-	 *
1968
-	 * @param string $dn the DN
1969
-	 * @param string[] $bases array containing the allowed base DN or DNs
1970
-	 * @return bool
1971
-	 */
1972
-	public function isDNPartOfBase($dn, $bases) {
1973
-		$belongsToBase = false;
1974
-		$bases = $this->helper->sanitizeDN($bases);
1975
-
1976
-		foreach ($bases as $base) {
1977
-			$belongsToBase = true;
1978
-			if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1979
-				$belongsToBase = false;
1980
-			}
1981
-			if ($belongsToBase) {
1982
-				break;
1983
-			}
1984
-		}
1985
-		return $belongsToBase;
1986
-	}
1987
-
1988
-	/**
1989
-	 * resets a running Paged Search operation
1990
-	 *
1991
-	 * @throws ServerNotAvailableException
1992
-	 */
1993
-	private function abandonPagedSearch() {
1994
-		if ($this->lastCookie === '') {
1995
-			return;
1996
-		}
1997
-		$cr = $this->connection->getConnectionResource();
1998
-		$this->invokeLDAPMethod('controlPagedResult', $cr, 0, false);
1999
-		$this->getPagedSearchResultState();
2000
-		$this->lastCookie = '';
2001
-	}
2002
-
2003
-	/**
2004
-	 * checks whether an LDAP paged search operation has more pages that can be
2005
-	 * retrieved, typically when offset and limit are provided.
2006
-	 *
2007
-	 * Be very careful to use it: the last cookie value, which is inspected, can
2008
-	 * be reset by other operations. Best, call it immediately after a search(),
2009
-	 * searchUsers() or searchGroups() call. count-methods are probably safe as
2010
-	 * well. Don't rely on it with any fetchList-method.
2011
-	 *
2012
-	 * @return bool
2013
-	 */
2014
-	public function hasMoreResults() {
2015
-		if (empty($this->lastCookie) && $this->lastCookie !== '0') {
2016
-			// as in RFC 2696, when all results are returned, the cookie will
2017
-			// be empty.
2018
-			return false;
2019
-		}
2020
-
2021
-		return true;
2022
-	}
2023
-
2024
-	/**
2025
-	 * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
2026
-	 *
2027
-	 * @return boolean|null true on success, null or false otherwise
2028
-	 */
2029
-	public function getPagedSearchResultState() {
2030
-		$result = $this->pagedSearchedSuccessful;
2031
-		$this->pagedSearchedSuccessful = null;
2032
-		return $result;
2033
-	}
2034
-
2035
-	/**
2036
-	 * Prepares a paged search, if possible
2037
-	 *
2038
-	 * @param string $filter the LDAP filter for the search
2039
-	 * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
2040
-	 * @param string[] $attr optional, when a certain attribute shall be filtered outside
2041
-	 * @param int $limit
2042
-	 * @param int $offset
2043
-	 * @return bool|true
2044
-	 * @throws ServerNotAvailableException
2045
-	 * @throws NoMoreResults
2046
-	 */
2047
-	private function initPagedSearch(
2048
-		string $filter,
2049
-		string $base,
2050
-		?array $attr,
2051
-		int $limit,
2052
-		int $offset
2053
-	): bool {
2054
-		$pagedSearchOK = false;
2055
-		if ($limit !== 0) {
2056
-			$this->logger->debug(
2057
-				'initializing paged search for filter {filter}, base {base}, attr {attr}, limit {limit}, offset {offset}',
2058
-				[
2059
-					'app' => 'user_ldap',
2060
-					'filter' => $filter,
2061
-					'base' => $base,
2062
-					'attr' => $attr,
2063
-					'limit' => $limit,
2064
-					'offset' => $offset
2065
-				]
2066
-			);
2067
-			//get the cookie from the search for the previous search, required by LDAP
2068
-			if (empty($this->lastCookie) && $this->lastCookie !== "0" && ($offset > 0)) {
2069
-				// no cookie known from a potential previous search. We need
2070
-				// to start from 0 to come to the desired page. cookie value
2071
-				// of '0' is valid, because 389ds
2072
-				$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
2073
-				$this->search($filter, $base, $attr, $limit, $reOffset, true);
2074
-				if (!$this->hasMoreResults()) {
2075
-					// when the cookie is reset with != 0 offset, there are no further
2076
-					// results, so stop.
2077
-					throw new NoMoreResults();
2078
-				}
2079
-			}
2080
-			if ($this->lastCookie !== '' && $offset === 0) {
2081
-				//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
2082
-				$this->abandonPagedSearch();
2083
-			}
2084
-			$pagedSearchOK = true === $this->invokeLDAPMethod(
2085
-					'controlPagedResult', $this->connection->getConnectionResource(), $limit, false
2086
-				);
2087
-			if ($pagedSearchOK) {
2088
-				$this->logger->debug('Ready for a paged search', ['app' => 'user_ldap']);
2089
-			}
2090
-			/* ++ Fixing RHDS searches with pages with zero results ++
1881
+            $this->logger->info(
1882
+                'Passed string does not resemble a valid GUID. Known UUID ' .
1883
+                '({uuid}) probably does not match UUID configuration.',
1884
+                ['app' => 'user_ldap', 'uuid' => $guid]
1885
+            );
1886
+            return $guid;
1887
+        }
1888
+        for ($i = 0; $i < 3; $i++) {
1889
+            $pairs = str_split($blocks[$i], 2);
1890
+            $pairs = array_reverse($pairs);
1891
+            $blocks[$i] = implode('', $pairs);
1892
+        }
1893
+        for ($i = 0; $i < 5; $i++) {
1894
+            $pairs = str_split($blocks[$i], 2);
1895
+            $blocks[$i] = '\\' . implode('\\', $pairs);
1896
+        }
1897
+        return implode('', $blocks);
1898
+    }
1899
+
1900
+    /**
1901
+     * gets a SID of the domain of the given dn
1902
+     *
1903
+     * @param string $dn
1904
+     * @return string|bool
1905
+     * @throws ServerNotAvailableException
1906
+     */
1907
+    public function getSID($dn) {
1908
+        $domainDN = $this->getDomainDNFromDN($dn);
1909
+        $cacheKey = 'getSID-' . $domainDN;
1910
+        $sid = $this->connection->getFromCache($cacheKey);
1911
+        if (!is_null($sid)) {
1912
+            return $sid;
1913
+        }
1914
+
1915
+        $objectSid = $this->readAttribute($domainDN, 'objectsid');
1916
+        if (!is_array($objectSid) || empty($objectSid)) {
1917
+            $this->connection->writeToCache($cacheKey, false);
1918
+            return false;
1919
+        }
1920
+        $domainObjectSid = $this->convertSID2Str($objectSid[0]);
1921
+        $this->connection->writeToCache($cacheKey, $domainObjectSid);
1922
+
1923
+        return $domainObjectSid;
1924
+    }
1925
+
1926
+    /**
1927
+     * converts a binary SID into a string representation
1928
+     *
1929
+     * @param string $sid
1930
+     * @return string
1931
+     */
1932
+    public function convertSID2Str($sid) {
1933
+        // The format of a SID binary string is as follows:
1934
+        // 1 byte for the revision level
1935
+        // 1 byte for the number n of variable sub-ids
1936
+        // 6 bytes for identifier authority value
1937
+        // n*4 bytes for n sub-ids
1938
+        //
1939
+        // Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1940
+        //  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1941
+        $revision = ord($sid[0]);
1942
+        $numberSubID = ord($sid[1]);
1943
+
1944
+        $subIdStart = 8; // 1 + 1 + 6
1945
+        $subIdLength = 4;
1946
+        if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1947
+            // Incorrect number of bytes present.
1948
+            return '';
1949
+        }
1950
+
1951
+        // 6 bytes = 48 bits can be represented using floats without loss of
1952
+        // precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1953
+        $iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1954
+
1955
+        $subIDs = [];
1956
+        for ($i = 0; $i < $numberSubID; $i++) {
1957
+            $subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1958
+            $subIDs[] = sprintf('%u', $subID[1]);
1959
+        }
1960
+
1961
+        // Result for example above: S-1-5-21-249921958-728525901-1594176202
1962
+        return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1963
+    }
1964
+
1965
+    /**
1966
+     * checks if the given DN is part of the given base DN(s)
1967
+     *
1968
+     * @param string $dn the DN
1969
+     * @param string[] $bases array containing the allowed base DN or DNs
1970
+     * @return bool
1971
+     */
1972
+    public function isDNPartOfBase($dn, $bases) {
1973
+        $belongsToBase = false;
1974
+        $bases = $this->helper->sanitizeDN($bases);
1975
+
1976
+        foreach ($bases as $base) {
1977
+            $belongsToBase = true;
1978
+            if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1979
+                $belongsToBase = false;
1980
+            }
1981
+            if ($belongsToBase) {
1982
+                break;
1983
+            }
1984
+        }
1985
+        return $belongsToBase;
1986
+    }
1987
+
1988
+    /**
1989
+     * resets a running Paged Search operation
1990
+     *
1991
+     * @throws ServerNotAvailableException
1992
+     */
1993
+    private function abandonPagedSearch() {
1994
+        if ($this->lastCookie === '') {
1995
+            return;
1996
+        }
1997
+        $cr = $this->connection->getConnectionResource();
1998
+        $this->invokeLDAPMethod('controlPagedResult', $cr, 0, false);
1999
+        $this->getPagedSearchResultState();
2000
+        $this->lastCookie = '';
2001
+    }
2002
+
2003
+    /**
2004
+     * checks whether an LDAP paged search operation has more pages that can be
2005
+     * retrieved, typically when offset and limit are provided.
2006
+     *
2007
+     * Be very careful to use it: the last cookie value, which is inspected, can
2008
+     * be reset by other operations. Best, call it immediately after a search(),
2009
+     * searchUsers() or searchGroups() call. count-methods are probably safe as
2010
+     * well. Don't rely on it with any fetchList-method.
2011
+     *
2012
+     * @return bool
2013
+     */
2014
+    public function hasMoreResults() {
2015
+        if (empty($this->lastCookie) && $this->lastCookie !== '0') {
2016
+            // as in RFC 2696, when all results are returned, the cookie will
2017
+            // be empty.
2018
+            return false;
2019
+        }
2020
+
2021
+        return true;
2022
+    }
2023
+
2024
+    /**
2025
+     * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
2026
+     *
2027
+     * @return boolean|null true on success, null or false otherwise
2028
+     */
2029
+    public function getPagedSearchResultState() {
2030
+        $result = $this->pagedSearchedSuccessful;
2031
+        $this->pagedSearchedSuccessful = null;
2032
+        return $result;
2033
+    }
2034
+
2035
+    /**
2036
+     * Prepares a paged search, if possible
2037
+     *
2038
+     * @param string $filter the LDAP filter for the search
2039
+     * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
2040
+     * @param string[] $attr optional, when a certain attribute shall be filtered outside
2041
+     * @param int $limit
2042
+     * @param int $offset
2043
+     * @return bool|true
2044
+     * @throws ServerNotAvailableException
2045
+     * @throws NoMoreResults
2046
+     */
2047
+    private function initPagedSearch(
2048
+        string $filter,
2049
+        string $base,
2050
+        ?array $attr,
2051
+        int $limit,
2052
+        int $offset
2053
+    ): bool {
2054
+        $pagedSearchOK = false;
2055
+        if ($limit !== 0) {
2056
+            $this->logger->debug(
2057
+                'initializing paged search for filter {filter}, base {base}, attr {attr}, limit {limit}, offset {offset}',
2058
+                [
2059
+                    'app' => 'user_ldap',
2060
+                    'filter' => $filter,
2061
+                    'base' => $base,
2062
+                    'attr' => $attr,
2063
+                    'limit' => $limit,
2064
+                    'offset' => $offset
2065
+                ]
2066
+            );
2067
+            //get the cookie from the search for the previous search, required by LDAP
2068
+            if (empty($this->lastCookie) && $this->lastCookie !== "0" && ($offset > 0)) {
2069
+                // no cookie known from a potential previous search. We need
2070
+                // to start from 0 to come to the desired page. cookie value
2071
+                // of '0' is valid, because 389ds
2072
+                $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
2073
+                $this->search($filter, $base, $attr, $limit, $reOffset, true);
2074
+                if (!$this->hasMoreResults()) {
2075
+                    // when the cookie is reset with != 0 offset, there are no further
2076
+                    // results, so stop.
2077
+                    throw new NoMoreResults();
2078
+                }
2079
+            }
2080
+            if ($this->lastCookie !== '' && $offset === 0) {
2081
+                //since offset = 0, this is a new search. We abandon other searches that might be ongoing.
2082
+                $this->abandonPagedSearch();
2083
+            }
2084
+            $pagedSearchOK = true === $this->invokeLDAPMethod(
2085
+                    'controlPagedResult', $this->connection->getConnectionResource(), $limit, false
2086
+                );
2087
+            if ($pagedSearchOK) {
2088
+                $this->logger->debug('Ready for a paged search', ['app' => 'user_ldap']);
2089
+            }
2090
+            /* ++ Fixing RHDS searches with pages with zero results ++
2091 2091
 			 * We coudn't get paged searches working with our RHDS for login ($limit = 0),
2092 2092
 			 * due to pages with zero results.
2093 2093
 			 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
2094 2094
 			 * if we don't have a previous paged search.
2095 2095
 			 */
2096
-		} elseif ($limit === 0 && !empty($this->lastCookie)) {
2097
-			// a search without limit was requested. However, if we do use
2098
-			// Paged Search once, we always must do it. This requires us to
2099
-			// initialize it with the configured page size.
2100
-			$this->abandonPagedSearch();
2101
-			// in case someone set it to 0 … use 500, otherwise no results will
2102
-			// be returned.
2103
-			$pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
2104
-			$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2105
-				$this->connection->getConnectionResource(),
2106
-				$pageSize, false);
2107
-		}
2108
-
2109
-		return $pagedSearchOK;
2110
-	}
2111
-
2112
-	/**
2113
-	 * Is more than one $attr used for search?
2114
-	 *
2115
-	 * @param string|string[]|null $attr
2116
-	 * @return bool
2117
-	 */
2118
-	private function manyAttributes($attr): bool {
2119
-		if (\is_array($attr)) {
2120
-			return \count($attr) > 1;
2121
-		}
2122
-		return false;
2123
-	}
2096
+        } elseif ($limit === 0 && !empty($this->lastCookie)) {
2097
+            // a search without limit was requested. However, if we do use
2098
+            // Paged Search once, we always must do it. This requires us to
2099
+            // initialize it with the configured page size.
2100
+            $this->abandonPagedSearch();
2101
+            // in case someone set it to 0 … use 500, otherwise no results will
2102
+            // be returned.
2103
+            $pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
2104
+            $pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2105
+                $this->connection->getConnectionResource(),
2106
+                $pageSize, false);
2107
+        }
2108
+
2109
+        return $pagedSearchOK;
2110
+    }
2111
+
2112
+    /**
2113
+     * Is more than one $attr used for search?
2114
+     *
2115
+     * @param string|string[]|null $attr
2116
+     * @return bool
2117
+     */
2118
+    private function manyAttributes($attr): bool {
2119
+        if (\is_array($attr)) {
2120
+            return \count($attr) > 1;
2121
+        }
2122
+        return false;
2123
+    }
2124 2124
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Mapping/AbstractMapping.php 2 patches
Indentation   +416 added lines, -416 removed lines patch added patch discarded remove patch
@@ -37,443 +37,443 @@
 block discarded – undo
37 37
  * @package OCA\User_LDAP\Mapping
38 38
  */
39 39
 abstract class AbstractMapping {
40
-	/**
41
-	 * @var \OCP\IDBConnection $dbc
42
-	 */
43
-	protected $dbc;
44
-
45
-	/**
46
-	 * returns the DB table name which holds the mappings
47
-	 *
48
-	 * @return string
49
-	 */
50
-	abstract protected function getTableName(bool $includePrefix = true);
51
-
52
-	/**
53
-	 * @param \OCP\IDBConnection $dbc
54
-	 */
55
-	public function __construct(\OCP\IDBConnection $dbc) {
56
-		$this->dbc = $dbc;
57
-	}
58
-
59
-	/** @var array caches Names (value) by DN (key) */
60
-	protected $cache = [];
61
-
62
-	/**
63
-	 * checks whether a provided string represents an existing table col
64
-	 *
65
-	 * @param string $col
66
-	 * @return bool
67
-	 */
68
-	public function isColNameValid($col) {
69
-		switch ($col) {
70
-			case 'ldap_dn':
71
-			case 'ldap_dn_hash':
72
-			case 'owncloud_name':
73
-			case 'directory_uuid':
74
-				return true;
75
-			default:
76
-				return false;
77
-		}
78
-	}
79
-
80
-	/**
81
-	 * Gets the value of one column based on a provided value of another column
82
-	 *
83
-	 * @param string $fetchCol
84
-	 * @param string $compareCol
85
-	 * @param string $search
86
-	 * @return string|false
87
-	 * @throws \Exception
88
-	 */
89
-	protected function getXbyY($fetchCol, $compareCol, $search) {
90
-		if (!$this->isColNameValid($fetchCol)) {
91
-			//this is used internally only, but we don't want to risk
92
-			//having SQL injection at all.
93
-			throw new \Exception('Invalid Column Name');
94
-		}
95
-		$query = $this->dbc->prepare('
40
+    /**
41
+     * @var \OCP\IDBConnection $dbc
42
+     */
43
+    protected $dbc;
44
+
45
+    /**
46
+     * returns the DB table name which holds the mappings
47
+     *
48
+     * @return string
49
+     */
50
+    abstract protected function getTableName(bool $includePrefix = true);
51
+
52
+    /**
53
+     * @param \OCP\IDBConnection $dbc
54
+     */
55
+    public function __construct(\OCP\IDBConnection $dbc) {
56
+        $this->dbc = $dbc;
57
+    }
58
+
59
+    /** @var array caches Names (value) by DN (key) */
60
+    protected $cache = [];
61
+
62
+    /**
63
+     * checks whether a provided string represents an existing table col
64
+     *
65
+     * @param string $col
66
+     * @return bool
67
+     */
68
+    public function isColNameValid($col) {
69
+        switch ($col) {
70
+            case 'ldap_dn':
71
+            case 'ldap_dn_hash':
72
+            case 'owncloud_name':
73
+            case 'directory_uuid':
74
+                return true;
75
+            default:
76
+                return false;
77
+        }
78
+    }
79
+
80
+    /**
81
+     * Gets the value of one column based on a provided value of another column
82
+     *
83
+     * @param string $fetchCol
84
+     * @param string $compareCol
85
+     * @param string $search
86
+     * @return string|false
87
+     * @throws \Exception
88
+     */
89
+    protected function getXbyY($fetchCol, $compareCol, $search) {
90
+        if (!$this->isColNameValid($fetchCol)) {
91
+            //this is used internally only, but we don't want to risk
92
+            //having SQL injection at all.
93
+            throw new \Exception('Invalid Column Name');
94
+        }
95
+        $query = $this->dbc->prepare('
96 96
 			SELECT `' . $fetchCol . '`
97 97
 			FROM `' . $this->getTableName() . '`
98 98
 			WHERE `' . $compareCol . '` = ?
99 99
 		');
100 100
 
101
-		try {
102
-			$res = $query->execute([$search]);
103
-			$data = $res->fetchOne();
104
-			$res->closeCursor();
105
-			return $data;
106
-		} catch (Exception $e) {
107
-			return false;
108
-		}
109
-	}
110
-
111
-	/**
112
-	 * Performs a DELETE or UPDATE query to the database.
113
-	 *
114
-	 * @param IPreparedStatement $statement
115
-	 * @param array $parameters
116
-	 * @return bool true if at least one row was modified, false otherwise
117
-	 */
118
-	protected function modify(IPreparedStatement $statement, $parameters) {
119
-		try {
120
-			$result = $statement->execute($parameters);
121
-			$updated = $result->rowCount() > 0;
122
-			$result->closeCursor();
123
-			return $updated;
124
-		} catch (Exception $e) {
125
-			return false;
126
-		}
127
-	}
128
-
129
-	/**
130
-	 * Gets the LDAP DN based on the provided name.
131
-	 * Replaces Access::ocname2dn
132
-	 *
133
-	 * @param string $name
134
-	 * @return string|false
135
-	 */
136
-	public function getDNByName($name) {
137
-		$dn = array_search($name, $this->cache);
138
-		if ($dn === false && ($dn = $this->getXbyY('ldap_dn', 'owncloud_name', $name)) !== false) {
139
-			$this->cache[$dn] = $name;
140
-		}
141
-		return $dn;
142
-	}
143
-
144
-	/**
145
-	 * Updates the DN based on the given UUID
146
-	 *
147
-	 * @param string $fdn
148
-	 * @param string $uuid
149
-	 * @return bool
150
-	 */
151
-	public function setDNbyUUID($fdn, $uuid) {
152
-		$oldDn = $this->getDnByUUID($uuid);
153
-		$statement = $this->dbc->prepare('
101
+        try {
102
+            $res = $query->execute([$search]);
103
+            $data = $res->fetchOne();
104
+            $res->closeCursor();
105
+            return $data;
106
+        } catch (Exception $e) {
107
+            return false;
108
+        }
109
+    }
110
+
111
+    /**
112
+     * Performs a DELETE or UPDATE query to the database.
113
+     *
114
+     * @param IPreparedStatement $statement
115
+     * @param array $parameters
116
+     * @return bool true if at least one row was modified, false otherwise
117
+     */
118
+    protected function modify(IPreparedStatement $statement, $parameters) {
119
+        try {
120
+            $result = $statement->execute($parameters);
121
+            $updated = $result->rowCount() > 0;
122
+            $result->closeCursor();
123
+            return $updated;
124
+        } catch (Exception $e) {
125
+            return false;
126
+        }
127
+    }
128
+
129
+    /**
130
+     * Gets the LDAP DN based on the provided name.
131
+     * Replaces Access::ocname2dn
132
+     *
133
+     * @param string $name
134
+     * @return string|false
135
+     */
136
+    public function getDNByName($name) {
137
+        $dn = array_search($name, $this->cache);
138
+        if ($dn === false && ($dn = $this->getXbyY('ldap_dn', 'owncloud_name', $name)) !== false) {
139
+            $this->cache[$dn] = $name;
140
+        }
141
+        return $dn;
142
+    }
143
+
144
+    /**
145
+     * Updates the DN based on the given UUID
146
+     *
147
+     * @param string $fdn
148
+     * @param string $uuid
149
+     * @return bool
150
+     */
151
+    public function setDNbyUUID($fdn, $uuid) {
152
+        $oldDn = $this->getDnByUUID($uuid);
153
+        $statement = $this->dbc->prepare('
154 154
 			UPDATE `' . $this->getTableName() . '`
155 155
 			SET `ldap_dn_hash` = ?, `ldap_dn` = ?
156 156
 			WHERE `directory_uuid` = ?
157 157
 		');
158 158
 
159
-		$r = $this->modify($statement, [$this->getDNHash($fdn), $fdn, $uuid]);
160
-
161
-		if ($r && is_string($oldDn) && isset($this->cache[$oldDn])) {
162
-			$this->cache[$fdn] = $this->cache[$oldDn];
163
-			unset($this->cache[$oldDn]);
164
-		}
165
-
166
-		return $r;
167
-	}
168
-
169
-	/**
170
-	 * Updates the UUID based on the given DN
171
-	 *
172
-	 * required by Migration/UUIDFix
173
-	 *
174
-	 * @param $uuid
175
-	 * @param $fdn
176
-	 * @return bool
177
-	 */
178
-	public function setUUIDbyDN($uuid, $fdn): bool {
179
-		$statement = $this->dbc->prepare('
159
+        $r = $this->modify($statement, [$this->getDNHash($fdn), $fdn, $uuid]);
160
+
161
+        if ($r && is_string($oldDn) && isset($this->cache[$oldDn])) {
162
+            $this->cache[$fdn] = $this->cache[$oldDn];
163
+            unset($this->cache[$oldDn]);
164
+        }
165
+
166
+        return $r;
167
+    }
168
+
169
+    /**
170
+     * Updates the UUID based on the given DN
171
+     *
172
+     * required by Migration/UUIDFix
173
+     *
174
+     * @param $uuid
175
+     * @param $fdn
176
+     * @return bool
177
+     */
178
+    public function setUUIDbyDN($uuid, $fdn): bool {
179
+        $statement = $this->dbc->prepare('
180 180
 			UPDATE `' . $this->getTableName() . '`
181 181
 			SET `directory_uuid` = ?
182 182
 			WHERE `ldap_dn_hash` = ?
183 183
 		');
184 184
 
185
-		unset($this->cache[$fdn]);
186
-
187
-		return $this->modify($statement, [$uuid, $this->getDNHash($fdn)]);
188
-	}
189
-
190
-	/**
191
-	 * Get the hash to store in database column ldap_dn_hash for a given dn
192
-	 */
193
-	protected function getDNHash(string $fdn): string {
194
-		$hash = hash('sha256', $fdn, false);
195
-		if (is_string($hash)) {
196
-			return $hash;
197
-		} else {
198
-			throw new \RuntimeException('hash function did not return a string');
199
-		}
200
-	}
201
-
202
-	/**
203
-	 * Gets the name based on the provided LDAP DN.
204
-	 *
205
-	 * @param string $fdn
206
-	 * @return string|false
207
-	 */
208
-	public function getNameByDN($fdn) {
209
-		if (!isset($this->cache[$fdn])) {
210
-			$this->cache[$fdn] = $this->getXbyY('owncloud_name', 'ldap_dn_hash', $this->getDNHash($fdn));
211
-		}
212
-		return $this->cache[$fdn];
213
-	}
214
-
215
-	/**
216
-	 * @param array<string> $hashList
217
-	 */
218
-	protected function prepareListOfIdsQuery(array $hashList): IQueryBuilder {
219
-		$qb = $this->dbc->getQueryBuilder();
220
-		$qb->select('owncloud_name', 'ldap_dn_hash', 'ldap_dn')
221
-			->from($this->getTableName(false))
222
-			->where($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($hashList, QueryBuilder::PARAM_STR_ARRAY)));
223
-		return $qb;
224
-	}
225
-
226
-	protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$results): void {
227
-		$stmt = $qb->execute();
228
-		while ($entry = $stmt->fetch(\Doctrine\DBAL\FetchMode::ASSOCIATIVE)) {
229
-			$results[$entry['ldap_dn']] = $entry['owncloud_name'];
230
-			$this->cache[$entry['ldap_dn']] = $entry['owncloud_name'];
231
-		}
232
-		$stmt->closeCursor();
233
-	}
234
-
235
-	/**
236
-	 * @param array<string> $fdns
237
-	 * @return array<string,string>
238
-	 */
239
-	public function getListOfIdsByDn(array $fdns): array {
240
-		$totalDBParamLimit = 65000;
241
-		$sliceSize = 1000;
242
-		$maxSlices = $totalDBParamLimit / $sliceSize;
243
-		$results = [];
244
-
245
-		$slice = 1;
246
-		$fdns = array_map([$this, 'getDNHash'], $fdns);
247
-		$fdnsSlice = count($fdns) > $sliceSize ? array_slice($fdns, 0, $sliceSize) : $fdns;
248
-		$qb = $this->prepareListOfIdsQuery($fdnsSlice);
249
-
250
-		while (isset($fdnsSlice[999])) {
251
-			// Oracle does not allow more than 1000 values in the IN list,
252
-			// but allows slicing
253
-			$slice++;
254
-			$fdnsSlice = array_slice($fdns, $sliceSize * ($slice - 1), $sliceSize);
255
-
256
-			/** @see https://github.com/vimeo/psalm/issues/4995 */
257
-			/** @psalm-suppress TypeDoesNotContainType */
258
-			if (!isset($qb)) {
259
-				$qb = $this->prepareListOfIdsQuery($fdnsSlice);
260
-				continue;
261
-			}
262
-
263
-			if (!empty($fdnsSlice)) {
264
-				$qb->orWhere($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($fdnsSlice, QueryBuilder::PARAM_STR_ARRAY)));
265
-			}
266
-
267
-			if ($slice % $maxSlices === 0) {
268
-				$this->collectResultsFromListOfIdsQuery($qb, $results);
269
-				unset($qb);
270
-			}
271
-		}
272
-
273
-		if (isset($qb)) {
274
-			$this->collectResultsFromListOfIdsQuery($qb, $results);
275
-		}
276
-
277
-		return $results;
278
-	}
279
-
280
-	/**
281
-	 * Searches mapped names by the giving string in the name column
282
-	 *
283
-	 * @param string $search
284
-	 * @param string $prefixMatch
285
-	 * @param string $postfixMatch
286
-	 * @return string[]
287
-	 */
288
-	public function getNamesBySearch($search, $prefixMatch = "", $postfixMatch = "") {
289
-		$statement = $this->dbc->prepare('
185
+        unset($this->cache[$fdn]);
186
+
187
+        return $this->modify($statement, [$uuid, $this->getDNHash($fdn)]);
188
+    }
189
+
190
+    /**
191
+     * Get the hash to store in database column ldap_dn_hash for a given dn
192
+     */
193
+    protected function getDNHash(string $fdn): string {
194
+        $hash = hash('sha256', $fdn, false);
195
+        if (is_string($hash)) {
196
+            return $hash;
197
+        } else {
198
+            throw new \RuntimeException('hash function did not return a string');
199
+        }
200
+    }
201
+
202
+    /**
203
+     * Gets the name based on the provided LDAP DN.
204
+     *
205
+     * @param string $fdn
206
+     * @return string|false
207
+     */
208
+    public function getNameByDN($fdn) {
209
+        if (!isset($this->cache[$fdn])) {
210
+            $this->cache[$fdn] = $this->getXbyY('owncloud_name', 'ldap_dn_hash', $this->getDNHash($fdn));
211
+        }
212
+        return $this->cache[$fdn];
213
+    }
214
+
215
+    /**
216
+     * @param array<string> $hashList
217
+     */
218
+    protected function prepareListOfIdsQuery(array $hashList): IQueryBuilder {
219
+        $qb = $this->dbc->getQueryBuilder();
220
+        $qb->select('owncloud_name', 'ldap_dn_hash', 'ldap_dn')
221
+            ->from($this->getTableName(false))
222
+            ->where($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($hashList, QueryBuilder::PARAM_STR_ARRAY)));
223
+        return $qb;
224
+    }
225
+
226
+    protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$results): void {
227
+        $stmt = $qb->execute();
228
+        while ($entry = $stmt->fetch(\Doctrine\DBAL\FetchMode::ASSOCIATIVE)) {
229
+            $results[$entry['ldap_dn']] = $entry['owncloud_name'];
230
+            $this->cache[$entry['ldap_dn']] = $entry['owncloud_name'];
231
+        }
232
+        $stmt->closeCursor();
233
+    }
234
+
235
+    /**
236
+     * @param array<string> $fdns
237
+     * @return array<string,string>
238
+     */
239
+    public function getListOfIdsByDn(array $fdns): array {
240
+        $totalDBParamLimit = 65000;
241
+        $sliceSize = 1000;
242
+        $maxSlices = $totalDBParamLimit / $sliceSize;
243
+        $results = [];
244
+
245
+        $slice = 1;
246
+        $fdns = array_map([$this, 'getDNHash'], $fdns);
247
+        $fdnsSlice = count($fdns) > $sliceSize ? array_slice($fdns, 0, $sliceSize) : $fdns;
248
+        $qb = $this->prepareListOfIdsQuery($fdnsSlice);
249
+
250
+        while (isset($fdnsSlice[999])) {
251
+            // Oracle does not allow more than 1000 values in the IN list,
252
+            // but allows slicing
253
+            $slice++;
254
+            $fdnsSlice = array_slice($fdns, $sliceSize * ($slice - 1), $sliceSize);
255
+
256
+            /** @see https://github.com/vimeo/psalm/issues/4995 */
257
+            /** @psalm-suppress TypeDoesNotContainType */
258
+            if (!isset($qb)) {
259
+                $qb = $this->prepareListOfIdsQuery($fdnsSlice);
260
+                continue;
261
+            }
262
+
263
+            if (!empty($fdnsSlice)) {
264
+                $qb->orWhere($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($fdnsSlice, QueryBuilder::PARAM_STR_ARRAY)));
265
+            }
266
+
267
+            if ($slice % $maxSlices === 0) {
268
+                $this->collectResultsFromListOfIdsQuery($qb, $results);
269
+                unset($qb);
270
+            }
271
+        }
272
+
273
+        if (isset($qb)) {
274
+            $this->collectResultsFromListOfIdsQuery($qb, $results);
275
+        }
276
+
277
+        return $results;
278
+    }
279
+
280
+    /**
281
+     * Searches mapped names by the giving string in the name column
282
+     *
283
+     * @param string $search
284
+     * @param string $prefixMatch
285
+     * @param string $postfixMatch
286
+     * @return string[]
287
+     */
288
+    public function getNamesBySearch($search, $prefixMatch = "", $postfixMatch = "") {
289
+        $statement = $this->dbc->prepare('
290 290
 			SELECT `owncloud_name`
291 291
 			FROM `' . $this->getTableName() . '`
292 292
 			WHERE `owncloud_name` LIKE ?
293 293
 		');
294 294
 
295
-		try {
296
-			$res = $statement->execute([$prefixMatch . $this->dbc->escapeLikeParameter($search) . $postfixMatch]);
297
-		} catch (Exception $e) {
298
-			return [];
299
-		}
300
-		$names = [];
301
-		while ($row = $res->fetch()) {
302
-			$names[] = $row['owncloud_name'];
303
-		}
304
-		return $names;
305
-	}
306
-
307
-	/**
308
-	 * Gets the name based on the provided LDAP UUID.
309
-	 *
310
-	 * @param string $uuid
311
-	 * @return string|false
312
-	 */
313
-	public function getNameByUUID($uuid) {
314
-		return $this->getXbyY('owncloud_name', 'directory_uuid', $uuid);
315
-	}
316
-
317
-	public function getDnByUUID($uuid) {
318
-		return $this->getXbyY('ldap_dn', 'directory_uuid', $uuid);
319
-	}
320
-
321
-	/**
322
-	 * Gets the UUID based on the provided LDAP DN
323
-	 *
324
-	 * @param string $dn
325
-	 * @return false|string
326
-	 * @throws \Exception
327
-	 */
328
-	public function getUUIDByDN($dn) {
329
-		return $this->getXbyY('directory_uuid', 'ldap_dn_hash', $this->getDNHash($dn));
330
-	}
331
-
332
-	public function getList(int $offset = 0, int $limit = null, bool $invalidatedOnly = false): array {
333
-		$select = $this->dbc->getQueryBuilder();
334
-		$select->selectAlias('ldap_dn', 'dn')
335
-			->selectAlias('owncloud_name', 'name')
336
-			->selectAlias('directory_uuid', 'uuid')
337
-			->from($this->getTableName())
338
-			->setMaxResults($limit)
339
-			->setFirstResult($offset);
340
-
341
-		if ($invalidatedOnly) {
342
-			$select->where($select->expr()->like('directory_uuid', $select->createNamedParameter('invalidated_%')));
343
-		}
344
-
345
-		$result = $select->executeQuery();
346
-		$entries = $result->fetchAll();
347
-		$result->closeCursor();
348
-
349
-		return $entries;
350
-	}
351
-
352
-	/**
353
-	 * attempts to map the given entry
354
-	 *
355
-	 * @param string $fdn fully distinguished name (from LDAP)
356
-	 * @param string $name
357
-	 * @param string $uuid a unique identifier as used in LDAP
358
-	 * @return bool
359
-	 */
360
-	public function map($fdn, $name, $uuid) {
361
-		if (mb_strlen($fdn) > 4096) {
362
-			\OC::$server->getLogger()->error(
363
-				'Cannot map, because the DN exceeds 4096 characters: {dn}',
364
-				[
365
-					'app' => 'user_ldap',
366
-					'dn' => $fdn,
367
-				]
368
-			);
369
-			return false;
370
-		}
371
-
372
-		$row = [
373
-			'ldap_dn_hash' => $this->getDNHash($fdn),
374
-			'ldap_dn' => $fdn,
375
-			'owncloud_name' => $name,
376
-			'directory_uuid' => $uuid
377
-		];
378
-
379
-		try {
380
-			$result = $this->dbc->insertIfNotExist($this->getTableName(), $row);
381
-			if ((bool)$result === true) {
382
-				$this->cache[$fdn] = $name;
383
-			}
384
-			// insertIfNotExist returns values as int
385
-			return (bool)$result;
386
-		} catch (\Exception $e) {
387
-			return false;
388
-		}
389
-	}
390
-
391
-	/**
392
-	 * removes a mapping based on the owncloud_name of the entry
393
-	 *
394
-	 * @param string $name
395
-	 * @return bool
396
-	 */
397
-	public function unmap($name) {
398
-		$statement = $this->dbc->prepare('
295
+        try {
296
+            $res = $statement->execute([$prefixMatch . $this->dbc->escapeLikeParameter($search) . $postfixMatch]);
297
+        } catch (Exception $e) {
298
+            return [];
299
+        }
300
+        $names = [];
301
+        while ($row = $res->fetch()) {
302
+            $names[] = $row['owncloud_name'];
303
+        }
304
+        return $names;
305
+    }
306
+
307
+    /**
308
+     * Gets the name based on the provided LDAP UUID.
309
+     *
310
+     * @param string $uuid
311
+     * @return string|false
312
+     */
313
+    public function getNameByUUID($uuid) {
314
+        return $this->getXbyY('owncloud_name', 'directory_uuid', $uuid);
315
+    }
316
+
317
+    public function getDnByUUID($uuid) {
318
+        return $this->getXbyY('ldap_dn', 'directory_uuid', $uuid);
319
+    }
320
+
321
+    /**
322
+     * Gets the UUID based on the provided LDAP DN
323
+     *
324
+     * @param string $dn
325
+     * @return false|string
326
+     * @throws \Exception
327
+     */
328
+    public function getUUIDByDN($dn) {
329
+        return $this->getXbyY('directory_uuid', 'ldap_dn_hash', $this->getDNHash($dn));
330
+    }
331
+
332
+    public function getList(int $offset = 0, int $limit = null, bool $invalidatedOnly = false): array {
333
+        $select = $this->dbc->getQueryBuilder();
334
+        $select->selectAlias('ldap_dn', 'dn')
335
+            ->selectAlias('owncloud_name', 'name')
336
+            ->selectAlias('directory_uuid', 'uuid')
337
+            ->from($this->getTableName())
338
+            ->setMaxResults($limit)
339
+            ->setFirstResult($offset);
340
+
341
+        if ($invalidatedOnly) {
342
+            $select->where($select->expr()->like('directory_uuid', $select->createNamedParameter('invalidated_%')));
343
+        }
344
+
345
+        $result = $select->executeQuery();
346
+        $entries = $result->fetchAll();
347
+        $result->closeCursor();
348
+
349
+        return $entries;
350
+    }
351
+
352
+    /**
353
+     * attempts to map the given entry
354
+     *
355
+     * @param string $fdn fully distinguished name (from LDAP)
356
+     * @param string $name
357
+     * @param string $uuid a unique identifier as used in LDAP
358
+     * @return bool
359
+     */
360
+    public function map($fdn, $name, $uuid) {
361
+        if (mb_strlen($fdn) > 4096) {
362
+            \OC::$server->getLogger()->error(
363
+                'Cannot map, because the DN exceeds 4096 characters: {dn}',
364
+                [
365
+                    'app' => 'user_ldap',
366
+                    'dn' => $fdn,
367
+                ]
368
+            );
369
+            return false;
370
+        }
371
+
372
+        $row = [
373
+            'ldap_dn_hash' => $this->getDNHash($fdn),
374
+            'ldap_dn' => $fdn,
375
+            'owncloud_name' => $name,
376
+            'directory_uuid' => $uuid
377
+        ];
378
+
379
+        try {
380
+            $result = $this->dbc->insertIfNotExist($this->getTableName(), $row);
381
+            if ((bool)$result === true) {
382
+                $this->cache[$fdn] = $name;
383
+            }
384
+            // insertIfNotExist returns values as int
385
+            return (bool)$result;
386
+        } catch (\Exception $e) {
387
+            return false;
388
+        }
389
+    }
390
+
391
+    /**
392
+     * removes a mapping based on the owncloud_name of the entry
393
+     *
394
+     * @param string $name
395
+     * @return bool
396
+     */
397
+    public function unmap($name) {
398
+        $statement = $this->dbc->prepare('
399 399
 			DELETE FROM `' . $this->getTableName() . '`
400 400
 			WHERE `owncloud_name` = ?');
401 401
 
402
-		$dn = array_search($name, $this->cache);
403
-		if ($dn !== false) {
404
-			unset($this->cache[$dn]);
405
-		}
406
-
407
-		return $this->modify($statement, [$name]);
408
-	}
409
-
410
-	/**
411
-	 * Truncates the mapping table
412
-	 *
413
-	 * @return bool
414
-	 */
415
-	public function clear() {
416
-		$sql = $this->dbc
417
-			->getDatabasePlatform()
418
-			->getTruncateTableSQL('`' . $this->getTableName() . '`');
419
-		try {
420
-			$this->dbc->executeQuery($sql);
421
-
422
-			return true;
423
-		} catch (Exception $e) {
424
-			return false;
425
-		}
426
-	}
427
-
428
-	/**
429
-	 * clears the mapping table one by one and executing a callback with
430
-	 * each row's id (=owncloud_name col)
431
-	 *
432
-	 * @param callable $preCallback
433
-	 * @param callable $postCallback
434
-	 * @return bool true on success, false when at least one row was not
435
-	 * deleted
436
-	 */
437
-	public function clearCb(callable $preCallback, callable $postCallback): bool {
438
-		$picker = $this->dbc->getQueryBuilder();
439
-		$picker->select('owncloud_name')
440
-			->from($this->getTableName());
441
-		$cursor = $picker->execute();
442
-		$result = true;
443
-		while ($id = $cursor->fetchOne()) {
444
-			$preCallback($id);
445
-			if ($isUnmapped = $this->unmap($id)) {
446
-				$postCallback($id);
447
-			}
448
-			$result &= $isUnmapped;
449
-		}
450
-		$cursor->closeCursor();
451
-		return $result;
452
-	}
453
-
454
-	/**
455
-	 * returns the number of entries in the mappings table
456
-	 *
457
-	 * @return int
458
-	 */
459
-	public function count(): int {
460
-		$query = $this->dbc->getQueryBuilder();
461
-		$query->select($query->func()->count('ldap_dn_hash'))
462
-			->from($this->getTableName());
463
-		$res = $query->execute();
464
-		$count = $res->fetchOne();
465
-		$res->closeCursor();
466
-		return (int)$count;
467
-	}
468
-
469
-	public function countInvalidated(): int {
470
-		$query = $this->dbc->getQueryBuilder();
471
-		$query->select($query->func()->count('ldap_dn_hash'))
472
-			->from($this->getTableName())
473
-			->where($query->expr()->like('directory_uuid', $query->createNamedParameter('invalidated_%')));
474
-		$res = $query->execute();
475
-		$count = $res->fetchOne();
476
-		$res->closeCursor();
477
-		return (int)$count;
478
-	}
402
+        $dn = array_search($name, $this->cache);
403
+        if ($dn !== false) {
404
+            unset($this->cache[$dn]);
405
+        }
406
+
407
+        return $this->modify($statement, [$name]);
408
+    }
409
+
410
+    /**
411
+     * Truncates the mapping table
412
+     *
413
+     * @return bool
414
+     */
415
+    public function clear() {
416
+        $sql = $this->dbc
417
+            ->getDatabasePlatform()
418
+            ->getTruncateTableSQL('`' . $this->getTableName() . '`');
419
+        try {
420
+            $this->dbc->executeQuery($sql);
421
+
422
+            return true;
423
+        } catch (Exception $e) {
424
+            return false;
425
+        }
426
+    }
427
+
428
+    /**
429
+     * clears the mapping table one by one and executing a callback with
430
+     * each row's id (=owncloud_name col)
431
+     *
432
+     * @param callable $preCallback
433
+     * @param callable $postCallback
434
+     * @return bool true on success, false when at least one row was not
435
+     * deleted
436
+     */
437
+    public function clearCb(callable $preCallback, callable $postCallback): bool {
438
+        $picker = $this->dbc->getQueryBuilder();
439
+        $picker->select('owncloud_name')
440
+            ->from($this->getTableName());
441
+        $cursor = $picker->execute();
442
+        $result = true;
443
+        while ($id = $cursor->fetchOne()) {
444
+            $preCallback($id);
445
+            if ($isUnmapped = $this->unmap($id)) {
446
+                $postCallback($id);
447
+            }
448
+            $result &= $isUnmapped;
449
+        }
450
+        $cursor->closeCursor();
451
+        return $result;
452
+    }
453
+
454
+    /**
455
+     * returns the number of entries in the mappings table
456
+     *
457
+     * @return int
458
+     */
459
+    public function count(): int {
460
+        $query = $this->dbc->getQueryBuilder();
461
+        $query->select($query->func()->count('ldap_dn_hash'))
462
+            ->from($this->getTableName());
463
+        $res = $query->execute();
464
+        $count = $res->fetchOne();
465
+        $res->closeCursor();
466
+        return (int)$count;
467
+    }
468
+
469
+    public function countInvalidated(): int {
470
+        $query = $this->dbc->getQueryBuilder();
471
+        $query->select($query->func()->count('ldap_dn_hash'))
472
+            ->from($this->getTableName())
473
+            ->where($query->expr()->like('directory_uuid', $query->createNamedParameter('invalidated_%')));
474
+        $res = $query->execute();
475
+        $count = $res->fetchOne();
476
+        $res->closeCursor();
477
+        return (int)$count;
478
+    }
479 479
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -93,9 +93,9 @@  discard block
 block discarded – undo
93 93
 			throw new \Exception('Invalid Column Name');
94 94
 		}
95 95
 		$query = $this->dbc->prepare('
96
-			SELECT `' . $fetchCol . '`
97
-			FROM `' . $this->getTableName() . '`
98
-			WHERE `' . $compareCol . '` = ?
96
+			SELECT `' . $fetchCol.'`
97
+			FROM `' . $this->getTableName().'`
98
+			WHERE `' . $compareCol.'` = ?
99 99
 		');
100 100
 
101 101
 		try {
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
 	public function setDNbyUUID($fdn, $uuid) {
152 152
 		$oldDn = $this->getDnByUUID($uuid);
153 153
 		$statement = $this->dbc->prepare('
154
-			UPDATE `' . $this->getTableName() . '`
154
+			UPDATE `' . $this->getTableName().'`
155 155
 			SET `ldap_dn_hash` = ?, `ldap_dn` = ?
156 156
 			WHERE `directory_uuid` = ?
157 157
 		');
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 	 */
178 178
 	public function setUUIDbyDN($uuid, $fdn): bool {
179 179
 		$statement = $this->dbc->prepare('
180
-			UPDATE `' . $this->getTableName() . '`
180
+			UPDATE `' . $this->getTableName().'`
181 181
 			SET `directory_uuid` = ?
182 182
 			WHERE `ldap_dn_hash` = ?
183 183
 		');
@@ -288,12 +288,12 @@  discard block
 block discarded – undo
288 288
 	public function getNamesBySearch($search, $prefixMatch = "", $postfixMatch = "") {
289 289
 		$statement = $this->dbc->prepare('
290 290
 			SELECT `owncloud_name`
291
-			FROM `' . $this->getTableName() . '`
291
+			FROM `' . $this->getTableName().'`
292 292
 			WHERE `owncloud_name` LIKE ?
293 293
 		');
294 294
 
295 295
 		try {
296
-			$res = $statement->execute([$prefixMatch . $this->dbc->escapeLikeParameter($search) . $postfixMatch]);
296
+			$res = $statement->execute([$prefixMatch.$this->dbc->escapeLikeParameter($search).$postfixMatch]);
297 297
 		} catch (Exception $e) {
298 298
 			return [];
299 299
 		}
@@ -378,11 +378,11 @@  discard block
 block discarded – undo
378 378
 
379 379
 		try {
380 380
 			$result = $this->dbc->insertIfNotExist($this->getTableName(), $row);
381
-			if ((bool)$result === true) {
381
+			if ((bool) $result === true) {
382 382
 				$this->cache[$fdn] = $name;
383 383
 			}
384 384
 			// insertIfNotExist returns values as int
385
-			return (bool)$result;
385
+			return (bool) $result;
386 386
 		} catch (\Exception $e) {
387 387
 			return false;
388 388
 		}
@@ -396,7 +396,7 @@  discard block
 block discarded – undo
396 396
 	 */
397 397
 	public function unmap($name) {
398 398
 		$statement = $this->dbc->prepare('
399
-			DELETE FROM `' . $this->getTableName() . '`
399
+			DELETE FROM `' . $this->getTableName().'`
400 400
 			WHERE `owncloud_name` = ?');
401 401
 
402 402
 		$dn = array_search($name, $this->cache);
@@ -415,7 +415,7 @@  discard block
 block discarded – undo
415 415
 	public function clear() {
416 416
 		$sql = $this->dbc
417 417
 			->getDatabasePlatform()
418
-			->getTruncateTableSQL('`' . $this->getTableName() . '`');
418
+			->getTruncateTableSQL('`'.$this->getTableName().'`');
419 419
 		try {
420 420
 			$this->dbc->executeQuery($sql);
421 421
 
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
 		$res = $query->execute();
464 464
 		$count = $res->fetchOne();
465 465
 		$res->closeCursor();
466
-		return (int)$count;
466
+		return (int) $count;
467 467
 	}
468 468
 
469 469
 	public function countInvalidated(): int {
@@ -474,6 +474,6 @@  discard block
 block discarded – undo
474 474
 		$res = $query->execute();
475 475
 		$count = $res->fetchOne();
476 476
 		$res->closeCursor();
477
-		return (int)$count;
477
+		return (int) $count;
478 478
 	}
479 479
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Command/UpdateUUID.php 2 patches
Indentation   +304 added lines, -304 removed lines patch added patch discarded remove patch
@@ -42,333 +42,333 @@
 block discarded – undo
42 42
 use function sprintf;
43 43
 
44 44
 class UuidUpdateReport {
45
-	const UNCHANGED = 0;
46
-	const UNKNOWN = 1;
47
-	const UNREADABLE = 2;
48
-	const UPDATED = 3;
49
-	const UNWRITABLE = 4;
50
-	const UNMAPPED = 5;
45
+    const UNCHANGED = 0;
46
+    const UNKNOWN = 1;
47
+    const UNREADABLE = 2;
48
+    const UPDATED = 3;
49
+    const UNWRITABLE = 4;
50
+    const UNMAPPED = 5;
51 51
 
52
-	public $id = '';
53
-	public $dn = '';
54
-	public $isUser = true;
55
-	public $state = self::UNCHANGED;
56
-	public $oldUuid = '';
57
-	public $newUuid = '';
52
+    public $id = '';
53
+    public $dn = '';
54
+    public $isUser = true;
55
+    public $state = self::UNCHANGED;
56
+    public $oldUuid = '';
57
+    public $newUuid = '';
58 58
 
59
-	public function __construct(string $id, string $dn, bool $isUser, int $state, string $oldUuid = '', string $newUuid = '') {
60
-		$this->id = $id;
61
-		$this->dn = $dn;
62
-		$this->isUser = $isUser;
63
-		$this->state = $state;
64
-		$this->oldUuid = $oldUuid;
65
-		$this->newUuid = $newUuid;
66
-	}
59
+    public function __construct(string $id, string $dn, bool $isUser, int $state, string $oldUuid = '', string $newUuid = '') {
60
+        $this->id = $id;
61
+        $this->dn = $dn;
62
+        $this->isUser = $isUser;
63
+        $this->state = $state;
64
+        $this->oldUuid = $oldUuid;
65
+        $this->newUuid = $newUuid;
66
+    }
67 67
 }
68 68
 
69 69
 class UpdateUUID extends Command {
70
-	/** @var UserMapping */
71
-	private $userMapping;
72
-	/** @var GroupMapping */
73
-	private $groupMapping;
74
-	/** @var User_Proxy */
75
-	private $userProxy;
76
-	/** @var Group_Proxy */
77
-	private $groupProxy;
78
-	/** @var array<UuidUpdateReport[]> */
79
-	protected $reports = [];
80
-	/** @var LoggerInterface */
81
-	private $logger;
82
-	/** @var bool */
83
-	private $dryRun = false;
70
+    /** @var UserMapping */
71
+    private $userMapping;
72
+    /** @var GroupMapping */
73
+    private $groupMapping;
74
+    /** @var User_Proxy */
75
+    private $userProxy;
76
+    /** @var Group_Proxy */
77
+    private $groupProxy;
78
+    /** @var array<UuidUpdateReport[]> */
79
+    protected $reports = [];
80
+    /** @var LoggerInterface */
81
+    private $logger;
82
+    /** @var bool */
83
+    private $dryRun = false;
84 84
 
85
-	public function __construct(UserMapping $userMapping, GroupMapping $groupMapping, User_Proxy $userProxy, Group_Proxy $groupProxy, LoggerInterface $logger) {
86
-		$this->userMapping = $userMapping;
87
-		$this->groupMapping = $groupMapping;
88
-		$this->userProxy = $userProxy;
89
-		$this->groupProxy = $groupProxy;
90
-		$this->logger = $logger;
91
-		$this->reports = [
92
-			UuidUpdateReport::UPDATED => [],
93
-			UuidUpdateReport::UNKNOWN => [],
94
-			UuidUpdateReport::UNREADABLE => [],
95
-			UuidUpdateReport::UNWRITABLE => [],
96
-			UuidUpdateReport::UNMAPPED => [],
97
-		];
98
-		parent::__construct();
99
-	}
85
+    public function __construct(UserMapping $userMapping, GroupMapping $groupMapping, User_Proxy $userProxy, Group_Proxy $groupProxy, LoggerInterface $logger) {
86
+        $this->userMapping = $userMapping;
87
+        $this->groupMapping = $groupMapping;
88
+        $this->userProxy = $userProxy;
89
+        $this->groupProxy = $groupProxy;
90
+        $this->logger = $logger;
91
+        $this->reports = [
92
+            UuidUpdateReport::UPDATED => [],
93
+            UuidUpdateReport::UNKNOWN => [],
94
+            UuidUpdateReport::UNREADABLE => [],
95
+            UuidUpdateReport::UNWRITABLE => [],
96
+            UuidUpdateReport::UNMAPPED => [],
97
+        ];
98
+        parent::__construct();
99
+    }
100 100
 
101
-	protected function configure(): void {
102
-		$this
103
-			->setName('ldap:update-uuid')
104
-			->setDescription('Attempts to update UUIDs of user and group entries. By default, the command attempts to update UUIDs that have been invalidated by a migration step.')
105
-			->addOption(
106
-				'all',
107
-				null,
108
-				InputOption::VALUE_NONE,
109
-				'updates every user and group. All other options are ignored.'
110
-			)
111
-			->addOption(
112
-				'userId',
113
-				null,
114
-				InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
115
-				'a user ID to update'
116
-			)
117
-			->addOption(
118
-				'groupId',
119
-				null,
120
-				InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
121
-				'a group ID to update'
122
-			)
123
-			->addOption(
124
-				'dn',
125
-				null,
126
-				InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
127
-				'a DN to update'
128
-			)
129
-			->addOption(
130
-				'dry-run',
131
-				null,
132
-				InputOption::VALUE_NONE,
133
-				'UUIDs will not be updated in the database'
134
-			)
135
-		;
136
-	}
101
+    protected function configure(): void {
102
+        $this
103
+            ->setName('ldap:update-uuid')
104
+            ->setDescription('Attempts to update UUIDs of user and group entries. By default, the command attempts to update UUIDs that have been invalidated by a migration step.')
105
+            ->addOption(
106
+                'all',
107
+                null,
108
+                InputOption::VALUE_NONE,
109
+                'updates every user and group. All other options are ignored.'
110
+            )
111
+            ->addOption(
112
+                'userId',
113
+                null,
114
+                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
115
+                'a user ID to update'
116
+            )
117
+            ->addOption(
118
+                'groupId',
119
+                null,
120
+                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
121
+                'a group ID to update'
122
+            )
123
+            ->addOption(
124
+                'dn',
125
+                null,
126
+                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
127
+                'a DN to update'
128
+            )
129
+            ->addOption(
130
+                'dry-run',
131
+                null,
132
+                InputOption::VALUE_NONE,
133
+                'UUIDs will not be updated in the database'
134
+            )
135
+        ;
136
+    }
137 137
 
138
-	protected function execute(InputInterface $input, OutputInterface $output): int {
139
-		$this->dryRun = $input->getOption('dry-run');
140
-		$entriesToUpdate = $this->estimateNumberOfUpdates($input);
141
-		$progress = new ProgressBar($output);
142
-		$progress->start($entriesToUpdate);
143
-		foreach($this->handleUpdates($input) as $_) {
144
-			$progress->advance();
145
-		}
146
-		$progress->finish();
147
-		$output->writeln('');
148
-		$this->printReport($output);
149
-		return count($this->reports[UuidUpdateReport::UNMAPPED]) === 0
150
-			&& count($this->reports[UuidUpdateReport::UNREADABLE]) === 0
151
-			&& count($this->reports[UuidUpdateReport::UNWRITABLE]) === 0
152
-			? 0
153
-			: 1;
154
-	}
138
+    protected function execute(InputInterface $input, OutputInterface $output): int {
139
+        $this->dryRun = $input->getOption('dry-run');
140
+        $entriesToUpdate = $this->estimateNumberOfUpdates($input);
141
+        $progress = new ProgressBar($output);
142
+        $progress->start($entriesToUpdate);
143
+        foreach($this->handleUpdates($input) as $_) {
144
+            $progress->advance();
145
+        }
146
+        $progress->finish();
147
+        $output->writeln('');
148
+        $this->printReport($output);
149
+        return count($this->reports[UuidUpdateReport::UNMAPPED]) === 0
150
+            && count($this->reports[UuidUpdateReport::UNREADABLE]) === 0
151
+            && count($this->reports[UuidUpdateReport::UNWRITABLE]) === 0
152
+            ? 0
153
+            : 1;
154
+    }
155 155
 
156
-	protected function printReport(OutputInterface $output): void {
157
-		if ($output->isQuiet()) {
158
-			return;
159
-		}
156
+    protected function printReport(OutputInterface $output): void {
157
+        if ($output->isQuiet()) {
158
+            return;
159
+        }
160 160
 
161
-		if (count($this->reports[UuidUpdateReport::UPDATED]) === 0) {
162
-			$output->writeln('<info>No record was updated.</info>');
163
-		} else {
164
-			$output->writeln(sprintf('<info>%d record(s) were updated.</info>', count($this->reports[UuidUpdateReport::UPDATED])));
165
-			if ($output->isVerbose()) {
166
-				/** @var UuidUpdateReport $report */
167
-				foreach ($this->reports[UuidUpdateReport::UPDATED] as $report) {
168
-					$output->writeln(sprintf('  %s had their old UUID %s updated to %s', $report->id, $report->oldUuid, $report->newUuid));
169
-				}
170
-				$output->writeln('');
171
-			}
172
-		}
161
+        if (count($this->reports[UuidUpdateReport::UPDATED]) === 0) {
162
+            $output->writeln('<info>No record was updated.</info>');
163
+        } else {
164
+            $output->writeln(sprintf('<info>%d record(s) were updated.</info>', count($this->reports[UuidUpdateReport::UPDATED])));
165
+            if ($output->isVerbose()) {
166
+                /** @var UuidUpdateReport $report */
167
+                foreach ($this->reports[UuidUpdateReport::UPDATED] as $report) {
168
+                    $output->writeln(sprintf('  %s had their old UUID %s updated to %s', $report->id, $report->oldUuid, $report->newUuid));
169
+                }
170
+                $output->writeln('');
171
+            }
172
+        }
173 173
 
174
-		if (count($this->reports[UuidUpdateReport::UNMAPPED]) > 0) {
175
-			$output->writeln(sprintf('<error>%d provided IDs were not mapped. These were:</error>', count($this->reports[UuidUpdateReport::UNMAPPED])));
176
-			/** @var UuidUpdateReport $report */
177
-			foreach ($this->reports[UuidUpdateReport::UNMAPPED] as $report) {
178
-				if (!empty($report->id)) {
179
-					$output->writeln(sprintf('  %s: %s',
180
-						$report->isUser ? 'User' : 'Group', $report->id));
181
-				} else if (!empty($report->dn)) {
182
-					$output->writeln(sprintf('  DN: %s', $report->dn));
183
-				}
184
-			}
185
-			$output->writeln('');
186
-		}
174
+        if (count($this->reports[UuidUpdateReport::UNMAPPED]) > 0) {
175
+            $output->writeln(sprintf('<error>%d provided IDs were not mapped. These were:</error>', count($this->reports[UuidUpdateReport::UNMAPPED])));
176
+            /** @var UuidUpdateReport $report */
177
+            foreach ($this->reports[UuidUpdateReport::UNMAPPED] as $report) {
178
+                if (!empty($report->id)) {
179
+                    $output->writeln(sprintf('  %s: %s',
180
+                        $report->isUser ? 'User' : 'Group', $report->id));
181
+                } else if (!empty($report->dn)) {
182
+                    $output->writeln(sprintf('  DN: %s', $report->dn));
183
+                }
184
+            }
185
+            $output->writeln('');
186
+        }
187 187
 
188
-		if (count($this->reports[UuidUpdateReport::UNKNOWN]) > 0) {
189
-			$output->writeln(sprintf('<info>%d provided IDs were unknown on LDAP.</info>', count($this->reports[UuidUpdateReport::UNKNOWN])));
190
-			if ($output->isVerbose()) {
191
-				/** @var UuidUpdateReport $report */
192
-				foreach ($this->reports[UuidUpdateReport::UNKNOWN] as $report) {
193
-					$output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
194
-				}
195
-				$output->writeln(PHP_EOL . 'Old users can be removed along with their data per occ user:delete.' . PHP_EOL);
196
-			}
197
-		}
188
+        if (count($this->reports[UuidUpdateReport::UNKNOWN]) > 0) {
189
+            $output->writeln(sprintf('<info>%d provided IDs were unknown on LDAP.</info>', count($this->reports[UuidUpdateReport::UNKNOWN])));
190
+            if ($output->isVerbose()) {
191
+                /** @var UuidUpdateReport $report */
192
+                foreach ($this->reports[UuidUpdateReport::UNKNOWN] as $report) {
193
+                    $output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
194
+                }
195
+                $output->writeln(PHP_EOL . 'Old users can be removed along with their data per occ user:delete.' . PHP_EOL);
196
+            }
197
+        }
198 198
 
199
-		if (count($this->reports[UuidUpdateReport::UNREADABLE]) > 0) {
200
-			$output->writeln(sprintf('<error>For %d records, the UUID could not be read. Double-check your configuration.</error>', count($this->reports[UuidUpdateReport::UNREADABLE])));
201
-			if ($output->isVerbose()) {
202
-				/** @var UuidUpdateReport $report */
203
-				foreach ($this->reports[UuidUpdateReport::UNREADABLE] as $report) {
204
-					$output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
205
-				}
206
-			}
207
-		}
199
+        if (count($this->reports[UuidUpdateReport::UNREADABLE]) > 0) {
200
+            $output->writeln(sprintf('<error>For %d records, the UUID could not be read. Double-check your configuration.</error>', count($this->reports[UuidUpdateReport::UNREADABLE])));
201
+            if ($output->isVerbose()) {
202
+                /** @var UuidUpdateReport $report */
203
+                foreach ($this->reports[UuidUpdateReport::UNREADABLE] as $report) {
204
+                    $output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
205
+                }
206
+            }
207
+        }
208 208
 
209
-		if (count($this->reports[UuidUpdateReport::UNWRITABLE]) > 0) {
210
-			$output->writeln(sprintf('<error>For %d records, the UUID could not be saved to database. Double-check your configuration.</error>', count($this->reports[UuidUpdateReport::UNWRITABLE])));
211
-			if ($output->isVerbose()) {
212
-				/** @var UuidUpdateReport $report */
213
-				foreach ($this->reports[UuidUpdateReport::UNWRITABLE] as $report) {
214
-					$output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
215
-				}
216
-			}
217
-		}
218
-	}
209
+        if (count($this->reports[UuidUpdateReport::UNWRITABLE]) > 0) {
210
+            $output->writeln(sprintf('<error>For %d records, the UUID could not be saved to database. Double-check your configuration.</error>', count($this->reports[UuidUpdateReport::UNWRITABLE])));
211
+            if ($output->isVerbose()) {
212
+                /** @var UuidUpdateReport $report */
213
+                foreach ($this->reports[UuidUpdateReport::UNWRITABLE] as $report) {
214
+                    $output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
215
+                }
216
+            }
217
+        }
218
+    }
219 219
 
220
-	protected function handleUpdates(InputInterface $input): \Generator {
221
-		if ($input->getOption('all')) {
222
-			foreach($this->handleMappingBasedUpdates(false) as $_) {
223
-				yield;
224
-			}
225
-		} else if ($input->getOption('userId')
226
-			|| $input->getOption('groupId')
227
-			|| $input->getOption('dn')
228
-		) {
229
-			foreach($this->handleUpdatesByUserId($input->getOption('userId')) as $_) {
230
-				yield;
231
-			}
232
-			foreach($this->handleUpdatesByGroupId($input->getOption('groupId')) as $_) {
233
-				yield;
234
-			}
235
-			foreach($this->handleUpdatesByDN($input->getOption('dn')) as $_) {
236
-				yield;
237
-			}
238
-		} else {
239
-			foreach($this->handleMappingBasedUpdates(true) as $_) {
240
-				yield;
241
-			}
242
-		}
243
-	}
220
+    protected function handleUpdates(InputInterface $input): \Generator {
221
+        if ($input->getOption('all')) {
222
+            foreach($this->handleMappingBasedUpdates(false) as $_) {
223
+                yield;
224
+            }
225
+        } else if ($input->getOption('userId')
226
+            || $input->getOption('groupId')
227
+            || $input->getOption('dn')
228
+        ) {
229
+            foreach($this->handleUpdatesByUserId($input->getOption('userId')) as $_) {
230
+                yield;
231
+            }
232
+            foreach($this->handleUpdatesByGroupId($input->getOption('groupId')) as $_) {
233
+                yield;
234
+            }
235
+            foreach($this->handleUpdatesByDN($input->getOption('dn')) as $_) {
236
+                yield;
237
+            }
238
+        } else {
239
+            foreach($this->handleMappingBasedUpdates(true) as $_) {
240
+                yield;
241
+            }
242
+        }
243
+    }
244 244
 
245
-	protected function handleUpdatesByUserId(array $userIds): \Generator {
246
-		foreach($this->handleUpdatesByEntryId($userIds, $this->userMapping) as $_) {
247
-			yield;
248
-		}
249
-	}
245
+    protected function handleUpdatesByUserId(array $userIds): \Generator {
246
+        foreach($this->handleUpdatesByEntryId($userIds, $this->userMapping) as $_) {
247
+            yield;
248
+        }
249
+    }
250 250
 
251
-	protected function handleUpdatesByGroupId(array $groupIds): \Generator {
252
-		foreach($this->handleUpdatesByEntryId($groupIds, $this->groupMapping) as $_) {
253
-			yield;
254
-		}
255
-	}
251
+    protected function handleUpdatesByGroupId(array $groupIds): \Generator {
252
+        foreach($this->handleUpdatesByEntryId($groupIds, $this->groupMapping) as $_) {
253
+            yield;
254
+        }
255
+    }
256 256
 
257
-	protected function handleUpdatesByDN(array $dns): \Generator {
258
-		$userList = $groupList = [];
259
-		while ($dn = array_pop($dns)) {
260
-			$uuid = $this->userMapping->getUUIDByDN($dn);
261
-			if ($uuid) {
262
-				$id = $this->userMapping->getNameByDN($dn);
263
-				$userList[] = ['name' => $id, 'uuid' => $uuid];
264
-				continue;
265
-			}
266
-			$uuid = $this->groupMapping->getUUIDByDN($dn);
267
-			if ($uuid) {
268
-				$id = $this->groupMapping->getNameByDN($dn);
269
-				$groupList[] = ['name' => $id, 'uuid' => $uuid];
270
-				continue;
271
-			}
272
-			$this->reports[UuidUpdateReport::UNMAPPED][] = new UuidUpdateReport('', $dn, true, UuidUpdateReport::UNMAPPED);
273
-			yield;
274
-		}
275
-		foreach($this->handleUpdatesByList($this->userMapping, $userList) as $_) {
276
-			yield;
277
-		}
278
-		foreach($this->handleUpdatesByList($this->groupMapping, $groupList) as $_) {
279
-			yield;
280
-		}
281
-	}
257
+    protected function handleUpdatesByDN(array $dns): \Generator {
258
+        $userList = $groupList = [];
259
+        while ($dn = array_pop($dns)) {
260
+            $uuid = $this->userMapping->getUUIDByDN($dn);
261
+            if ($uuid) {
262
+                $id = $this->userMapping->getNameByDN($dn);
263
+                $userList[] = ['name' => $id, 'uuid' => $uuid];
264
+                continue;
265
+            }
266
+            $uuid = $this->groupMapping->getUUIDByDN($dn);
267
+            if ($uuid) {
268
+                $id = $this->groupMapping->getNameByDN($dn);
269
+                $groupList[] = ['name' => $id, 'uuid' => $uuid];
270
+                continue;
271
+            }
272
+            $this->reports[UuidUpdateReport::UNMAPPED][] = new UuidUpdateReport('', $dn, true, UuidUpdateReport::UNMAPPED);
273
+            yield;
274
+        }
275
+        foreach($this->handleUpdatesByList($this->userMapping, $userList) as $_) {
276
+            yield;
277
+        }
278
+        foreach($this->handleUpdatesByList($this->groupMapping, $groupList) as $_) {
279
+            yield;
280
+        }
281
+    }
282 282
 
283
-	protected function handleUpdatesByEntryId(array $ids, AbstractMapping $mapping): \Generator {
284
-		$isUser = $mapping instanceof UserMapping;
285
-		$list = [];
286
-		while ($id = array_pop($ids)) {
287
-			if(!$dn = $mapping->getDNByName($id)) {
288
-				$this->reports[UuidUpdateReport::UNMAPPED][] = new UuidUpdateReport($id, '', $isUser, UuidUpdateReport::UNMAPPED);
289
-				yield;
290
-				continue;
291
-			}
292
-			// Since we know it was mapped the UUID is populated
293
-			$uuid = $mapping->getUUIDByDN($dn);
294
-			$list[] = ['name' => $id, 'uuid' => $uuid];
295
-		}
296
-		foreach($this->handleUpdatesByList($mapping, $list) as $_) {
297
-			yield;
298
-		}
299
-	}
283
+    protected function handleUpdatesByEntryId(array $ids, AbstractMapping $mapping): \Generator {
284
+        $isUser = $mapping instanceof UserMapping;
285
+        $list = [];
286
+        while ($id = array_pop($ids)) {
287
+            if(!$dn = $mapping->getDNByName($id)) {
288
+                $this->reports[UuidUpdateReport::UNMAPPED][] = new UuidUpdateReport($id, '', $isUser, UuidUpdateReport::UNMAPPED);
289
+                yield;
290
+                continue;
291
+            }
292
+            // Since we know it was mapped the UUID is populated
293
+            $uuid = $mapping->getUUIDByDN($dn);
294
+            $list[] = ['name' => $id, 'uuid' => $uuid];
295
+        }
296
+        foreach($this->handleUpdatesByList($mapping, $list) as $_) {
297
+            yield;
298
+        }
299
+    }
300 300
 
301
-	protected function handleMappingBasedUpdates(bool $invalidatedOnly): \Generator {
302
-		$limit = 1000;
303
-		/** @var AbstractMapping $mapping*/
304
-		foreach([$this->userMapping, $this->groupMapping] as $mapping) {
305
-			$offset = 0;
306
-			do {
307
-				$list = $mapping->getList($offset, $limit, $invalidatedOnly);
308
-				$offset += $limit;
301
+    protected function handleMappingBasedUpdates(bool $invalidatedOnly): \Generator {
302
+        $limit = 1000;
303
+        /** @var AbstractMapping $mapping*/
304
+        foreach([$this->userMapping, $this->groupMapping] as $mapping) {
305
+            $offset = 0;
306
+            do {
307
+                $list = $mapping->getList($offset, $limit, $invalidatedOnly);
308
+                $offset += $limit;
309 309
 
310
-				foreach($this->handleUpdatesByList($mapping, $list) as $tick) {
311
-					yield; // null, for it only advances progress counter
312
-				}
313
-			} while (count($list) === $limit);
314
-		}
315
-	}
310
+                foreach($this->handleUpdatesByList($mapping, $list) as $tick) {
311
+                    yield; // null, for it only advances progress counter
312
+                }
313
+            } while (count($list) === $limit);
314
+        }
315
+    }
316 316
 
317
-	protected function handleUpdatesByList(AbstractMapping $mapping, array $list): \Generator {
318
-		if ($mapping instanceof UserMapping) {
319
-			$isUser = true;
320
-			$backendProxy = $this->userProxy;
321
-		} else {
322
-			$isUser = false;
323
-			$backendProxy = $this->groupProxy;
324
-		}
317
+    protected function handleUpdatesByList(AbstractMapping $mapping, array $list): \Generator {
318
+        if ($mapping instanceof UserMapping) {
319
+            $isUser = true;
320
+            $backendProxy = $this->userProxy;
321
+        } else {
322
+            $isUser = false;
323
+            $backendProxy = $this->groupProxy;
324
+        }
325 325
 
326
-		foreach ($list as $row) {
327
-			$access = $backendProxy->getLDAPAccess($row['name']);
328
-			if ($access instanceof Access
329
-				&& $dn = $mapping->getDNByName($row['name']))
330
-			{
331
-				if ($uuid = $access->getUUID($dn, $isUser)) {
332
-					if ($uuid !== $row['uuid']) {
333
-						if ($this->dryRun || $mapping->setUUIDbyDN($uuid, $dn)) {
334
-							$this->reports[UuidUpdateReport::UPDATED][]
335
-								= new UuidUpdateReport($row['name'], $dn, $isUser, UuidUpdateReport::UPDATED, $row['uuid'], $uuid);
336
-						} else {
337
-							$this->reports[UuidUpdateReport::UNWRITABLE][]
338
-								= new UuidUpdateReport($row['name'], $dn, $isUser, UuidUpdateReport::UNWRITABLE, $row['uuid'], $uuid);
339
-						}
340
-						$this->logger->info('UUID of {id} was updated from {from} to {to}',
341
-							[
342
-								'appid' => 'user_ldap',
343
-								'id' => $row['name'],
344
-								'from' => $row['uuid'],
345
-								'to' => $uuid,
346
-							]
347
-						);
348
-					}
349
-				} else {
350
-					$this->reports[UuidUpdateReport::UNREADABLE][] = new UuidUpdateReport($row['name'], $dn, $isUser, UuidUpdateReport::UNREADABLE);
351
-				}
352
-			} else {
353
-				$this->reports[UuidUpdateReport::UNKNOWN][] = new UuidUpdateReport($row['name'], '', $isUser, UuidUpdateReport::UNKNOWN);
354
-			}
355
-			yield; // null, for it only advances progress counter
356
-		}
357
-	}
326
+        foreach ($list as $row) {
327
+            $access = $backendProxy->getLDAPAccess($row['name']);
328
+            if ($access instanceof Access
329
+                && $dn = $mapping->getDNByName($row['name']))
330
+            {
331
+                if ($uuid = $access->getUUID($dn, $isUser)) {
332
+                    if ($uuid !== $row['uuid']) {
333
+                        if ($this->dryRun || $mapping->setUUIDbyDN($uuid, $dn)) {
334
+                            $this->reports[UuidUpdateReport::UPDATED][]
335
+                                = new UuidUpdateReport($row['name'], $dn, $isUser, UuidUpdateReport::UPDATED, $row['uuid'], $uuid);
336
+                        } else {
337
+                            $this->reports[UuidUpdateReport::UNWRITABLE][]
338
+                                = new UuidUpdateReport($row['name'], $dn, $isUser, UuidUpdateReport::UNWRITABLE, $row['uuid'], $uuid);
339
+                        }
340
+                        $this->logger->info('UUID of {id} was updated from {from} to {to}',
341
+                            [
342
+                                'appid' => 'user_ldap',
343
+                                'id' => $row['name'],
344
+                                'from' => $row['uuid'],
345
+                                'to' => $uuid,
346
+                            ]
347
+                        );
348
+                    }
349
+                } else {
350
+                    $this->reports[UuidUpdateReport::UNREADABLE][] = new UuidUpdateReport($row['name'], $dn, $isUser, UuidUpdateReport::UNREADABLE);
351
+                }
352
+            } else {
353
+                $this->reports[UuidUpdateReport::UNKNOWN][] = new UuidUpdateReport($row['name'], '', $isUser, UuidUpdateReport::UNKNOWN);
354
+            }
355
+            yield; // null, for it only advances progress counter
356
+        }
357
+    }
358 358
 
359
-	protected function estimateNumberOfUpdates(InputInterface $input): int {
360
-		if ($input->getOption('all')) {
361
-			return $this->userMapping->count() + $this->groupMapping->count();
362
-		} else if ($input->getOption('userId')
363
-			|| $input->getOption('groupId')
364
-			|| $input->getOption('dn')
365
-		) {
366
-			return count($input->getOption('userId'))
367
-				+ count($input->getOption('groupId'))
368
-				+ count($input->getOption('dn'));
369
-		} else {
370
-			return $this->userMapping->countInvalidated() + $this->groupMapping->countInvalidated();
371
-		}
372
-	}
359
+    protected function estimateNumberOfUpdates(InputInterface $input): int {
360
+        if ($input->getOption('all')) {
361
+            return $this->userMapping->count() + $this->groupMapping->count();
362
+        } else if ($input->getOption('userId')
363
+            || $input->getOption('groupId')
364
+            || $input->getOption('dn')
365
+        ) {
366
+            return count($input->getOption('userId'))
367
+                + count($input->getOption('groupId'))
368
+                + count($input->getOption('dn'));
369
+        } else {
370
+            return $this->userMapping->countInvalidated() + $this->groupMapping->countInvalidated();
371
+        }
372
+    }
373 373
 
374 374
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 		$entriesToUpdate = $this->estimateNumberOfUpdates($input);
141 141
 		$progress = new ProgressBar($output);
142 142
 		$progress->start($entriesToUpdate);
143
-		foreach($this->handleUpdates($input) as $_) {
143
+		foreach ($this->handleUpdates($input) as $_) {
144 144
 			$progress->advance();
145 145
 		}
146 146
 		$progress->finish();
@@ -190,9 +190,9 @@  discard block
 block discarded – undo
190 190
 			if ($output->isVerbose()) {
191 191
 				/** @var UuidUpdateReport $report */
192 192
 				foreach ($this->reports[UuidUpdateReport::UNKNOWN] as $report) {
193
-					$output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
193
+					$output->writeln(sprintf('  %s: %s', $report->isUser ? 'User' : 'Group', $report->id));
194 194
 				}
195
-				$output->writeln(PHP_EOL . 'Old users can be removed along with their data per occ user:delete.' . PHP_EOL);
195
+				$output->writeln(PHP_EOL.'Old users can be removed along with their data per occ user:delete.'.PHP_EOL);
196 196
 			}
197 197
 		}
198 198
 
@@ -201,7 +201,7 @@  discard block
 block discarded – undo
201 201
 			if ($output->isVerbose()) {
202 202
 				/** @var UuidUpdateReport $report */
203 203
 				foreach ($this->reports[UuidUpdateReport::UNREADABLE] as $report) {
204
-					$output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
204
+					$output->writeln(sprintf('  %s: %s', $report->isUser ? 'User' : 'Group', $report->id));
205 205
 				}
206 206
 			}
207 207
 		}
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
 			if ($output->isVerbose()) {
212 212
 				/** @var UuidUpdateReport $report */
213 213
 				foreach ($this->reports[UuidUpdateReport::UNWRITABLE] as $report) {
214
-					$output->writeln(sprintf('  %s: %s',$report->isUser ? 'User' : 'Group', $report->id));
214
+					$output->writeln(sprintf('  %s: %s', $report->isUser ? 'User' : 'Group', $report->id));
215 215
 				}
216 216
 			}
217 217
 		}
@@ -219,37 +219,37 @@  discard block
 block discarded – undo
219 219
 
220 220
 	protected function handleUpdates(InputInterface $input): \Generator {
221 221
 		if ($input->getOption('all')) {
222
-			foreach($this->handleMappingBasedUpdates(false) as $_) {
222
+			foreach ($this->handleMappingBasedUpdates(false) as $_) {
223 223
 				yield;
224 224
 			}
225 225
 		} else if ($input->getOption('userId')
226 226
 			|| $input->getOption('groupId')
227 227
 			|| $input->getOption('dn')
228 228
 		) {
229
-			foreach($this->handleUpdatesByUserId($input->getOption('userId')) as $_) {
229
+			foreach ($this->handleUpdatesByUserId($input->getOption('userId')) as $_) {
230 230
 				yield;
231 231
 			}
232
-			foreach($this->handleUpdatesByGroupId($input->getOption('groupId')) as $_) {
232
+			foreach ($this->handleUpdatesByGroupId($input->getOption('groupId')) as $_) {
233 233
 				yield;
234 234
 			}
235
-			foreach($this->handleUpdatesByDN($input->getOption('dn')) as $_) {
235
+			foreach ($this->handleUpdatesByDN($input->getOption('dn')) as $_) {
236 236
 				yield;
237 237
 			}
238 238
 		} else {
239
-			foreach($this->handleMappingBasedUpdates(true) as $_) {
239
+			foreach ($this->handleMappingBasedUpdates(true) as $_) {
240 240
 				yield;
241 241
 			}
242 242
 		}
243 243
 	}
244 244
 
245 245
 	protected function handleUpdatesByUserId(array $userIds): \Generator {
246
-		foreach($this->handleUpdatesByEntryId($userIds, $this->userMapping) as $_) {
246
+		foreach ($this->handleUpdatesByEntryId($userIds, $this->userMapping) as $_) {
247 247
 			yield;
248 248
 		}
249 249
 	}
250 250
 
251 251
 	protected function handleUpdatesByGroupId(array $groupIds): \Generator {
252
-		foreach($this->handleUpdatesByEntryId($groupIds, $this->groupMapping) as $_) {
252
+		foreach ($this->handleUpdatesByEntryId($groupIds, $this->groupMapping) as $_) {
253 253
 			yield;
254 254
 		}
255 255
 	}
@@ -272,10 +272,10 @@  discard block
 block discarded – undo
272 272
 			$this->reports[UuidUpdateReport::UNMAPPED][] = new UuidUpdateReport('', $dn, true, UuidUpdateReport::UNMAPPED);
273 273
 			yield;
274 274
 		}
275
-		foreach($this->handleUpdatesByList($this->userMapping, $userList) as $_) {
275
+		foreach ($this->handleUpdatesByList($this->userMapping, $userList) as $_) {
276 276
 			yield;
277 277
 		}
278
-		foreach($this->handleUpdatesByList($this->groupMapping, $groupList) as $_) {
278
+		foreach ($this->handleUpdatesByList($this->groupMapping, $groupList) as $_) {
279 279
 			yield;
280 280
 		}
281 281
 	}
@@ -284,7 +284,7 @@  discard block
 block discarded – undo
284 284
 		$isUser = $mapping instanceof UserMapping;
285 285
 		$list = [];
286 286
 		while ($id = array_pop($ids)) {
287
-			if(!$dn = $mapping->getDNByName($id)) {
287
+			if (!$dn = $mapping->getDNByName($id)) {
288 288
 				$this->reports[UuidUpdateReport::UNMAPPED][] = new UuidUpdateReport($id, '', $isUser, UuidUpdateReport::UNMAPPED);
289 289
 				yield;
290 290
 				continue;
@@ -293,7 +293,7 @@  discard block
 block discarded – undo
293 293
 			$uuid = $mapping->getUUIDByDN($dn);
294 294
 			$list[] = ['name' => $id, 'uuid' => $uuid];
295 295
 		}
296
-		foreach($this->handleUpdatesByList($mapping, $list) as $_) {
296
+		foreach ($this->handleUpdatesByList($mapping, $list) as $_) {
297 297
 			yield;
298 298
 		}
299 299
 	}
@@ -301,13 +301,13 @@  discard block
 block discarded – undo
301 301
 	protected function handleMappingBasedUpdates(bool $invalidatedOnly): \Generator {
302 302
 		$limit = 1000;
303 303
 		/** @var AbstractMapping $mapping*/
304
-		foreach([$this->userMapping, $this->groupMapping] as $mapping) {
304
+		foreach ([$this->userMapping, $this->groupMapping] as $mapping) {
305 305
 			$offset = 0;
306 306
 			do {
307 307
 				$list = $mapping->getList($offset, $limit, $invalidatedOnly);
308 308
 				$offset += $limit;
309 309
 
310
-				foreach($this->handleUpdatesByList($mapping, $list) as $tick) {
310
+				foreach ($this->handleUpdatesByList($mapping, $list) as $tick) {
311 311
 					yield; // null, for it only advances progress counter
312 312
 				}
313 313
 			} while (count($list) === $limit);
Please login to merge, or discard this patch.
apps/user_ldap/lib/Migration/Version1130Date20211102154716.php 2 patches
Indentation   +214 added lines, -214 removed lines patch added patch discarded remove patch
@@ -39,238 +39,238 @@
 block discarded – undo
39 39
 
40 40
 class Version1130Date20211102154716 extends SimpleMigrationStep {
41 41
 
42
-	/** @var IDBConnection */
43
-	private $dbc;
44
-	/** @var LoggerInterface */
45
-	private $logger;
42
+    /** @var IDBConnection */
43
+    private $dbc;
44
+    /** @var LoggerInterface */
45
+    private $logger;
46 46
 
47
-	public function __construct(IDBConnection $dbc, LoggerInterface $logger) {
48
-		$this->dbc = $dbc;
49
-		$this->logger = $logger;
50
-	}
47
+    public function __construct(IDBConnection $dbc, LoggerInterface $logger) {
48
+        $this->dbc = $dbc;
49
+        $this->logger = $logger;
50
+    }
51 51
 
52
-	public function getName() {
53
-		return 'Adjust LDAP user and group ldap_dn column lengths and add ldap_dn_hash columns';
54
-	}
52
+    public function getName() {
53
+        return 'Adjust LDAP user and group ldap_dn column lengths and add ldap_dn_hash columns';
54
+    }
55 55
 
56
-	public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
57
-		foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) {
58
-			$this->processDuplicateUUIDs($tableName);
59
-		}
56
+    public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
57
+        foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) {
58
+            $this->processDuplicateUUIDs($tableName);
59
+        }
60 60
 
61
-		/** @var ISchemaWrapper $schema */
62
-		$schema = $schemaClosure();
63
-		if ($schema->hasTable('ldap_group_mapping_backup')) {
64
-			// Previous upgrades of a broken release might have left an incomplete
65
-			// ldap_group_mapping_backup table. No need to recreate, but it
66
-			// should be empty.
67
-			// TRUNCATE is not available from Query Builder, but faster than DELETE FROM.
68
-			$sql = $this->dbc->getDatabasePlatform()->getTruncateTableSQL('ldap_group_mapping_backup', false);
69
-			$this->dbc->executeStatement($sql);
70
-		}
71
-	}
61
+        /** @var ISchemaWrapper $schema */
62
+        $schema = $schemaClosure();
63
+        if ($schema->hasTable('ldap_group_mapping_backup')) {
64
+            // Previous upgrades of a broken release might have left an incomplete
65
+            // ldap_group_mapping_backup table. No need to recreate, but it
66
+            // should be empty.
67
+            // TRUNCATE is not available from Query Builder, but faster than DELETE FROM.
68
+            $sql = $this->dbc->getDatabasePlatform()->getTruncateTableSQL('ldap_group_mapping_backup', false);
69
+            $this->dbc->executeStatement($sql);
70
+        }
71
+    }
72 72
 
73
-	/**
74
-	 * @param IOutput $output
75
-	 * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
76
-	 * @param array $options
77
-	 * @return null|ISchemaWrapper
78
-	 */
79
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
80
-		/** @var ISchemaWrapper $schema */
81
-		$schema = $schemaClosure();
73
+    /**
74
+     * @param IOutput $output
75
+     * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
76
+     * @param array $options
77
+     * @return null|ISchemaWrapper
78
+     */
79
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
80
+        /** @var ISchemaWrapper $schema */
81
+        $schema = $schemaClosure();
82 82
 
83
-		$changeSchema = false;
84
-		foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) {
85
-			$table = $schema->getTable($tableName);
86
-			if (!$table->hasColumn('ldap_dn_hash')) {
87
-				$table->addColumn('ldap_dn_hash', Types::STRING, [
88
-					'notnull' => false,
89
-					'length' => 64,
90
-				]);
91
-				$changeSchema = true;
92
-			}
93
-			$column = $table->getColumn('ldap_dn');
94
-			if ($tableName === 'ldap_user_mapping') {
95
-				if ($column->getLength() < 4096) {
96
-					$column->setLength(4096);
97
-					$changeSchema = true;
98
-				}
83
+        $changeSchema = false;
84
+        foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) {
85
+            $table = $schema->getTable($tableName);
86
+            if (!$table->hasColumn('ldap_dn_hash')) {
87
+                $table->addColumn('ldap_dn_hash', Types::STRING, [
88
+                    'notnull' => false,
89
+                    'length' => 64,
90
+                ]);
91
+                $changeSchema = true;
92
+            }
93
+            $column = $table->getColumn('ldap_dn');
94
+            if ($tableName === 'ldap_user_mapping') {
95
+                if ($column->getLength() < 4096) {
96
+                    $column->setLength(4096);
97
+                    $changeSchema = true;
98
+                }
99 99
 
100
-				if ($table->hasIndex('ldap_dn_users')) {
101
-					$table->dropIndex('ldap_dn_users');
102
-					$changeSchema = true;
103
-				}
104
-				if (!$table->hasIndex('ldap_user_dn_hashes')) {
105
-					$table->addUniqueIndex(['ldap_dn_hash'], 'ldap_user_dn_hashes');
106
-					$changeSchema = true;
107
-				}
108
-				if (!$table->hasIndex('ldap_user_directory_uuid')) {
109
-					$table->addUniqueIndex(['directory_uuid'], 'ldap_user_directory_uuid');
110
-					$changeSchema = true;
111
-				}
112
-			} else if (!$schema->hasTable('ldap_group_mapping_backup')) {
113
-				// We need to copy the table twice to be able to change primary key, prepare the backup table
114
-				$table2 = $schema->createTable('ldap_group_mapping_backup');
115
-				$table2->addColumn('ldap_dn', Types::STRING, [
116
-					'notnull' => true,
117
-					'length' => 4096,
118
-					'default' => '',
119
-				]);
120
-				$table2->addColumn('owncloud_name', Types::STRING, [
121
-					'notnull' => true,
122
-					'length' => 64,
123
-					'default' => '',
124
-				]);
125
-				$table2->addColumn('directory_uuid', Types::STRING, [
126
-					'notnull' => true,
127
-					'length' => 255,
128
-					'default' => '',
129
-				]);
130
-				$table2->addColumn('ldap_dn_hash', Types::STRING, [
131
-					'notnull' => false,
132
-					'length' => 64,
133
-				]);
134
-				$table2->setPrimaryKey(['owncloud_name'], 'lgm_backup_primary');
135
-				$changeSchema = true;
136
-			}
137
-		}
100
+                if ($table->hasIndex('ldap_dn_users')) {
101
+                    $table->dropIndex('ldap_dn_users');
102
+                    $changeSchema = true;
103
+                }
104
+                if (!$table->hasIndex('ldap_user_dn_hashes')) {
105
+                    $table->addUniqueIndex(['ldap_dn_hash'], 'ldap_user_dn_hashes');
106
+                    $changeSchema = true;
107
+                }
108
+                if (!$table->hasIndex('ldap_user_directory_uuid')) {
109
+                    $table->addUniqueIndex(['directory_uuid'], 'ldap_user_directory_uuid');
110
+                    $changeSchema = true;
111
+                }
112
+            } else if (!$schema->hasTable('ldap_group_mapping_backup')) {
113
+                // We need to copy the table twice to be able to change primary key, prepare the backup table
114
+                $table2 = $schema->createTable('ldap_group_mapping_backup');
115
+                $table2->addColumn('ldap_dn', Types::STRING, [
116
+                    'notnull' => true,
117
+                    'length' => 4096,
118
+                    'default' => '',
119
+                ]);
120
+                $table2->addColumn('owncloud_name', Types::STRING, [
121
+                    'notnull' => true,
122
+                    'length' => 64,
123
+                    'default' => '',
124
+                ]);
125
+                $table2->addColumn('directory_uuid', Types::STRING, [
126
+                    'notnull' => true,
127
+                    'length' => 255,
128
+                    'default' => '',
129
+                ]);
130
+                $table2->addColumn('ldap_dn_hash', Types::STRING, [
131
+                    'notnull' => false,
132
+                    'length' => 64,
133
+                ]);
134
+                $table2->setPrimaryKey(['owncloud_name'], 'lgm_backup_primary');
135
+                $changeSchema = true;
136
+            }
137
+        }
138 138
 
139
-		return $changeSchema ? $schema : null;
140
-	}
139
+        return $changeSchema ? $schema : null;
140
+    }
141 141
 
142
-	/**
143
-	 * @param IOutput $output
144
-	 * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
145
-	 * @param array $options
146
-	 */
147
-	public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
148
-		$this->handleDNHashes('ldap_group_mapping');
149
-		$this->handleDNHashes('ldap_user_mapping');
150
-	}
142
+    /**
143
+     * @param IOutput $output
144
+     * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
145
+     * @param array $options
146
+     */
147
+    public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
148
+        $this->handleDNHashes('ldap_group_mapping');
149
+        $this->handleDNHashes('ldap_user_mapping');
150
+    }
151 151
 
152
-	protected function handleDNHashes(string $table): void {
153
-		$select = $this->getSelectQuery($table);
154
-		$update = $this->getUpdateQuery($table);
152
+    protected function handleDNHashes(string $table): void {
153
+        $select = $this->getSelectQuery($table);
154
+        $update = $this->getUpdateQuery($table);
155 155
 
156
-		$result = $select->executeQuery();
157
-		while ($row = $result->fetch()) {
158
-			$dnHash = hash('sha256', $row['ldap_dn'], false);
159
-			$update->setParameter('name', $row['owncloud_name']);
160
-			$update->setParameter('dn_hash', $dnHash);
161
-			try {
162
-				$update->executeStatement();
163
-			} catch (Exception $e) {
164
-				$this->logger->error('Failed to add hash "{dnHash}" ("{name}" of {table})',
165
-					[
166
-						'app' => 'user_ldap',
167
-						'name' => $row['owncloud_name'],
168
-						'dnHash' => $dnHash,
169
-						'table' => $table,
170
-						'exception' => $e,
171
-					]
172
-				);
173
-			}
174
-		}
175
-		$result->closeCursor();
176
-	}
156
+        $result = $select->executeQuery();
157
+        while ($row = $result->fetch()) {
158
+            $dnHash = hash('sha256', $row['ldap_dn'], false);
159
+            $update->setParameter('name', $row['owncloud_name']);
160
+            $update->setParameter('dn_hash', $dnHash);
161
+            try {
162
+                $update->executeStatement();
163
+            } catch (Exception $e) {
164
+                $this->logger->error('Failed to add hash "{dnHash}" ("{name}" of {table})',
165
+                    [
166
+                        'app' => 'user_ldap',
167
+                        'name' => $row['owncloud_name'],
168
+                        'dnHash' => $dnHash,
169
+                        'table' => $table,
170
+                        'exception' => $e,
171
+                    ]
172
+                );
173
+            }
174
+        }
175
+        $result->closeCursor();
176
+    }
177 177
 
178
-	protected function getSelectQuery(string $table): IQueryBuilder {
179
-		$qb = $this->dbc->getQueryBuilder();
180
-		$qb->select('owncloud_name', 'ldap_dn', 'ldap_dn_hash')
181
-			->from($table)
182
-			->where($qb->expr()->isNull('ldap_dn_hash'));
183
-		return $qb;
184
-	}
178
+    protected function getSelectQuery(string $table): IQueryBuilder {
179
+        $qb = $this->dbc->getQueryBuilder();
180
+        $qb->select('owncloud_name', 'ldap_dn', 'ldap_dn_hash')
181
+            ->from($table)
182
+            ->where($qb->expr()->isNull('ldap_dn_hash'));
183
+        return $qb;
184
+    }
185 185
 
186
-	protected function getUpdateQuery(string $table): IQueryBuilder {
187
-		$qb = $this->dbc->getQueryBuilder();
188
-		$qb->update($table)
189
-			->set('ldap_dn_hash', $qb->createParameter('dn_hash'))
190
-			->where($qb->expr()->eq('owncloud_name', $qb->createParameter('name')));
191
-		return $qb;
192
-	}
186
+    protected function getUpdateQuery(string $table): IQueryBuilder {
187
+        $qb = $this->dbc->getQueryBuilder();
188
+        $qb->update($table)
189
+            ->set('ldap_dn_hash', $qb->createParameter('dn_hash'))
190
+            ->where($qb->expr()->eq('owncloud_name', $qb->createParameter('name')));
191
+        return $qb;
192
+    }
193 193
 
194
-	/**
195
-	 * @throws Exception
196
-	 */
197
-	protected function processDuplicateUUIDs(string $table): void {
198
-		$uuids = $this->getDuplicatedUuids($table);
199
-		$idsWithUuidToInvalidate = [];
200
-		foreach ($uuids as $uuid) {
201
-			array_push($idsWithUuidToInvalidate, ...$this->getNextcloudIdsByUuid($table, $uuid));
202
-		}
203
-		$this->invalidateUuids($table, $idsWithUuidToInvalidate);
204
-	}
194
+    /**
195
+     * @throws Exception
196
+     */
197
+    protected function processDuplicateUUIDs(string $table): void {
198
+        $uuids = $this->getDuplicatedUuids($table);
199
+        $idsWithUuidToInvalidate = [];
200
+        foreach ($uuids as $uuid) {
201
+            array_push($idsWithUuidToInvalidate, ...$this->getNextcloudIdsByUuid($table, $uuid));
202
+        }
203
+        $this->invalidateUuids($table, $idsWithUuidToInvalidate);
204
+    }
205 205
 
206
-	/**
207
-	 * @throws Exception
208
-	 */
209
-	protected function invalidateUuids(string $table, array $idList): void {
210
-		$update = $this->dbc->getQueryBuilder();
211
-		$update->update($table)
212
-			->set('directory_uuid', $update->createParameter('invalidatedUuid'))
213
-			->where($update->expr()->eq('owncloud_name', $update->createParameter('nextcloudId')));
206
+    /**
207
+     * @throws Exception
208
+     */
209
+    protected function invalidateUuids(string $table, array $idList): void {
210
+        $update = $this->dbc->getQueryBuilder();
211
+        $update->update($table)
212
+            ->set('directory_uuid', $update->createParameter('invalidatedUuid'))
213
+            ->where($update->expr()->eq('owncloud_name', $update->createParameter('nextcloudId')));
214 214
 
215
-		while ($nextcloudId = array_shift($idList)) {
216
-			$update->setParameter('nextcloudId', $nextcloudId);
217
-			$update->setParameter('invalidatedUuid', 'invalidated_' . \bin2hex(\random_bytes(6)));
218
-			try {
219
-				$update->executeStatement();
220
-				$this->logger->warning(
221
-					'LDAP user or group with ID {nid} has a duplicated UUID value which therefore was invalidated. You may double-check your LDAP configuration and trigger an update of the UUID.',
222
-					[
223
-						'app' => 'user_ldap',
224
-						'nid' => $nextcloudId,
225
-					]
226
-				);
227
-			} catch (Exception $e) {
228
-				// Catch possible, but unlikely duplications if new invalidated errors.
229
-				// There is the theoretical chance of an infinity loop is, when
230
-				// the constraint violation has a different background. I cannot
231
-				// think of one at the moment.
232
-				if ($e->getReason() !== Exception::REASON_CONSTRAINT_VIOLATION) {
233
-					throw $e;
234
-				}
235
-				$idList[] = $nextcloudId;
236
-			}
237
-		}
238
-	}
215
+        while ($nextcloudId = array_shift($idList)) {
216
+            $update->setParameter('nextcloudId', $nextcloudId);
217
+            $update->setParameter('invalidatedUuid', 'invalidated_' . \bin2hex(\random_bytes(6)));
218
+            try {
219
+                $update->executeStatement();
220
+                $this->logger->warning(
221
+                    'LDAP user or group with ID {nid} has a duplicated UUID value which therefore was invalidated. You may double-check your LDAP configuration and trigger an update of the UUID.',
222
+                    [
223
+                        'app' => 'user_ldap',
224
+                        'nid' => $nextcloudId,
225
+                    ]
226
+                );
227
+            } catch (Exception $e) {
228
+                // Catch possible, but unlikely duplications if new invalidated errors.
229
+                // There is the theoretical chance of an infinity loop is, when
230
+                // the constraint violation has a different background. I cannot
231
+                // think of one at the moment.
232
+                if ($e->getReason() !== Exception::REASON_CONSTRAINT_VIOLATION) {
233
+                    throw $e;
234
+                }
235
+                $idList[] = $nextcloudId;
236
+            }
237
+        }
238
+    }
239 239
 
240
-	/**
241
-	 * @throws \OCP\DB\Exception
242
-	 * @return array<string>
243
-	 */
244
-	protected function getNextcloudIdsByUuid(string $table, string $uuid): array {
245
-		$select = $this->dbc->getQueryBuilder();
246
-		$select->select('owncloud_name')
247
-			->from($table)
248
-			->where($select->expr()->eq('directory_uuid', $select->createNamedParameter($uuid)));
240
+    /**
241
+     * @throws \OCP\DB\Exception
242
+     * @return array<string>
243
+     */
244
+    protected function getNextcloudIdsByUuid(string $table, string $uuid): array {
245
+        $select = $this->dbc->getQueryBuilder();
246
+        $select->select('owncloud_name')
247
+            ->from($table)
248
+            ->where($select->expr()->eq('directory_uuid', $select->createNamedParameter($uuid)));
249 249
 
250
-		$result = $select->executeQuery();
251
-		$idList = [];
252
-		while ($id = $result->fetchOne()) {
253
-			$idList[] = $id;
254
-		}
255
-		$result->closeCursor();
256
-		return $idList;
257
-	}
250
+        $result = $select->executeQuery();
251
+        $idList = [];
252
+        while ($id = $result->fetchOne()) {
253
+            $idList[] = $id;
254
+        }
255
+        $result->closeCursor();
256
+        return $idList;
257
+    }
258 258
 
259
-	/**
260
-	 * @return Generator<string>
261
-	 * @throws \OCP\DB\Exception
262
-	 */
263
-	protected function getDuplicatedUuids(string $table): Generator{
264
-		$select = $this->dbc->getQueryBuilder();
265
-		$select->select('directory_uuid')
266
-			->from($table)
267
-			->groupBy('directory_uuid')
268
-			->having($select->expr()->gt($select->func()->count('owncloud_name'), $select->createNamedParameter(1)));
259
+    /**
260
+     * @return Generator<string>
261
+     * @throws \OCP\DB\Exception
262
+     */
263
+    protected function getDuplicatedUuids(string $table): Generator{
264
+        $select = $this->dbc->getQueryBuilder();
265
+        $select->select('directory_uuid')
266
+            ->from($table)
267
+            ->groupBy('directory_uuid')
268
+            ->having($select->expr()->gt($select->func()->count('owncloud_name'), $select->createNamedParameter(1)));
269 269
 
270
-		$result = $select->executeQuery();
271
-		while ($uuid = $result->fetchOne()) {
272
-			yield $uuid;
273
-		}
274
-		$result->closeCursor();
275
-	}
270
+        $result = $select->executeQuery();
271
+        while ($uuid = $result->fetchOne()) {
272
+            yield $uuid;
273
+        }
274
+        $result->closeCursor();
275
+    }
276 276
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -214,7 +214,7 @@
 block discarded – undo
214 214
 
215 215
 		while ($nextcloudId = array_shift($idList)) {
216 216
 			$update->setParameter('nextcloudId', $nextcloudId);
217
-			$update->setParameter('invalidatedUuid', 'invalidated_' . \bin2hex(\random_bytes(6)));
217
+			$update->setParameter('invalidatedUuid', 'invalidated_'.\bin2hex(\random_bytes(6)));
218 218
 			try {
219 219
 				$update->executeStatement();
220 220
 				$this->logger->warning(
Please login to merge, or discard this patch.
apps/user_ldap/composer/composer/autoload_static.php 1 patch
Spacing   +84 added lines, -84 removed lines patch added patch discarded remove patch
@@ -6,103 +6,103 @@
 block discarded – undo
6 6
 
7 7
 class ComposerStaticInitUser_LDAP
8 8
 {
9
-    public static $prefixLengthsPsr4 = array (
9
+    public static $prefixLengthsPsr4 = array(
10 10
         'O' => 
11
-        array (
11
+        array(
12 12
             'OCA\\User_LDAP\\' => 14,
13 13
         ),
14 14
     );
15 15
 
16
-    public static $prefixDirsPsr4 = array (
16
+    public static $prefixDirsPsr4 = array(
17 17
         'OCA\\User_LDAP\\' => 
18
-        array (
19
-            0 => __DIR__ . '/..' . '/../lib',
18
+        array(
19
+            0 => __DIR__.'/..'.'/../lib',
20 20
         ),
21 21
     );
22 22
 
23
-    public static $classMap = array (
24
-        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
25
-        'OCA\\User_LDAP\\Access' => __DIR__ . '/..' . '/../lib/Access.php',
26
-        'OCA\\User_LDAP\\AccessFactory' => __DIR__ . '/..' . '/../lib/AccessFactory.php',
27
-        'OCA\\User_LDAP\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
28
-        'OCA\\User_LDAP\\BackendUtility' => __DIR__ . '/..' . '/../lib/BackendUtility.php',
29
-        'OCA\\User_LDAP\\Command\\CheckUser' => __DIR__ . '/..' . '/../lib/Command/CheckUser.php',
30
-        'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => __DIR__ . '/..' . '/../lib/Command/CreateEmptyConfig.php',
31
-        'OCA\\User_LDAP\\Command\\DeleteConfig' => __DIR__ . '/..' . '/../lib/Command/DeleteConfig.php',
32
-        'OCA\\User_LDAP\\Command\\ResetUser' => __DIR__ . '/..' . '/../lib/Command/ResetUser.php',
33
-        'OCA\\User_LDAP\\Command\\Search' => __DIR__ . '/..' . '/../lib/Command/Search.php',
34
-        'OCA\\User_LDAP\\Command\\SetConfig' => __DIR__ . '/..' . '/../lib/Command/SetConfig.php',
35
-        'OCA\\User_LDAP\\Command\\ShowConfig' => __DIR__ . '/..' . '/../lib/Command/ShowConfig.php',
36
-        'OCA\\User_LDAP\\Command\\ShowRemnants' => __DIR__ . '/..' . '/../lib/Command/ShowRemnants.php',
37
-        'OCA\\User_LDAP\\Command\\TestConfig' => __DIR__ . '/..' . '/../lib/Command/TestConfig.php',
38
-        'OCA\\User_LDAP\\Command\\UpdateUUID' => __DIR__ . '/..' . '/../lib/Command/UpdateUUID.php',
39
-        'OCA\\User_LDAP\\Configuration' => __DIR__ . '/..' . '/../lib/Configuration.php',
40
-        'OCA\\User_LDAP\\Connection' => __DIR__ . '/..' . '/../lib/Connection.php',
41
-        'OCA\\User_LDAP\\ConnectionFactory' => __DIR__ . '/..' . '/../lib/ConnectionFactory.php',
42
-        'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__ . '/..' . '/../lib/Controller/ConfigAPIController.php',
43
-        'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__ . '/..' . '/../lib/Controller/RenewPasswordController.php',
44
-        'OCA\\User_LDAP\\Events\\GroupBackendRegistered' => __DIR__ . '/..' . '/../lib/Events/GroupBackendRegistered.php',
45
-        'OCA\\User_LDAP\\Events\\UserBackendRegistered' => __DIR__ . '/..' . '/../lib/Events/UserBackendRegistered.php',
46
-        'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => __DIR__ . '/..' . '/../lib/Exceptions/AttributeNotSet.php',
47
-        'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => __DIR__ . '/..' . '/../lib/Exceptions/ConstraintViolationException.php',
48
-        'OCA\\User_LDAP\\Exceptions\\NoMoreResults' => __DIR__ . '/..' . '/../lib/Exceptions/NoMoreResults.php',
49
-        'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => __DIR__ . '/..' . '/../lib/Exceptions/NotOnLDAP.php',
50
-        'OCA\\User_LDAP\\FilesystemHelper' => __DIR__ . '/..' . '/../lib/FilesystemHelper.php',
51
-        'OCA\\User_LDAP\\GroupPluginManager' => __DIR__ . '/..' . '/../lib/GroupPluginManager.php',
52
-        'OCA\\User_LDAP\\Group_LDAP' => __DIR__ . '/..' . '/../lib/Group_LDAP.php',
53
-        'OCA\\User_LDAP\\Group_Proxy' => __DIR__ . '/..' . '/../lib/Group_Proxy.php',
54
-        'OCA\\User_LDAP\\Handler\\ExtStorageConfigHandler' => __DIR__ . '/..' . '/../lib/Handler/ExtStorageConfigHandler.php',
55
-        'OCA\\User_LDAP\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
56
-        'OCA\\User_LDAP\\IGroupLDAP' => __DIR__ . '/..' . '/../lib/IGroupLDAP.php',
57
-        'OCA\\User_LDAP\\ILDAPGroupPlugin' => __DIR__ . '/..' . '/../lib/ILDAPGroupPlugin.php',
58
-        'OCA\\User_LDAP\\ILDAPUserPlugin' => __DIR__ . '/..' . '/../lib/ILDAPUserPlugin.php',
59
-        'OCA\\User_LDAP\\ILDAPWrapper' => __DIR__ . '/..' . '/../lib/ILDAPWrapper.php',
60
-        'OCA\\User_LDAP\\IUserLDAP' => __DIR__ . '/..' . '/../lib/IUserLDAP.php',
61
-        'OCA\\User_LDAP\\Jobs\\CleanUp' => __DIR__ . '/..' . '/../lib/Jobs/CleanUp.php',
62
-        'OCA\\User_LDAP\\Jobs\\Sync' => __DIR__ . '/..' . '/../lib/Jobs/Sync.php',
63
-        'OCA\\User_LDAP\\Jobs\\UpdateGroups' => __DIR__ . '/..' . '/../lib/Jobs/UpdateGroups.php',
64
-        'OCA\\User_LDAP\\LDAP' => __DIR__ . '/..' . '/../lib/LDAP.php',
65
-        'OCA\\User_LDAP\\LDAPProvider' => __DIR__ . '/..' . '/../lib/LDAPProvider.php',
66
-        'OCA\\User_LDAP\\LDAPProviderFactory' => __DIR__ . '/..' . '/../lib/LDAPProviderFactory.php',
67
-        'OCA\\User_LDAP\\LDAPUtility' => __DIR__ . '/..' . '/../lib/LDAPUtility.php',
68
-        'OCA\\User_LDAP\\Mapping\\AbstractMapping' => __DIR__ . '/..' . '/../lib/Mapping/AbstractMapping.php',
69
-        'OCA\\User_LDAP\\Mapping\\GroupMapping' => __DIR__ . '/..' . '/../lib/Mapping/GroupMapping.php',
70
-        'OCA\\User_LDAP\\Mapping\\UserMapping' => __DIR__ . '/..' . '/../lib/Mapping/UserMapping.php',
71
-        'OCA\\User_LDAP\\Migration\\GroupMappingMigration' => __DIR__ . '/..' . '/../lib/Migration/GroupMappingMigration.php',
72
-        'OCA\\User_LDAP\\Migration\\RemoveRefreshTime' => __DIR__ . '/..' . '/../lib/Migration/RemoveRefreshTime.php',
73
-        'OCA\\User_LDAP\\Migration\\SetDefaultProvider' => __DIR__ . '/..' . '/../lib/Migration/SetDefaultProvider.php',
74
-        'OCA\\User_LDAP\\Migration\\UUIDFix' => __DIR__ . '/..' . '/../lib/Migration/UUIDFix.php',
75
-        'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => __DIR__ . '/..' . '/../lib/Migration/UUIDFixGroup.php',
76
-        'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => __DIR__ . '/..' . '/../lib/Migration/UUIDFixInsert.php',
77
-        'OCA\\User_LDAP\\Migration\\UUIDFixUser' => __DIR__ . '/..' . '/../lib/Migration/UUIDFixUser.php',
78
-        'OCA\\User_LDAP\\Migration\\UnsetDefaultProvider' => __DIR__ . '/..' . '/../lib/Migration/UnsetDefaultProvider.php',
79
-        'OCA\\User_LDAP\\Migration\\Version1010Date20200630192842' => __DIR__ . '/..' . '/../lib/Migration/Version1010Date20200630192842.php',
80
-        'OCA\\User_LDAP\\Migration\\Version1120Date20210917155206' => __DIR__ . '/..' . '/../lib/Migration/Version1120Date20210917155206.php',
81
-        'OCA\\User_LDAP\\Migration\\Version1130Date20211102154716' => __DIR__ . '/..' . '/../lib/Migration/Version1130Date20211102154716.php',
82
-        'OCA\\User_LDAP\\Migration\\Version1130Date20220110154717' => __DIR__ . '/..' . '/../lib/Migration/Version1130Date20220110154717.php',
83
-        'OCA\\User_LDAP\\Migration\\Version1130Date20220110154718' => __DIR__ . '/..' . '/../lib/Migration/Version1130Date20220110154718.php',
84
-        'OCA\\User_LDAP\\Migration\\Version1130Date20220110154719' => __DIR__ . '/..' . '/../lib/Migration/Version1130Date20220110154719.php',
85
-        'OCA\\User_LDAP\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
86
-        'OCA\\User_LDAP\\PagedResults\\IAdapter' => __DIR__ . '/..' . '/../lib/PagedResults/IAdapter.php',
87
-        'OCA\\User_LDAP\\PagedResults\\Php73' => __DIR__ . '/..' . '/../lib/PagedResults/Php73.php',
88
-        'OCA\\User_LDAP\\PagedResults\\TLinkId' => __DIR__ . '/..' . '/../lib/PagedResults/TLinkId.php',
89
-        'OCA\\User_LDAP\\Proxy' => __DIR__ . '/..' . '/../lib/Proxy.php',
90
-        'OCA\\User_LDAP\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
91
-        'OCA\\User_LDAP\\Settings\\Section' => __DIR__ . '/..' . '/../lib/Settings/Section.php',
92
-        'OCA\\User_LDAP\\UserPluginManager' => __DIR__ . '/..' . '/../lib/UserPluginManager.php',
93
-        'OCA\\User_LDAP\\User\\DeletedUsersIndex' => __DIR__ . '/..' . '/../lib/User/DeletedUsersIndex.php',
94
-        'OCA\\User_LDAP\\User\\Manager' => __DIR__ . '/..' . '/../lib/User/Manager.php',
95
-        'OCA\\User_LDAP\\User\\OfflineUser' => __DIR__ . '/..' . '/../lib/User/OfflineUser.php',
96
-        'OCA\\User_LDAP\\User\\User' => __DIR__ . '/..' . '/../lib/User/User.php',
97
-        'OCA\\User_LDAP\\User_LDAP' => __DIR__ . '/..' . '/../lib/User_LDAP.php',
98
-        'OCA\\User_LDAP\\User_Proxy' => __DIR__ . '/..' . '/../lib/User_Proxy.php',
99
-        'OCA\\User_LDAP\\Wizard' => __DIR__ . '/..' . '/../lib/Wizard.php',
100
-        'OCA\\User_LDAP\\WizardResult' => __DIR__ . '/..' . '/../lib/WizardResult.php',
23
+    public static $classMap = array(
24
+        'Composer\\InstalledVersions' => __DIR__.'/..'.'/composer/InstalledVersions.php',
25
+        'OCA\\User_LDAP\\Access' => __DIR__.'/..'.'/../lib/Access.php',
26
+        'OCA\\User_LDAP\\AccessFactory' => __DIR__.'/..'.'/../lib/AccessFactory.php',
27
+        'OCA\\User_LDAP\\AppInfo\\Application' => __DIR__.'/..'.'/../lib/AppInfo/Application.php',
28
+        'OCA\\User_LDAP\\BackendUtility' => __DIR__.'/..'.'/../lib/BackendUtility.php',
29
+        'OCA\\User_LDAP\\Command\\CheckUser' => __DIR__.'/..'.'/../lib/Command/CheckUser.php',
30
+        'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => __DIR__.'/..'.'/../lib/Command/CreateEmptyConfig.php',
31
+        'OCA\\User_LDAP\\Command\\DeleteConfig' => __DIR__.'/..'.'/../lib/Command/DeleteConfig.php',
32
+        'OCA\\User_LDAP\\Command\\ResetUser' => __DIR__.'/..'.'/../lib/Command/ResetUser.php',
33
+        'OCA\\User_LDAP\\Command\\Search' => __DIR__.'/..'.'/../lib/Command/Search.php',
34
+        'OCA\\User_LDAP\\Command\\SetConfig' => __DIR__.'/..'.'/../lib/Command/SetConfig.php',
35
+        'OCA\\User_LDAP\\Command\\ShowConfig' => __DIR__.'/..'.'/../lib/Command/ShowConfig.php',
36
+        'OCA\\User_LDAP\\Command\\ShowRemnants' => __DIR__.'/..'.'/../lib/Command/ShowRemnants.php',
37
+        'OCA\\User_LDAP\\Command\\TestConfig' => __DIR__.'/..'.'/../lib/Command/TestConfig.php',
38
+        'OCA\\User_LDAP\\Command\\UpdateUUID' => __DIR__.'/..'.'/../lib/Command/UpdateUUID.php',
39
+        'OCA\\User_LDAP\\Configuration' => __DIR__.'/..'.'/../lib/Configuration.php',
40
+        'OCA\\User_LDAP\\Connection' => __DIR__.'/..'.'/../lib/Connection.php',
41
+        'OCA\\User_LDAP\\ConnectionFactory' => __DIR__.'/..'.'/../lib/ConnectionFactory.php',
42
+        'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__.'/..'.'/../lib/Controller/ConfigAPIController.php',
43
+        'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__.'/..'.'/../lib/Controller/RenewPasswordController.php',
44
+        'OCA\\User_LDAP\\Events\\GroupBackendRegistered' => __DIR__.'/..'.'/../lib/Events/GroupBackendRegistered.php',
45
+        'OCA\\User_LDAP\\Events\\UserBackendRegistered' => __DIR__.'/..'.'/../lib/Events/UserBackendRegistered.php',
46
+        'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => __DIR__.'/..'.'/../lib/Exceptions/AttributeNotSet.php',
47
+        'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => __DIR__.'/..'.'/../lib/Exceptions/ConstraintViolationException.php',
48
+        'OCA\\User_LDAP\\Exceptions\\NoMoreResults' => __DIR__.'/..'.'/../lib/Exceptions/NoMoreResults.php',
49
+        'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => __DIR__.'/..'.'/../lib/Exceptions/NotOnLDAP.php',
50
+        'OCA\\User_LDAP\\FilesystemHelper' => __DIR__.'/..'.'/../lib/FilesystemHelper.php',
51
+        'OCA\\User_LDAP\\GroupPluginManager' => __DIR__.'/..'.'/../lib/GroupPluginManager.php',
52
+        'OCA\\User_LDAP\\Group_LDAP' => __DIR__.'/..'.'/../lib/Group_LDAP.php',
53
+        'OCA\\User_LDAP\\Group_Proxy' => __DIR__.'/..'.'/../lib/Group_Proxy.php',
54
+        'OCA\\User_LDAP\\Handler\\ExtStorageConfigHandler' => __DIR__.'/..'.'/../lib/Handler/ExtStorageConfigHandler.php',
55
+        'OCA\\User_LDAP\\Helper' => __DIR__.'/..'.'/../lib/Helper.php',
56
+        'OCA\\User_LDAP\\IGroupLDAP' => __DIR__.'/..'.'/../lib/IGroupLDAP.php',
57
+        'OCA\\User_LDAP\\ILDAPGroupPlugin' => __DIR__.'/..'.'/../lib/ILDAPGroupPlugin.php',
58
+        'OCA\\User_LDAP\\ILDAPUserPlugin' => __DIR__.'/..'.'/../lib/ILDAPUserPlugin.php',
59
+        'OCA\\User_LDAP\\ILDAPWrapper' => __DIR__.'/..'.'/../lib/ILDAPWrapper.php',
60
+        'OCA\\User_LDAP\\IUserLDAP' => __DIR__.'/..'.'/../lib/IUserLDAP.php',
61
+        'OCA\\User_LDAP\\Jobs\\CleanUp' => __DIR__.'/..'.'/../lib/Jobs/CleanUp.php',
62
+        'OCA\\User_LDAP\\Jobs\\Sync' => __DIR__.'/..'.'/../lib/Jobs/Sync.php',
63
+        'OCA\\User_LDAP\\Jobs\\UpdateGroups' => __DIR__.'/..'.'/../lib/Jobs/UpdateGroups.php',
64
+        'OCA\\User_LDAP\\LDAP' => __DIR__.'/..'.'/../lib/LDAP.php',
65
+        'OCA\\User_LDAP\\LDAPProvider' => __DIR__.'/..'.'/../lib/LDAPProvider.php',
66
+        'OCA\\User_LDAP\\LDAPProviderFactory' => __DIR__.'/..'.'/../lib/LDAPProviderFactory.php',
67
+        'OCA\\User_LDAP\\LDAPUtility' => __DIR__.'/..'.'/../lib/LDAPUtility.php',
68
+        'OCA\\User_LDAP\\Mapping\\AbstractMapping' => __DIR__.'/..'.'/../lib/Mapping/AbstractMapping.php',
69
+        'OCA\\User_LDAP\\Mapping\\GroupMapping' => __DIR__.'/..'.'/../lib/Mapping/GroupMapping.php',
70
+        'OCA\\User_LDAP\\Mapping\\UserMapping' => __DIR__.'/..'.'/../lib/Mapping/UserMapping.php',
71
+        'OCA\\User_LDAP\\Migration\\GroupMappingMigration' => __DIR__.'/..'.'/../lib/Migration/GroupMappingMigration.php',
72
+        'OCA\\User_LDAP\\Migration\\RemoveRefreshTime' => __DIR__.'/..'.'/../lib/Migration/RemoveRefreshTime.php',
73
+        'OCA\\User_LDAP\\Migration\\SetDefaultProvider' => __DIR__.'/..'.'/../lib/Migration/SetDefaultProvider.php',
74
+        'OCA\\User_LDAP\\Migration\\UUIDFix' => __DIR__.'/..'.'/../lib/Migration/UUIDFix.php',
75
+        'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => __DIR__.'/..'.'/../lib/Migration/UUIDFixGroup.php',
76
+        'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => __DIR__.'/..'.'/../lib/Migration/UUIDFixInsert.php',
77
+        'OCA\\User_LDAP\\Migration\\UUIDFixUser' => __DIR__.'/..'.'/../lib/Migration/UUIDFixUser.php',
78
+        'OCA\\User_LDAP\\Migration\\UnsetDefaultProvider' => __DIR__.'/..'.'/../lib/Migration/UnsetDefaultProvider.php',
79
+        'OCA\\User_LDAP\\Migration\\Version1010Date20200630192842' => __DIR__.'/..'.'/../lib/Migration/Version1010Date20200630192842.php',
80
+        'OCA\\User_LDAP\\Migration\\Version1120Date20210917155206' => __DIR__.'/..'.'/../lib/Migration/Version1120Date20210917155206.php',
81
+        'OCA\\User_LDAP\\Migration\\Version1130Date20211102154716' => __DIR__.'/..'.'/../lib/Migration/Version1130Date20211102154716.php',
82
+        'OCA\\User_LDAP\\Migration\\Version1130Date20220110154717' => __DIR__.'/..'.'/../lib/Migration/Version1130Date20220110154717.php',
83
+        'OCA\\User_LDAP\\Migration\\Version1130Date20220110154718' => __DIR__.'/..'.'/../lib/Migration/Version1130Date20220110154718.php',
84
+        'OCA\\User_LDAP\\Migration\\Version1130Date20220110154719' => __DIR__.'/..'.'/../lib/Migration/Version1130Date20220110154719.php',
85
+        'OCA\\User_LDAP\\Notification\\Notifier' => __DIR__.'/..'.'/../lib/Notification/Notifier.php',
86
+        'OCA\\User_LDAP\\PagedResults\\IAdapter' => __DIR__.'/..'.'/../lib/PagedResults/IAdapter.php',
87
+        'OCA\\User_LDAP\\PagedResults\\Php73' => __DIR__.'/..'.'/../lib/PagedResults/Php73.php',
88
+        'OCA\\User_LDAP\\PagedResults\\TLinkId' => __DIR__.'/..'.'/../lib/PagedResults/TLinkId.php',
89
+        'OCA\\User_LDAP\\Proxy' => __DIR__.'/..'.'/../lib/Proxy.php',
90
+        'OCA\\User_LDAP\\Settings\\Admin' => __DIR__.'/..'.'/../lib/Settings/Admin.php',
91
+        'OCA\\User_LDAP\\Settings\\Section' => __DIR__.'/..'.'/../lib/Settings/Section.php',
92
+        'OCA\\User_LDAP\\UserPluginManager' => __DIR__.'/..'.'/../lib/UserPluginManager.php',
93
+        'OCA\\User_LDAP\\User\\DeletedUsersIndex' => __DIR__.'/..'.'/../lib/User/DeletedUsersIndex.php',
94
+        'OCA\\User_LDAP\\User\\Manager' => __DIR__.'/..'.'/../lib/User/Manager.php',
95
+        'OCA\\User_LDAP\\User\\OfflineUser' => __DIR__.'/..'.'/../lib/User/OfflineUser.php',
96
+        'OCA\\User_LDAP\\User\\User' => __DIR__.'/..'.'/../lib/User/User.php',
97
+        'OCA\\User_LDAP\\User_LDAP' => __DIR__.'/..'.'/../lib/User_LDAP.php',
98
+        'OCA\\User_LDAP\\User_Proxy' => __DIR__.'/..'.'/../lib/User_Proxy.php',
99
+        'OCA\\User_LDAP\\Wizard' => __DIR__.'/..'.'/../lib/Wizard.php',
100
+        'OCA\\User_LDAP\\WizardResult' => __DIR__.'/..'.'/../lib/WizardResult.php',
101 101
     );
102 102
 
103 103
     public static function getInitializer(ClassLoader $loader)
104 104
     {
105
-        return \Closure::bind(function () use ($loader) {
105
+        return \Closure::bind(function() use ($loader) {
106 106
             $loader->prefixLengthsPsr4 = ComposerStaticInitUser_LDAP::$prefixLengthsPsr4;
107 107
             $loader->prefixDirsPsr4 = ComposerStaticInitUser_LDAP::$prefixDirsPsr4;
108 108
             $loader->classMap = ComposerStaticInitUser_LDAP::$classMap;
Please login to merge, or discard this patch.
apps/user_ldap/composer/composer/autoload_classmap.php 1 patch
Spacing   +77 added lines, -77 removed lines patch added patch discarded remove patch
@@ -6,81 +6,81 @@
 block discarded – undo
6 6
 $baseDir = $vendorDir;
7 7
 
8 8
 return array(
9
-    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
10
-    'OCA\\User_LDAP\\Access' => $baseDir . '/../lib/Access.php',
11
-    'OCA\\User_LDAP\\AccessFactory' => $baseDir . '/../lib/AccessFactory.php',
12
-    'OCA\\User_LDAP\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
13
-    'OCA\\User_LDAP\\BackendUtility' => $baseDir . '/../lib/BackendUtility.php',
14
-    'OCA\\User_LDAP\\Command\\CheckUser' => $baseDir . '/../lib/Command/CheckUser.php',
15
-    'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => $baseDir . '/../lib/Command/CreateEmptyConfig.php',
16
-    'OCA\\User_LDAP\\Command\\DeleteConfig' => $baseDir . '/../lib/Command/DeleteConfig.php',
17
-    'OCA\\User_LDAP\\Command\\ResetUser' => $baseDir . '/../lib/Command/ResetUser.php',
18
-    'OCA\\User_LDAP\\Command\\Search' => $baseDir . '/../lib/Command/Search.php',
19
-    'OCA\\User_LDAP\\Command\\SetConfig' => $baseDir . '/../lib/Command/SetConfig.php',
20
-    'OCA\\User_LDAP\\Command\\ShowConfig' => $baseDir . '/../lib/Command/ShowConfig.php',
21
-    'OCA\\User_LDAP\\Command\\ShowRemnants' => $baseDir . '/../lib/Command/ShowRemnants.php',
22
-    'OCA\\User_LDAP\\Command\\TestConfig' => $baseDir . '/../lib/Command/TestConfig.php',
23
-    'OCA\\User_LDAP\\Command\\UpdateUUID' => $baseDir . '/../lib/Command/UpdateUUID.php',
24
-    'OCA\\User_LDAP\\Configuration' => $baseDir . '/../lib/Configuration.php',
25
-    'OCA\\User_LDAP\\Connection' => $baseDir . '/../lib/Connection.php',
26
-    'OCA\\User_LDAP\\ConnectionFactory' => $baseDir . '/../lib/ConnectionFactory.php',
27
-    'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir . '/../lib/Controller/ConfigAPIController.php',
28
-    'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir . '/../lib/Controller/RenewPasswordController.php',
29
-    'OCA\\User_LDAP\\Events\\GroupBackendRegistered' => $baseDir . '/../lib/Events/GroupBackendRegistered.php',
30
-    'OCA\\User_LDAP\\Events\\UserBackendRegistered' => $baseDir . '/../lib/Events/UserBackendRegistered.php',
31
-    'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => $baseDir . '/../lib/Exceptions/AttributeNotSet.php',
32
-    'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => $baseDir . '/../lib/Exceptions/ConstraintViolationException.php',
33
-    'OCA\\User_LDAP\\Exceptions\\NoMoreResults' => $baseDir . '/../lib/Exceptions/NoMoreResults.php',
34
-    'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => $baseDir . '/../lib/Exceptions/NotOnLDAP.php',
35
-    'OCA\\User_LDAP\\FilesystemHelper' => $baseDir . '/../lib/FilesystemHelper.php',
36
-    'OCA\\User_LDAP\\GroupPluginManager' => $baseDir . '/../lib/GroupPluginManager.php',
37
-    'OCA\\User_LDAP\\Group_LDAP' => $baseDir . '/../lib/Group_LDAP.php',
38
-    'OCA\\User_LDAP\\Group_Proxy' => $baseDir . '/../lib/Group_Proxy.php',
39
-    'OCA\\User_LDAP\\Handler\\ExtStorageConfigHandler' => $baseDir . '/../lib/Handler/ExtStorageConfigHandler.php',
40
-    'OCA\\User_LDAP\\Helper' => $baseDir . '/../lib/Helper.php',
41
-    'OCA\\User_LDAP\\IGroupLDAP' => $baseDir . '/../lib/IGroupLDAP.php',
42
-    'OCA\\User_LDAP\\ILDAPGroupPlugin' => $baseDir . '/../lib/ILDAPGroupPlugin.php',
43
-    'OCA\\User_LDAP\\ILDAPUserPlugin' => $baseDir . '/../lib/ILDAPUserPlugin.php',
44
-    'OCA\\User_LDAP\\ILDAPWrapper' => $baseDir . '/../lib/ILDAPWrapper.php',
45
-    'OCA\\User_LDAP\\IUserLDAP' => $baseDir . '/../lib/IUserLDAP.php',
46
-    'OCA\\User_LDAP\\Jobs\\CleanUp' => $baseDir . '/../lib/Jobs/CleanUp.php',
47
-    'OCA\\User_LDAP\\Jobs\\Sync' => $baseDir . '/../lib/Jobs/Sync.php',
48
-    'OCA\\User_LDAP\\Jobs\\UpdateGroups' => $baseDir . '/../lib/Jobs/UpdateGroups.php',
49
-    'OCA\\User_LDAP\\LDAP' => $baseDir . '/../lib/LDAP.php',
50
-    'OCA\\User_LDAP\\LDAPProvider' => $baseDir . '/../lib/LDAPProvider.php',
51
-    'OCA\\User_LDAP\\LDAPProviderFactory' => $baseDir . '/../lib/LDAPProviderFactory.php',
52
-    'OCA\\User_LDAP\\LDAPUtility' => $baseDir . '/../lib/LDAPUtility.php',
53
-    'OCA\\User_LDAP\\Mapping\\AbstractMapping' => $baseDir . '/../lib/Mapping/AbstractMapping.php',
54
-    'OCA\\User_LDAP\\Mapping\\GroupMapping' => $baseDir . '/../lib/Mapping/GroupMapping.php',
55
-    'OCA\\User_LDAP\\Mapping\\UserMapping' => $baseDir . '/../lib/Mapping/UserMapping.php',
56
-    'OCA\\User_LDAP\\Migration\\GroupMappingMigration' => $baseDir . '/../lib/Migration/GroupMappingMigration.php',
57
-    'OCA\\User_LDAP\\Migration\\RemoveRefreshTime' => $baseDir . '/../lib/Migration/RemoveRefreshTime.php',
58
-    'OCA\\User_LDAP\\Migration\\SetDefaultProvider' => $baseDir . '/../lib/Migration/SetDefaultProvider.php',
59
-    'OCA\\User_LDAP\\Migration\\UUIDFix' => $baseDir . '/../lib/Migration/UUIDFix.php',
60
-    'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => $baseDir . '/../lib/Migration/UUIDFixGroup.php',
61
-    'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => $baseDir . '/../lib/Migration/UUIDFixInsert.php',
62
-    'OCA\\User_LDAP\\Migration\\UUIDFixUser' => $baseDir . '/../lib/Migration/UUIDFixUser.php',
63
-    'OCA\\User_LDAP\\Migration\\UnsetDefaultProvider' => $baseDir . '/../lib/Migration/UnsetDefaultProvider.php',
64
-    'OCA\\User_LDAP\\Migration\\Version1010Date20200630192842' => $baseDir . '/../lib/Migration/Version1010Date20200630192842.php',
65
-    'OCA\\User_LDAP\\Migration\\Version1120Date20210917155206' => $baseDir . '/../lib/Migration/Version1120Date20210917155206.php',
66
-    'OCA\\User_LDAP\\Migration\\Version1130Date20211102154716' => $baseDir . '/../lib/Migration/Version1130Date20211102154716.php',
67
-    'OCA\\User_LDAP\\Migration\\Version1130Date20220110154717' => $baseDir . '/../lib/Migration/Version1130Date20220110154717.php',
68
-    'OCA\\User_LDAP\\Migration\\Version1130Date20220110154718' => $baseDir . '/../lib/Migration/Version1130Date20220110154718.php',
69
-    'OCA\\User_LDAP\\Migration\\Version1130Date20220110154719' => $baseDir . '/../lib/Migration/Version1130Date20220110154719.php',
70
-    'OCA\\User_LDAP\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
71
-    'OCA\\User_LDAP\\PagedResults\\IAdapter' => $baseDir . '/../lib/PagedResults/IAdapter.php',
72
-    'OCA\\User_LDAP\\PagedResults\\Php73' => $baseDir . '/../lib/PagedResults/Php73.php',
73
-    'OCA\\User_LDAP\\PagedResults\\TLinkId' => $baseDir . '/../lib/PagedResults/TLinkId.php',
74
-    'OCA\\User_LDAP\\Proxy' => $baseDir . '/../lib/Proxy.php',
75
-    'OCA\\User_LDAP\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
76
-    'OCA\\User_LDAP\\Settings\\Section' => $baseDir . '/../lib/Settings/Section.php',
77
-    'OCA\\User_LDAP\\UserPluginManager' => $baseDir . '/../lib/UserPluginManager.php',
78
-    'OCA\\User_LDAP\\User\\DeletedUsersIndex' => $baseDir . '/../lib/User/DeletedUsersIndex.php',
79
-    'OCA\\User_LDAP\\User\\Manager' => $baseDir . '/../lib/User/Manager.php',
80
-    'OCA\\User_LDAP\\User\\OfflineUser' => $baseDir . '/../lib/User/OfflineUser.php',
81
-    'OCA\\User_LDAP\\User\\User' => $baseDir . '/../lib/User/User.php',
82
-    'OCA\\User_LDAP\\User_LDAP' => $baseDir . '/../lib/User_LDAP.php',
83
-    'OCA\\User_LDAP\\User_Proxy' => $baseDir . '/../lib/User_Proxy.php',
84
-    'OCA\\User_LDAP\\Wizard' => $baseDir . '/../lib/Wizard.php',
85
-    'OCA\\User_LDAP\\WizardResult' => $baseDir . '/../lib/WizardResult.php',
9
+    'Composer\\InstalledVersions' => $vendorDir.'/composer/InstalledVersions.php',
10
+    'OCA\\User_LDAP\\Access' => $baseDir.'/../lib/Access.php',
11
+    'OCA\\User_LDAP\\AccessFactory' => $baseDir.'/../lib/AccessFactory.php',
12
+    'OCA\\User_LDAP\\AppInfo\\Application' => $baseDir.'/../lib/AppInfo/Application.php',
13
+    'OCA\\User_LDAP\\BackendUtility' => $baseDir.'/../lib/BackendUtility.php',
14
+    'OCA\\User_LDAP\\Command\\CheckUser' => $baseDir.'/../lib/Command/CheckUser.php',
15
+    'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => $baseDir.'/../lib/Command/CreateEmptyConfig.php',
16
+    'OCA\\User_LDAP\\Command\\DeleteConfig' => $baseDir.'/../lib/Command/DeleteConfig.php',
17
+    'OCA\\User_LDAP\\Command\\ResetUser' => $baseDir.'/../lib/Command/ResetUser.php',
18
+    'OCA\\User_LDAP\\Command\\Search' => $baseDir.'/../lib/Command/Search.php',
19
+    'OCA\\User_LDAP\\Command\\SetConfig' => $baseDir.'/../lib/Command/SetConfig.php',
20
+    'OCA\\User_LDAP\\Command\\ShowConfig' => $baseDir.'/../lib/Command/ShowConfig.php',
21
+    'OCA\\User_LDAP\\Command\\ShowRemnants' => $baseDir.'/../lib/Command/ShowRemnants.php',
22
+    'OCA\\User_LDAP\\Command\\TestConfig' => $baseDir.'/../lib/Command/TestConfig.php',
23
+    'OCA\\User_LDAP\\Command\\UpdateUUID' => $baseDir.'/../lib/Command/UpdateUUID.php',
24
+    'OCA\\User_LDAP\\Configuration' => $baseDir.'/../lib/Configuration.php',
25
+    'OCA\\User_LDAP\\Connection' => $baseDir.'/../lib/Connection.php',
26
+    'OCA\\User_LDAP\\ConnectionFactory' => $baseDir.'/../lib/ConnectionFactory.php',
27
+    'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir.'/../lib/Controller/ConfigAPIController.php',
28
+    'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir.'/../lib/Controller/RenewPasswordController.php',
29
+    'OCA\\User_LDAP\\Events\\GroupBackendRegistered' => $baseDir.'/../lib/Events/GroupBackendRegistered.php',
30
+    'OCA\\User_LDAP\\Events\\UserBackendRegistered' => $baseDir.'/../lib/Events/UserBackendRegistered.php',
31
+    'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => $baseDir.'/../lib/Exceptions/AttributeNotSet.php',
32
+    'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => $baseDir.'/../lib/Exceptions/ConstraintViolationException.php',
33
+    'OCA\\User_LDAP\\Exceptions\\NoMoreResults' => $baseDir.'/../lib/Exceptions/NoMoreResults.php',
34
+    'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => $baseDir.'/../lib/Exceptions/NotOnLDAP.php',
35
+    'OCA\\User_LDAP\\FilesystemHelper' => $baseDir.'/../lib/FilesystemHelper.php',
36
+    'OCA\\User_LDAP\\GroupPluginManager' => $baseDir.'/../lib/GroupPluginManager.php',
37
+    'OCA\\User_LDAP\\Group_LDAP' => $baseDir.'/../lib/Group_LDAP.php',
38
+    'OCA\\User_LDAP\\Group_Proxy' => $baseDir.'/../lib/Group_Proxy.php',
39
+    'OCA\\User_LDAP\\Handler\\ExtStorageConfigHandler' => $baseDir.'/../lib/Handler/ExtStorageConfigHandler.php',
40
+    'OCA\\User_LDAP\\Helper' => $baseDir.'/../lib/Helper.php',
41
+    'OCA\\User_LDAP\\IGroupLDAP' => $baseDir.'/../lib/IGroupLDAP.php',
42
+    'OCA\\User_LDAP\\ILDAPGroupPlugin' => $baseDir.'/../lib/ILDAPGroupPlugin.php',
43
+    'OCA\\User_LDAP\\ILDAPUserPlugin' => $baseDir.'/../lib/ILDAPUserPlugin.php',
44
+    'OCA\\User_LDAP\\ILDAPWrapper' => $baseDir.'/../lib/ILDAPWrapper.php',
45
+    'OCA\\User_LDAP\\IUserLDAP' => $baseDir.'/../lib/IUserLDAP.php',
46
+    'OCA\\User_LDAP\\Jobs\\CleanUp' => $baseDir.'/../lib/Jobs/CleanUp.php',
47
+    'OCA\\User_LDAP\\Jobs\\Sync' => $baseDir.'/../lib/Jobs/Sync.php',
48
+    'OCA\\User_LDAP\\Jobs\\UpdateGroups' => $baseDir.'/../lib/Jobs/UpdateGroups.php',
49
+    'OCA\\User_LDAP\\LDAP' => $baseDir.'/../lib/LDAP.php',
50
+    'OCA\\User_LDAP\\LDAPProvider' => $baseDir.'/../lib/LDAPProvider.php',
51
+    'OCA\\User_LDAP\\LDAPProviderFactory' => $baseDir.'/../lib/LDAPProviderFactory.php',
52
+    'OCA\\User_LDAP\\LDAPUtility' => $baseDir.'/../lib/LDAPUtility.php',
53
+    'OCA\\User_LDAP\\Mapping\\AbstractMapping' => $baseDir.'/../lib/Mapping/AbstractMapping.php',
54
+    'OCA\\User_LDAP\\Mapping\\GroupMapping' => $baseDir.'/../lib/Mapping/GroupMapping.php',
55
+    'OCA\\User_LDAP\\Mapping\\UserMapping' => $baseDir.'/../lib/Mapping/UserMapping.php',
56
+    'OCA\\User_LDAP\\Migration\\GroupMappingMigration' => $baseDir.'/../lib/Migration/GroupMappingMigration.php',
57
+    'OCA\\User_LDAP\\Migration\\RemoveRefreshTime' => $baseDir.'/../lib/Migration/RemoveRefreshTime.php',
58
+    'OCA\\User_LDAP\\Migration\\SetDefaultProvider' => $baseDir.'/../lib/Migration/SetDefaultProvider.php',
59
+    'OCA\\User_LDAP\\Migration\\UUIDFix' => $baseDir.'/../lib/Migration/UUIDFix.php',
60
+    'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => $baseDir.'/../lib/Migration/UUIDFixGroup.php',
61
+    'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => $baseDir.'/../lib/Migration/UUIDFixInsert.php',
62
+    'OCA\\User_LDAP\\Migration\\UUIDFixUser' => $baseDir.'/../lib/Migration/UUIDFixUser.php',
63
+    'OCA\\User_LDAP\\Migration\\UnsetDefaultProvider' => $baseDir.'/../lib/Migration/UnsetDefaultProvider.php',
64
+    'OCA\\User_LDAP\\Migration\\Version1010Date20200630192842' => $baseDir.'/../lib/Migration/Version1010Date20200630192842.php',
65
+    'OCA\\User_LDAP\\Migration\\Version1120Date20210917155206' => $baseDir.'/../lib/Migration/Version1120Date20210917155206.php',
66
+    'OCA\\User_LDAP\\Migration\\Version1130Date20211102154716' => $baseDir.'/../lib/Migration/Version1130Date20211102154716.php',
67
+    'OCA\\User_LDAP\\Migration\\Version1130Date20220110154717' => $baseDir.'/../lib/Migration/Version1130Date20220110154717.php',
68
+    'OCA\\User_LDAP\\Migration\\Version1130Date20220110154718' => $baseDir.'/../lib/Migration/Version1130Date20220110154718.php',
69
+    'OCA\\User_LDAP\\Migration\\Version1130Date20220110154719' => $baseDir.'/../lib/Migration/Version1130Date20220110154719.php',
70
+    'OCA\\User_LDAP\\Notification\\Notifier' => $baseDir.'/../lib/Notification/Notifier.php',
71
+    'OCA\\User_LDAP\\PagedResults\\IAdapter' => $baseDir.'/../lib/PagedResults/IAdapter.php',
72
+    'OCA\\User_LDAP\\PagedResults\\Php73' => $baseDir.'/../lib/PagedResults/Php73.php',
73
+    'OCA\\User_LDAP\\PagedResults\\TLinkId' => $baseDir.'/../lib/PagedResults/TLinkId.php',
74
+    'OCA\\User_LDAP\\Proxy' => $baseDir.'/../lib/Proxy.php',
75
+    'OCA\\User_LDAP\\Settings\\Admin' => $baseDir.'/../lib/Settings/Admin.php',
76
+    'OCA\\User_LDAP\\Settings\\Section' => $baseDir.'/../lib/Settings/Section.php',
77
+    'OCA\\User_LDAP\\UserPluginManager' => $baseDir.'/../lib/UserPluginManager.php',
78
+    'OCA\\User_LDAP\\User\\DeletedUsersIndex' => $baseDir.'/../lib/User/DeletedUsersIndex.php',
79
+    'OCA\\User_LDAP\\User\\Manager' => $baseDir.'/../lib/User/Manager.php',
80
+    'OCA\\User_LDAP\\User\\OfflineUser' => $baseDir.'/../lib/User/OfflineUser.php',
81
+    'OCA\\User_LDAP\\User\\User' => $baseDir.'/../lib/User/User.php',
82
+    'OCA\\User_LDAP\\User_LDAP' => $baseDir.'/../lib/User_LDAP.php',
83
+    'OCA\\User_LDAP\\User_Proxy' => $baseDir.'/../lib/User_Proxy.php',
84
+    'OCA\\User_LDAP\\Wizard' => $baseDir.'/../lib/Wizard.php',
85
+    'OCA\\User_LDAP\\WizardResult' => $baseDir.'/../lib/WizardResult.php',
86 86
 );
Please login to merge, or discard this patch.
apps/user_ldap/composer/composer/installed.php 1 patch
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -3,7 +3,7 @@  discard block
 block discarded – undo
3 3
         'pretty_version' => 'dev-master',
4 4
         'version' => 'dev-master',
5 5
         'type' => 'library',
6
-        'install_path' => __DIR__ . '/../',
6
+        'install_path' => __DIR__.'/../',
7 7
         'aliases' => array(),
8 8
         'reference' => '9915dc6785d1660068a51604f9379e8b1dc1418c',
9 9
         'name' => '__root__',
@@ -14,7 +14,7 @@  discard block
 block discarded – undo
14 14
             'pretty_version' => 'dev-master',
15 15
             'version' => 'dev-master',
16 16
             'type' => 'library',
17
-            'install_path' => __DIR__ . '/../',
17
+            'install_path' => __DIR__.'/../',
18 18
             'aliases' => array(),
19 19
             'reference' => '9915dc6785d1660068a51604f9379e8b1dc1418c',
20 20
             'dev_requirement' => false,
Please login to merge, or discard this patch.
apps/settings/lib/SetupChecks/LdapInvalidUuids.php 1 patch
Indentation   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -34,36 +34,36 @@
 block discarded – undo
34 34
 
35 35
 class LdapInvalidUuids {
36 36
 
37
-	/** @var IAppManager */
38
-	private $appManager;
39
-	/** @var IL10N */
40
-	private $l10n;
41
-	/** @var IServerContainer */
42
-	private $server;
37
+    /** @var IAppManager */
38
+    private $appManager;
39
+    /** @var IL10N */
40
+    private $l10n;
41
+    /** @var IServerContainer */
42
+    private $server;
43 43
 
44
-	public function __construct(IAppManager $appManager, IL10N $l10n, IServerContainer $server) {
45
-		$this->appManager = $appManager;
46
-		$this->l10n = $l10n;
47
-		$this->server = $server;
48
-	}
44
+    public function __construct(IAppManager $appManager, IL10N $l10n, IServerContainer $server) {
45
+        $this->appManager = $appManager;
46
+        $this->l10n = $l10n;
47
+        $this->server = $server;
48
+    }
49 49
 
50
-	public function description(): string {
51
-		return $this->l10n->t('Invalid UUIDs of LDAP users or groups have been found. Please review your "Override UUID detection" settings in the Expert part of the LDAP configuration and use "occ ldap:update-uuid" to update them.');
52
-	}
50
+    public function description(): string {
51
+        return $this->l10n->t('Invalid UUIDs of LDAP users or groups have been found. Please review your "Override UUID detection" settings in the Expert part of the LDAP configuration and use "occ ldap:update-uuid" to update them.');
52
+    }
53 53
 
54
-	public function severity(): string {
55
-		return 'warning';
56
-	}
54
+    public function severity(): string {
55
+        return 'warning';
56
+    }
57 57
 
58
-	public function run(): bool {
59
-		if (!$this->appManager->isEnabledForUser('user_ldap')) {
60
-			return true;
61
-		}
62
-		/** @var UserMapping $userMapping */
63
-		$userMapping = $this->server->get(UserMapping::class);
64
-		/** @var GroupMapping $groupMapping */
65
-		$groupMapping = $this->server->get(GroupMapping::class);
66
-		return count($userMapping->getList(0, 1, true)) === 0
67
-			&& count($groupMapping->getList(0, 1, true)) === 0;
68
-	}
58
+    public function run(): bool {
59
+        if (!$this->appManager->isEnabledForUser('user_ldap')) {
60
+            return true;
61
+        }
62
+        /** @var UserMapping $userMapping */
63
+        $userMapping = $this->server->get(UserMapping::class);
64
+        /** @var GroupMapping $groupMapping */
65
+        $groupMapping = $this->server->get(GroupMapping::class);
66
+        return count($userMapping->getList(0, 1, true)) === 0
67
+            && count($groupMapping->getList(0, 1, true)) === 0;
68
+    }
69 69
 }
Please login to merge, or discard this patch.