Passed
Push — master ( d60172...75f17b )
by Joas
16:26 queued 12s
created

hasAnnotationOrAttribute()   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 3
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright 2018, Roeland Jago Douma <[email protected]>
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Roeland Jago Douma <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
namespace OC\AppFramework\Middleware\Security;
25
26
use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException;
27
use OC\AppFramework\Utility\ControllerMethodReflector;
28
use OCP\AppFramework\Controller;
29
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
30
use OCP\AppFramework\Middleware;
31
use OCP\AppFramework\Utility\ITimeFactory;
32
use OCP\ISession;
33
use OCP\IUserSession;
34
use OCP\User\Backend\IPasswordConfirmationBackend;
35
use ReflectionMethod;
36
37
class PasswordConfirmationMiddleware extends Middleware {
38
	/** @var ControllerMethodReflector */
39
	private $reflector;
40
	/** @var ISession */
41
	private $session;
42
	/** @var IUserSession */
43
	private $userSession;
44
	/** @var ITimeFactory */
45
	private $timeFactory;
46
	/** @var array */
47
	private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
48
49
	/**
50
	 * PasswordConfirmationMiddleware constructor.
51
	 *
52
	 * @param ControllerMethodReflector $reflector
53
	 * @param ISession $session
54
	 * @param IUserSession $userSession
55
	 * @param ITimeFactory $timeFactory
56
	 */
57
	public function __construct(ControllerMethodReflector $reflector,
58
								ISession $session,
59
								IUserSession $userSession,
60
								ITimeFactory $timeFactory) {
61
		$this->reflector = $reflector;
62
		$this->session = $session;
63
		$this->userSession = $userSession;
64
		$this->timeFactory = $timeFactory;
65
	}
66
67
	/**
68
	 * @param Controller $controller
69
	 * @param string $methodName
70
	 * @throws NotConfirmedException
71
	 */
72
	public function beforeController($controller, $methodName) {
73
		$reflectionMethod = new ReflectionMethod($controller, $methodName);
74
75
		if ($this->hasAnnotationOrAttribute($reflectionMethod, 'PasswordConfirmationRequired', PasswordConfirmationRequired::class)) {
76
			$user = $this->userSession->getUser();
77
			$backendClassName = '';
78
			if ($user !== null) {
79
				$backend = $user->getBackend();
80
				if ($backend instanceof IPasswordConfirmationBackend) {
81
					if (!$backend->canConfirmPassword($user->getUID())) {
82
						return;
83
					}
84
				}
85
86
				$backendClassName = $user->getBackendClassName();
87
			}
88
89
			$lastConfirm = (int) $this->session->get('last-password-confirm');
90
			// we can't check the password against a SAML backend, so skip password confirmation in this case
91
			if (!isset($this->excludedUserBackEnds[$backendClassName]) && $lastConfirm < ($this->timeFactory->getTime() - (30 * 60 + 15))) { // allow 15 seconds delay
92
				throw new NotConfirmedException();
93
			}
94
		}
95
	}
96
97
	/**
98
	 * @template T
99
	 *
100
	 * @param ReflectionMethod $reflectionMethod
101
	 * @param string $annotationName
102
	 * @param class-string<T> $attributeClass
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
103
	 * @return boolean
104
	 */
105
	protected function hasAnnotationOrAttribute(ReflectionMethod $reflectionMethod, string $annotationName, string $attributeClass): bool {
106
		if (!empty($reflectionMethod->getAttributes($attributeClass))) {
107
			return true;
108
		}
109
110
		if ($this->reflector->hasAnnotation($annotationName)) {
111
			return true;
112
		}
113
114
		return false;
115
	}
116
}
117