Passed
Pull Request — master (#1266)
by Matthew
04:19
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
	private ScrobblerService $scrobblerService;
49
50
	public function __construct(string $appName,
51
								IRequest $request,
52
								AmpacheSessionMapper $ampacheSessionMapper,
53
								AmpacheUserMapper $ampacheUserMapper,
54
								Scanner $scanner,
55
								?string $userId,
56
								LibrarySettings $librarySettings,
57
								ISecureRandom $secureRandom,
58
								IURLGenerator $urlGenerator,
59
								Logger $logger,
60
								ScrobblerService $scrobblerService) {
61
		parent::__construct($appName, $request);
62
63
		$this->ampacheSessionMapper = $ampacheSessionMapper;
64
		$this->ampacheUserMapper = $ampacheUserMapper;
65
		$this->scanner = $scanner;
66
		$this->userId = $userId ?? ''; // ensure non-null to satisfy Scrutinizer; the null case should happen only when the user has already logged out
67
		$this->librarySettings = $librarySettings;
68
		$this->secureRandom = $secureRandom;
69
		$this->urlGenerator = $urlGenerator;
70
		$this->logger = $logger;
71
		$this->scrobblerService = $scrobblerService;
72
	}
73
74
	/**
75
	 * @NoAdminRequired
76
	 * @NoCSRFRequired
77
	 * @UseSession to keep the session reserved while execution in progress
78
	 */
79
	public function userPath(string $value) : JSONResponse {
80
		$prevPath = $this->librarySettings->getPath($this->userId);
81
		$success = $this->librarySettings->setPath($this->userId, $value);
82
83
		if ($success) {
84
			$this->scanner->updatePath($prevPath, $value, $this->userId);
85
		}
86
87
		return new JSONResponse(['success' => $success]);
88
	}
89
90
	/**
91
	 * @NoAdminRequired
92
	 * @NoCSRFRequired
93
	 */
94
	public function userExcludedPaths(array $value) : JSONResponse {
95
		$success = $this->librarySettings->setExcludedPaths($this->userId, $value);
96
		return new JSONResponse(['success' => $success]);
97
	}
98
99
	/**
100
	 * @NoAdminRequired
101
	 * @NoCSRFRequired
102
	 */
103
	public function enableScanMetadata(bool $value) : JSONResponse {
104
		$this->librarySettings->setScanMetadataEnabled($this->userId, $value);
105
		return new JSONResponse(['success' => true]);
106
	}
107
108
	/**
109
	 * @NoAdminRequired
110
	 * @NoCSRFRequired
111
	 */
112
	public function ignoredArticles(array $value) : JSONResponse {
113
		$this->librarySettings->setIgnoredArticles($this->userId, $value);
114
		return new JSONResponse(['success' => true]);
115
	}
116
117
	/**
118
	 * @NoAdminRequired
119
	 * @NoCSRFRequired
120
	 */
121
	public function getAll() : JSONResponse {
122
		return new JSONResponse([
123
			'path' => $this->librarySettings->getPath($this->userId),
124
			'excludedPaths' => $this->librarySettings->getExcludedPaths($this->userId),
125
			'scanMetadata' => $this->librarySettings->getScanMetadataEnabled($this->userId),
126
			'ignoredArticles' => $this->librarySettings->getIgnoredArticles($this->userId),
127
			'ampacheUrl' => $this->getAmpacheUrl(),
128
			'subsonicUrl' => $this->getSubsonicUrl(),
129
			'ampacheKeys' => $this->ampacheUserMapper->getAll($this->userId),
130
			'appVersion' => AppInfo::getVersion(),
131
			'user' => $this->userId,
132
			'scrobbler' => $this->getScrobbleAuth()
133
		]);
134
	}
135
136
	/**
137
	 * @NoAdminRequired
138
	 * @NoCSRFRequired
139
	 */
140
	public function getUserKeys() : JSONResponse {
141
		return new JSONResponse($this->ampacheUserMapper->getAll($this->userId));
142
	}
143
144
	private function getAmpacheUrl() : string {
145
		return (string)\str_replace('/server/xml.server.php', '',
146
				$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('music.ampache.xmlApi')));
147
	}
148
149
	private function getSubsonicUrl() : string {
150
		return (string)\str_replace('/rest/dummy', '',
151
				$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute(
152
						'music.subsonic.handleRequest', ['method' => 'dummy'])));
153
	}
154
155
	private function getScrobbleAuth(): array {
156
		$tokenRequestUrl = $this->scrobblerService->getTokenRequestUrl();
157
		return [
158
			'apiService' => $this->scrobblerService->getApiService(),
159
            'configured' => $tokenRequestUrl !== null,
160
            'tokenRequestUrl' => $tokenRequestUrl,
161
            'hasSession' => $this->scrobblerService->getApiSession($this->userId) !== null,
162
            'service' => $this->scrobblerService->getName()
163
		];
164
	}
165
166
	private function storeUserKey(?string $description, string $password) : ?int {
167
		$hash = \hash('sha256', $password);
168
		$description = StringUtil::truncate($description, 64); // some DB setups can't truncate automatically to column max size
169
		return $this->ampacheUserMapper->addUserKey($this->userId, $hash, $description);
170
	}
171
172
	/**
173
	 * @NoAdminRequired
174
	 */
175
	public function createUserKey(?int $length, ?string $description) : JSONResponse {
176
		if ($length === null || $length < self::DEFAULT_PASSWORD_LENGTH) {
177
			$length = self::DEFAULT_PASSWORD_LENGTH;
178
		}
179
180
		$password = $this->secureRandom->generate($length, self::API_KEY_CHARSET);
181
182
		$id = $this->storeUserKey($description, $password);
183
184
		if ($id === null) {
185
			return new ErrorResponse(Http::STATUS_INTERNAL_SERVER_ERROR, 'Error while saving the credentials');
186
		}
187
188
		return new JSONResponse(['id' => $id, 'password' => $password, 'description' => $description], Http::STATUS_CREATED);
189
	}
190
191
	/**
192
	 * The CORS-version of the key creation function is targeted for external clients. We need separate function
193
	 * because the CORS middleware blocks the normal internal access on Nextcloud versions older than 25 as well
194
	 * as on ownCloud 10.0, at least (but not on OC 10.4+).
195
	 *
196
	 * @NoAdminRequired
197
	 * @CORS
198
	 */
199
	public function createUserKeyCors(?int $length, ?string $description) : JSONResponse {
200
		return $this->createUserKey($length, $description);
201
	}
202
203
	/**
204
	 * @NoAdminRequired
205
	 * @NoCSRFRequired
206
	 */
207
	public function removeUserKey(int $id) : JSONResponse {
208
		$this->ampacheSessionMapper->revokeSessions($id);
209
		$this->ampacheUserMapper->removeUserKey($this->userId, $id);
210
		return new JSONResponse(['success' => true]);
211
	}
212
}
213