Passed
Pull Request — master (#1266)
by Matthew
04:57
created

SettingController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 11
dl 0
loc 22
rs 9.9332
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 - 2025
13
 */
14
15
namespace OCA\Music\Controller;
16
17
use OCA\Music\Service\ScrobblerService;
18
use OCP\AppFramework\Controller;
19
use OCP\AppFramework\Http;
20
use OCP\AppFramework\Http\JSONResponse;
21
use OCP\IRequest;
22
use OCP\IURLGenerator;
23
use OCP\Security\ISecureRandom;
24
25
use OCA\Music\AppFramework\Core\Logger;
26
use OCA\Music\Db\AmpacheSessionMapper;
27
use OCA\Music\Db\AmpacheUserMapper;
28
use OCA\Music\Http\ErrorResponse;
29
use OCA\Music\Service\LibrarySettings;
30
use OCA\Music\Service\Scanner;
31
use OCA\Music\Utility\AppInfo;
32
use OCA\Music\Utility\StringUtil;
33
34
class SettingController extends Controller {
35
	const DEFAULT_PASSWORD_LENGTH = 10;
36
	/* Character set without look-alike characters. Similar but even more stripped set would be found
37
	 * on Nextcloud as ISecureRandom::CHAR_HUMAN_READABLE but that's not available on ownCloud. */
38
	const API_KEY_CHARSET = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
39
40
	private AmpacheSessionMapper $ampacheSessionMapper;
41
	private AmpacheUserMapper $ampacheUserMapper;
42
	private Scanner $scanner;
43
	private string $userId;
44
	private LibrarySettings $librarySettings;
45
	private ISecureRandom $secureRandom;
46
	private IURLGenerator $urlGenerator;
47
	private Logger $logger;
48
	/** @var ScrobblerService[] $scrobblerServices */
49
	private array $scrobblerServices;
50
51
	public function __construct(string $appName,
52
								IRequest $request,
53
								AmpacheSessionMapper $ampacheSessionMapper,
54
								AmpacheUserMapper $ampacheUserMapper,
55
								Scanner $scanner,
56
								?string $userId,
57
								LibrarySettings $librarySettings,
58
								ISecureRandom $secureRandom,
59
								IURLGenerator $urlGenerator,
60
								Logger $logger,
61
								array $scrobblerServices) {
62
		parent::__construct($appName, $request);
63
64
		$this->ampacheSessionMapper = $ampacheSessionMapper;
65
		$this->ampacheUserMapper = $ampacheUserMapper;
66
		$this->scanner = $scanner;
67
		$this->userId = $userId ?? ''; // ensure non-null to satisfy Scrutinizer; the null case should happen only when the user has already logged out
68
		$this->librarySettings = $librarySettings;
69
		$this->secureRandom = $secureRandom;
70
		$this->urlGenerator = $urlGenerator;
71
		$this->logger = $logger;
72
		$this->scrobblerServices = $scrobblerServices;
73
	}
74
75
	/**
76
	 * @NoAdminRequired
77
	 * @NoCSRFRequired
78
	 * @UseSession to keep the session reserved while execution in progress
79
	 */
80
	public function userPath(string $value) : JSONResponse {
81
		$prevPath = $this->librarySettings->getPath($this->userId);
82
		$success = $this->librarySettings->setPath($this->userId, $value);
83
84
		if ($success) {
85
			$this->scanner->updatePath($prevPath, $value, $this->userId);
86
		}
87
88
		return new JSONResponse(['success' => $success]);
89
	}
90
91
	/**
92
	 * @NoAdminRequired
93
	 * @NoCSRFRequired
94
	 */
95
	public function userExcludedPaths(array $value) : JSONResponse {
96
		$success = $this->librarySettings->setExcludedPaths($this->userId, $value);
97
		return new JSONResponse(['success' => $success]);
98
	}
99
100
	/**
101
	 * @NoAdminRequired
102
	 * @NoCSRFRequired
103
	 */
104
	public function enableScanMetadata(bool $value) : JSONResponse {
105
		$this->librarySettings->setScanMetadataEnabled($this->userId, $value);
106
		return new JSONResponse(['success' => true]);
107
	}
108
109
	/**
110
	 * @NoAdminRequired
111
	 * @NoCSRFRequired
112
	 */
113
	public function ignoredArticles(array $value) : JSONResponse {
114
		$this->librarySettings->setIgnoredArticles($this->userId, $value);
115
		return new JSONResponse(['success' => true]);
116
	}
117
118
	/**
119
	 * @NoAdminRequired
120
	 * @NoCSRFRequired
121
	 */
122
	public function getAll() : JSONResponse {
123
		return new JSONResponse([
124
			'path' => $this->librarySettings->getPath($this->userId),
125
			'excludedPaths' => $this->librarySettings->getExcludedPaths($this->userId),
126
			'scanMetadata' => $this->librarySettings->getScanMetadataEnabled($this->userId),
127
			'ignoredArticles' => $this->librarySettings->getIgnoredArticles($this->userId),
128
			'ampacheUrl' => $this->getAmpacheUrl(),
129
			'subsonicUrl' => $this->getSubsonicUrl(),
130
			'ampacheKeys' => $this->ampacheUserMapper->getAll($this->userId),
131
			'appVersion' => AppInfo::getVersion(),
132
			'user' => $this->userId,
133
			'scrobblers' => $this->getScrobbleAuth()
134
		]);
135
	}
136
137
	/**
138
	 * @NoAdminRequired
139
	 * @NoCSRFRequired
140
	 */
141
	public function getUserKeys() : JSONResponse {
142
		return new JSONResponse($this->ampacheUserMapper->getAll($this->userId));
143
	}
144
145
	private function getAmpacheUrl() : string {
146
		return (string)\str_replace('/server/xml.server.php', '',
147
				$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('music.ampache.xmlApi')));
148
	}
149
150
	private function getSubsonicUrl() : string {
151
		return (string)\str_replace('/rest/dummy', '',
152
				$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute(
153
						'music.subsonic.handleRequest', ['method' => 'dummy'])));
154
	}
155
156
	private function getScrobbleAuth(): array {
157
		$services = [];
158
		foreach ($this->scrobblerServices as $scrobblerService) {
159
			$tokenRequestUrl = $scrobblerService->getTokenRequestUrl();
160
			$services[] = [
161
				'service' => $scrobblerService->getName(),
162
				'identifier' => $scrobblerService->getIdentifier(),
163
				'configured' => $tokenRequestUrl && $scrobblerService->getApiSecret(),
164
				'tokenRequestUrl' => $tokenRequestUrl,
165
				'hasSession' => $scrobblerService->getApiSession($this->userId) !== null
166
			];
167
		}
168
169
		return $services;
170
	}
171
172
	private function storeUserKey(?string $description, string $password) : ?int {
173
		$hash = \hash('sha256', $password);
174
		$description = StringUtil::truncate($description, 64); // some DB setups can't truncate automatically to column max size
175
		return $this->ampacheUserMapper->addUserKey($this->userId, $hash, $description);
176
	}
177
178
	/**
179
	 * @NoAdminRequired
180
	 */
181
	public function createUserKey(?int $length, ?string $description) : JSONResponse {
182
		if ($length === null || $length < self::DEFAULT_PASSWORD_LENGTH) {
183
			$length = self::DEFAULT_PASSWORD_LENGTH;
184
		}
185
186
		$password = $this->secureRandom->generate($length, self::API_KEY_CHARSET);
187
188
		$id = $this->storeUserKey($description, $password);
189
190
		if ($id === null) {
191
			return new ErrorResponse(Http::STATUS_INTERNAL_SERVER_ERROR, 'Error while saving the credentials');
192
		}
193
194
		return new JSONResponse(['id' => $id, 'password' => $password, 'description' => $description], Http::STATUS_CREATED);
195
	}
196
197
	/**
198
	 * The CORS-version of the key creation function is targeted for external clients. We need separate function
199
	 * because the CORS middleware blocks the normal internal access on Nextcloud versions older than 25 as well
200
	 * as on ownCloud 10.0, at least (but not on OC 10.4+).
201
	 *
202
	 * @NoAdminRequired
203
	 * @CORS
204
	 */
205
	public function createUserKeyCors(?int $length, ?string $description) : JSONResponse {
206
		return $this->createUserKey($length, $description);
207
	}
208
209
	/**
210
	 * @NoAdminRequired
211
	 * @NoCSRFRequired
212
	 */
213
	public function removeUserKey(int $id) : JSONResponse {
214
		$this->ampacheSessionMapper->revokeSessions($id);
215
		$this->ampacheUserMapper->removeUserKey($this->userId, $id);
216
		return new JSONResponse(['success' => true]);
217
	}
218
}
219