Completed
Push — master ( abc7b1...9cce5e )
by Phil
62:45 queued 51:52
created

SecurityMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 9
dl 0
loc 19
rs 9.4285
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
2
/**
3
 * @author Bernhard Posselt <[email protected]>
4
 * @author Lukas Reschke <[email protected]>
5
 * @author Morris Jobke <[email protected]>
6
 * @author Roeland Jago Douma <[email protected]>
7
 * @author Stefan Weil <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 * @author Thomas Tanghus <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2017, ownCloud GmbH
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
29
namespace OC\AppFramework\Middleware\Security;
30
31
use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException;
32
use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException;
33
use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
34
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
35
use OC\AppFramework\Utility\ControllerMethodReflector;
36
use OC\Core\Controller\LoginController;
37
use OC\Security\CSP\ContentSecurityPolicyManager;
38
use OCP\AppFramework\Http\ContentSecurityPolicy;
39
use OCP\AppFramework\Http\RedirectResponse;
40
use OCP\AppFramework\Http\TemplateResponse;
41
use OCP\AppFramework\Middleware;
42
use OCP\AppFramework\Http\Response;
43
use OCP\AppFramework\Http\JSONResponse;
44
use OCP\INavigationManager;
45
use OCP\IURLGenerator;
46
use OCP\IRequest;
47
use OCP\ILogger;
48
use OCP\AppFramework\Controller;
49
use OCP\Util;
50
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
51
52
/**
53
 * Used to do all the authentication and checking stuff for a controller method
54
 * It reads out the annotations of a controller method and checks which if
55
 * security things should be checked and also handles errors in case a security
56
 * check fails
57
 */
58
class SecurityMiddleware extends Middleware {
59
	/** @var INavigationManager */
60
	private $navigationManager;
61
	/** @var IRequest */
62
	private $request;
63
	/** @var ControllerMethodReflector */
64
	private $reflector;
65
	/** @var string */
66
	private $appName;
67
	/** @var IURLGenerator */
68
	private $urlGenerator;
69
	/** @var ILogger */
70
	private $logger;
71
	/** @var bool */
72
	private $isLoggedIn;
73
	/** @var bool */
74
	private $isAdminUser;
75
	/** @var ContentSecurityPolicyManager */
76
	private $contentSecurityPolicyManager;
77
78
	/**
79
	 * @param IRequest $request
80
	 * @param ControllerMethodReflector $reflector
81
	 * @param INavigationManager $navigationManager
82
	 * @param IURLGenerator $urlGenerator
83
	 * @param ILogger $logger
84
	 * @param string $appName
85
	 * @param bool $isLoggedIn
86
	 * @param bool $isAdminUser
87
	 * @param ContentSecurityPolicyManager $contentSecurityPolicyManager
88
	 */
89
	public function __construct(IRequest $request,
90
								ControllerMethodReflector $reflector,
91
								INavigationManager $navigationManager,
92
								IURLGenerator $urlGenerator,
93
								ILogger $logger,
94
								$appName,
95
								$isLoggedIn,
96
								$isAdminUser,
97
								ContentSecurityPolicyManager $contentSecurityPolicyManager) {
98
		$this->navigationManager = $navigationManager;
99
		$this->request = $request;
100
		$this->reflector = $reflector;
101
		$this->appName = $appName;
102
		$this->urlGenerator = $urlGenerator;
103
		$this->logger = $logger;
104
		$this->isLoggedIn = $isLoggedIn;
105
		$this->isAdminUser = $isAdminUser;
106
		$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
107
	}
108
109
110
	/**
111
	 * This runs all the security checks before a method call. The
112
	 * security checks are determined by inspecting the controller method
113
	 * annotations
114
	 * @param string $controller the controllername or string
115
	 * @param string $methodName the name of the method
116
	 * @throws SecurityException when a security check fails
117
	 */
118
	public function beforeController($controller, $methodName) {
119
120
		// this will set the current navigation entry of the app, use this only
121
		// for normal HTML requests and not for AJAX requests
122
		$this->navigationManager->setActiveEntry($this->appName);
123
124
		// security checks
125
		$isPublicPage = $this->reflector->hasAnnotation('PublicPage');
126
		if(!$isPublicPage) {
127
			if(!$this->isLoggedIn) {
128
				throw new NotLoggedInException();
129
			}
130
131
			if(!$this->reflector->hasAnnotation('NoAdminRequired')) {
132
				if(!$this->isAdminUser) {
133
					throw new NotAdminException();
134
				}
135
			}
136
		}
137
138
		// CSRF check - also registers the CSRF token since the session may be closed later
139
		Util::callRegister();
140
		if(!$this->reflector->hasAnnotation('NoCSRFRequired')) {
141
			if(!$this->request->passesCSRFCheck()) {
142
				throw new CrossSiteRequestForgeryException();
143
			}
144
		}
145
146
		/**
147
		 * FIXME: Use DI once available
148
		 * Checks if app is enabled (also includes a check whether user is allowed to access the resource)
149
		 * The getAppPath() check is here since components such as settings also use the AppFramework and
150
		 * therefore won't pass this check.
151
		 */
152
		if(\OC_App::getAppPath($this->appName) !== false && !\OC_App::isEnabled($this->appName)) {
153
			throw new AppNotEnabledException();
154
		}
155
156
	}
157
158
	/**
159
	 * Performs the default CSP modifications that may be injected by other
160
	 * applications
161
	 *
162
	 * @param Controller $controller
163
	 * @param string $methodName
164
	 * @param Response $response
165
	 * @return Response
166
	 */
167
	public function afterController($controller, $methodName, Response $response) {
168
		$policy = !is_null($response->getContentSecurityPolicy()) ? $response->getContentSecurityPolicy() : new ContentSecurityPolicy();
169
170
		$defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy();
171
		$defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy);
172
173
		$response->setContentSecurityPolicy($defaultPolicy);
174
175
		return $response;
176
	}
177
178
	/**
179
	 * If an SecurityException is being caught, ajax requests return a JSON error
180
	 * response and non ajax requests redirect to the index
181
	 * @param Controller $controller the controller that is being called
182
	 * @param string $methodName the name of the method that will be called on
183
	 *                           the controller
184
	 * @param \Exception $exception the thrown exception
185
	 * @throws \Exception the passed in exception if it can't handle it
186
	 * @return Response a Response object or null in case that the exception could not be handled
187
	 */
188
	public function afterException($controller, $methodName, \Exception $exception) {
189
		if($exception instanceof SecurityException) {
190
191
			if (stripos($this->request->getHeader('Accept'),'html') === false) {
192
				$response = new JSONResponse(
193
					['message' => $exception->getMessage()],
194
					$exception->getCode()
195
				);
196
			} else {
197
				if($exception instanceof NotLoggedInException) {
198
					$url = $this->urlGenerator->linkToRoute(
199
						'core.login.showLoginForm',
200
						[
201
							'redirect_url' => urlencode($this->request->server['REQUEST_URI']),
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
202
						]
203
					);
204
					$response = new RedirectResponse($url);
205
				} elseif (
206
					$methodName === 'tryLogin'
207
					&& $exception instanceof CrossSiteRequestForgeryException
208
					&& $controller instanceof LoginController
209
				) {
210
					$this->logger->debug($exception->getMessage());
211
					$controller->getSession()->set(
212
						'loginMessages',
213
						[
214
							['csrf_error'],
215
							[]
216
						]
217
					);
218
					return $controller->showLoginForm(null, null, null);
219
				} else {
220
					$response = new TemplateResponse('core', '403', ['file' => $exception->getMessage()], 'guest');
221
					$response->setStatus($exception->getCode());
222
				}
223
			}
224
225
			$this->logger->debug($exception->getMessage());
226
			return $response;
227
		}
228
229
		throw $exception;
230
	}
231
232
}
233