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:
Complex classes like User often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use User, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class User extends DataObject |
||
29 | { |
||
30 | const STATUS_ACTIVE = 'Active'; |
||
31 | const STATUS_SUSPENDED = 'Suspended'; |
||
32 | const STATUS_DECLINED = 'Declined'; |
||
33 | const STATUS_NEW = 'New'; |
||
34 | private $username; |
||
35 | private $email; |
||
36 | private $password; |
||
37 | private $status = self::STATUS_NEW; |
||
38 | private $onwikiname = "##OAUTH##"; |
||
39 | private $welcome_sig = ""; |
||
40 | private $lastactive = "0000-00-00 00:00:00"; |
||
41 | private $forcelogout = 0; |
||
42 | private $forceidentified = null; |
||
43 | private $welcome_template = 0; |
||
44 | private $abortpref = 0; |
||
45 | private $confirmationdiff = 0; |
||
46 | private $emailsig = ""; |
||
47 | /** @var null|string */ |
||
48 | private $oauthrequesttoken = null; |
||
49 | /** @var null|string */ |
||
50 | private $oauthrequestsecret = null; |
||
51 | /** @var null|string */ |
||
52 | private $oauthaccesstoken = null; |
||
53 | /** @var null|string */ |
||
54 | private $oauthaccesssecret = null; |
||
55 | private $oauthidentitycache = null; |
||
56 | /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */ |
||
57 | private static $currentUser; |
||
58 | /** @var null|JWT The identity cache */ |
||
59 | private $identityCache = null; |
||
60 | #region Object load methods |
||
61 | |||
62 | /** |
||
63 | * Gets the currently logged in user |
||
64 | * |
||
65 | * @param PdoDatabase $database |
||
66 | * |
||
67 | * @return User|CommunityUser |
||
68 | */ |
||
69 | public static function getCurrent(PdoDatabase $database) |
||
70 | { |
||
71 | if (self::$currentUser === null) { |
||
72 | $sessionId = WebRequest::getSessionUserId(); |
||
73 | |||
74 | if ($sessionId !== null) { |
||
75 | /** @var User $user */ |
||
76 | $user = self::getById($sessionId, $database); |
||
77 | |||
78 | if ($user === false) { |
||
79 | self::$currentUser = new CommunityUser(); |
||
80 | } |
||
81 | else { |
||
82 | self::$currentUser = $user; |
||
83 | } |
||
84 | } |
||
85 | else { |
||
86 | $anonymousCoward = new CommunityUser(); |
||
87 | |||
88 | self::$currentUser = $anonymousCoward; |
||
89 | } |
||
90 | } |
||
91 | |||
92 | return self::$currentUser; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Gets a user by their user ID |
||
97 | * |
||
98 | * Pass -1 to get the community user. |
||
99 | * |
||
100 | * @param int|null $id |
||
101 | * @param PdoDatabase $database |
||
102 | * |
||
103 | * @return User|false |
||
104 | */ |
||
105 | public static function getById($id, PdoDatabase $database) |
||
116 | |||
117 | /** |
||
118 | * @return CommunityUser |
||
119 | */ |
||
120 | public static function getCommunity() |
||
121 | { |
||
122 | return new CommunityUser(); |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Gets a user by their username |
||
127 | * |
||
128 | * @param string $username |
||
129 | * @param PdoDatabase $database |
||
130 | * |
||
131 | * @return CommunityUser|User|false |
||
132 | */ |
||
133 | public static function getByUsername($username, PdoDatabase $database) |
||
153 | |||
154 | /** |
||
155 | * Gets a user by their on-wiki username. |
||
156 | * |
||
157 | * Don't use without asking me first. It's really inefficient in it's current implementation. |
||
158 | * We need to restructure the user table again to make this more efficient. |
||
159 | * We don't actually store the on-wiki name in the table any more, instead we |
||
160 | * are storing JSON in a column (!!). Yep, my fault. Code review is an awesome thing. |
||
161 | * -- stw 2015-10-20 |
||
162 | * |
||
163 | * @param string $username |
||
164 | * @param PdoDatabase $database |
||
165 | * |
||
166 | * @return User|false |
||
167 | */ |
||
168 | public static function getByOnWikiUsername($username, PdoDatabase $database) |
||
169 | { |
||
170 | // Firstly, try to search by the efficient database lookup. |
||
171 | $statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;"); |
||
172 | $statement->bindValue(":id", $username); |
||
173 | $statement->execute(); |
||
174 | |||
175 | $resultObject = $statement->fetchObject(get_called_class()); |
||
176 | |||
177 | if ($resultObject != false) { |
||
178 | $resultObject->setDatabase($database); |
||
179 | |||
180 | return $resultObject; |
||
181 | } |
||
182 | |||
183 | // For active users, the above has failed. Let's do it the hard way. |
||
184 | $sqlStatement = "SELECT * FROM user WHERE onwikiname = '##OAUTH##' AND oauthaccesstoken IS NOT NULL;"; |
||
185 | $statement = $database->prepare($sqlStatement); |
||
186 | $statement->execute(); |
||
187 | $resultSet = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class()); |
||
188 | |||
189 | /** @var User $user */ |
||
190 | foreach ($resultSet as $user) { |
||
191 | // We have to set this before doing OAuth queries. :( |
||
192 | $user->setDatabase($database); |
||
193 | |||
194 | // Using cached data here! |
||
195 | if ($user->getOAuthOnWikiName(true) == $username) { |
||
196 | // Success. |
||
197 | return $user; |
||
198 | } |
||
199 | } |
||
200 | |||
201 | // Cached data failed. Let's do it the *REALLY* hard way. |
||
202 | foreach ($resultSet as $user) { |
||
203 | // We have to set this before doing OAuth queries. :( |
||
204 | $user->setDatabase($database); |
||
205 | |||
206 | // Don't use the cached data, but instead query the API. |
||
207 | if ($user->getOAuthOnWikiName(false) == $username) { |
||
208 | // Success. |
||
209 | return $user; |
||
210 | } |
||
211 | } |
||
212 | |||
213 | // Nope. Sorry. |
||
214 | return false; |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * Gets a user by their OAuth request token |
||
219 | * |
||
220 | * @param string $requestToken |
||
221 | * @param PdoDatabase $database |
||
222 | * |
||
223 | * @return User|false |
||
224 | */ |
||
225 | public static function getByRequestToken($requestToken, PdoDatabase $database) |
||
226 | { |
||
227 | $statement = $database->prepare("SELECT * FROM user WHERE oauthrequesttoken = :id LIMIT 1;"); |
||
228 | $statement->bindValue(":id", $requestToken); |
||
229 | |||
230 | $statement->execute(); |
||
231 | |||
232 | $resultObject = $statement->fetchObject(get_called_class()); |
||
233 | |||
234 | if ($resultObject != false) { |
||
235 | $resultObject->setDatabase($database); |
||
236 | } |
||
237 | |||
238 | return $resultObject; |
||
239 | } |
||
240 | |||
241 | #endregion |
||
242 | |||
243 | /** |
||
244 | * Saves the current object |
||
245 | * |
||
246 | * @throws Exception |
||
247 | */ |
||
248 | public function save() |
||
249 | { |
||
250 | if ($this->isNew()) { |
||
251 | // insert |
||
252 | $statement = $this->dbObject->prepare(<<<SQL |
||
253 | INSERT INTO `user` ( |
||
254 | username, email, password, status, onwikiname, welcome_sig, |
||
255 | lastactive, forcelogout, forceidentified, |
||
256 | welcome_template, abortpref, confirmationdiff, emailsig, |
||
257 | oauthrequesttoken, oauthrequestsecret, |
||
258 | oauthaccesstoken, oauthaccesssecret |
||
259 | ) VALUES ( |
||
260 | :username, :email, :password, :status, :onwikiname, :welcome_sig, |
||
261 | :lastactive, :forcelogout, :forceidentified, |
||
262 | :welcome_template, :abortpref, :confirmationdiff, :emailsig, |
||
263 | :ort, :ors, :oat, :oas |
||
264 | ); |
||
265 | SQL |
||
266 | ); |
||
267 | $statement->bindValue(":username", $this->username); |
||
268 | $statement->bindValue(":email", $this->email); |
||
269 | $statement->bindValue(":password", $this->password); |
||
270 | $statement->bindValue(":status", $this->status); |
||
271 | $statement->bindValue(":onwikiname", $this->onwikiname); |
||
272 | $statement->bindValue(":welcome_sig", $this->welcome_sig); |
||
273 | $statement->bindValue(":lastactive", $this->lastactive); |
||
274 | $statement->bindValue(":forcelogout", $this->forcelogout); |
||
275 | $statement->bindValue(":forceidentified", $this->forceidentified); |
||
276 | $statement->bindValue(":welcome_template", $this->welcome_template); |
||
277 | $statement->bindValue(":abortpref", $this->abortpref); |
||
278 | $statement->bindValue(":confirmationdiff", $this->confirmationdiff); |
||
279 | $statement->bindValue(":emailsig", $this->emailsig); |
||
280 | $statement->bindValue(":ort", $this->oauthrequesttoken); |
||
281 | $statement->bindValue(":ors", $this->oauthrequestsecret); |
||
282 | $statement->bindValue(":oat", $this->oauthaccesstoken); |
||
283 | $statement->bindValue(":oas", $this->oauthaccesssecret); |
||
284 | |||
285 | if ($statement->execute()) { |
||
286 | $this->id = (int)$this->dbObject->lastInsertId(); |
||
287 | } |
||
288 | else { |
||
289 | throw new Exception($statement->errorInfo()); |
||
290 | } |
||
291 | } |
||
292 | else { |
||
293 | // update |
||
294 | $statement = $this->dbObject->prepare(<<<SQL |
||
295 | UPDATE `user` SET |
||
296 | username = :username, email = :email, |
||
297 | password = :password, status = :status, |
||
298 | onwikiname = :onwikiname, welcome_sig = :welcome_sig, |
||
299 | lastactive = :lastactive, forcelogout = :forcelogout, |
||
300 | forceidentified = :forceidentified, |
||
301 | welcome_template = :welcome_template, abortpref = :abortpref, |
||
302 | confirmationdiff = :confirmationdiff, emailsig = :emailsig, |
||
303 | oauthrequesttoken = :ort, oauthrequestsecret = :ors, |
||
304 | oauthaccesstoken = :oat, oauthaccesssecret = :oas, |
||
305 | updateversion = updateversion + 1 |
||
306 | WHERE id = :id AND updateversion = :updateversion |
||
307 | LIMIT 1; |
||
308 | SQL |
||
309 | ); |
||
310 | $statement->bindValue(":forceidentified", $this->forceidentified); |
||
311 | |||
312 | $statement->bindValue(':id', $this->id); |
||
313 | $statement->bindValue(':updateversion', $this->updateversion); |
||
314 | |||
315 | $statement->bindValue(':username', $this->username); |
||
316 | $statement->bindValue(':email', $this->email); |
||
317 | $statement->bindValue(':password', $this->password); |
||
318 | $statement->bindValue(':status', $this->status); |
||
319 | $statement->bindValue(':onwikiname', $this->onwikiname); |
||
320 | $statement->bindValue(':welcome_sig', $this->welcome_sig); |
||
321 | $statement->bindValue(':lastactive', $this->lastactive); |
||
322 | $statement->bindValue(':forcelogout', $this->forcelogout); |
||
323 | $statement->bindValue(':forceidentified', $this->forceidentified); |
||
324 | $statement->bindValue(':welcome_template', $this->welcome_template); |
||
325 | $statement->bindValue(':abortpref', $this->abortpref); |
||
326 | $statement->bindValue(':confirmationdiff', $this->confirmationdiff); |
||
327 | $statement->bindValue(':emailsig', $this->emailsig); |
||
328 | $statement->bindValue(':ort', $this->oauthrequesttoken); |
||
329 | $statement->bindValue(':ors', $this->oauthrequestsecret); |
||
330 | $statement->bindValue(':oat', $this->oauthaccesstoken); |
||
331 | $statement->bindValue(':oas', $this->oauthaccesssecret); |
||
332 | |||
333 | if (!$statement->execute()) { |
||
334 | throw new Exception($statement->errorInfo()); |
||
335 | } |
||
336 | |||
337 | if ($statement->rowCount() !== 1) { |
||
338 | throw new OptimisticLockFailedException(); |
||
339 | } |
||
340 | |||
341 | $this->updateversion++; |
||
342 | } |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Authenticates the user with the supplied password |
||
347 | * |
||
348 | * @param string $password |
||
349 | * |
||
350 | * @return bool |
||
351 | * @throws Exception |
||
352 | * @category Security-Critical |
||
353 | */ |
||
354 | public function authenticate($password) |
||
355 | { |
||
356 | $result = AuthUtility::testCredentials($password, $this->password); |
||
357 | |||
358 | if ($result === true) { |
||
359 | // password version is out of date, update it. |
||
360 | if (!AuthUtility::isCredentialVersionLatest($this->password)) { |
||
361 | $this->password = AuthUtility::encryptPassword($password); |
||
362 | $this->save(); |
||
363 | } |
||
364 | } |
||
365 | |||
366 | return $result; |
||
367 | } |
||
368 | |||
369 | #region properties |
||
370 | |||
371 | /** |
||
372 | * Gets the tool username |
||
373 | * @return string |
||
374 | */ |
||
375 | public function getUsername() |
||
379 | |||
380 | /** |
||
381 | * Sets the tool username |
||
382 | * |
||
383 | * @param string $username |
||
384 | */ |
||
385 | public function setUsername($username) |
||
386 | { |
||
387 | $this->username = $username; |
||
388 | |||
389 | // If this isn't a brand new user, then it's a rename, force the logout |
||
390 | if (!$this->isNew()) { |
||
391 | $this->forcelogout = 1; |
||
392 | } |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Gets the user's email address |
||
397 | * @return string |
||
398 | */ |
||
399 | public function getEmail() |
||
403 | |||
404 | /** |
||
405 | * Sets the user's email address |
||
406 | * |
||
407 | * @param string $email |
||
408 | */ |
||
409 | public function setEmail($email) |
||
410 | { |
||
411 | $this->email = $email; |
||
412 | } |
||
413 | |||
414 | /** |
||
415 | * Sets the user's password |
||
416 | * |
||
417 | * @param string $password the plaintext password |
||
418 | * |
||
419 | * @category Security-Critical |
||
420 | */ |
||
421 | public function setPassword($password) |
||
422 | { |
||
423 | $this->password = AuthUtility::encryptPassword($password); |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user. |
||
428 | * @return string |
||
429 | */ |
||
430 | public function getStatus() |
||
431 | { |
||
432 | return $this->status; |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * @param string $status |
||
437 | */ |
||
438 | public function setStatus($status) |
||
439 | { |
||
440 | $this->status = $status; |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * Gets the user's on-wiki name |
||
445 | * @return string |
||
446 | */ |
||
447 | public function getOnWikiName() |
||
448 | { |
||
449 | if ($this->oauthaccesstoken !== null) { |
||
450 | try { |
||
451 | return $this->getOAuthOnWikiName(); |
||
452 | } |
||
453 | catch (Exception $ex) { |
||
454 | // urm.. log this? |
||
455 | return $this->onwikiname; |
||
456 | } |
||
457 | } |
||
458 | |||
459 | return $this->onwikiname; |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * This is probably NOT the function you want! |
||
464 | * |
||
465 | * Take a look at getOnWikiName() instead. |
||
466 | * @return string |
||
467 | */ |
||
468 | public function getStoredOnWikiName() |
||
469 | { |
||
470 | return $this->onwikiname; |
||
471 | } |
||
472 | |||
473 | /** |
||
474 | * Sets the user's on-wiki name |
||
475 | * |
||
476 | * This can have interesting side-effects with OAuth. |
||
477 | * |
||
478 | * @param string $onWikiName |
||
479 | */ |
||
480 | public function setOnWikiName($onWikiName) |
||
481 | { |
||
482 | $this->onwikiname = $onWikiName; |
||
483 | } |
||
484 | |||
485 | /** |
||
486 | * Gets the welcome signature |
||
487 | * @return string |
||
488 | */ |
||
489 | public function getWelcomeSig() |
||
490 | { |
||
491 | return $this->welcome_sig; |
||
492 | } |
||
493 | |||
494 | /** |
||
495 | * Sets the welcome signature |
||
496 | * |
||
497 | * @param string $welcomeSig |
||
498 | */ |
||
499 | public function setWelcomeSig($welcomeSig) |
||
503 | |||
504 | /** |
||
505 | * Gets the last activity date for the user |
||
506 | * |
||
507 | * @return string |
||
508 | * @todo This should probably return an instance of DateTime |
||
509 | */ |
||
510 | public function getLastActive() |
||
511 | { |
||
512 | return $this->lastactive; |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * Gets the user's forced logout status |
||
517 | * |
||
518 | * @return bool |
||
519 | */ |
||
520 | public function getForceLogout() |
||
521 | { |
||
522 | return $this->forcelogout == 1; |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Sets the user's forced logout status |
||
527 | * |
||
528 | * @param bool $forceLogout |
||
529 | */ |
||
530 | public function setForceLogout($forceLogout) |
||
534 | |||
535 | /** |
||
536 | * Returns the ID of the welcome template used. |
||
537 | * @return int |
||
538 | */ |
||
539 | public function getWelcomeTemplate() |
||
543 | |||
544 | /** |
||
545 | * Sets the ID of the welcome template used. |
||
546 | * |
||
547 | * @param int $welcomeTemplate |
||
548 | */ |
||
549 | public function setWelcomeTemplate($welcomeTemplate) |
||
550 | { |
||
551 | $this->welcome_template = $welcomeTemplate; |
||
552 | } |
||
553 | |||
554 | /** |
||
555 | * Gets the user's abort preference |
||
556 | * @todo this is badly named too! Also a bool that's actually an int. |
||
557 | * @return int |
||
558 | */ |
||
559 | public function getAbortPref() |
||
560 | { |
||
561 | return $this->abortpref; |
||
562 | } |
||
563 | |||
564 | /** |
||
565 | * Sets the user's abort preference |
||
566 | * @todo rename, retype, and re-comment. |
||
567 | * |
||
568 | * @param int $abortPreference |
||
569 | */ |
||
570 | public function setAbortPref($abortPreference) |
||
571 | { |
||
572 | $this->abortpref = $abortPreference; |
||
573 | } |
||
574 | |||
575 | /** |
||
576 | * Gets the user's confirmation diff. Unused if OAuth is in use. |
||
577 | * @return int the diff ID |
||
578 | */ |
||
579 | public function getConfirmationDiff() |
||
580 | { |
||
581 | return $this->confirmationdiff; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * Sets the user's confirmation diff. |
||
586 | * |
||
587 | * @param int $confirmationDiff |
||
588 | */ |
||
589 | public function setConfirmationDiff($confirmationDiff) |
||
590 | { |
||
591 | $this->confirmationdiff = $confirmationDiff; |
||
592 | } |
||
593 | |||
594 | /** |
||
595 | * Gets the users' email signature used on outbound mail. |
||
596 | * @todo rename me! |
||
597 | * @return string |
||
598 | */ |
||
599 | public function getEmailSig() |
||
600 | { |
||
601 | return $this->emailsig; |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Sets the user's email signature for outbound mail. |
||
606 | * |
||
607 | * @param string $emailSignature |
||
608 | */ |
||
609 | public function setEmailSig($emailSignature) |
||
610 | { |
||
611 | $this->emailsig = $emailSignature; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Gets the user's OAuth request token. |
||
616 | * |
||
617 | * @todo move me to a collaborator. |
||
618 | * @return null|string |
||
619 | */ |
||
620 | public function getOAuthRequestToken() |
||
621 | { |
||
622 | return $this->oauthrequesttoken; |
||
623 | } |
||
624 | |||
625 | /** |
||
626 | * Sets the user's OAuth request token |
||
627 | * @todo move me to a collaborator |
||
628 | * |
||
629 | * @param string $oAuthRequestToken |
||
630 | */ |
||
631 | public function setOAuthRequestToken($oAuthRequestToken) |
||
632 | { |
||
633 | $this->oauthrequesttoken = $oAuthRequestToken; |
||
634 | } |
||
635 | |||
636 | /** |
||
637 | * Gets the users OAuth request secret |
||
638 | * @category Security-Critical |
||
639 | * @todo move me to a collaborator |
||
640 | * @return null|string |
||
641 | */ |
||
642 | public function getOAuthRequestSecret() |
||
643 | { |
||
644 | return $this->oauthrequestsecret; |
||
645 | } |
||
646 | |||
647 | /** |
||
648 | * Sets the user's OAuth request secret |
||
649 | * @todo move me to a collaborator |
||
650 | * |
||
651 | * @param string $oAuthRequestSecret |
||
652 | */ |
||
653 | public function setOAuthRequestSecret($oAuthRequestSecret) |
||
654 | { |
||
655 | $this->oauthrequestsecret = $oAuthRequestSecret; |
||
656 | } |
||
657 | |||
658 | /** |
||
659 | * Gets the user's access token |
||
660 | * @category Security-Critical |
||
661 | * @todo move me to a collaborator |
||
662 | * @return null|string |
||
663 | */ |
||
664 | public function getOAuthAccessToken() |
||
665 | { |
||
666 | return $this->oauthaccesstoken; |
||
667 | } |
||
668 | |||
669 | /** |
||
670 | * Sets the user's access token |
||
671 | * @todo move me to a collaborator |
||
672 | * |
||
673 | * @param string $oAuthAccessToken |
||
674 | */ |
||
675 | public function setOAuthAccessToken($oAuthAccessToken) |
||
679 | |||
680 | /** |
||
681 | * Gets the user's OAuth access secret |
||
682 | * @category Security-Critical |
||
683 | * @todo move me to a collaborator |
||
684 | * @return null|string |
||
685 | */ |
||
686 | public function getOAuthAccessSecret() |
||
687 | { |
||
688 | return $this->oauthaccesssecret; |
||
689 | } |
||
690 | |||
691 | /** |
||
692 | * Sets the user's OAuth access secret |
||
693 | * @todo move me to a collaborator |
||
694 | * |
||
695 | * @param string $oAuthAccessSecret |
||
696 | */ |
||
697 | public function setOAuthAccessSecret($oAuthAccessSecret) |
||
701 | |||
702 | #endregion |
||
703 | |||
704 | #region user access checks |
||
705 | |||
706 | public function isActive() |
||
707 | { |
||
708 | return $this->status == self::STATUS_ACTIVE; |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * Tests if the user is identified |
||
713 | * |
||
714 | * @param IdentificationVerifier $iv |
||
715 | * |
||
716 | * @return bool |
||
717 | * @todo Figure out what on earth is going on with PDO's typecasting here. Apparently, it returns string("0") for |
||
718 | * the force-unidentified case, and int(1) for the identified case?! This is quite ugly, but probably needed |
||
719 | * to play it safe for now. |
||
720 | * @category Security-Critical |
||
721 | */ |
||
722 | public function isIdentified(IdentificationVerifier $iv) |
||
723 | { |
||
724 | if ($this->forceidentified === 0 || $this->forceidentified === "0") { |
||
725 | // User forced to unidentified in the database. |
||
726 | return false; |
||
727 | } |
||
728 | elseif ($this->forceidentified === 1 || $this->forceidentified === "1") { |
||
729 | // User forced to identified in the database. |
||
730 | return true; |
||
731 | } |
||
732 | else { |
||
733 | // User not forced to any particular identified status; consult IdentificationVerifier |
||
734 | return $iv->isUserIdentified($this->getOnWikiName()); |
||
735 | } |
||
736 | } |
||
737 | |||
738 | /** |
||
739 | * Tests if the user is suspended |
||
740 | * @return bool |
||
741 | * @category Security-Critical |
||
742 | */ |
||
743 | public function isSuspended() |
||
747 | |||
748 | /** |
||
749 | * Tests if the user is new |
||
750 | * @return bool |
||
751 | * @category Security-Critical |
||
752 | */ |
||
753 | public function isNewUser() |
||
757 | |||
758 | /** |
||
759 | * Tests if the user has been declined access to the tool |
||
760 | * @return bool |
||
761 | * @category Security-Critical |
||
762 | */ |
||
763 | public function isDeclined() |
||
767 | |||
768 | /** |
||
769 | * Tests if the user is the community user |
||
770 | * |
||
771 | * @todo decide if this means logged out. I think it usually does. |
||
772 | * @return bool |
||
773 | * @category Security-Critical |
||
774 | */ |
||
775 | public function isCommunityUser() |
||
776 | { |
||
777 | return false; |
||
778 | } |
||
779 | |||
780 | #endregion |
||
781 | |||
782 | #region OAuth |
||
783 | |||
784 | /** |
||
785 | * @todo move me to a collaborator |
||
786 | * |
||
787 | * @param bool $useCached |
||
788 | * |
||
789 | * @return mixed|null |
||
790 | * @category Security-Critical |
||
791 | */ |
||
792 | public function getOAuthIdentity($useCached = false) |
||
793 | { |
||
794 | if ($this->oauthaccesstoken === null) { |
||
795 | $this->clearOAuthData(); |
||
796 | } |
||
797 | |||
798 | global $oauthConsumerToken, $oauthMediaWikiCanonicalServer; |
||
799 | |||
800 | if ($this->oauthidentitycache == null) { |
||
801 | $this->identityCache = null; |
||
802 | } |
||
803 | else { |
||
804 | $this->identityCache = unserialize($this->oauthidentitycache); |
||
805 | } |
||
806 | |||
807 | // check the cache |
||
808 | if ( |
||
809 | $this->identityCache != null && |
||
810 | $this->identityCache->aud == $oauthConsumerToken && |
||
811 | $this->identityCache->iss == $oauthMediaWikiCanonicalServer |
||
812 | ) { |
||
813 | if ( |
||
814 | $useCached || ( |
||
815 | DateTime::createFromFormat("U", $this->identityCache->iat) < new DateTime() && |
||
816 | DateTime::createFromFormat("U", $this->identityCache->exp) > new DateTime() |
||
817 | ) |
||
818 | ) { |
||
819 | // Use cached value - it's either valid or we don't care. |
||
820 | return $this->identityCache; |
||
821 | } |
||
822 | else { |
||
823 | // Cache expired and not forcing use of cached value |
||
824 | $this->getIdentityCache(); |
||
825 | |||
826 | return $this->identityCache; |
||
827 | } |
||
828 | } |
||
829 | else { |
||
830 | // Cache isn't ours or doesn't exist |
||
831 | $this->getIdentityCache(); |
||
832 | |||
833 | return $this->identityCache; |
||
834 | } |
||
835 | } |
||
836 | |||
837 | /** |
||
838 | * @todo move me to a collaborator |
||
839 | * |
||
840 | * @param mixed $useCached Set to false for everything where up-to-date data is important. |
||
841 | * |
||
842 | * @return mixed |
||
843 | * @category Security-Critical |
||
844 | */ |
||
845 | private function getOAuthOnWikiName($useCached = false) |
||
846 | { |
||
847 | $identity = $this->getOAuthIdentity($useCached); |
||
848 | if ($identity !== null) { |
||
849 | return $identity->username; |
||
850 | } |
||
851 | |||
852 | return false; |
||
853 | } |
||
854 | |||
855 | /** |
||
856 | * @return bool |
||
857 | * @todo move me to a collaborator |
||
858 | */ |
||
859 | public function isOAuthLinked() |
||
867 | |||
868 | /** |
||
869 | * @return null |
||
870 | * @todo move me to a collaborator |
||
871 | */ |
||
872 | public function clearOAuthData() |
||
873 | { |
||
874 | $this->identityCache = null; |
||
875 | $this->oauthidentitycache = null; |
||
876 | $clearCacheQuery = "UPDATE user SET oauthidentitycache = NULL WHERE id = :id;"; |
||
877 | $this->dbObject->prepare($clearCacheQuery)->execute(array(":id" => $this->id)); |
||
878 | |||
879 | return null; |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * @throws Exception |
||
884 | * @todo move me to a collaborator |
||
885 | * @category Security-Critical |
||
886 | */ |
||
887 | private function getIdentityCache() |
||
888 | { |
||
889 | /** @var IOAuthHelper $oauthHelper */ |
||
890 | global $oauthHelper; |
||
891 | |||
892 | try { |
||
893 | $this->identityCache = $oauthHelper->getIdentityTicket($this->oauthaccesstoken, $this->oauthaccesssecret); |
||
894 | |||
895 | $this->oauthidentitycache = serialize($this->identityCache); |
||
896 | $this->dbObject->prepare("UPDATE user SET oauthidentitycache = :identity WHERE id = :id;") |
||
897 | ->execute(array(":id" => $this->id, ":identity" => $this->oauthidentitycache)); |
||
898 | } |
||
899 | catch (UnexpectedValueException $ex) { |
||
900 | $this->identityCache = null; |
||
901 | $this->oauthidentitycache = null; |
||
902 | $this->dbObject->prepare("UPDATE user SET oauthidentitycache = NULL WHERE id = :id;") |
||
903 | ->execute(array(":id" => $this->id)); |
||
908 | |||
909 | /** |
||
910 | * @return bool |
||
911 | * @todo move me to a collaborator |
||
912 | */ |
||
913 | public function oauthCanUse() |
||
922 | |||
923 | /** |
||
924 | * @return bool |
||
925 | * @todo move me to a collaborator |
||
926 | */ |
||
927 | View Code Duplication | public function oauthCanEdit() |
|
940 | |||
941 | /** |
||
942 | * @return bool |
||
943 | * @todo move me to a collaborator |
||
944 | */ |
||
945 | View Code Duplication | public function oauthCanCreateAccount() |
|
957 | |||
958 | /** |
||
959 | * @return bool |
||
960 | * @todo move me to a collaborator |
||
961 | * @category Security-Critical |
||
962 | */ |
||
963 | protected function oauthCanCheckUser() |
||
978 | |||
979 | #endregion |
||
980 | |||
981 | /** |
||
982 | * Gets a hash of data for the user to reset their password with. |
||
983 | * @category Security-Critical |
||
984 | * @return string |
||
985 | */ |
||
986 | public function getForgottenPasswordHash() |
||
990 | |||
991 | /** |
||
992 | * Gets the approval date of the user |
||
993 | * @return DateTime|false |
||
994 | */ |
||
995 | public function getApprovalDate() |
||
1014 | } |
||
1015 |
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.