Passed
Push — master ( b6a24f...41eec8 )
by Daniel
15:31 queued 12s
created

TwoFactorMiddleware::beforeController()   D

Complexity

Conditions 18
Paths 24

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 18
eloc 22
c 1
b 0
f 0
nc 24
nop 2
dl 0
loc 40
rs 4.8666

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
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 *
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
namespace OC\Core\Middleware;
26
27
use Exception;
28
use OC\Authentication\Exceptions\TwoFactorAuthRequiredException;
29
use OC\Authentication\Exceptions\UserAlreadyLoggedInException;
30
use OC\Authentication\TwoFactorAuth\Manager;
31
use OC\Core\Controller\LoginController;
32
use OC\Core\Controller\TwoFactorChallengeController;
33
use OC\User\Session;
34
use OCP\AppFramework\Controller;
35
use OCP\AppFramework\Http\RedirectResponse;
36
use OCP\AppFramework\Middleware;
37
use OCP\AppFramework\Utility\IControllerMethodReflector;
38
use OCP\Authentication\TwoFactorAuth\ALoginSetupController;
39
use OCP\IRequest;
40
use OCP\ISession;
41
use OCP\IURLGenerator;
42
use OCP\IUser;
43
44
class TwoFactorMiddleware extends Middleware {
45
46
	/** @var Manager */
47
	private $twoFactorManager;
48
49
	/** @var Session */
50
	private $userSession;
51
52
	/** @var ISession */
53
	private $session;
54
55
	/** @var IURLGenerator */
56
	private $urlGenerator;
57
58
	/** @var IControllerMethodReflector */
59
	private $reflector;
60
61
	/** @var IRequest */
62
	private $request;
63
64
	/**
65
	 * @param Manager $twoFactorManager
66
	 * @param Session $userSession
67
	 * @param ISession $session
68
	 * @param IURLGenerator $urlGenerator
69
	 */
70
	public function __construct(Manager $twoFactorManager, Session $userSession, ISession $session,
71
		IURLGenerator $urlGenerator, IControllerMethodReflector $reflector, IRequest $request) {
72
		$this->twoFactorManager = $twoFactorManager;
73
		$this->userSession = $userSession;
74
		$this->session = $session;
75
		$this->urlGenerator = $urlGenerator;
76
		$this->reflector = $reflector;
77
		$this->request = $request;
78
	}
79
80
	/**
81
	 * @param Controller $controller
82
	 * @param string $methodName
83
	 */
84
	public function beforeController($controller, $methodName) {
85
		if ($this->reflector->hasAnnotation('PublicPage')) {
86
			// Don't block public pages
87
			return;
88
		}
89
90
		if ($controller instanceof TwoFactorChallengeController
91
			&& $this->userSession->getUser() !== null
92
			&& !$this->reflector->hasAnnotation('TwoFactorSetUpDoneRequired')) {
93
			$providers = $this->twoFactorManager->getProviderSet($this->userSession->getUser());
94
95
			if (!($providers->getProviders() === [] && !$providers->isProviderMissing())) {
96
				throw new TwoFactorAuthRequiredException();
97
			}
98
		}
99
100
		if ($controller instanceof ALoginSetupController
101
			&& $this->userSession->getUser() !== null
102
			&& $this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) {
103
			$providers = $this->twoFactorManager->getProviderSet($this->userSession->getUser());
104
105
			if ($providers->getProviders() === [] && !$providers->isProviderMissing()) {
106
				return;
107
			}
108
		}
109
110
		if ($controller instanceof LoginController && $methodName === 'logout') {
111
			// Don't block the logout page, to allow canceling the 2FA
112
			return;
113
		}
114
115
		if ($this->userSession->isLoggedIn()) {
116
			$user = $this->userSession->getUser();
117
118
			if ($this->session->exists('app_password') || $this->twoFactorManager->isTwoFactorAuthenticated($user)) {
119
				$this->checkTwoFactor($controller, $methodName, $user);
120
			} elseif ($controller instanceof TwoFactorChallengeController) {
121
				// Allow access to the two-factor controllers only if two-factor authentication
122
				// is in progress.
123
				throw new UserAlreadyLoggedInException();
124
			}
125
		}
126
		// TODO: dont check/enforce 2FA if a auth token is used
127
	}
128
129
	private function checkTwoFactor(Controller $controller, $methodName, IUser $user) {
0 ignored issues
show
Unused Code introduced by
The parameter $methodName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

129
	private function checkTwoFactor(Controller $controller, /** @scrutinizer ignore-unused */ $methodName, IUser $user) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
130
		// If two-factor auth is in progress disallow access to any controllers
131
		// defined within "LoginController".
132
		$needsSecondFactor = $this->twoFactorManager->needsSecondFactor($user);
133
		$twoFactor = $controller instanceof TwoFactorChallengeController;
134
135
		// Disallow access to any controller if 2FA needs to be checked
136
		if ($needsSecondFactor && !$twoFactor) {
137
			throw new TwoFactorAuthRequiredException();
138
		}
139
140
		// Allow access to the two-factor controllers only if two-factor authentication
141
		// is in progress.
142
		if (!$needsSecondFactor && $twoFactor) {
143
			throw new UserAlreadyLoggedInException();
144
		}
145
	}
146
147
	public function afterException($controller, $methodName, Exception $exception) {
148
		if ($exception instanceof TwoFactorAuthRequiredException) {
149
			$params = [];
150
			if (isset($this->request->server['REQUEST_URI'])) {
151
				$params['redirect_url'] = $this->request->server['REQUEST_URI'];
152
			}
153
			return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge', $params));
154
		}
155
		if ($exception instanceof UserAlreadyLoggedInException) {
156
			return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index'));
157
		}
158
159
		throw $exception;
160
	}
161
}
162