Passed
Push — master ( 67e138...790a65 )
by Pauli
14:32
created

SettingController::getSubsonicUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Morris Jobke <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Morris Jobke 2013, 2014
12
 * @copyright Pauli Järvinen 2017 - 2022
13
 */
14
15
namespace OCA\Music\Controller;
16
17
use OCP\AppFramework\Controller;
18
use OCP\AppFramework\Http;
19
use OCP\AppFramework\Http\JSONResponse;
20
use OCP\IRequest;
21
use OCP\IURLGenerator;
22
use OCP\Security\ISecureRandom;
23
24
use OCA\Music\AppFramework\Core\Logger;
25
use OCA\Music\Db\AmpacheUserMapper;
26
use OCA\Music\Http\ErrorResponse;
27
use OCA\Music\Utility\AppInfo;
28
use OCA\Music\Utility\LibrarySettings;
29
use OCA\Music\Utility\Scanner;
30
use OCA\Music\Utility\Util;
31
32
class SettingController extends Controller {
33
	const DEFAULT_PASSWORD_LENGTH = 10;
34
	/* Character set without look-alike characters. Similar but even more stripped set would be found
35
	 * on Nextcloud as ISecureRandom::CHAR_HUMAN_READABLE but that's not availalbe on ownCloud. */
36
	const API_KEY_CHARSET = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
37
38
	private $ampacheUserMapper;
39
	private $scanner;
40
	private $userId;
41
	private $librarySettings;
42
	private $secureRandom;
43
	private $urlGenerator;
44
	private $logger;
45
46
	public function __construct(string $appName,
47
								IRequest $request,
48
								AmpacheUserMapper $ampacheUserMapper,
49
								Scanner $scanner,
50
								?string $userId,
51
								LibrarySettings $librarySettings,
52
								ISecureRandom $secureRandom,
53
								IURLGenerator $urlGenerator,
54
								Logger $logger) {
55
		parent::__construct($appName, $request);
56
57
		$this->ampacheUserMapper = $ampacheUserMapper;
58
		$this->scanner = $scanner;
59
		$this->userId = $userId ?? ''; // ensure non-null to satisfy Scrutinizer; the null case should happen only when the user has already logged out
60
		$this->librarySettings = $librarySettings;
61
		$this->secureRandom = $secureRandom;
62
		$this->urlGenerator = $urlGenerator;
63
		$this->logger = $logger;
64
	}
65
66
	/**
67
	 * @NoAdminRequired
68
	 * @NoCSRFRequired
69
	 * @UseSession to keep the session reserved while execution in progress
70
	 */
71
	public function userPath(string $value) {
72
		$prevPath = $this->librarySettings->getPath($this->userId);
73
		$success = $this->librarySettings->setPath($this->userId, $value);
74
75
		if ($success) {
76
			$this->scanner->updatePath($prevPath, $value, $this->userId);
77
		}
78
79
		return new JSONResponse(['success' => $success]);
80
	}
81
82
	/**
83
	 * @NoAdminRequired
84
	 * @NoCSRFRequired
85
	 */
86
	public function userExcludedPaths(array $value) {
87
		$success = $this->librarySettings->setExcludedPaths($this->userId, $value);
88
		return new JSONResponse(['success' => $success]);
89
	}
90
91
	/**
92
	 * @NoAdminRequired
93
	 * @NoCSRFRequired
94
	 */
95
	public function enableScanMetadata(bool $value) {
96
		$this->librarySettings->setScanMetadataEnabled($this->userId, $value);
97
		return new JSONResponse(['success' => true]);
98
	}
99
100
	/**
101
	 * @NoAdminRequired
102
	 * @NoCSRFRequired
103
	 */
104
	public function ignoredArticles(array $value) {
105
		$this->librarySettings->setIgnoredArticles($this->userId, $value);
106
		return new JSONResponse(['success' => true]);
107
	}
108
109
	/**
110
	 * @NoAdminRequired
111
	 * @NoCSRFRequired
112
	 */
113
	public function getAll() {
114
		return [
115
			'path' => $this->librarySettings->getPath($this->userId),
116
			'excludedPaths' => $this->librarySettings->getExcludedPaths($this->userId),
117
			'scanMetadata' => $this->librarySettings->getScanMetadataEnabled($this->userId),
118
			'ignoredArticles' => $this->librarySettings->getIgnoredArticles($this->userId),
119
			'ampacheUrl' => $this->getAmpacheUrl(),
120
			'subsonicUrl' => $this->getSubsonicUrl(),
121
			'ampacheKeys' => $this->getAmpacheKeys(),
122
			'appVersion' => AppInfo::getVersion(),
123
			'user' => $this->userId
124
		];
125
	}
126
127
	private function getAmpacheUrl() {
128
		return \str_replace('/server/xml.server.php', '',
129
				$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('music.ampache.xmlApi')));
130
	}
131
132
	private function getSubsonicUrl() {
133
		return \str_replace('/rest/dummy', '',
134
				$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute(
135
						'music.subsonic.handleRequest', ['method' => 'dummy'])));
136
	}
137
138
	private function getAmpacheKeys() {
139
		return $this->ampacheUserMapper->getAll($this->userId);
140
	}
141
142
	private function storeUserKey($description, $password) {
143
		$hash = \hash('sha256', $password);
144
		$description = Util::truncate($description, 64); // some DB setups can't truncate automatically to column max size
145
		return $this->ampacheUserMapper->addUserKey($this->userId, $hash, $description);
146
	}
147
148
	/**
149
	 * @NoAdminRequired
150
	 * @CORS
151
	 */
152
	public function generateUserKey($length, $description) {
153
		if ($length == null || $length < self::DEFAULT_PASSWORD_LENGTH) {
154
			$length = self::DEFAULT_PASSWORD_LENGTH;
155
		}
156
157
		$password = $this->secureRandom->generate($length, self::API_KEY_CHARSET);
158
159
		$id = $this->storeUserKey($description, $password);
160
161
		if ($id === null) {
162
			return new ErrorResponse(Http::STATUS_INTERNAL_SERVER_ERROR, 'Error while saving the credentials');
163
		}
164
165
		return new JSONResponse(['id' => $id, 'password' => $password, 'description' => $description], Http::STATUS_CREATED);
166
	}
167
168
	/**
169
	 * @NoAdminRequired
170
	 * @NoCSRFRequired
171
	 */
172
	public function removeUserKey($id) {
173
		$this->ampacheUserMapper->removeUserKey($this->userId, (int)$id);
174
		return new JSONResponse(['success' => true]);
175
	}
176
}
177