MasterPasswordBehavior.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <?php
  2. App::uses('ModelBehavior', 'Model');
  3. /**
  4. * MasterPassword Behavior for admin views
  5. *
  6. * Uses
  7. * - Tools.Hash Shell to hash password
  8. * - master_password element of Tools plugin for Form input
  9. *
  10. * Usage:
  11. * In the controller:
  12. * $this->ModelName->Behaviors->load('Tools.MasterPassword');
  13. * In the view:
  14. * echo $this->element('master_password', array(), array('plugin'=>'tools'));
  15. * Put this into your private configs:
  16. * Configure::write('MasterPassword.password', 'your_hashed_pwd_string');
  17. * You can also use an array to store multiple passwords
  18. *
  19. * Note:
  20. * sha1 is the default hashing algorithm
  21. *
  22. * Use Configure::write('MasterPassword.password', false) to deactivate
  23. *
  24. * Copyright 2011, dereuromark (http://www.dereuromark.de)
  25. *
  26. * @author Mark Scherer
  27. * @link http://github.com/dereuromark/
  28. * @license http://opensource.org/licenses/mit-license.php MIT
  29. */
  30. /**
  31. */
  32. class MasterPasswordBehavior extends ModelBehavior {
  33. protected $_defaultConfig = [
  34. 'message' => 'Incorrect Master Password',
  35. 'field' => 'master_pwd',
  36. 'model' => null,
  37. 'before' => 'validate',
  38. 'hash' => 'sha1',
  39. 'salt' => false, //TODO: maybe allow to use core salt for additional security?
  40. 'log' => false //TODO: log the usage of pwds to a log file `master_password`
  41. ];
  42. public function setup(Model $Model, $config = []) {
  43. if (!isset($this->settings[$Model->alias])) {
  44. $this->settings[$Model->alias] = $this->_defaultConfig;
  45. }
  46. $this->settings[$Model->alias] = $config + $this->settings[$Model->alias];
  47. // deactivate dynamically
  48. if (Configure::read('MasterPassword.password') === false) {
  49. $this->settings[$Model->alias]['before'] = '';
  50. }
  51. }
  52. public function beforeValidate(Model $Model, $options = []) {
  53. $return = parent::beforeValidate($Model, $options);
  54. if ($this->settings[$Model->alias]['before'] === 'validate') {
  55. // we dont want to return the value, because other fields might then not be validated
  56. // (save will not continue with errors, anyway)
  57. $this->confirm($Model, $return);
  58. }
  59. return $return;
  60. }
  61. public function beforeSave(Model $Model, $options = []) {
  62. $return = parent::beforeSave($Model, $options);
  63. if ($this->settings[$Model->alias]['before'] === 'save') {
  64. return $this->confirm($Model, $return);
  65. }
  66. return $return;
  67. }
  68. /**
  69. * Run before a model is saved, used...
  70. *
  71. * @param Model $Model Model about to be saved.
  72. * @return bool true if save should proceed, false otherwise
  73. */
  74. public function confirm(Model $Model, $return = true) {
  75. $field = $this->settings[$Model->alias]['field'];
  76. $message = $this->settings[$Model->alias]['message'];
  77. if (!$this->isAuthorized($Model, $field)) {
  78. $Model->invalidate($field, $message);
  79. return false;
  80. }
  81. return $return;
  82. }
  83. /**
  84. * Checks a string against the stored hash values of master passwords
  85. *
  86. * @param string $pwd: plain password string (not hashed etc)
  87. * @return bool Success
  88. */
  89. public function isAuthorized(Model $Model, $field) {
  90. if (empty($Model->data[$Model->alias][$field])) {
  91. return false;
  92. }
  93. $masterPwds = (array)Configure::read('MasterPassword.password');
  94. $pwd = $this->_hash($Model->data[$Model->alias][$field], $this->settings[$Model->alias]['hash'], $this->settings[$Model->alias]['salt']);
  95. foreach ($masterPwds as $masterPwd) {
  96. if ($masterPwd === $pwd) {
  97. return true;
  98. }
  99. }
  100. return false;
  101. }
  102. /**
  103. * @return string hash or FALSE on failure
  104. */
  105. protected function _hash($string, $algorithm, $salt) {
  106. if ($salt) {
  107. if (is_string($salt)) {
  108. $string = $salt . $string;
  109. } else {
  110. $string = Configure::read('Security.salt') . $string;
  111. }
  112. }
  113. if ($algorithm === 'sha1') {
  114. return sha1($string);
  115. }
  116. if ($algorithm === 'md5') {
  117. return md5($string);
  118. }
  119. // mcrypt installed?
  120. if (function_exists('hash') && in_array($algorithm, hash_algos())) {
  121. return hash($algorithm, $string);
  122. }
  123. trigger_error('Hash method not available');
  124. return false;
  125. }
  126. }