Passed
Push — master ( b42b26...10e2ed )
by Roeland
10:17 queued 10s
created

BackendService::callForRegistrations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
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 callForRegistrations() {
110
		static $eventSent = false;
111
		if(!$eventSent) {
112
			\OC::$server->getEventDispatcher()->dispatch(
113
				'OCA\\Files_External::loadAdditionalBackends'
114
			);
115
			$eventSent = true;
116
		}
117
	}
118
119
	private function loadBackendProviders() {
120
		$this->callForRegistrations();
121
		foreach ($this->backendProviders as $provider) {
122
			$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

122
			/** @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...
123
		}
124
		$this->backendProviders = [];
125
	}
126
127
	/**
128
	 * Register an auth mechanism provider
129
	 *
130
	 * @since 9.1.0
131
	 * @param IAuthMechanismProvider $provider
132
	 */
133
	public function registerAuthMechanismProvider(IAuthMechanismProvider $provider) {
134
		$this->authMechanismProviders[] = $provider;
135
	}
136
137
	private function loadAuthMechanismProviders() {
138
		$this->callForRegistrations();
139
		foreach ($this->authMechanismProviders as $provider) {
140
			$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

140
			/** @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...
141
		}
142
		$this->authMechanismProviders = [];
143
	}
144
145
	/**
146
	 * Register a backend
147
	 *
148
	 * @deprecated 9.1.0 use registerBackendProvider()
149
	 * @param Backend $backend
150
	 */
151
	public function registerBackend(Backend $backend) {
152
		if (!$this->isAllowedUserBackend($backend)) {
153
			$backend->removeVisibility(BackendService::VISIBILITY_PERSONAL);
154
		}
155
		foreach ($backend->getIdentifierAliases() as $alias) {
156
			$this->backends[$alias] = $backend;
157
		}
158
	}
159
160
	/**
161
	 * @deprecated 9.1.0 use registerBackendProvider()
162
	 * @param Backend[] $backends
163
	 */
164
	public function registerBackends(array $backends) {
165
		foreach ($backends as $backend) {
166
			$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

166
			/** @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...
167
		}
168
	}
169
	/**
170
	 * Register an authentication mechanism
171
	 *
172
	 * @deprecated 9.1.0 use registerAuthMechanismProvider()
173
	 * @param AuthMechanism $authMech
174
	 */
175
	public function registerAuthMechanism(AuthMechanism $authMech) {
176
		if (!$this->isAllowedAuthMechanism($authMech)) {
177
			$authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL);
178
		}
179
		foreach ($authMech->getIdentifierAliases() as $alias) {
180
			$this->authMechanisms[$alias] = $authMech;
181
		}
182
	}
183
184
	/**
185
	 * @deprecated 9.1.0 use registerAuthMechanismProvider()
186
	 * @param AuthMechanism[] $mechanisms
187
	 */
188
	public function registerAuthMechanisms(array $mechanisms) {
189
		foreach ($mechanisms as $mechanism) {
190
			$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

190
			/** @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...
191
		}
192
	}
193
194
	/**
195
	 * Get all backends
196
	 *
197
	 * @return Backend[]
198
	 */
199
	public function getBackends() {
200
		$this->loadBackendProviders();
201
		// only return real identifiers, no aliases
202
		$backends = [];
203
		foreach ($this->backends as $backend) {
204
			$backends[$backend->getIdentifier()] = $backend;
205
		}
206
		return $backends;
207
	}
208
209
	/**
210
	 * Get all available backends
211
	 *
212
	 * @return Backend[]
213
	 */
214
	public function getAvailableBackends() {
215
		return array_filter($this->getBackends(), function($backend) {
216
			return !$backend->checkDependencies();
217
		});
218
	}
219
220
	/**
221
	 * @param string $identifier
222
	 * @return Backend|null
223
	 */
224
	public function getBackend($identifier) {
225
		$this->loadBackendProviders();
226
		if (isset($this->backends[$identifier])) {
227
			return $this->backends[$identifier];
228
		}
229
		return null;
230
	}
231
232
	/**
233
	 * Get all authentication mechanisms
234
	 *
235
	 * @return AuthMechanism[]
236
	 */
237
	public function getAuthMechanisms() {
238
		$this->loadAuthMechanismProviders();
239
		// only return real identifiers, no aliases
240
		$mechanisms = [];
241
		foreach ($this->authMechanisms as $mechanism) {
242
			$mechanisms[$mechanism->getIdentifier()] = $mechanism;
243
		}
244
		return $mechanisms;
245
	}
246
247
	/**
248
	 * Get all authentication mechanisms for schemes
249
	 *
250
	 * @param string[] $schemes
251
	 * @return AuthMechanism[]
252
	 */
253
	public function getAuthMechanismsByScheme(array $schemes) {
254
		return array_filter($this->getAuthMechanisms(), function($authMech) use ($schemes) {
255
			return in_array($authMech->getScheme(), $schemes, true);
256
		});
257
	}
258
259
	/**
260
	 * @param string $identifier
261
	 * @return AuthMechanism|null
262
	 */
263
	public function getAuthMechanism($identifier) {
264
		$this->loadAuthMechanismProviders();
265
		if (isset($this->authMechanisms[$identifier])) {
266
			return $this->authMechanisms[$identifier];
267
		}
268
		return null;
269
	}
270
271
	/**
272
	 * @return bool
273
	 */
274
	public function isUserMountingAllowed() {
275
		return $this->userMountingAllowed;
276
	}
277
278
	/**
279
	 * Check a backend if a user is allowed to mount it
280
	 *
281
	 * @param Backend $backend
282
	 * @return bool
283
	 */
284
	protected function isAllowedUserBackend(Backend $backend) {
285
		if ($this->userMountingAllowed &&
286
			array_intersect($backend->getIdentifierAliases(), $this->userMountingBackends)
287
		) {
288
			return true;
289
		}
290
		return false;
291
	}
292
293
	/**
294
	 * Check an authentication mechanism if a user is allowed to use it
295
	 *
296
	 * @param AuthMechanism $authMechanism
297
	 * @return bool
298
	 */
299
	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

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