Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
15 | final class SecurityManager |
||
16 | { |
||
17 | const ALLOWED = 1; |
||
18 | const ERROR_NOT_IDENTIFIED = 2; |
||
19 | const ERROR_DENIED = 3; |
||
20 | /** @var IdentificationVerifier */ |
||
21 | private $identificationVerifier; |
||
22 | /** |
||
23 | * @var RoleConfiguration |
||
24 | */ |
||
25 | private $roleConfiguration; |
||
26 | |||
27 | /** |
||
28 | * SecurityManager constructor. |
||
29 | * |
||
30 | * @param IdentificationVerifier $identificationVerifier |
||
31 | * @param RoleConfiguration $roleConfiguration |
||
32 | */ |
||
33 | 2 | public function __construct( |
|
40 | |||
41 | /** |
||
42 | * Tests if a user is allowed to perform an action. |
||
43 | * |
||
44 | * This method should form a hard, deterministic security barrier, and only return true if it is absolutely sure |
||
45 | * that a user should have access to something. |
||
46 | * |
||
47 | * @param string $page |
||
48 | * @param string $route |
||
49 | * @param User $user |
||
50 | * |
||
51 | * @return int |
||
52 | * |
||
53 | * @category Security-Critical |
||
54 | */ |
||
55 | 2 | public function allows($page, $route, User $user) |
|
79 | |||
80 | /** |
||
81 | * @param array $pseudoRole The role (flattened) to check |
||
82 | * @param string $page The page class to check |
||
83 | * @param string $route The page route to check |
||
84 | * |
||
85 | * @return int|null |
||
86 | */ |
||
87 | 2 | private function findResult($pseudoRole, $page, $route) |
|
88 | { |
||
89 | 2 | if (isset($pseudoRole[$page])) { |
|
90 | // check for deny on catch-all route |
||
91 | 2 | View Code Duplication | if (isset($pseudoRole[$page][RoleConfiguration::ALL])) { |
|
|||
92 | 2 | if ($pseudoRole[$page][RoleConfiguration::ALL] === RoleConfiguration::ACCESS_DENY) { |
|
93 | 1 | return self::ERROR_DENIED; |
|
94 | } |
||
95 | 1 | } |
|
96 | |||
97 | // check normal route |
||
98 | 1 | if (isset($pseudoRole[$page][$route])) { |
|
99 | if ($pseudoRole[$page][$route] === RoleConfiguration::ACCESS_DENY) { |
||
100 | return self::ERROR_DENIED; |
||
101 | } |
||
102 | |||
103 | if ($pseudoRole[$page][$route] === RoleConfiguration::ACCESS_ALLOW) { |
||
104 | return self::ALLOWED; |
||
105 | } |
||
106 | } |
||
107 | |||
108 | // check for allowed on catch-all route |
||
109 | 1 | View Code Duplication | if (isset($pseudoRole[$page][RoleConfiguration::ALL])) { |
110 | 1 | if ($pseudoRole[$page][RoleConfiguration::ALL] === RoleConfiguration::ACCESS_ALLOW) { |
|
111 | 1 | return self::ALLOWED; |
|
112 | } |
||
113 | } |
||
114 | } |
||
115 | |||
116 | // return indeterminate result |
||
117 | return null; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Takes an array of roles and flattens the values to a single set. |
||
122 | * |
||
123 | * @param array $activeRoles |
||
124 | * |
||
125 | * @return array |
||
126 | */ |
||
127 | 2 | private function flattenRoles($activeRoles) |
|
128 | { |
||
129 | 2 | $result = array(); |
|
130 | |||
131 | 2 | $roleConfig = $this->roleConfiguration->getApplicableRoles($activeRoles); |
|
132 | |||
133 | // Iterate over every page in every role |
||
134 | 2 | foreach ($roleConfig as $role) { |
|
135 | 2 | foreach ($role as $page => $pageRights) { |
|
136 | // Create holder in result for this page |
||
137 | 2 | if (!isset($result[$page])) { |
|
138 | 2 | $result[$page] = array(); |
|
139 | 2 | } |
|
140 | |||
141 | 2 | foreach ($pageRights as $action => $permission) { |
|
142 | // Deny takes precedence, so if it's set, don't change it. |
||
143 | 2 | if (isset($result[$page][$action])) { |
|
144 | if ($result[$page][$action] === RoleConfiguration::ACCESS_DENY) { |
||
145 | continue; |
||
146 | } |
||
147 | } |
||
148 | |||
149 | 2 | if ($permission === RoleConfiguration::ACCESS_DEFAULT) { |
|
150 | // Configured to do precisely nothing. |
||
151 | continue; |
||
152 | } |
||
153 | |||
154 | 2 | $result[$page][$action] = $permission; |
|
155 | 2 | } |
|
156 | 2 | } |
|
157 | 2 | } |
|
158 | |||
159 | 2 | return $result; |
|
160 | } |
||
161 | |||
162 | /** |
||
163 | * @param User $user |
||
164 | * @param array $activeRoles |
||
165 | * @param array $inactiveRoles |
||
166 | */ |
||
167 | 2 | public function getActiveRoles(User $user, &$activeRoles, &$inactiveRoles) |
|
168 | { |
||
169 | // Default to the community user here, because the main user is logged out |
||
170 | 2 | $identified = false; |
|
171 | 2 | $userRoles = array('public'); |
|
172 | |||
173 | // if we're not the community user, get our real rights. |
||
174 | 2 | if (!$user->isCommunityUser()) { |
|
175 | // Check the user's status - only active users are allowed the effects of roles |
||
176 | |||
177 | $userRoles[] = 'loggedIn'; |
||
178 | |||
179 | if ($user->isActive()) { |
||
180 | $ur = UserRole::getForUser($user->getId(), $user->getDatabase()); |
||
181 | |||
182 | // NOTE: public is still in this array. |
||
183 | foreach ($ur as $r) { |
||
184 | $userRoles[] = $r->getRole(); |
||
185 | } |
||
186 | |||
187 | $identified = $user->isIdentified($this->identificationVerifier); |
||
188 | } |
||
189 | } |
||
190 | |||
191 | 2 | $activeRoles = array(); |
|
192 | 2 | $inactiveRoles = array(); |
|
193 | |||
194 | /** @var string $v */ |
||
195 | 2 | foreach ($userRoles as $v) { |
|
196 | 2 | if ($this->roleConfiguration->roleNeedsIdentification($v)) { |
|
197 | if ($identified) { |
||
198 | $activeRoles[] = $v; |
||
199 | } |
||
200 | else { |
||
201 | $inactiveRoles[] = $v; |
||
202 | } |
||
203 | } |
||
204 | else { |
||
205 | 2 | $activeRoles[] = $v; |
|
206 | } |
||
207 | 2 | } |
|
208 | 2 | } |
|
209 | |||
210 | public function getRoleConfiguration(){ |
||
213 | } |
||
214 |
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.