• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FastyBird / simple-auth / 10030599651

21 Jul 2024 06:48PM UTC coverage: 68.983% (+1.1%) from 67.871%
10030599651

push

github

web-flow
User roles check as was before casbin (#11)

* User roles check as was before casbin

* Fix interface

* Use storage instead of user

* QA fixes

45 of 49 new or added lines in 4 files covered. (91.84%)

1 existing line in 1 file now uncovered.

536 of 777 relevant lines covered (68.98%)

5.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.76
/src/Security/AnnotationChecker.php
1
<?php declare(strict_types = 1);
2

3
/**
4
 * AnnotationChecker.php
5
 *
6
 * @license        More in LICENSE.md
7
 * @copyright      https://www.fastybird.com
8
 * @author         Adam Kadlec <adam.kadlec@fastybird.com>
9
 * @package        FastyBird:SimpleAuth!
10
 * @subpackage     Security
11
 * @since          0.1.0
12
 *
13
 * @date           29.08.20
14
 */
15

16
namespace FastyBird\SimpleAuth\Security;
17

18
use FastyBird\SimpleAuth\Exceptions;
19
use ReflectionClass;
20
use ReflectionException;
21
use Reflector;
22
use function array_key_exists;
23
use function call_user_func;
24
use function class_exists;
25
use function count;
26
use function end;
27
use function in_array;
28
use function is_bool;
29
use function is_callable;
30
use function is_string;
31
use function preg_match_all;
32
use function preg_quote;
33
use function preg_split;
34
use function strtolower;
35
use function strval;
36
use const PREG_SPLIT_NO_EMPTY;
37

38
/**
39
 * Class security annotation checker
40
 *
41
 * @package        FastyBird:SimpleAuth!
42
 * @subpackage     Security
43
 *
44
 * @author         Adam Kadlec <adam.kadlec@fastybird.com>
45
 */
46
class AnnotationChecker
47
{
48

49
        /**
50
         * @param class-string $controllerClass
51
         *
52
         * @throws Exceptions\InvalidArgument
53
         */
54
        public function checkAccess(
55
                User|null $user,
56
                string $controllerClass,
57
                string|null $controllerMethod,
58
        ): bool
59
        {
60
                try {
61
                        if (class_exists($controllerClass)) {
12✔
62
                                $reflection = new ReflectionClass($controllerClass);
12✔
63

64
                                if ($controllerMethod !== null) {
12✔
65
                                        foreach ([$reflection, $reflection->getMethod($controllerMethod)] as $element) {
12✔
66
                                                if (!$this->isAllowed($user, $element)) {
12✔
67
                                                        return false;
6✔
68
                                                }
69
                                        }
70
                                } else {
71
                                        if (!$this->isAllowed($user, $reflection)) {
×
72
                                                return false;
6✔
73
                                        }
74
                                }
75
                        }
76
                } catch (ReflectionException) {
×
77
                        return false;
×
78
                }
79

80
                return true;
6✔
81
        }
82

83
        /**
84
         * @throws Exceptions\InvalidArgument
85
         */
86
        private function isAllowed(User|null $user, Reflector $element): bool
87
        {
88
                if ($this->parseAnnotation($element, 'Secured') === null) {
12✔
89
                        return true;
12✔
90
                }
91

92
                if ($user === null) {
12✔
93
                        return false;
×
94
                }
95

96
                // Check annotations only if element have to be secured
97
                return $this->checkUser($user, $element) && $this->checkRoles($user, $element);
12✔
98
        }
99

100
        /**
101
         * @return array<mixed>|null
102
         */
103
        private function parseAnnotation(Reflector $ref, string $name): array|null
104
        {
105
                $callable = [$ref, 'getDocComment'];
12✔
106

107
                if (!is_callable($callable)) {
12✔
108
                        return null;
×
109
                }
110

111
                $result = preg_match_all(
12✔
112
                        '#[\s*]@' . preg_quote($name, '#') . '(?:\(\s*([^)]*)\s*\)|\s|$)#',
12✔
113
                        strval(call_user_func($callable)),
12✔
114
                        $m,
12✔
115
                );
12✔
116

117
                if ($result === false || $result === 0) {
12✔
118
                        return null;
12✔
119
                }
120

121
                static $tokens = ['true' => true, 'false' => false, 'null' => null];
12✔
122

123
                $res = [];
12✔
124

125
                foreach ($m[1] as $s) {
12✔
126
                        $items = preg_split('#\s*,\s*#', $s, -1, PREG_SPLIT_NO_EMPTY);
12✔
127

128
                        foreach ($items !== false ? $items : ['true'] as $item) {
12✔
129
                                $tmp = strtolower($item);
12✔
130

131
                                if (!array_key_exists($tmp, $tokens) && $item !== '') {
12✔
132
                                        $res[] = $item;
12✔
133
                                }
134
                        }
135
                }
136

137
                return $res;
12✔
138
        }
139

140
        /**
141
         * @throws Exceptions\InvalidArgument
142
         */
143
        private function checkUser(User $user, Reflector $element): bool
144
        {
145
                // Check if element has @Secured\User annotation
146
                if ($this->parseAnnotation($element, 'Secured\User') !== null) {
12✔
147
                        // Get user annotation
148
                        $result = $this->parseAnnotation($element, 'Secured\User');
9✔
149

150
                        if (count($result) > 0) {
9✔
151
                                $userAnnotation = end($result);
9✔
152

153
                                // Annotation is single string
154
                                if (in_array($userAnnotation, ['loggedIn', 'guest'], true)) {
9✔
155
                                        // User have to be logged in and is not
156
                                        if ($userAnnotation === 'loggedIn' && $user->isLoggedIn() === false) {
9✔
157
                                                return false;
3✔
158

159
                                                // User have to be logged out and is logged in
160
                                        } elseif ($userAnnotation === 'guest' && $user->isLoggedIn() === true) {
6✔
161
                                                return false;
2✔
162
                                        }
163

164
                                        // Annotation have wrong definition
165
                                } else {
166
                                        throw new Exceptions\InvalidArgument(
×
167
                                                'In @Security\User annotation is allowed only one from two strings: \'loggedIn\' & \'guest\'',
×
168
                                        );
×
169
                                }
170
                        }
171

172
                        return true;
6✔
173
                }
174

175
                return true;
3✔
176
        }
177

178
        private function checkRoles(User $user, Reflector $element): bool
179
        {
180
                // Check if element has @Secured\Role annotation
181
                if ($this->parseAnnotation($element, 'Secured\Role') !== null) {
9✔
182
                        $rolesAnnotation = $this->parseAnnotation($element, 'Secured\Role');
3✔
183

184
                        foreach ($rolesAnnotation as $role) {
3✔
185
                                // Check if role name is defined
186
                                if (is_bool($role) || $role === null) {
3✔
187
                                        continue;
×
188
                                }
189

190
                                if (is_string($role) && $user->isInRole($role)) {
3✔
UNCOV
191
                                        return true;
×
192
                                }
193
                        }
194

195
                        return false;
3✔
196
                }
197

198
                return true;
6✔
199
        }
200

201
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc