modGeneratePassPerso.class.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. /* Copyright (C) 2006-2011 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2014 Teddy Andreotti <125155@supinfo.com>
  4. * Copyright (C) 2017 Regis Houssin <regis.houssin@inodbox.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. * or see https://www.gnu.org/
  19. */
  20. /**
  21. * \file htdocs/core/modules/security/generate/modGeneratePassPerso.class.php
  22. * \ingroup core
  23. * \brief File to manage no password generation.
  24. */
  25. require_once DOL_DOCUMENT_ROOT.'/core/modules/security/generate/modules_genpassword.php';
  26. /**
  27. * Class to generate a password according to personal rules
  28. */
  29. class modGeneratePassPerso extends ModeleGenPassword
  30. {
  31. /**
  32. * @var int ID
  33. */
  34. public $id;
  35. public $picto = 'fa-shield-alt';
  36. /**
  37. * Minimum length (text visible by end user)
  38. *
  39. * @var string
  40. */
  41. public $length;
  42. /**
  43. * Minimum length in number of characters
  44. *
  45. * @var integer
  46. */
  47. public $length2;
  48. public $NbMaj;
  49. public $NbNum;
  50. public $NbSpe;
  51. public $NbRepeat;
  52. /**
  53. * Flag to 1 if we must clean ambiguous charaters for the autogeneration of password (List of ambiguous char is in $this->Ambi)
  54. *
  55. * @var integer
  56. */
  57. public $WithoutAmbi = 0;
  58. /**
  59. * @var DoliDB Database handler.
  60. */
  61. public $db;
  62. public $conf;
  63. public $lang;
  64. public $user;
  65. public $Maj;
  66. public $Min;
  67. public $Nb;
  68. public $Spe;
  69. public $Ambi;
  70. public $All;
  71. /**
  72. * Constructor
  73. *
  74. * @param DoliDB $db Database handler
  75. * @param Conf $conf Handler de conf
  76. * @param Translate $langs Handler de langue
  77. * @param User $user Handler du user connecte
  78. */
  79. public function __construct($db, $conf, $langs, $user)
  80. {
  81. $this->id = "Perso";
  82. $this->length = $langs->trans("SetupPerso");
  83. $this->db = $db;
  84. $this->conf = $conf;
  85. $this->langs = $langs;
  86. $this->user = $user;
  87. if (empty($conf->global->USER_PASSWORD_PATTERN)) {
  88. // default value at auto generation (12 chars, 1 uppercase, 1 digit, 0 special char, 3 repeat max, exclude ambiguous characters).
  89. dolibarr_set_const($db, "USER_PASSWORD_PATTERN", '12;1;1;0;3;1', 'chaine', 0, '', $conf->entity);
  90. }
  91. $this->Maj = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  92. $this->Min = strtolower($this->Maj);
  93. $this->Nb = "0123456789";
  94. $this->Spe = "!@#$%&*()_-+={}[]\\|:;'/";
  95. $this->Ambi = array("1", "I", "l", "|", "O", "0");
  96. $tabConf = explode(";", $conf->global->USER_PASSWORD_PATTERN);
  97. $this->length2 = $tabConf[0];
  98. $this->NbMaj = $tabConf[1];
  99. $this->NbNum = $tabConf[2];
  100. $this->NbSpe = $tabConf[3];
  101. $this->NbRepeat = $tabConf[4];
  102. $this->WithoutAmbi = $tabConf[5];
  103. }
  104. /**
  105. * Init the property ->All and clean ->Maj, ->Min, ->Nb and ->Spe with list of valid chars
  106. *
  107. * @return void
  108. */
  109. private function initAll()
  110. {
  111. if ($this->WithoutAmbi) {
  112. $this->Maj = str_replace($this->Ambi, "", $this->Maj);
  113. $this->Min = str_replace($this->Ambi, "", $this->Min);
  114. $this->Nb = str_replace($this->Ambi, "", $this->Nb);
  115. $this->Spe = str_replace($this->Ambi, "", $this->Spe);
  116. }
  117. $pattern = $this->Min.(!empty($this->NbMaj) ? $this->Maj : '').(!empty($this->NbNum) ? $this->Nb : '').(!empty($this->NbSpe) ? $this->Spe : '');
  118. $this->All = str_shuffle($pattern);
  119. }
  120. /**
  121. * Return description of module
  122. *
  123. * @return string Description of text
  124. */
  125. public function getDescription()
  126. {
  127. global $langs;
  128. return $langs->trans("PasswordGenerationPerso");
  129. }
  130. /**
  131. * Return an example of password generated by this module
  132. *
  133. * @return string Example of password
  134. */
  135. public function getExample()
  136. {
  137. return $this->getNewGeneratedPassword();
  138. }
  139. /**
  140. * Build new password
  141. *
  142. * @return string Return a new generated password
  143. */
  144. public function getNewGeneratedPassword()
  145. {
  146. $this->initAll();
  147. $pass = "";
  148. for ($i = 0; $i < $this->NbMaj; $i++) {
  149. // Y
  150. $pass .= $this->Maj[mt_rand(0, strlen($this->Maj) - 1)];
  151. }
  152. for ($i = 0; $i < $this->NbNum; $i++) {
  153. // X
  154. $pass .= $this->Nb[mt_rand(0, strlen($this->Nb) - 1)];
  155. }
  156. for ($i = 0; $i < $this->NbSpe; $i++) {
  157. // @
  158. $pass .= $this->Spe[mt_rand(0, strlen($this->Spe) - 1)];
  159. }
  160. for ($i = strlen($pass); $i < $this->length2; $i++) {
  161. // y
  162. $pass .= $this->All[mt_rand(0, strlen($this->All) - 1)];
  163. }
  164. $pass = str_shuffle($pass);
  165. if ($this->validatePassword($pass)) {
  166. return $pass;
  167. }
  168. return $this->getNewGeneratedPassword(); // warning, may generate infinite loop if conditions are not possible
  169. }
  170. /**
  171. * Validate a password.
  172. * This function is called by User->setPassword() and internally to validate that the password matches the constraints.
  173. *
  174. * @param string $password Password to check
  175. * @return int 0 if KO, >0 if OK
  176. */
  177. public function validatePassword($password)
  178. {
  179. global $langs;
  180. $this->initAll(); // For the case this method is called alone
  181. $password_a = preg_split('//u', $password, null, PREG_SPLIT_NO_EMPTY);
  182. $maj = preg_split('//u', $this->Maj, null, PREG_SPLIT_NO_EMPTY);
  183. $num = preg_split('//u', $this->Nb, null, PREG_SPLIT_NO_EMPTY);;
  184. $spe = preg_split('//u', $this->Spe, null, PREG_SPLIT_NO_EMPTY);
  185. /*
  186. $password_a = str_split($password);
  187. $maj = str_split($this->Maj);
  188. $num = str_split($this->Nb);
  189. $spe = str_split($this->Spe);
  190. */
  191. if (dol_strlen($password) < $this->length2) {
  192. $langs->load("other");
  193. $this->error = $langs->trans("YourPasswordMustHaveAtLeastXChars", $this->length2);
  194. return 0;
  195. }
  196. if (count(array_intersect($password_a, $maj)) < $this->NbMaj) {
  197. $langs->load("other");
  198. $this->error = $langs->trans('PasswordNeedAtLeastXUpperCaseChars', $this->NbMaj);
  199. return 0;
  200. }
  201. if (count(array_intersect($password_a, $num)) < $this->NbNum) {
  202. $langs->load("other");
  203. $this->error = $langs->trans('PasswordNeedAtLeastXDigitChars', $this->NbNum);
  204. return 0;
  205. }
  206. if (count(array_intersect($password_a, $spe)) < $this->NbSpe) {
  207. $langs->load("other");
  208. $this->error = $langs->trans('PasswordNeedAtLeastXSpecialChars', $this->NbSpe);
  209. return 0;
  210. }
  211. if (!$this->consecutiveIterationSameCharacter($password)) {
  212. $langs->load("other");
  213. $this->error = $langs->trans('PasswordNeedNoXConsecutiveChars', $this->NbRepeat);
  214. return 0;
  215. }
  216. return 1;
  217. }
  218. /**
  219. * Check the consecutive iterations of the same character.
  220. *
  221. * @param string $password Password to check
  222. * @return bool False if the number doesn't match the maximum consecutive value allowed.
  223. */
  224. public function consecutiveIterationSameCharacter($password)
  225. {
  226. $this->initAll();
  227. if (empty($this->NbRepeat)) {
  228. return true;
  229. }
  230. $char = preg_split('//u', $password, null, PREG_SPLIT_NO_EMPTY);
  231. $last = "";
  232. $count = 0;
  233. foreach ($char as $c) {
  234. if ($c != $last) {
  235. $last = $c;
  236. $count = 1;
  237. //print "Char $c - count = $count\n";
  238. continue;
  239. }
  240. $count++;
  241. //print "Char $c - count = $count\n";
  242. if ($count > $this->NbRepeat) {
  243. return false;
  244. }
  245. }
  246. return true;
  247. }
  248. }