Completed
Push — master ( 732552...f98d97 )
by Maxence
02:26 queued 10s
created

ConfigService::getLocalInstance()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 4
nc 4
nop 0
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Circles\Service;
28
29
use daita\MySmallPhpTools\Model\Nextcloud\NC19Request;
30
use OCA\Circles\Exceptions\GSStatusException;
31
use OCA\Circles\Model\Circle;
32
use OCP\IConfig;
33
use OCP\IRequest;
34
use OCP\IURLGenerator;
35
use OCP\PreConditionNotMetException;
36
use OCP\Util;
37
38
class ConfigService {
39
40
	const CIRCLES_ALLOW_CIRCLES = 'allow_circles';
41
	const CIRCLES_CONTACT_BACKEND = 'contact_backend';
42
	const CIRCLES_STILL_FRONTEND = 'still_frontend';
43
	const CIRCLES_SWAP_TO_TEAMS = 'swap_to_teams';
44
	const CIRCLES_ALLOW_FEDERATED_CIRCLES = 'allow_federated';
45
	const CIRCLES_GS_ENABLED = 'gs_enabled';
46
	const CIRCLES_MEMBERS_LIMIT = 'members_limit';
47
	const CIRCLES_ACCOUNTS_ONLY = 'accounts_only';
48
	const CIRCLES_ALLOW_LINKED_GROUPS = 'allow_linked_groups';
49
	const CIRCLES_ALLOW_NON_SSL_LINKS = 'allow_non_ssl_links';
50
	const CIRCLES_NON_SSL_LOCAL = 'local_is_non_ssl';
51
	const CIRCLES_SELF_SIGNED = 'self_signed_cert';
52
	const LOCAL_CLOUD_ID = 'local_cloud_id';
53
	const CIRCLES_LOCAL_GSKEY = 'local_gskey';
54
	const CIRCLES_ACTIVITY_ON_CREATION = 'creation_activity';
55
	const CIRCLES_SKIP_INVITATION_STEP = 'skip_invitation_to_closed_circles';
56
	const CIRCLES_SEARCH_FROM_COLLABORATOR = 'search_from_collaborator';
57
	const CIRCLES_TEST_ASYNC_LOCK = 'test_async_lock';
58
	const CIRCLES_TEST_ASYNC_INIT = 'test_async_init';
59
	const CIRCLES_TEST_ASYNC_HAND = 'test_async_hand';
60
	const CIRCLES_TEST_ASYNC_COUNT = 'test_async_count';
61
	const FORCE_NC_BASE = 'force_nc_base';
62
	const TEST_NC_BASE = 'test_nc_base';
63
64
	const GS_ENABLED = 'enabled';
65
	const GS_MODE = 'mode';
66
	const GS_KEY = 'key';
67
	const GS_LOOKUP = 'lookup';
68
69
	const GS_LOOKUP_INSTANCES = '/instances';
70
	const GS_LOOKUP_USERS = '/users';
71
72
73
	private $defaults = [
74
		self::CIRCLES_ALLOW_CIRCLES            => Circle::CIRCLES_ALL,
75
		self::CIRCLES_CONTACT_BACKEND          => '0',
76
		self::CIRCLES_STILL_FRONTEND           => '0',
77
		self::CIRCLES_TEST_ASYNC_INIT          => '0',
78
		self::CIRCLES_SWAP_TO_TEAMS            => '0',
79
		self::CIRCLES_ACCOUNTS_ONLY            => '0',
80
		self::CIRCLES_MEMBERS_LIMIT            => '50',
81
		self::CIRCLES_ALLOW_LINKED_GROUPS      => '0',
82
		self::CIRCLES_ALLOW_FEDERATED_CIRCLES  => '0',
83
		self::CIRCLES_GS_ENABLED               => '0',
84
		self::CIRCLES_LOCAL_GSKEY              => '',
85
		self::CIRCLES_ALLOW_NON_SSL_LINKS      => '0',
86
		self::CIRCLES_NON_SSL_LOCAL            => '0',
87
		self::CIRCLES_SELF_SIGNED              => '0',
88
		self::LOCAL_CLOUD_ID                   => '',
89
		self::FORCE_NC_BASE                    => '',
90
		self::CIRCLES_ACTIVITY_ON_CREATION     => '1',
91
		self::CIRCLES_SKIP_INVITATION_STEP     => '0',
92
		self::CIRCLES_SEARCH_FROM_COLLABORATOR => '0'
93
	];
94
95
	/** @var string */
96
	private $appName;
97
98
	/** @var IConfig */
99
	private $config;
100
101
	/** @var string */
102
	private $userId;
103
104
	/** @var IRequest */
105
	private $request;
106
107
	/** @var IURLGenerator */
108
	private $urlGenerator;
109
110
	/** @var MiscService */
111
	private $miscService;
112
113
	/** @var int */
114
	private $allowedCircle = -1;
115
116
	/** @var int */
117
	private $allowedLinkedGroups = -1;
118
119
	/** @var int */
120
	private $allowedFederatedCircles = -1;
121
122
	/** @var int */
123
	private $allowedNonSSLLinks = -1;
124
125
	/** @var int */
126
	private $localNonSSL = -1;
127
128
	/**
129
	 * ConfigService constructor.
130
	 *
131
	 * @param string $appName
132
	 * @param IConfig $config
133
	 * @param IRequest $request
134
	 * @param string $userId
135
	 * @param IURLGenerator $urlGenerator
136
	 * @param MiscService $miscService
137
	 */
138
	public function __construct(
139
		$appName, IConfig $config, IRequest $request, $userId, IURLGenerator $urlGenerator,
140
		MiscService $miscService
141
	) {
142
		$this->appName = $appName;
143
		$this->config = $config;
144
		$this->request = $request;
145
		$this->userId = $userId;
146
		$this->urlGenerator = $urlGenerator;
147
		$this->miscService = $miscService;
148
	}
149
150
151
	/**
152
	 * @return string
153
	 * @deprecated
154
	 */
155
	public function getLocalAddress() {
156
		return (($this->isLocalNonSSL()) ? 'http://' : '')
157
			   . $this->request->getServerHost();
158
	}
159
160
161
	/**
162
	 * returns if this type of circle is allowed by the current configuration.
163
	 *
164
	 * @param $type
165
	 *
166
	 * @return int
167
	 */
168
	public function isCircleAllowed($type) {
169
		if ($this->allowedCircle === -1) {
170
			$this->allowedCircle = (int)$this->getAppValue(self::CIRCLES_ALLOW_CIRCLES);
171
		}
172
173
		return ((int)$type & (int)$this->allowedCircle);
174
	}
175
176
177
	/**
178
	 * @return bool
179
	 * @throws GSStatusException
180
	 */
181
	public function isLinkedGroupsAllowed() {
182
		if ($this->allowedLinkedGroups === -1) {
183
			$allowed = ($this->getAppValue(self::CIRCLES_ALLOW_LINKED_GROUPS) === '1'
184
						&& !$this->getGSStatus(self::GS_ENABLED));
185
			$this->allowedLinkedGroups = ($allowed) ? 1 : 0;
186
		}
187
188
		return ($this->allowedLinkedGroups === 1);
189
	}
190
191
192
	/**
193
	 * @return bool
194
	 */
195
	public function isFederatedCirclesAllowed() {
196
		if ($this->allowedFederatedCircles === -1) {
197
			$this->allowedFederatedCircles =
198
				(int)$this->getAppValue(self::CIRCLES_ALLOW_FEDERATED_CIRCLES);
199
		}
200
201
		return ($this->allowedFederatedCircles === 1);
202
	}
203
204
	/**
205
	 * @return bool
206
	 */
207
	public function isInvitationSkipped() {
208
		return (int)$this->getAppValue(self::CIRCLES_SKIP_INVITATION_STEP) === 1;
209
	}
210
211
	/**
212
	 * @return bool
213
	 */
214
	public function isLocalNonSSL() {
215
		if ($this->localNonSSL === -1) {
216
			$this->localNonSSL =
217
				(int)$this->getAppValue(self::CIRCLES_NON_SSL_LOCAL);
218
		}
219
220
		return ($this->localNonSSL === 1);
221
	}
222
223
224
	/**
225
	 * @return bool
226
	 */
227
	public function isNonSSLLinksAllowed() {
228
		if ($this->allowedNonSSLLinks === -1) {
229
			$this->allowedNonSSLLinks =
230
				(int)$this->getAppValue(self::CIRCLES_ALLOW_NON_SSL_LINKS);
231
		}
232
233
		return ($this->allowedNonSSLLinks === 1);
234
	}
235
236
237
	/**
238
	 * @param string $remote
239
	 *
240
	 * @return string
241
	 */
242
	public function generateRemoteHost($remote) {
243
		if ((!$this->isNonSSLLinksAllowed() || strpos($remote, 'http://') !== 0)
244
			&& strpos($remote, 'https://') !== 0
245
		) {
246
			$remote = 'https://' . $remote;
247
		}
248
249
		return rtrim($remote, '/');
250
	}
251
252
253
	/**
254
	 * Get a value by key
255
	 *
256
	 * @param string $key
257
	 *
258
	 * @return string
259
	 */
260
	public function getCoreValue($key) {
261
		$defaultValue = null;
262
263
		return $this->config->getAppValue('core', $key, $defaultValue);
264
	}
265
266
	/**
267
	 * Get a value by key
268
	 *
269
	 * @param string $key
270
	 *
271
	 * @return string
272
	 */
273
	public function getSystemValue($key) {
274
		$defaultValue = null;
275
276
		return $this->config->getSystemValue($key, $defaultValue);
277
	}
278
279
280
	/**
281
	 * Get available hosts
282
	 *
283
	 * @return array
284
	 */
285
	public function getAvailableHosts(): array {
286
		return $this->config->getSystemValue('trusted_domains', []);
287
	}
288
289
290
	/**
291
	 * Get a value by key
292
	 *
293
	 * @param string $key
294
	 *
295
	 * @return string
296
	 */
297
	public function getAppValue($key) {
298
		$defaultValue = null;
299
300
		if (array_key_exists($key, $this->defaults)) {
301
			$defaultValue = $this->defaults[$key];
302
		}
303
304
		return $this->config->getAppValue($this->appName, $key, $defaultValue);
305
	}
306
307
	/**
308
	 * Set a value by key
309
	 *
310
	 * @param string $key
311
	 * @param string $value
312
	 *
313
	 * @return void
314
	 */
315
	public function setAppValue($key, $value) {
316
		$this->config->setAppValue($this->appName, $key, $value);
317
	}
318
319
	/**
320
	 * remove a key
321
	 *
322
	 * @param string $key
323
	 *
324
	 * @return string
325
	 */
326
	public function deleteAppValue($key) {
327
		return $this->config->deleteAppValue($this->appName, $key);
328
	}
329
330
	/**
331
	 * Get a user value by key
332
	 *
333
	 * @param string $key
334
	 *
335
	 * @return string
336
	 */
337
	public function getUserValue($key) {
338
		return $this->config->getUserValue($this->userId, $this->appName, $key);
339
	}
340
341
	/**
342
	 * Set a user value by key
343
	 *
344
	 * @param string $key
345
	 * @param string $value
346
	 *
347
	 * @return string
348
	 * @throws PreConditionNotMetException
349
	 */
350
	public function setUserValue($key, $value) {
351
		return $this->config->setUserValue($this->userId, $this->appName, $key, $value);
352
	}
353
354
355
	/**
356
	 * Get a user value by key and user
357
	 *
358
	 * @param string $userId
359
	 * @param string $key
360
	 *
361
	 * @param string $default
362
	 *
363
	 * @return string
364
	 */
365
	public function getCoreValueForUser($userId, $key, $default = '') {
366
		return $this->config->getUserValue($userId, 'core', $key, $default);
367
	}
368
369
370
	/**
371
	 * Get a user value by key and user
372
	 *
373
	 * @param string $userId
374
	 * @param string $key
375
	 *
376
	 * @return string
377
	 */
378
	public function getValueForUser($userId, $key) {
379
		return $this->config->getUserValue($userId, $this->appName, $key);
380
	}
381
382
	/**
383
	 * Set a user value by key
384
	 *
385
	 * @param string $userId
386
	 * @param string $key
387
	 * @param string $value
388
	 *
389
	 * @return string
390
	 * @throws PreConditionNotMetException
391
	 */
392
	public function setValueForUser($userId, $key, $value) {
393
		return $this->config->setUserValue($userId, $this->appName, $key, $value);
394
	}
395
396
	/**
397
	 * return the cloud version.
398
	 * if $complete is true, return a string x.y.z
399
	 *
400
	 * @param boolean $complete
401
	 *
402
	 * @return string|integer
403
	 */
404
	public function getCloudVersion($complete = false) {
405
		$ver = Util::getVersion();
406
407
		if ($complete) {
408
			return implode('.', $ver);
409
		}
410
411
		return $ver[0];
412
	}
413
414
415
	/**
416
	 * @return bool
417
	 */
418
	public function isAccountOnly() {
419
		return ($this->getAppValue(self::CIRCLES_ACCOUNTS_ONLY) === '1');
420
	}
421
422
423
	/**
424
	 * @return bool
425
	 */
426
	public function isContactsBackend(): bool {
427
		return ($this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND) !== '0'
428
				&& $this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND) !== '');
429
	}
430
431
432
	/**
433
	 * @return int
434
	 */
435
	public function contactsBackendType(): int {
436
		return (int)$this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND);
437
	}
438
439
440
	/**
441
	 * @return bool
442
	 */
443
	public function stillFrontEnd(): bool {
444
		if (!$this->isContactsBackend()) {
445
			return true;
446
		}
447
448
		if ($this->getAppValue(self::CIRCLES_STILL_FRONTEND) === '1') {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $this->getAppValu...TILL_FRONTEND) === '1';.
Loading history...
449
			return true;
450
		}
451
452
		return false;
453
	}
454
455
456
	/**
457
	 * should the password for a mail share be send to the recipient
458
	 *
459
	 * @return bool
460
	 */
461
	public function sendPasswordByMail() {
462
		if ($this->isContactsBackend()) {
463
			return false;
464
		}
465
466
		return ($this->config->getAppValue('sharebymail', 'sendpasswordmail', 'yes') === 'yes');
467
	}
468
469
	/**
470
	 * do we require a share by mail to be password protected
471
	 *
472
	 * @param Circle $circle
473
	 *
474
	 * @return bool
475
	 */
476
	public function enforcePasswordProtection(Circle $circle) {
477
		if ($this->isContactsBackend()) {
478
			return false;
479
		}
480
481
		if ($circle->getSetting('password_enforcement') === 'true') {
482
			return true;
483
		}
484
485
		return ($this->config->getAppValue('sharebymail', 'enforcePasswordProtection', 'no') === 'yes');
486
	}
487
488
489
	/**
490
	 * @param string $type
491
	 *
492
	 * @return array|bool|mixed
493
	 * @throws GSStatusException
494
	 */
495
	public function getGSStatus(string $type = '') {
496
		$enabled = $this->config->getSystemValueBool('gs.enabled', false);
497
		$lookup = $this->config->getSystemValue('lookup_server', '');
498
499
		if ($lookup === '' || !$enabled) {
500
			if ($type === self::GS_ENABLED) {
501
				return false;
502
			}
503
504
			throw new GSStatusException('GS and lookup are not configured : ' . $lookup . ', ' . $enabled);
505
		}
506
507
		$clef = $this->config->getSystemValue('gss.jwt.key', '');
508
		$mode = $this->config->getSystemValue('gss.mode', '');
509
510
		switch ($type) {
511
			case self::GS_ENABLED:
512
				return $enabled;
513
514
			case self::GS_MODE:
515
				return $mode;
516
517
			case self::GS_KEY:
518
				return $clef;
519
520
			case self::GS_LOOKUP:
521
				return $lookup;
522
		}
523
524
		return [
525
			self::GS_ENABLED => $enabled,
526
			self::GS_LOOKUP  => $lookup,
527
			self::GS_MODE    => $clef,
528
			self::GS_KEY     => $mode,
529
		];
530
	}
531
532
533
	/**
534
	 * @return array
535
	 */
536
	public function getTrustedDomains(): array {
537
		return array_values($this->config->getSystemValue('trusted_domains', []));
538
	}
539
540
541
	/**
542
	 * - returns host+port, does not specify any protocol
543
	 * - can be forced using LOCAL_CLOUD_ID
544
	 * - use 'overwrite.cli.url'
545
	 * - can use the first entry from trusted_domains is LOCAL_CLOUD_ID = 'use-trusted-domain'
546
	 * - used mainly to assign instance and source to a request
547
	 * - important only in remote environment; can be totally random in a jailed environment
548
	 *
549
	 * @return string
550
	 */
551
	public function getLocalInstance(): string {
552
		$localCloudId = $this->getAppValue(self::LOCAL_CLOUD_ID);
553
		if ($localCloudId === '') {
554
			$cliUrl = $this->config->getSystemValue('overwrite.cli.url', '');
555
			$local = parse_url($cliUrl);
556
			if (array_key_exists('port', $local)) {
557
				return $local['host'] . ':' . $local['port'];
558
			} else {
559
				return $local['host'];
560
			}
561
		} else if ($localCloudId === 'use-trusted-domain') {
562
			return $this->getTrustedDomains()[0];
563
		} else {
564
			return $localCloudId;
565
		}
566
	}
567
568
569
	/**
570
	 * @param string $instance
571
	 *
572
	 * @return bool
573
	 */
574
	public function isLocalInstance(string $instance): bool {
575
		if ($instance === $this->getLocalInstance()) {
576
			return true;
577
		}
578
579
		if ($this->getAppValue(self::LOCAL_CLOUD_ID) === 'use-trusted-domain') {
580
			return (in_array($instance, $this->getTrustedDomains()));
581
		}
582
583
		return false;
584
	}
585
586
587
	/**
588
	 * @param NC19Request $request
589
	 * @param string $routeName
590
	 * @param array $args
591
	 */
592
	public function configureRequest(NC19Request $request, string $routeName = '', array $args = []) {
593
		$this->configureRequestAddress($request, $routeName, $args);
594
595
		$request->setVerifyPeer($this->getAppValue(ConfigService::CIRCLES_SELF_SIGNED) !== '1');
596
		$request->setLocalAddressAllowed(true);
597
	}
598
599
	/**
600
	 * - Create route using overwrite.cli.url.
601
	 * - can be forced using FORCE_NC_BASE or TEST_BC_BASE (temporary)
602
	 * - can also be overwritten in config/config.php: 'circles.force_nc_base'
603
	 * - perfect for loopback request.
604
	 *
605
	 * @param NC19Request $request
606
	 * @param string $routeName
607
	 * @param array $args
608
	 *
609
	 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
610
	 */
611
	private function configureRequestAddress(NC19Request $request, string $routeName, array $args = []) {
612
		if ($routeName === '') {
613
			return;
614
		}
615
616
		$ncBase = ($this->getAppValue(self::TEST_NC_BASE)) ?
617
			$this->getAppValue(self::TEST_NC_BASE) : $this->getForcedNcBase();
618
619
		if ($ncBase !== '') {
620
			$absolute = rtrim($ncBase, '/') . $this->urlGenerator->linkToRoute($routeName, $args);
621
		} else {
622
			$absolute = $this->urlGenerator->linkToRouteAbsolute($routeName, $args);
623
		}
624
625
		$request->basedOnUrl($absolute);
626
	}
627
628
629
	/**
630
	 * - return force_nc_base from config/config.php, then from FORCE_NC_BASE.
631
	 *
632
	 * @return string
633
	 */
634
	private function getForcedNcBase(): string {
635
		$fromConfig = $this->config->getSystemValue('circles.force_nc_base', '');
636
		if ($fromConfig !== '') {
637
			return $fromConfig;
638
		}
639
640
		return $this->getAppValue(self::FORCE_NC_BASE);
641
	}
642
643
}
644
645