Completed
Push — master ( c7d566...e2979b )
by Maxence
03:33 queued 01:09
created

ConfigService::unsetAppConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
6
/**
7
 * Circles - Bring cloud-users closer together.
8
 *
9
 * This file is licensed under the Affero General Public License version 3 or
10
 * later. See the COPYING file.
11
 *
12
 * @author Maxence Lange <[email protected]>
13
 * @copyright 2021
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
32
namespace OCA\Circles\Service;
33
34
35
use daita\MySmallPhpTools\Model\Nextcloud\nc22\NC22Request;
36
use daita\MySmallPhpTools\Traits\TArrayTools;
37
use daita\MySmallPhpTools\Traits\TStringTools;
38
use OCA\Circles\AppInfo\Application;
39
use OCA\Circles\Exceptions\GSStatusException;
40
use OCA\Circles\Model\Circle;
41
use OCA\Circles\Model\DeprecatedCircle;
42
use OCA\Circles\Model\Member;
43
use OCP\IConfig;
44
use OCP\IURLGenerator;
45
46
47
/**
48
 * Class ConfigService
49
 *
50
 * @package OCA\Circles\Service
51
 */
52
class ConfigService {
53
54
55
	use TStringTools;
56
	use TArrayTools;
57
58
59
	const FRONTAL_CLOUD_ID = 'frontal_cloud_id';
60
	const FRONTAL_CLOUD_SCHEME = 'frontal_cloud_scheme';
61
	const INTERNAL_CLOUD_ID = 'internal_cloud_id';
62
	const INTERNAL_CLOUD_SCHEME = 'internal_cloud_scheme';
63
	const LOOPBACK_CLOUD_ID = 'loopback_cloud_id';
64
	const LOOPBACK_CLOUD_SCHEME = 'loopback_cloud_scheme';
65
	const IFACE0_CLOUD_ID = 'iface0_cloud_id';
66
	const IFACE0_CLOUD_SCHEME = 'iface0_cloud_scheme';
67
	const IFACE1_CLOUD_ID = 'iface1_cloud_id';
68
	const IFACE1_CLOUD_SCHEME = 'iface1_cloud_scheme';
69
	const IFACE2_CLOUD_ID = 'iface2_cloud_id';
70
	const IFACE2_CLOUD_SCHEME = 'iface2_cloud_scheme';
71
	const IFACE3_CLOUD_ID = 'iface3_cloud_id';
72
	const IFACE3_CLOUD_SCHEME = 'iface3_cloud_scheme';
73
	const IFACE4_CLOUD_ID = 'iface4_cloud_id';
74
	const IFACE4_CLOUD_SCHEME = 'iface4_cloud_scheme';
75
76
	const SELF_SIGNED_CERT = 'self_signed_cert';
77
	const MEMBERS_LIMIT = 'members_limit';
78
	const ACTIVITY_ON_NEW_CIRCLE = 'creation_activity';
79
	const MIGRATION_22 = 'migration_22';
80
81
	const LOOPBACK_TMP_ID = 'loopback_tmp_id';
82
	const LOOPBACK_TMP_SCHEME = 'loopback_tmp_scheme';
83
84
	const GS_MODE = 'mode';
85
	const GS_KEY = 'key';
86
87
	const GS_LOOKUP_INSTANCES = '/instances';
88
	const GS_LOOKUP_USERS = '/users';
89
90
91
	// deprecated -- removing in NC25
92
	const CIRCLES_CONTACT_BACKEND = 'contact_backend';
93
	const CIRCLES_ACCOUNTS_ONLY = 'accounts_only'; // only UserType=1
94
	const CIRCLES_SEARCH_FROM_COLLABORATOR = 'search_from_collaborator';
95
96
	const FORCE_NC_BASE = 'force_nc_base';
97
	const TEST_NC_BASE = 'test_nc_base';
98
99
100
	private $defaults = [
101
		self::FRONTAL_CLOUD_ID      => '',
102
		self::FRONTAL_CLOUD_SCHEME  => 'https',
103
		self::INTERNAL_CLOUD_ID     => '',
104
		self::INTERNAL_CLOUD_SCHEME => 'https',
105
		self::LOOPBACK_CLOUD_ID     => '',
106
		self::LOOPBACK_CLOUD_SCHEME => 'https',
107
		self::LOOPBACK_TMP_ID       => '',
108
		self::LOOPBACK_TMP_SCHEME   => '',
109
		self::IFACE0_CLOUD_ID       => '',
110
		self::IFACE0_CLOUD_SCHEME   => 'https',
111
		self::IFACE1_CLOUD_ID       => '',
112
		self::IFACE1_CLOUD_SCHEME   => 'https',
113
		self::IFACE2_CLOUD_ID       => '',
114
		self::IFACE2_CLOUD_SCHEME   => 'https',
115
		self::IFACE3_CLOUD_ID       => '',
116
		self::IFACE3_CLOUD_SCHEME   => 'https',
117
		self::IFACE4_CLOUD_ID       => '',
118
		self::IFACE4_CLOUD_SCHEME   => 'https',
119
120
		self::SELF_SIGNED_CERT       => '0',
121
		self::MEMBERS_LIMIT          => '50',
122
		self::ACTIVITY_ON_NEW_CIRCLE => '1',
123
		self::MIGRATION_22           => '0',
124
125
		self::FORCE_NC_BASE                    => '',
126
		self::TEST_NC_BASE                     => '',
127
		self::CIRCLES_CONTACT_BACKEND          => '0',
128
		self::CIRCLES_ACCOUNTS_ONLY            => '0',
129
		self::CIRCLES_SEARCH_FROM_COLLABORATOR => '0',
130
	];
131
132
133
	/** @var IConfig */
134
	private $config;
135
136
	/** @var IURLGenerator */
137
	private $urlGenerator;
138
139
140
	/**
141
	 * ConfigService constructor.
142
	 *
143
	 * @param IConfig $config
144
	 * @param IURLGenerator $urlGenerator
145
	 */
146
	public function __construct(IConfig $config, IURLGenerator $urlGenerator) {
147
		$this->config = $config;
148
		$this->urlGenerator = $urlGenerator;
149
	}
150
151
152
	/**
153
	 * Get a value by key
154
	 *
155
	 * @param string $key
156
	 *
157
	 * @return string
158
	 */
159
	public function getAppValue(string $key): string {
160
		if (($value = $this->config->getAppValue(Application::APP_ID, $key, '')) !== '') {
161
			return $value;
162
		}
163
164
		if (($value = $this->config->getSystemValue('circles.' . $key, '')) !== '') {
165
			return $value;
166
		}
167
168
		return $this->get($key, $this->defaults);
169
	}
170
171
	/**
172
	 * @param string $key
173
	 *
174
	 * @return int
175
	 */
176
	public function getAppValueInt(string $key): int {
177
		return (int)$this->getAppValue($key);
178
	}
179
180
	/**
181
	 * @param string $key
182
	 *
183
	 * @return bool
184
	 */
185
	public function getAppValueBool(string $key): bool {
186
		return ($this->getAppValueInt($key) === 1);
187
	}
188
189
190
	/**
191
	 * Set a value by key
192
	 *
193
	 * @param string $key
194
	 * @param string $value
195
	 *
196
	 * @return void
197
	 */
198
	public function setAppValue(string $key, string $value): void {
199
		$this->config->setAppValue(Application::APP_ID, $key, $value);
200
	}
201
202
203
	/**
204
	 *
205
	 */
206
	public function unsetAppConfig(): void {
207
		$this->config->deleteAppValues(Application::APP_ID);
208
	}
209
210
211
	/**
212
	 * Get available hosts
213
	 *
214
	 * @return array
215
	 */
216
	public function getAvailableHosts(): array {
217
		return $this->config->getSystemValue('trusted_domains', []);
218
	}
219
220
221
	/**
222
	 * Get a user value by key and user
223
	 *
224
	 *
225
	 * @param string $userId
226
	 * @param string $key
227
	 *
228
	 * @param string $default
229
	 *
230
	 * @return string
231
	 */
232
	public function getCoreValueForUser($userId, $key, $default = '') {
233
		return $this->config->getUserValue($userId, 'core', $key, $default);
234
	}
235
236
237
	/**
238
	 * @return bool
239
	 * @deprecated
240
	 */
241
	public function isContactsBackend(): bool {
242
		return ($this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND) !== '0'
243
				&& $this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND) !== '');
244
	}
245
246
247
	/**
248
	 * @return int
249
	 * @deprecated
250
	 */
251
	public function contactsBackendType(): int {
252
		return (int)$this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND);
253
	}
254
255
256
	/**
257
	 * @return bool
258
	 * @deprecated
259
	 * should the password for a mail share be send to the recipient
260
	 *
261
	 */
262
	public function sendPasswordByMail() {
263
		if ($this->isContactsBackend()) {
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Service\Conf...ce::isContactsBackend() has been deprecated.

This method has been deprecated.

Loading history...
264
			return false;
265
		}
266
267
		return ($this->config->getAppValue('sharebymail', 'sendpasswordmail', 'yes') === 'yes');
268
	}
269
270
	/**
271
	 * @param DeprecatedCircle $circle
272
	 *
273
	 * @return bool
274
	 * @deprecated
275
	 * do we require a share by mail to be password protected
276
	 *
277
	 */
278
	public function enforcePasswordProtection(DeprecatedCircle $circle) {
279
		if ($this->isContactsBackend()) {
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Service\Conf...ce::isContactsBackend() has been deprecated.

This method has been deprecated.

Loading history...
280
			return false;
281
		}
282
283
		if ($circle->getSetting('password_enforcement') === 'true') {
284
			return true;
285
		}
286
287
		return ($this->config->getAppValue('sharebymail', 'enforcePasswordProtection', 'no') === 'yes');
288
	}
289
290
291
	/**
292
	 * // TODO: fetch data from somewhere else than hard coded...
293
	 *
294
	 * @return array
295
	 */
296
	public function getSettings(): array {
297
		return [
298
			'allowedCircles'   => Circle::$DEF_CFG_MAX,
299
			'allowedUserTypes' => Member::$DEF_TYPE_MAX,
300
			'membersLimit'     => $this->getAppValueInt(self::MEMBERS_LIMIT)
301
		];
302
	}
303
304
305
	/**
306
	 * @return bool
307
	 */
308
	public function isGSAvailable(): bool {
309
		if (!empty($this->getGSSMockup())) {
310
			return true;
311
		}
312
313
		return $this->config->getSystemValueBool('gs.enabled', false);
314
	}
315
316
317
	/**
318
	 * @return string
319
	 * @throws GSStatusException
320
	 */
321
	public function getGSLookup(): string {
322
		$lookup = $this->config->getSystemValue('lookup_server', '');
323
324
		if (!$this->isGSAvailable() || $lookup === '') {
325
			throw new  GSStatusException();
326
		}
327
328
		return $lookup;
329
	}
330
331
332
	/**
333
	 * @return array
334
	 */
335
	public function getGSSMockup(): array {
336
		return $this->config->getSystemValue('gss.mockup', []);
337
	}
338
339
340
	/**
341
	 * @param string $type
342
	 *
343
	 * @return string
344
	 */
345
	public function getGSInfo(string $type): string {
346
		$clef = $this->config->getSystemValue('gss.jwt.key', '');
347
		$mode = $this->config->getSystemValue('gss.mode', '');
348
349
		switch ($type) {
350
			case self::GS_MODE:
351
				return $mode;
352
353
			case self::GS_KEY:
354
				return $clef;
355
		}
356
357
358
		return '';
359
	}
360
361
362
	/**
363
	 * @return array
364
	 * @throws GSStatusException
365
	 */
366
	public function getGSData(): array {
367
		return [
368
			'enabled'     => $this->isGSAvailable(),
369
			'lookup'      => $this->getGSLookup(),
370
			'mockup'      => $this->getGSSMockup(),
371
			self::GS_MODE => $this->config->getSystemValue('gss.mode', ''),
372
			self::GS_KEY  => $this->config->getSystemValue('gss.jwt.key', ''),
373
		];
374
	}
375
376
377
	/**
378
	 * @return array
379
	 */
380
	public function getTrustedDomains(): array {
381
		return array_map(
382
			function(string $address) {
383
				return strtolower($address);
384
			}, $this->config->getSystemValue('trusted_domains', [])
385
		);
386
	}
387
388
389
	/**
390
	 * @return string
391
	 */
392
	public function getLoopbackInstance(): string {
393
		$loopbackCloudId = $this->getAppValue(self::LOOPBACK_TMP_ID);
394
		if ($loopbackCloudId !== '') {
395
			return $loopbackCloudId;
396
		}
397
398
		$loopbackCloudId = $this->getAppValue(self::LOOPBACK_CLOUD_ID);
399
		if ($loopbackCloudId !== '') {
400
			return $loopbackCloudId;
401
		}
402
403
		$cliUrl = $this->getAppValue(self::FORCE_NC_BASE);
404
		if ($cliUrl === '') {
405
			$cliUrl = $this->config->getSystemValue('circles.force_nc_base', '');
406
		}
407
408
		if ($cliUrl === '') {
409
			$cliUrl = $this->config->getSystemValue('overwrite.cli.url', '');
410
		}
411
412
		$loopback = parse_url($cliUrl);
413
		if (!is_array($loopback) || !array_key_exists('host', $loopback)) {
414
			return $cliUrl;
415
		}
416
417
		if (array_key_exists('port', $loopback)) {
418
			$loopbackCloudId = $loopback['host'] . ':' . $loopback['port'];
419
		} else {
420
			$loopbackCloudId = $loopback['host'];
421
		}
422
423
		if (array_key_exists('scheme', $loopback)
424
			&& $this->getAppValue(self::LOOPBACK_TMP_SCHEME) !== $loopback['scheme']) {
425
			$this->setAppValue(self::LOOPBACK_TMP_SCHEME, $loopback['scheme']);
426
		}
427
428
		return $loopbackCloudId;
429
	}
430
431
	/**
432
	 * returns loopback address based on getLoopbackInstance and LOOPBACK_CLOUD_SCHEME
433
	 * should be used to async process
434
	 *
435
	 * @param string $route
436
	 * @param array $args
437
	 *
438
	 * @return string
439
	 */
440
	public function getLoopbackPath(string $route = '', array $args = []): string {
441
		$instance = $this->getLoopbackInstance();
442
		$scheme = $this->getAppValue(self::LOOPBACK_TMP_SCHEME);
443
		if ($scheme === '') {
444
			$scheme = $this->getAppValue(self::LOOPBACK_CLOUD_SCHEME);
445
		}
446
447
		$base = $scheme . '://' . $instance;
448
		if ($route === '') {
449
			return $base;
450
		}
451
452
		return $base . $this->urlGenerator->linkToRoute($route, $args);
453
	}
454
455
456
	/**
457
	 * - must be configured using INTERNAL_CLOUD_ID
458
	 * - returns host+port, does not specify any protocol
459
	 * - used mainly to assign instance and source to a request to local GlobalScale
460
	 * - important only in GlobalScale environment
461
	 *
462
	 * @return string
463
	 *
464
	 */
465
	public function getInternalInstance(): string {
466
		return $this->getAppValue(self::INTERNAL_CLOUD_ID);
467
	}
468
469
470
	/**
471
	 * - must be configured using FRONTAL_CLOUD_ID
472
	 * - returns host+port, does not specify any protocol
473
	 * - used mainly to assign instance and source to a request
474
	 * - important only in remote environment
475
	 *
476
	 * @return string
477
	 */
478
	public function getFrontalInstance(): string {
479
		$frontalCloudId = $this->getAppValue(self::FRONTAL_CLOUD_ID);
480
481
		// using old settings local_cloud_id from NC20, deprecated in NC25
482
		if ($frontalCloudId === '') {
483
			$frontalCloudId = $this->config->getAppValue(Application::APP_ID, 'local_cloud_id', '');
484
			if ($frontalCloudId !== '') {
485
				$this->setAppValue(self::FRONTAL_CLOUD_ID, $frontalCloudId);
486
			}
487
		}
488
489
		return $frontalCloudId;
490
	}
491
492
493
	/**
494
	 * @param int $iface
495
	 *
496
	 * @return string
497
	 */
498
	public function getIfaceInstance(int $iface): string {
499
		switch ($iface) {
500
			case InterfaceService::IFACE0:
501
				return $this->getAppValue(self::IFACE0_CLOUD_ID);
502
			case InterfaceService::IFACE1:
503
				return $this->getAppValue(self::IFACE1_CLOUD_ID);
504
			case InterfaceService::IFACE2:
505
				return $this->getAppValue(self::IFACE2_CLOUD_ID);
506
			case InterfaceService::IFACE3:
507
				return $this->getAppValue(self::IFACE3_CLOUD_ID);
508
			case InterfaceService::IFACE4:
509
				return $this->getAppValue(self::IFACE4_CLOUD_ID);
510
		}
511
512
		return '';
513
	}
514
515
516
	/**
517
	 * @param string $instance
518
	 *
519
	 * @return bool
520
	 */
521
	public function isLocalInstance(string $instance): bool {
522
		if ($instance === '') { // TODO: is it an existing condition ?
523
			return false;
524
		}
525
526
		$instance = strtolower($instance);
527
		if ($instance === strtolower($this->getInternalInstance())) {
528
			return true;
529
		}
530
531
		if ($instance === strtolower($this->getFrontalInstance())) {
532
			return true;
533
		}
534
535
		return (in_array($instance, $this->getTrustedDomains()));
536
	}
537
538
	/**
539
	 * @param string $instance
540
	 *
541
	 * @return string
542
	 */
543
	public function displayInstance(string $instance): string {
544
		if ($this->isLocalInstance($instance)) {
545
			return '';
546
		}
547
548
		return $instance;
549
	}
550
551
552
	/**
553
	 * - Create route using getLoopbackAddress()
554
	 * - perfect for loopback request.
555
	 *
556
	 * @param NC22Request $request
557
	 * @param string $route
558
	 * @param array $args
559
	 */
560
	public function configureLoopbackRequest(
561
		NC22Request $request,
562
		string $route = '',
563
		array $args = []
564
	): void {
565
		$this->configureRequest($request);
566
		$request->basedOnUrl($this->getLoopbackPath($route, $args));
567
	}
568
569
570
	/**
571
	 * @param NC22Request $request
572
	 */
573
	public function configureRequest(NC22Request $request): void {
574
		$request->setVerifyPeer($this->getAppValue(ConfigService::SELF_SIGNED_CERT) !== '1');
575
		$request->setProtocols(['https', 'http']);
576
		$request->setHttpErrorsAllowed(true);
577
		$request->setLocalAddressAllowed(true);
578
		$request->setFollowLocation(true);
579
		$request->setTimeout(5);
580
	}
581
582
}
583
584