dolibarr 25.0.0-alpha
modGeneratePassPerso.class.php
Go to the documentation of this file.
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 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * or see https://www.gnu.org/
21 */
22
29require_once DOL_DOCUMENT_ROOT.'/core/modules/security/generate/modules_genpassword.php';
30
31
36{
40 public $id;
41
42 public $picto = 'fa-shield-alt';
43
47 public $NbMaj;
51 public $NbNum;
55 public $NbSpe;
59 public $NbRepeat;
60
66 public $WithoutAmbi = 0;
67
71 public $Maj;
75 public $Min;
79 public $Nb;
83 public $Spe;
87 public $Ambi;
91 public $All;
92
101 public function __construct($db, $conf, $langs, $user)
102 {
103 $this->id = "Perso";
104 $this->length = $langs->trans("SetupPerso");
105
106 $this->db = $db;
107 $this->conf = $conf;
108 $this->langs = $langs;
109 $this->user = $user;
110
111 if (!getDolGlobalString('USER_PASSWORD_PATTERN')) {
112 // default value at auto generation (12 chars, 1 uppercase, 1 digit, 0 special char, 3 repeat max, exclude ambiguous characters).
113 dolibarr_set_const($db, "USER_PASSWORD_PATTERN", '12;1;1;0;3;1', 'chaine', 0, '', $conf->entity);
114 }
115
116 $this->Maj = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
117 $this->Min = strtolower($this->Maj);
118 $this->Nb = "0123456789";
119 $this->Spe = "!@#$%&*()_-+={}[]\\|:;'/";
120 $this->Ambi = array("1", "I", "l", "|", "O", "0");
121
122 $tabConf = explode(";", getDolGlobalString('USER_PASSWORD_PATTERN'));
123 // When the 'none' model is forbidden on this installation, the Perso model cannot enforce a
124 // minimum length below the floor returned by getPasswordPatternMinLength(), even if the
125 // stored pattern still holds a lower value.
126 $this->length2 = max((int) $tabConf[0], getPasswordPatternMinLength());
127 $this->NbMaj = $tabConf[1];
128 $this->NbNum = $tabConf[2];
129 $this->NbSpe = $tabConf[3];
130 $this->NbRepeat = $tabConf[4];
131 $this->WithoutAmbi = (int) $tabConf[5];
132 }
133
139 private function initAll()
140 {
141 if ($this->WithoutAmbi) {
142 $this->Maj = str_replace($this->Ambi, "", $this->Maj);
143 $this->Min = str_replace($this->Ambi, "", $this->Min);
144 $this->Nb = str_replace($this->Ambi, "", $this->Nb);
145 $this->Spe = str_replace($this->Ambi, "", $this->Spe);
146 }
147
148 $pattern = $this->Min.(!empty($this->NbMaj) ? $this->Maj : '').(!empty($this->NbNum) ? $this->Nb : '').(!empty($this->NbSpe) ? $this->Spe : '');
149
150 // Use the Fisher-Yate to shake (this replace str_shuffle)
151 $passwordArray = str_split($pattern);
152 for ($i = count($passwordArray) - 1; $i > 0; $i--) {
153 $j = random_int(0, $i);
154 $tmp = $passwordArray[$i];
155 $passwordArray[$i] = $passwordArray[$j];
156 $passwordArray[$j] = $tmp;
157 }
158 $this->All = implode('', $passwordArray);
159 }
160
166 public function getDescription()
167 {
168 global $langs;
169 return $langs->trans("PasswordGenerationPerso");
170 }
171
177 public function getExample()
178 {
179 return $this->getNewGeneratedPassword();
180 }
181
189 public function getNewGeneratedPassword()
190 {
191 $this->initAll();
192
193 $pass = "";
194 for ($i = 0; $i < $this->NbMaj; $i++) {
195 // Y
196 $pass .= $this->Maj[mt_rand(0, strlen($this->Maj) - 1)];
197 }
198
199 for ($i = 0; $i < $this->NbNum; $i++) {
200 // X
201 $pass .= $this->Nb[mt_rand(0, strlen($this->Nb) - 1)];
202 }
203
204 for ($i = 0; $i < $this->NbSpe; $i++) {
205 // @
206 $pass .= $this->Spe[mt_rand(0, strlen($this->Spe) - 1)];
207 }
208
209 for ($i = strlen($pass); $i < $this->length2; $i++) {
210 // y
211 $pass .= $this->All[mt_rand(0, strlen($this->All) - 1)];
212 }
213
214 // Use the Fisher-Yate to shake (this replace str_shuffle)
215 $passwordArray = str_split($pass);
216 for ($i = count($passwordArray) - 1; $i > 0; $i--) {
217 $j = random_int(0, $i);
218 $tmp = $passwordArray[$i];
219 $passwordArray[$i] = $passwordArray[$j];
220 $passwordArray[$j] = $tmp;
221 }
222 $pass = implode('', $passwordArray);
223
224 // Check that generation was ok
225 if ($this->validatePassword($pass)) {
226 return $pass;
227 }
228
229 return $this->getNewGeneratedPassword(); // warning, may generate infinite loop if conditions are not possible
230 }
231
239 public function validatePassword($password)
240 {
241 global $langs;
242
243 $this->initAll(); // For the case this method is called alone
244
245 dol_syslog("modGeneratePassPerso::validatePassword");
246
247 $password_a = preg_split('//u', $password, 0, PREG_SPLIT_NO_EMPTY);
248 $maj = preg_split('//u', $this->Maj, 0, PREG_SPLIT_NO_EMPTY);
249 $num = preg_split('//u', $this->Nb, 0, PREG_SPLIT_NO_EMPTY);
250 $spe = preg_split('//u', $this->Spe, 0, PREG_SPLIT_NO_EMPTY);
251 /*
252 $password_a = str_split($password);
253 $maj = str_split($this->Maj);
254 $num = str_split($this->Nb);
255 $spe = str_split($this->Spe);
256 */
257
258 if (dol_strlen($password) < $this->length2) {
259 $langs->load("other");
260 $this->error = $langs->trans("YourPasswordMustHaveAtLeastXChars", $this->length2);
261 return 0;
262 }
263
264 if (count(array_intersect($password_a, $maj)) < $this->NbMaj) {
265 $langs->load("other");
266 $this->error = $langs->trans('PasswordNeedAtLeastXUpperCaseChars', $this->NbMaj);
267 return 0;
268 }
269
270 if (count(array_intersect($password_a, $num)) < $this->NbNum) {
271 $langs->load("other");
272 $this->error = $langs->trans('PasswordNeedAtLeastXDigitChars', $this->NbNum);
273 return 0;
274 }
275
276 if (count(array_intersect($password_a, $spe)) < $this->NbSpe) {
277 $langs->load("other");
278 $this->error = $langs->trans('PasswordNeedAtLeastXSpecialChars', $this->NbSpe);
279 return 0;
280 }
281
282 if (!$this->consecutiveIterationSameCharacter($password)) {
283 $langs->load("other");
284 $this->error = $langs->trans('PasswordNeedNoXConsecutiveChars', $this->NbRepeat);
285 return 0;
286 }
287
288 return 1;
289 }
290
297 public function consecutiveIterationSameCharacter($password)
298 {
299 $this->initAll();
300
301 if (empty($this->NbRepeat)) {
302 return true;
303 }
304
305 $char = preg_split('//u', $password, 0, PREG_SPLIT_NO_EMPTY);
306
307 $last = "";
308 $count = 0;
309 foreach ($char as $c) {
310 if ($c != $last) {
311 $last = $c;
312 $count = 1;
313 //print "Char $c - count = $count\n";
314 continue;
315 }
316
317 $count++;
318 //print "Char $c - count = $count\n";
319
320 if ($count > $this->NbRepeat) {
321 return false;
322 }
323 }
324
325 return true;
326 }
327}
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
$c
Definition line.php:346
Parent class for password rules/management modules.
Class to generate a password according to personal rules.
validatePassword($password)
Validate a password.
consecutiveIterationSameCharacter($password)
Check the consecutive iterations of the same character.
__construct($db, $conf, $langs, $user)
Constructor.
getExample()
Return an example of password generated by this module.
initAll()
Init the property ->All and clean ->Maj, ->Min, ->Nb and ->Spe with list of valid chars.
getNewGeneratedPassword()
Build new password.
getDescription()
Return description of module.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
conf($dolibarr_main_document_root, $realpathconf=null)
Load conf file (file must exists)
Definition inc.php:431
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:141
getPasswordPatternMinLength()
Return the lowest value allowed for the minimum length (first field of USER_PASSWORD_PATTERN) of the ...