Completed
Pull Request — master (#718)
by Pauli
08:54 queued 07:34
created

SubsonicMiddleware::credentialsAreValid()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
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 Pauli Järvinen <[email protected]>
10
 * @copyright Pauli Järvinen 2019
11
 */
12
13
namespace OCA\Music\Middleware;
14
15
use \OCP\IRequest;
0 ignored issues
show
Bug introduced by
The type OCP\IRequest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use \OCP\AppFramework\Http\Response;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use \OCP\AppFramework\Middleware;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Middleware was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
19
use \OCA\Music\AppFramework\BusinessLayer\BusinessLayerException;
20
use \OCA\Music\Controller\SubsonicController;
21
use \OCA\Music\Db\AmpacheUserMapper;
22
use \OCA\Music\Utility\Util;
23
24
25
/**
26
 * Checks the authentication on each Subsonic API call before the
27
 * request is allowed to be passed to SubsonicController.
28
 * Map SubsonicExceptions from the controller to proper Subsonic error results.
29
 */
30
class SubsonicMiddleware extends Middleware {
31
	private $request;
32
	private $userMapper;
33
34
	public function __construct(IRequest $request, AmpacheUserMapper $userMapper) {
35
		$this->request = $request;
36
		$this->userMapper = $userMapper;
37
	}
38
39
	/**
40
	 * This function is run before any HTTP request handler method, but it does
41
	 * nothing if the call in question is not routed to SubsonicController. In
42
	 * case of Subsonic call, this checks the user authentication.
43
	 * 
44
	 * @param Controller $controller the controller that is being called
0 ignored issues
show
Bug introduced by
The type OCA\Music\Middleware\Controller was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
45
	 * @param string $methodName the name of the method
46
	 * @throws SubsonicException when a security check fails
47
	 */
48
	public function beforeController($controller, $methodName) {
49
		if ($controller instanceof SubsonicController) {
50
			$user = $this->request->getParam('u');
51
			$pass = $this->request->getParam('p');
52
53
			if ($user === null || $pass === null) {
54
				if ($this->request->getParam('t') !== null) {
55
					throw new SubsonicException('Token-based authentication not supported', 41);
56
				} else {
57
					throw new SubsonicException('Required credentials missing', 10);
58
				}
59
			}
60
61
			// The password may be given in hexadecimal format
62
			if (Util::startsWith($pass, 'enc:')) {
63
				$pass = \hex2bin(\substr($pass, \strlen('enc:')));
64
			}
65
66
			if ($this->credentialsAreValid($user, $pass)) {
67
				$controller->setAuthenticatedUser($user);
68
			} else {
69
				throw new SubsonicException('Invalid Login', 40);
70
			}
71
		}
72
	}
73
74
	/**
75
	 * @param string $user Username
76
	 * @param string $pass Password
77
	 * @return boolean
78
	 */
79
	private function credentialsAreValid($user, $pass) {
80
		$hashes = $this->userMapper->getPasswordHashes($user);
81
82
		foreach ($hashes as $hash) {
83
			if ($hash === \hash('sha256', $pass)) {
84
				return true;
85
			}
86
		}
87
88
		return false;
89
	}
90
91
	/**
92
	 * Catch SubsonicException and BusinessLayerExcpetion instances thrown when handling
93
	 * Subsonic requests, and render the the appropiate Subsonic error response. Any other
94
	 * exceptions are allowed to flow through, reaching eventually the default handler if
95
	 * no-one else intercepts them. The default handler logs the error and returns response
96
	 * code 500.
97
	 * 
98
	 * @param Controller $controller the controller that was being called
99
	 * @param string $methodName the name of the method that was called on the controller
100
	 * @param \Exception $exception the thrown exception
101
	 * @throws \Exception the passed in exception if it couldn't be handled
102
	 * @return Response a Response object in case the exception could be handled
103
	 */
104
	public function afterException($controller, $methodName, \Exception $exception) {
105
		if ($controller instanceof SubsonicController) {
106
			if ($exception instanceof SubsonicException) {
107
				return $controller->subsonicErrorResponse(
108
						$exception->getCode(),
109
						$exception->getMessage()
110
				);
111
			}
112
			elseif ($exception instanceof BusinessLayerException) {
113
				return $controller->subsonicErrorResponse(70, 'Entity not found');
114
			}
115
		}
116
		throw $exception;
117
	}
118
}
119