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 namespace Myth\Api\Auth; |
||
41 | class APIAuthentication extends LocalAuthentication { |
||
42 | |||
43 | protected $logged_in = false; |
||
44 | |||
45 | protected $realm = 'WallyWorld'; |
||
46 | |||
47 | //-------------------------------------------------------------------- |
||
48 | |||
49 | public function __construct($ci=null) |
||
68 | |||
69 | //-------------------------------------------------------------------- |
||
70 | |||
71 | /** |
||
72 | * Sets the realm used by the authentication. The system truly only |
||
73 | * supports a single realm across the entire application, but this |
||
74 | * allows it to be set by the controller. |
||
75 | * |
||
76 | * @param $realm |
||
77 | * |
||
78 | * @return $this |
||
79 | */ |
||
80 | public function setRealm($realm) |
||
85 | |||
86 | //-------------------------------------------------------------------- |
||
87 | |||
88 | /** |
||
89 | * Checks to see if someone is authorized via HTTP Basic Authentication. |
||
90 | * |
||
91 | * @return bool |
||
92 | */ |
||
93 | public function tryBasicAuthentication() |
||
130 | |||
131 | //-------------------------------------------------------------------- |
||
132 | |||
133 | /** |
||
134 | * Checks to see if someone is authorized via HTTP Digest Authentication. |
||
135 | * |
||
136 | * NOTE: This requires that a new field, 'digest_key', be added to the user's |
||
137 | * table and, during new user creation, or password reset, that the digest_key |
||
138 | * be calculated as md5({username}:{realm}:{password}) |
||
139 | * |
||
140 | * References: |
||
141 | * - http://www.faqs.org/rfcs/rfc2617.html |
||
142 | * - http://www.sitepoint.com/understanding-http-digest-access-authentication/ |
||
143 | */ |
||
144 | public function tryDigestAuthentication() |
||
145 | { |
||
146 | $digest_string = ''; |
||
147 | |||
148 | // We need to test which server authentication variable to use |
||
149 | // because the PHP ISAPI module in IIS acts different from CGI |
||
150 | if ($this->ci->input->server('PHP_AUTH_DIGEST')) |
||
151 | { |
||
152 | $digest_string = $this->ci->input->server('PHP_AUTH_DIGEST'); |
||
153 | } |
||
154 | elseif ($this->ci->input->server('HTTP_AUTHORIZATION')) |
||
155 | { |
||
156 | $digest_string = $this->ci->input->server('HTTP_AUTHORIZATION'); |
||
157 | } |
||
158 | |||
159 | $nonce = md5(uniqid()); |
||
160 | $opaque = md5(uniqid()); |
||
161 | |||
162 | // No digest string? Then you're done. Go home. |
||
163 | View Code Duplication | if (empty($digest_string)) |
|
|
|||
164 | { |
||
165 | $this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) ); |
||
166 | return false; |
||
167 | } |
||
168 | |||
169 | // Grab the parts from the digest string. |
||
170 | // They will be provided as an array of the parts: username, nonce, uri, nc, cnonce, qop, response |
||
171 | $matches = []; |
||
172 | preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); |
||
173 | $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); |
||
174 | |||
175 | View Code Duplication | if (! array_key_exists('username', $digest)) |
|
176 | { |
||
177 | $this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) ); |
||
178 | return false; |
||
179 | } |
||
180 | |||
181 | // Grab the user that corresponds to that "username" |
||
182 | // exact field determined in the api config file - api.auth_field setting. |
||
183 | $user = $this->user_model->as_array()->find_by( config_item('api.auth_field'), $digest['username'] ); |
||
184 | if (! $user) |
||
185 | { |
||
186 | $this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) ); |
||
187 | $this->ci->login_model->recordLoginAttempt($this->ci->input->ip_address()); |
||
188 | return false; |
||
189 | } |
||
190 | |||
191 | // Calc the correct response |
||
192 | $A1 = $user['digest_key']; |
||
193 | |||
194 | if ($digest['qop'] == 'auth') |
||
195 | { |
||
196 | $A2 = md5( strtoupper( $_SERVER['REQUEST_METHOD'] ) .':'. $digest['uri'] ); |
||
197 | } else { |
||
198 | $body = file_get_contents('php://input'); |
||
199 | $A2 = md5( strtoupper( $_SERVER['REQUEST_METHOD'] ) .':'. $digest['uri'] .':'. md5($body) ); |
||
200 | } |
||
201 | $valid_response = md5($A1 .':'. $digest['nonce'].':'. $digest['nc'] .':'. $digest['cnonce'] .':'. $digest['qop'] .':'. $A2); |
||
202 | |||
203 | if ($digest['response'] != $valid_response) |
||
204 | { |
||
205 | $this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) ); |
||
206 | $this->ci->login_model->recordLoginAttempt($this->ci->input->ip_address(), $user['id']); |
||
207 | return false; |
||
208 | } |
||
209 | |||
210 | $this->user = $user; |
||
211 | |||
212 | return $user; |
||
213 | } |
||
214 | |||
215 | //-------------------------------------------------------------------- |
||
216 | |||
217 | /** |
||
218 | * Attempts to log a user into the API via the configured 'api.auth_type' |
||
219 | * config variable in config/api.php. |
||
220 | * |
||
221 | * NOTE: Since this is intended for API use, it is a STATELESS implementation |
||
222 | * and does not support remember me functionality. |
||
223 | * |
||
224 | * This basically replaces the login() method due to the way the AuthTrait |
||
225 | * works. |
||
226 | * |
||
227 | * @return bool |
||
228 | */ |
||
229 | public function viaRemember() |
||
268 | |||
269 | //-------------------------------------------------------------------- |
||
270 | |||
271 | //-------------------------------------------------------------------- |
||
272 | // Protected Methods |
||
273 | //-------------------------------------------------------------------- |
||
274 | |||
275 | /** |
||
276 | * Checks the client's IP address against any IP addresses specified |
||
277 | * in the api config file. If any are found, the client is refused |
||
278 | * access immediately. |
||
279 | */ |
||
280 | View Code Duplication | public function checkIPBlacklist() |
|
295 | |||
296 | //-------------------------------------------------------------------- |
||
297 | |||
298 | /** |
||
299 | * Checks the client's IP address against any IP addresses specified |
||
300 | * in the api config file. If the client is not accessing the site |
||
301 | * from one of those addresses then their access is denied. |
||
302 | */ |
||
303 | View Code Duplication | public function checkIPWhitelist() |
|
320 | |||
321 | //-------------------------------------------------------------------- |
||
322 | |||
323 | /** |
||
324 | * Handles the nitty gritty of actually logging our user into the system. |
||
325 | * Does NOT perform the authentication, just sets the system up so that |
||
326 | * it knows we're here. |
||
327 | * |
||
328 | * @param $user |
||
329 | */ |
||
330 | protected function loginUser($user) |
||
346 | |||
347 | //-------------------------------------------------------------------- |
||
348 | |||
349 | //-------------------------------------------------------------------- |
||
350 | // UNUSED METHOD OVERRIDES |
||
351 | //-------------------------------------------------------------------- |
||
352 | |||
353 | /** |
||
354 | * Attempt to log a user into the system. |
||
355 | * |
||
356 | * $credentials is an array of key/value pairs needed to log the user in. |
||
357 | * This is often email/password, or username/password. |
||
358 | * |
||
359 | * NOTE: Since this is intended for API use, it is a STATELESS implementation |
||
360 | * and does not support remember me functionality. |
||
361 | * |
||
362 | * Valid credentials: |
||
363 | * - username |
||
364 | |||
365 | * - realm |
||
366 | * |
||
367 | * @param $credentials |
||
368 | * @param bool $remember |
||
369 | * |
||
370 | * @return bool|mixed|void |
||
371 | */ |
||
372 | public function login($credentials, $remember=false) |
||
376 | |||
377 | //-------------------------------------------------------------------- |
||
378 | |||
379 | /** |
||
380 | * Logs a user out and removes all session information. |
||
381 | * |
||
382 | * NOTE: Since this is intended for API use, it is a STATELESS implementation |
||
383 | * and does not support remember me functionality. |
||
384 | * |
||
385 | * @return mixed |
||
386 | */ |
||
387 | public function logout() |
||
391 | |||
392 | //-------------------------------------------------------------------- |
||
393 | |||
394 | /** |
||
395 | * Checks whether a user is logged in or not. |
||
396 | * |
||
397 | * @return bool |
||
398 | */ |
||
399 | public function isLoggedIn() |
||
403 | |||
404 | //-------------------------------------------------------------------- |
||
405 | |||
406 | } |
||
407 |
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.