/security/PasswordValidator.php

https://github.com/markjames/sapphire · PHP · 93 lines · 49 code · 10 blank · 34 comment · 9 complexity · fa673fff56c87211e9eee981b6e7b129 MD5 · raw file

  1. <?php
  2. /**
  3. * This class represents a validator for member passwords.
  4. *
  5. * <code>
  6. * $pwdVal = new PasswordValidator();
  7. * $pwdValidator->minLength(7);
  8. * $pwdValidator->checkHistoricalPasswords(6);
  9. * $pwdValidator->characterStrength('lowercase','uppercase','digits','punctuation');
  10. *
  11. * Member::set_password_validator($pwdValidator);
  12. * </code>
  13. *
  14. * @package sapphire
  15. * @subpackage security
  16. */
  17. class PasswordValidator extends Object {
  18. static $character_strength_tests = array(
  19. 'lowercase' => '/[a-z]/',
  20. 'uppercase' => '/[A-Z]/',
  21. 'digits' => '/[0-9]/',
  22. 'punctuation' => '/[^A-Za-z0-9]/',
  23. );
  24. protected $minLength, $minScore, $testNames, $historicalPasswordCount;
  25. /**
  26. * Minimum password length
  27. */
  28. function minLength($minLength) {
  29. $this->minLength = $minLength;
  30. }
  31. /**
  32. * Check the character strength of the password.
  33. *
  34. * Eg: $this->characterStrength(3, array("lowercase", "uppercase", "digits", "punctuation"))
  35. *
  36. * @param $minScore The minimum number of character tests that must pass
  37. * @param $testNames The names of the tests to perform
  38. */
  39. function characterStrength($minScore, $testNames) {
  40. $this->minScore = $minScore;
  41. $this->testNames = $testNames;
  42. }
  43. /**
  44. * Check a number of previous passwords that the user has used, and don't let them change to that.
  45. */
  46. function checkHistoricalPasswords($count) {
  47. $this->historicalPasswordCount = $count;
  48. }
  49. /**
  50. * @param String $password
  51. * @param Member $member
  52. * @return ValidationResult
  53. */
  54. function validate($password, $member) {
  55. $valid = new ValidationResult();
  56. if($this->minLength) {
  57. if(strlen($password) < $this->minLength) $valid->error(sprintf("Password is too short, it must be %s or more characters long.", $this->minLength), "TOO_SHORT");
  58. }
  59. if($this->minScore) {
  60. $score = 0;
  61. $missedTests = array();
  62. foreach($this->testNames as $name) {
  63. if(preg_match(self::$character_strength_tests[$name], $password)) $score++;
  64. else $missedTests[] = $name;
  65. }
  66. if($score < $this->minScore) {
  67. $valid->error("You need to increase the strength of your passwords by adding some of the following characters: " . implode(", ", $missedTests), "LOW_CHARACTER_STRENGTH");
  68. }
  69. }
  70. if($this->historicalPasswordCount) {
  71. $previousPasswords = DataObject::get("MemberPassword", "\"MemberID\" = $member->ID", "\"Created\" DESC, \"ID\" Desc", "", $this->historicalPasswordCount);
  72. if($previousPasswords) foreach($previousPasswords as $previousPasswords) {
  73. if($previousPasswords->checkPassword($password)) {
  74. $valid->error("You've already used that password in the past, please choose a new password", "PREVIOUS_PASSWORD");
  75. break;
  76. }
  77. }
  78. }
  79. return $valid;
  80. }
  81. }