Completed
Push — master ( 7e98b2...89d085 )
by Adam
02:18
created

AnnotationChecker::checkPrivileges()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 26
Code Lines 12

Duplication

Lines 26
Ratio 100 %

Code Coverage

Tests 2
CRAP Score 33.8395

Importance

Changes 0
Metric Value
dl 26
loc 26
ccs 2
cts 11
cp 0.1818
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 12
nc 6
nop 1
crap 33.8395
1
<?php
2
/**
3
 * AnnotationChecker.php
4
 *
5
 * @copyright      More in license.md
6
 * @license        http://www.ipublikuj.eu
7
 * @author         Adam Kadlec http://www.ipublikuj.eu
8
 * @package        iPublikuj:Permissions!
9
 * @subpackage     Access
10
 * @since          1.0.0
11
 *
12
 * @date           13.10.14
13
 */
14
15
declare(strict_types = 1);
16
17
namespace IPub\Permissions\Access;
18
19
use Nette;
20
use Nette\Application\UI;
21
use Nette\Utils;
22
use Nette\Security as NS;
23
24
use IPub;
25
use IPub\Permissions\Entities;
26
use IPub\Permissions\Exceptions;
27
use IPub\Permissions\Security;
28
29
/**
30
 * Presenter & component annotation access checker
31
 *
32
 * @package        iPublikuj:Permissions!
33
 * @subpackage     Access
34
 *
35
 * @author         Adam Kadlec <[email protected]>
36
 */
37 1
final class AnnotationChecker extends Nette\Object implements IChecker, ICheckRequirements
38
{
39
	/**
40
	 * @var NS\User
41
	 */
42
	private $user;
43
44
	/**
45
	 * @param NS\User $user
46
	 */
47
	public function __construct(NS\User $user)
48
	{
49 1
		$this->user = $user;
50 1
	}
51
52
	/**
53
	 * {@inheritdoc}
54
	 */
55
	public function isAllowed($element) : bool
56
	{
57
		// Check annotations only if element have to be secured
58
		if (
59 1
			($element instanceof UI\ComponentReflection || $element instanceof UI\MethodReflection)
60 1
			&& $element->hasAnnotation('Secured')
61
		) {
62 1
			return $this->checkUser($element)
63 1
			&& $this->checkResources($element)
64 1
			&& $this->checkPrivileges($element)
65 1
			&& $this->checkPermission($element)
66 1
			&& $this->checkRoles($element);
67
68
		} else {
69 1
			return TRUE;
70
		}
71
	}
72
73
	/**
74
	 * @param UI\ComponentReflection|UI\MethodReflection $element
75
	 *
76
	 * @return bool
77
	 *
78
	 * @throws Exceptions\InvalidArgumentException
79
	 */
80 View Code Duplication
	private function checkUser($element) : bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
81
	{
82
		// Check if element has @Secured\User annotation
83 1
		if ($element->hasAnnotation('Secured\User')) {
84
			// Get user annotation
85 1
			$user = $element->getAnnotation('Secured\User');
86
87
			// Annotation is single string
88 1
			if (is_string($user) && in_array($user, ['loggedIn', 'guest'], TRUE)) {
89
				// User have to be logged in and is not
90 1
				if ($user === 'loggedIn' && $this->user->isLoggedIn() === FALSE) {
91
					return FALSE;
92
93
				// User have to be logged out and is logged in
94 1
				} elseif ($user === 'guest' && $this->user->isLoggedIn() === TRUE) {
95 1
					return FALSE;
96
				}
97
98
			// Annotation have wrong definition
99
			} else {
100
				throw new Exceptions\InvalidArgumentException('In @Security\User annotation is allowed only one from two strings: \'loggedIn\' & \'guest\'');
101
			}
102
103 1
			return TRUE;
104
		}
105
106 1
		return TRUE;
107
	}
108
109
	/**
110
	 * @param UI\ComponentReflection|UI\MethodReflection $element
111
	 *
112
	 * @return bool
113
	 *
114
	 * @throws Exceptions\InvalidStateException
115
	 */
116
	private function checkResources($element) : bool
117
	{
118
		// Check if element has @Security\Resource annotation & @Secured\Privilege annotation
119 1
		if ($element->hasAnnotation('Secured\Resource')) {
120 1
			$resources = UI\ComponentReflection::parseAnnotation($element, 'Secured\Resource');
121
122 1
			if (count($resources) != 1) {
123
				throw new Exceptions\InvalidStateException('Invalid resources count in @Security\Resource annotation!');
124
			}
125
126 1
			$privileges = UI\ComponentReflection::parseAnnotation($element, 'Secured\Privilege');
127
128 1 View Code Duplication
			foreach ($resources as $resource) {
0 ignored issues
show
Bug introduced by
The expression $resources of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129 1
				if ($privileges !== FALSE) {
130 1
					foreach ($privileges as $privilege) {
131 1
						if ($this->user->isAllowed($resource, $privilege)) {
132 1
							return TRUE;
133
						}
134
					}
135
136
				} else {
137
					if ($this->user->isAllowed($resource)) {
138
						return TRUE;
139
					}
140
				}
141
			}
142
143
			return FALSE;
144
		}
145
146 1
		return TRUE;
147
	}
148
149
	/**
150
	 * @param UI\ComponentReflection|UI\MethodReflection $element
151
	 *
152
	 * @return bool
153
	 *
154
	 * @throws Exceptions\InvalidStateException
155
	 */
156 View Code Duplication
	private function checkPrivileges($element) : bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
	{
158
		// Check if element has @Secured\Privilege annotation & hasn't @Secured\Resource annotation
159 1
		if (!$element->hasAnnotation('Secured\Resource') && $element->hasAnnotation('Secured\Privilege')) {
160
			$privileges = UI\ComponentReflection::parseAnnotation($element, 'Secured\Privilege');
161
162
			if (count($privileges) != 1) {
163
				throw new Exceptions\InvalidStateException('Invalid privileges count in @Security\Privilege annotation!');
164
			}
165
166
			foreach ($privileges as $privilege) {
0 ignored issues
show
Bug introduced by
The expression $privileges of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
167
				// Check if privilege name is defined
168
				if ($privilege === TRUE) {
169
					continue;
170
				}
171
172
				if ($this->user->isAllowed(NS\IAuthorizator::ALL, $privilege)) {
173
					return TRUE;
174
				}
175
			}
176
177
			return FALSE;
178
		}
179
180 1
		return TRUE;
181
	}
182
183
	/**
184
	 * @param UI\ComponentReflection|UI\MethodReflection $element
185
	 *
186
	 * @return bool
187
	 */
188 View Code Duplication
	private function checkPermission($element) : bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
189
	{
190
		// Check if element has @Secured\Permission annotation
191 1
		if ($element->hasAnnotation('Secured\Permission')) {
192 1
			$permissions = UI\ComponentReflection::parseAnnotation($element, 'Secured\Permission');
193
194 1
			foreach ($permissions as $permission) {
0 ignored issues
show
Bug introduced by
The expression $permissions of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
195
				// Check if parameters are defined
196 1
				if ($permission === TRUE) {
197
					continue;
198
				}
199
200
				// Parse resource & privilege from permission
201 1
				list($resource, $privilege) = explode(Entities\IPermission::DELIMITER, $permission);
202
203
				// Remove white spaces
204 1
				$resource = Utils\Strings::trim($resource);
205 1
				$privilege = Utils\Strings::trim($privilege);
206
207 1
				if ($this->user->isAllowed($resource, $privilege)) {
208 1
					return TRUE;
209
				}
210
			}
211
212
			return FALSE;
213
		}
214
215 1
		return TRUE;
216
	}
217
218
	/**
219
	 * @param UI\ComponentReflection|UI\MethodReflection $element
220
	 *
221
	 * @return bool
222
	 */
223
	private function checkRoles($element) : bool
224
	{
225
		// Check if element has @Secured\Role annotation
226 1
		if ($element->hasAnnotation('Secured\Role')) {
227 1
			$roles = UI\ComponentReflection::parseAnnotation($element, 'Secured\Role');
228
229 1
			foreach ($roles as $role) {
0 ignored issues
show
Bug introduced by
The expression $roles of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
230
				// Check if role name is defined
231 1
				if ($role === TRUE) {
232
					continue;
233
				}
234
235 1
				if ($this->user->isInRole($role)) {
236 1
					return TRUE;
237
				}
238
			}
239
240 1
			return FALSE;
241
		}
242
243 1
		return TRUE;
244
	}
245
}
246