Completed
Pull Request — master (#23530)
by Lukas
11:27
created

Auth::auth()   C

Complexity

Conditions 14
Paths 15

Size

Total Lines 45
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 45
rs 5.0864
cc 14
eloc 30
nc 15
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Jakob Sack <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Markus Goetz <[email protected]>
8
 * @author Michael Gapczynski <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 *
14
 * @copyright Copyright (c) 2016, ownCloud, Inc.
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
namespace OCA\DAV\Connector\Sabre;
31
32
use Exception;
33
use OC\AppFramework\Http\Request;
34
use OCP\IRequest;
35
use OCP\ISession;
36
use OCP\IUserSession;
37
use Sabre\DAV\Auth\Backend\AbstractBasic;
38
use Sabre\DAV\Exception\NotAuthenticated;
39
use Sabre\DAV\Exception\ServiceUnavailable;
40
use Sabre\HTTP\RequestInterface;
41
use Sabre\HTTP\ResponseInterface;
42
43
class Auth extends AbstractBasic {
44
	const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND';
45
46
	/** @var ISession */
47
	private $session;
48
	/** @var IUserSession */
49
	private $userSession;
50
	/** @var IRequest */
51
	private $request;
52
	/** @var string */
53
	private $currentUser;
54
55
	/**
56
	 * @param ISession $session
57
	 * @param IUserSession $userSession
58
	 * @param IRequest $request
59
	 * @param string $principalPrefix
60
	 */
61
	public function __construct(ISession $session,
62
								IUserSession $userSession,
63
								IRequest $request,
64
								$principalPrefix = 'principals/users/') {
65
		$this->session = $session;
66
		$this->userSession = $userSession;
67
		$this->request = $request;
68
		$this->principalPrefix = $principalPrefix;
69
	}
70
71
	/**
72
	 * Whether the user has initially authenticated via DAV
73
	 *
74
	 * This is required for WebDAV clients that resent the cookies even when the
75
	 * account was changed.
76
	 *
77
	 * @see https://github.com/owncloud/core/issues/13245
78
	 *
79
	 * @param string $username
80
	 * @return bool
81
	 */
82
	public function isDavAuthenticated($username) {
83
		return !is_null($this->session->get(self::DAV_AUTHENTICATED)) &&
84
		$this->session->get(self::DAV_AUTHENTICATED) === $username;
85
	}
86
87
	/**
88
	 * Validates a username and password
89
	 *
90
	 * This method should return true or false depending on if login
91
	 * succeeded.
92
	 *
93
	 * @param string $username
94
	 * @param string $password
95
	 * @return bool
96
	 */
97
	protected function validateUserPass($username, $password) {
98
		if ($this->userSession->isLoggedIn() &&
99
			$this->isDavAuthenticated($this->userSession->getUser()->getUID())
100
		) {
101
			\OC_Util::setupFS($this->userSession->getUser()->getUID());
102
			$this->session->close();
103
			return true;
104
		} else {
105
			\OC_Util::setUpFS(); //login hooks may need early access to the filesystem
106
			if($this->userSession->login($username, $password)) {
107
				\OC_Util::setUpFS($this->userSession->getUser()->getUID());
108
				$this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID());
109
				$this->session->close();
110
				return true;
111
			} else {
112
				$this->session->close();
113
				return false;
114
			}
115
		}
116
	}
117
118
	/**
119
	 * @param RequestInterface $request
120
	 * @param ResponseInterface $response
121
	 * @return array
122
	 * @throws NotAuthenticated
123
	 * @throws ServiceUnavailable
124
	 */
125
	function check(RequestInterface $request, ResponseInterface $response) {
126
		try {
127
			$result = $this->auth($request, $response);
128
			return $result;
129
		} catch (NotAuthenticated $e) {
0 ignored issues
show
Bug introduced by
The class Sabre\DAV\Exception\NotAuthenticated does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
130
			throw $e;
131
		} catch (Exception $e) {
132
			$class = get_class($e);
133
			$msg = $e->getMessage();
134
			throw new ServiceUnavailable("$class: $msg");
135
		}
136
	}
137
138
	/**
139
	 * Checks whether a CSRF check is required on the request
140
	 *
141
	 * @return bool
142
	 */
143
	private function requiresCSRFCheck() {
144
		// GET requires no check at all
145
		if($this->request->getMethod() === 'GET') {
146
			return false;
147
		}
148
149
		// Official ownCloud clients require no checks
150
		if($this->request->isUserAgent([
151
			Request::USER_AGENT_OWNCLOUD_DESKTOP,
152
			Request::USER_AGENT_OWNCLOUD_ANDROID,
153
			Request::USER_AGENT_OWNCLOUD_IOS,
154
		])) {
155
			return false;
156
		}
157
158
		// If not logged-in no check is required
159
		if(!$this->userSession->isLoggedIn()) {
160
			return false;
161
		}
162
163
		// POST always requires a check
164
		if($this->request->getMethod() === 'POST') {
165
			return true;
166
		}
167
168
		// If logged-in AND DAV authenticated no check is required
169
		if($this->userSession->isLoggedIn() &&
170
			$this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
171
			return false;
172
		}
173
174
		return true;
175
	}
176
177
	/**
178
	 * @param RequestInterface $request
179
	 * @param ResponseInterface $response
180
	 * @return array
181
	 * @throws NotAuthenticated
182
	 */
183
	private function auth(RequestInterface $request, ResponseInterface $response) {
184
		$forcedLogout = false;
185
		if(!$this->request->passesCSRFCheck() &&
186
			$this->requiresCSRFCheck()) {
187
			// In case of a fail with POST we need to recheck the credentials
188
			if($this->request->getMethod() === 'POST') {
189
				$forcedLogout = true;
190
			} else {
191
				$response->setStatus(401);
192
				throw new \Sabre\DAV\Exception\NotAuthenticated('CSRF check not passed.');
193
			}
194
		}
195
196
		if($forcedLogout) {
197
			$this->userSession->logout();
198
		} else {
199
			if (\OC_User::handleApacheAuth() ||
200
				//Fix for broken webdav clients
201
				($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) ||
202
				//Well behaved clients that only send the cookie are allowed
203
				($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && $request->getHeader('Authorization') === null)
204
			) {
205
				$user = $this->userSession->getUser()->getUID();
206
				\OC_Util::setupFS($user);
207
				$this->currentUser = $user;
208
				$this->session->close();
209
				return [true, $this->principalPrefix . $user];
210
			}
211
		}
212
213
		if (!$this->userSession->isLoggedIn() && in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With')))) {
214
			// do not re-authenticate over ajax, use dummy auth name to prevent browser popup
215
			$response->addHeader('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"');
216
			$response->setStatus(401);
217
			throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
218
		}
219
220
		$data = parent::check($request, $response);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (check() instead of auth()). Are you sure this is correct? If so, you might want to change this to $this->check().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
221
		if($data[0] === true) {
222
			$startPos = strrpos($data[1], '/') + 1;
223
			$user = $this->userSession->getUser()->getUID();
224
			$data[1] = substr_replace($data[1], $user, $startPos);
225
		}
226
		return $data;
227
	}
228
}
229