Passed
Push — master ( bf1943...bfd61d )
by Blizzz
09:37
created

BackendService::loadAuthMechanismProviders()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Morris Jobke <[email protected]>
6
 * @author Robin McCorkell <[email protected]>
7
 * @author Arthur Schiwon <[email protected]>
8
 *
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
namespace OCA\Files_External\Service;
26
27
use OCA\Files_External\Config\IConfigHandler;
28
use \OCP\IConfig;
29
30
use \OCA\Files_External\Lib\Backend\Backend;
31
use \OCA\Files_External\Lib\Auth\AuthMechanism;
32
use \OCA\Files_External\Lib\Config\IBackendProvider;
33
use \OCA\Files_External\Lib\Config\IAuthMechanismProvider;
34
35
/**
36
 * Service class to manage backend definitions
37
 */
38
class BackendService {
39
40
	/** Visibility constants for VisibilityTrait */
41
	const VISIBILITY_NONE = 0;
42
	const VISIBILITY_PERSONAL = 1;
43
	const VISIBILITY_ADMIN = 2;
44
	//const VISIBILITY_ALIENS = 4;
45
46
	const VISIBILITY_DEFAULT = 3; // PERSONAL | ADMIN
47
48
	/** Priority constants for PriorityTrait */
49
	const PRIORITY_DEFAULT = 100;
50
51
	/** @var IConfig */
52
	protected $config;
53
54
	/** @var bool */
55
	private $userMountingAllowed = true;
56
57
	/** @var string[] */
58
	private $userMountingBackends = [];
59
60
	/** @var Backend[] */
61
	private $backends = [];
62
63
	/** @var IBackendProvider[] */
64
	private $backendProviders = [];
65
66
	/** @var AuthMechanism[] */
67
	private $authMechanisms = [];
68
69
	/** @var IAuthMechanismProvider[] */
70
	private $authMechanismProviders = [];
71
72
	/** @var callable[] */
73
	private $configHandlerLoaders = [];
74
75
	private $configHandlers = [];
76
77
	/**
78
	 * @param IConfig $config
79
	 */
80
	public function __construct(
81
		IConfig $config
82
	) {
83
		$this->config = $config;
84
85
		// Load config values
86
		if ($this->config->getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') {
87
			$this->userMountingAllowed = false;
88
		}
89
		$this->userMountingBackends = explode(',',
90
			$this->config->getAppValue('files_external', 'user_mounting_backends', '')
91
		);
92
93
		// if no backend is in the list an empty string is in the array and user mounting is disabled
94
		if ($this->userMountingBackends === ['']) {
95
			$this->userMountingAllowed = false;
96
		}
97
	}
98
99
	/**
100
	 * Register a backend provider
101
	 *
102
	 * @since 9.1.0
103
	 * @param IBackendProvider $provider
104
	 */
105
	public function registerBackendProvider(IBackendProvider $provider) {
106
		$this->backendProviders[] = $provider;
107
	}
108
109
	private function loadBackendProviders() {
110
		foreach ($this->backendProviders as $provider) {
111
			$this->registerBackends($provider->getBackends());
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Files_External\Servi...ice::registerBackends() has been deprecated: 9.1.0 use registerBackendProvider() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

111
			/** @scrutinizer ignore-deprecated */ $this->registerBackends($provider->getBackends());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
112
		}
113
		$this->backendProviders = [];
114
	}
115
116
	/**
117
	 * Register an auth mechanism provider
118
	 *
119
	 * @since 9.1.0
120
	 * @param IAuthMechanismProvider $provider
121
	 */
122
	public function registerAuthMechanismProvider(IAuthMechanismProvider $provider) {
123
		$this->authMechanismProviders[] = $provider;
124
	}
125
126
	private function loadAuthMechanismProviders() {
127
		foreach ($this->authMechanismProviders as $provider) {
128
			$this->registerAuthMechanisms($provider->getAuthMechanisms());
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Files_External\Servi...egisterAuthMechanisms() has been deprecated: 9.1.0 use registerAuthMechanismProvider() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

128
			/** @scrutinizer ignore-deprecated */ $this->registerAuthMechanisms($provider->getAuthMechanisms());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
129
		}
130
		$this->authMechanismProviders = [];
131
	}
132
133
	/**
134
	 * Register a backend
135
	 *
136
	 * @deprecated 9.1.0 use registerBackendProvider()
137
	 * @param Backend $backend
138
	 */
139
	public function registerBackend(Backend $backend) {
140
		if (!$this->isAllowedUserBackend($backend)) {
141
			$backend->removeVisibility(BackendService::VISIBILITY_PERSONAL);
142
		}
143
		foreach ($backend->getIdentifierAliases() as $alias) {
144
			$this->backends[$alias] = $backend;
145
		}
146
	}
147
148
	/**
149
	 * @deprecated 9.1.0 use registerBackendProvider()
150
	 * @param Backend[] $backends
151
	 */
152
	public function registerBackends(array $backends) {
153
		foreach ($backends as $backend) {
154
			$this->registerBackend($backend);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Files_External\Servi...vice::registerBackend() has been deprecated: 9.1.0 use registerBackendProvider() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

154
			/** @scrutinizer ignore-deprecated */ $this->registerBackend($backend);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
155
		}
156
	}
157
	/**
158
	 * Register an authentication mechanism
159
	 *
160
	 * @deprecated 9.1.0 use registerAuthMechanismProvider()
161
	 * @param AuthMechanism $authMech
162
	 */
163
	public function registerAuthMechanism(AuthMechanism $authMech) {
164
		if (!$this->isAllowedAuthMechanism($authMech)) {
165
			$authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL);
166
		}
167
		foreach ($authMech->getIdentifierAliases() as $alias) {
168
			$this->authMechanisms[$alias] = $authMech;
169
		}
170
	}
171
172
	/**
173
	 * @deprecated 9.1.0 use registerAuthMechanismProvider()
174
	 * @param AuthMechanism[] $mechanisms
175
	 */
176
	public function registerAuthMechanisms(array $mechanisms) {
177
		foreach ($mechanisms as $mechanism) {
178
			$this->registerAuthMechanism($mechanism);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Files_External\Servi...registerAuthMechanism() has been deprecated: 9.1.0 use registerAuthMechanismProvider() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

178
			/** @scrutinizer ignore-deprecated */ $this->registerAuthMechanism($mechanism);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
179
		}
180
	}
181
182
	/**
183
	 * Get all backends
184
	 *
185
	 * @return Backend[]
186
	 */
187
	public function getBackends() {
188
		$this->loadBackendProviders();
189
		// only return real identifiers, no aliases
190
		$backends = [];
191
		foreach ($this->backends as $backend) {
192
			$backends[$backend->getIdentifier()] = $backend;
193
		}
194
		return $backends;
195
	}
196
197
	/**
198
	 * Get all available backends
199
	 *
200
	 * @return Backend[]
201
	 */
202
	public function getAvailableBackends() {
203
		return array_filter($this->getBackends(), function($backend) {
204
			return !$backend->checkDependencies();
205
		});
206
	}
207
208
	/**
209
	 * @param string $identifier
210
	 * @return Backend|null
211
	 */
212
	public function getBackend($identifier) {
213
		$this->loadBackendProviders();
214
		if (isset($this->backends[$identifier])) {
215
			return $this->backends[$identifier];
216
		}
217
		return null;
218
	}
219
220
	/**
221
	 * Get all authentication mechanisms
222
	 *
223
	 * @return AuthMechanism[]
224
	 */
225
	public function getAuthMechanisms() {
226
		$this->loadAuthMechanismProviders();
227
		// only return real identifiers, no aliases
228
		$mechanisms = [];
229
		foreach ($this->authMechanisms as $mechanism) {
230
			$mechanisms[$mechanism->getIdentifier()] = $mechanism;
231
		}
232
		return $mechanisms;
233
	}
234
235
	/**
236
	 * Get all authentication mechanisms for schemes
237
	 *
238
	 * @param string[] $schemes
239
	 * @return AuthMechanism[]
240
	 */
241
	public function getAuthMechanismsByScheme(array $schemes) {
242
		return array_filter($this->getAuthMechanisms(), function($authMech) use ($schemes) {
243
			return in_array($authMech->getScheme(), $schemes, true);
244
		});
245
	}
246
247
	/**
248
	 * @param string $identifier
249
	 * @return AuthMechanism|null
250
	 */
251
	public function getAuthMechanism($identifier) {
252
		$this->loadAuthMechanismProviders();
253
		if (isset($this->authMechanisms[$identifier])) {
254
			return $this->authMechanisms[$identifier];
255
		}
256
		return null;
257
	}
258
259
	/**
260
	 * @return bool
261
	 */
262
	public function isUserMountingAllowed() {
263
		return $this->userMountingAllowed;
264
	}
265
266
	/**
267
	 * Check a backend if a user is allowed to mount it
268
	 *
269
	 * @param Backend $backend
270
	 * @return bool
271
	 */
272
	protected function isAllowedUserBackend(Backend $backend) {
273
		if ($this->userMountingAllowed &&
274
			array_intersect($backend->getIdentifierAliases(), $this->userMountingBackends)
275
		) {
276
			return true;
277
		}
278
		return false;
279
	}
280
281
	/**
282
	 * Check an authentication mechanism if a user is allowed to use it
283
	 *
284
	 * @param AuthMechanism $authMechanism
285
	 * @return bool
286
	 */
287
	protected function isAllowedAuthMechanism(AuthMechanism $authMechanism) {
0 ignored issues
show
Unused Code introduced by
The parameter $authMechanism is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

287
	protected function isAllowedAuthMechanism(/** @scrutinizer ignore-unused */ AuthMechanism $authMechanism) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
288
		return true; // not implemented
289
	}
290
291
	/**
292
	 * registers a configuration handler
293
	 *
294
	 * The function of the provided $placeholder is mostly to act a sorting
295
	 * criteria, so longer placeholders are replaced first. This avoids
296
	 * "$user" overwriting parts of "$userMail" and "$userLang", for example.
297
	 * The provided value should not contain the $ prefix, only a-z0-9 are
298
	 * allowed. Upper case letters are lower cased, the replacement is case-
299
	 * insensitive.
300
	 *
301
	 * The configHandlerLoader should just instantiate the handler on demand.
302
	 * For now all handlers are instantiated when a mount is loaded, independent
303
	 * of whether the placeholder is present or not. This may change in future.
304
	 *
305
	 * @since 16.0.0
306
	 */
307
	public function registerConfigHandler(string $placeholder, callable $configHandlerLoader) {
308
		$placeholder = trim(strtolower($placeholder));
309
		if(!(bool)\preg_match('/^[a-z0-9]*$/', $placeholder)) {
310
			throw new \RuntimeException(sprintf(
311
				'Invalid placeholder %s, only [a-z0-9] are allowed', $placeholder
312
			));
313
		}
314
		if($placeholder === '') {
315
			throw new \RuntimeException('Invalid empty placeholder');
316
		}
317
		if(isset($this->configHandlerLoaders[$placeholder]) || isset($this->configHandlers[$placeholder])) {
318
			throw new \RuntimeException(sprintf('A handler is already registered for %s', $placeholder));
319
		}
320
		$this->configHandlerLoaders[$placeholder] = $configHandlerLoader;
321
	}
322
323
	protected function loadConfigHandlers():void {
324
		$newLoaded = false;
325
		foreach ($this->configHandlerLoaders as $placeholder => $loader) {
326
			$handler = $loader();
327
			if(!$handler instanceof IConfigHandler) {
328
				throw new \RuntimeException(sprintf(
329
					'Handler for %s is not an instance of IConfigHandler', $placeholder
330
				));
331
			}
332
			$this->configHandlers[$placeholder] = $handler;
333
			$newLoaded = true;
334
		}
335
		$this->configHandlerLoaders = [];
336
		if($newLoaded) {
337
			// ensure those with longest placeholders come first,
338
			// to avoid substring matches
339
			uksort($this->configHandlers, function ($phA, $phB) {
340
				return strlen($phB) <=> strlen($phA);
341
			});
342
		}
343
	}
344
345
	/**
346
	 * @since 16.0.0
347
	 */
348
	public function getConfigHandlers() {
349
		$this->loadConfigHandlers();
350
		return $this->configHandlers;
351
	}
352
}
353