Completed
Pull Request — master (#479)
by Lukas
33:52 queued 25:33
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) 2016, ownCloud, Inc.
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\Middleware\Security\Exceptions\StrictCookieMissingException;
36
use OC\AppFramework\Utility\ControllerMethodReflector;
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
		// Check for strict cookie requirement
139
		if($this->reflector->hasAnnotation('StrictCookieRequired') || !$this->reflector->hasAnnotation('NoCSRFRequired')) {
140
			if(!$this->request->passesStrictCookieCheck()) {
141
				throw new StrictCookieMissingException();
142
			}
143
		}
144
		// CSRF check - also registers the CSRF token since the session may be closed later
145
		Util::callRegister();
146
		if(!$this->reflector->hasAnnotation('NoCSRFRequired')) {
147
			if(!$this->request->passesCSRFCheck()) {
148
				throw new CrossSiteRequestForgeryException();
149
			}
150
		}
151
152
		/**
153
		 * FIXME: Use DI once available
154
		 * Checks if app is enabled (also includes a check whether user is allowed to access the resource)
155
		 * The getAppPath() check is here since components such as settings also use the AppFramework and
156
		 * therefore won't pass this check.
157
		 */
158
		if(\OC_App::getAppPath($this->appName) !== false && !\OC_App::isEnabled($this->appName)) {
159
			throw new AppNotEnabledException();
160
		}
161
162
	}
163
164
	/**
165
	 * Performs the default CSP modifications that may be injected by other
166
	 * applications
167
	 *
168
	 * @param Controller $controller
169
	 * @param string $methodName
170
	 * @param Response $response
171
	 * @return Response
172
	 */
173
	public function afterController($controller, $methodName, Response $response) {
174
		$policy = !is_null($response->getContentSecurityPolicy()) ? $response->getContentSecurityPolicy() : new ContentSecurityPolicy();
175
176
		$defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy();
177
		$defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy);
178
179
		$response->setContentSecurityPolicy($defaultPolicy);
180
181
		return $response;
182
	}
183
184
	/**
185
	 * If an SecurityException is being caught, ajax requests return a JSON error
186
	 * response and non ajax requests redirect to the index
187
	 * @param Controller $controller the controller that is being called
188
	 * @param string $methodName the name of the method that will be called on
189
	 *                           the controller
190
	 * @param \Exception $exception the thrown exception
191
	 * @throws \Exception the passed in exception if it can't handle it
192
	 * @return Response a Response object or null in case that the exception could not be handled
193
	 */
194
	public function afterException($controller, $methodName, \Exception $exception) {
195
		if($exception instanceof SecurityException) {
196
			if($exception instanceof StrictCookieMissingException) {
197
				return new RedirectResponse(\OC::$WEBROOT);
198
 			}
199
			if (stripos($this->request->getHeader('Accept'),'html') === false) {
200
				$response = new JSONResponse(
201
					array('message' => $exception->getMessage()),
202
					$exception->getCode()
203
				);
204
			} else {
205
				if($exception instanceof NotLoggedInException) {
206
					$url = $this->urlGenerator->linkToRoute(
207
						'core.login.showLoginForm',
208
						[
209
							'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...
210
						]
211
					);
212
					$response = new RedirectResponse($url);
213
				} else {
214
					$response = new TemplateResponse('core', '403', ['file' => $exception->getMessage()], 'guest');
215
					$response->setStatus($exception->getCode());
216
				}
217
			}
218
219
			$this->logger->debug($exception->getMessage());
220
			return $response;
221
		}
222
223
		throw $exception;
224
	}
225
226
}
227