Witam.
Jednak życie zmusiło mnie abym to sam napisał. Nie mam czasu - więc powstało rozwiązanie w najprostszej możliwej wersji.
Może być więcej osób zainteresowanych tym tematem - więc poniżej przedstawiam co zrobiłem.
Modyfikowałem wersję epesi 1.5.5
Dążyłem do:
- Wymuszenia wygenerowania zgodnego z GIODO rodzaju hasła przy odzyskaniu hasła (pkt 1)
- weryfikacji, czy hasło spełnia minimalne wymaganie na jego siłę w formularzu zmiany hasła usera (2)
- takiej samej weryfikacji w kontaktach przy edycji parametrów hasła usera przez administratora (3)
- i wygasania hasła co 30 dni.(4)
1: Zmiana procedury generującej hasło automatycznie z linku na stronie logowania.
Procedura ta jest zawarta w /include/misc.php
Zamiast aktualnej funkcji
generate_password($length = 8){
...
}
Wstawiamy poniższą funkcję (renameujemy dotychczasową generate_password jako np generate_password_old)
//Funkcja z publicznego internetu. Dokładnie tak jak była zamieszczona.
// Generates a strong password of N length containing at least one lower case letter,
// one uppercase letter, one digit, and one special character. The remaining characters
// in the password are chosen at random from those four sets.
//
// The available characters in each set are user friendly - there are no ambiguous
// characters such as i, l, 1, o, 0, etc. This, coupled with the $add_dashes option,
// makes it much easier for users to manually type or speak their passwords.
//
// Note: the $add_dashes option will increase the length of the password by
// floor(sqrt(N)) characters.
function generate_password($length = 8, $add_dashes = false, $available_sets = 'luds'){
$sets = array();
if(strpos($available_sets, 'l') !== false)
$sets[] = 'abcdefghjkmnpqrstuvwxyz';
if(strpos($available_sets, 'u') !== false)
$sets[] = 'ABCDEFGHJKMNPQRSTUVWXYZ';
if(strpos($available_sets, 'd') !== false)
$sets[] = '23456789';
if(strpos($available_sets, 's') !== false)
$sets[] = '!@#$%&*?';
$all = '';
$password = '';
foreach($sets as $set)
{
$password .= $set[array_rand(str_split($set))];
$all .= $set;
}
$all = str_split($all);
for($i = 0; $i < $length - count($sets); $i++)
$password .= $all[array_rand($all)];
$password = str_shuffle($password);
if(!$add_dashes)
return $password;
$dash_len = floor(sqrt($length));
$dash_str = '';
while(strlen($password) > $dash_len)
{
$dash_str .= substr($password, 0, $dash_len) . '-';
$password = substr($password, $dash_len);
}
$dash_str .= $password;
return $dash_str;
}
2: Wymuszenie weryfikacji przy wpisywaniu hasła w formularzu zmiany hasła przez użytkownika (zgodnie z naszym założeniem 8 znaków, duże małe litery, cyfry, znaki specjalne)
Formularz zmiany hasła przez użytkownika jest w
/modules/base/user/administrator/Administrator_0.php
Funkcja body()
Około linii 28 trzeba dodać rule do tej quickform:
$form->addRule(array('new_pass', 'new_pass_c'), __('Your passwords don\'t match'), 'compare'); //to istnieje
$form->addRule('new_pass', __('Your password must be longer then 7 chars'), 'minlength', 8); //to istnieje, ale zmieniliśmy długość. Trzeba poprawić tłumaczenie.
$form->addRule('new_pass', 'Hasło za słabe. Potrzebne duże małe litery, cyfry, znaki specjalne.', 'regex', '/^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[,<.>;:\?\/!@#$%^&\*\(\)\-\_\|\[\]\{\}+=]).*$/'); //to dodajemy
3: Wymuszenie weryfikacji siły hasła przy wpisywaniu przez administratora hasła w kontakcie danego usera
a) Edytujemy plik CRM/Contacts/ContactsCommon_0.php
W funkcji QFfield_repassword dodajemy na końcu linię
$form->addFormRule(array('CRM_ContactsCommon', 'check_pass_complex'));
(poniżej linii zawierającej: $form->addFormRule(array('CRM_ContactsCommon', 'check_pass'));
b)
Dodajemy tam nową funkcję
//140327PR - sprawdzanie złożoności hasła
public static function check_pass_complex($data){
if (isset($data['login']) && !$data['login']) {
return true;
}
$pass = & $data['set_password'];
if ($pass == '') return true; //Jeśli nie zmienialiśmy hasła to przepuść.
if (preg_match('/^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[,<.>;:\?\/!@#$%^&\*\(\)\-\_\|\[\]\{\}+=]).*$/',$pass)) return true;
return array('set_password'=>__('Za mało złożone hasło. Użyj małych, dużych liter, cyfr, znaków specjalnych.'));
}
//140327PR
4: Wymuszenie okresowej zmiany hasła podczas logowania
a) musimy dodać znacznik czasu do zapisanego hasła usera. Robimy to bezpośrednio na bazie. Do tablicy user_password dodajemy kolumnę time_stamp (typ TIMESTAMP, domyślnie ma przyjmować wartości CURRENT_TIMESTAMP ON UPDATE CURENT_TIMESTAMPT)
czyli ręcznie przez SQL: (mozna to dodać do instalacji modułu z zakładaniem tablicy user_password)
ALTER TABLE `user_password`
ADD COLUMN `time_stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER `mail`;
b) teraz musimy przy logowaniu sprawdzać ilość dni od ostatniej zmiany hasła, i jeśli upłynęło 30 dni nie pozwolić na zalogowanie się (z odpowiednim komunikatem).
Modyfikujemy base/user/login/login_0.php
W funkcji body()
Na końcu sekcji definiowania rules do kontrolek Login Name i Password tuż przed linią
if($form->isSubmitted() && $form->validate()) {
(ok. linii 103 dla epesi 1.5.5)
dodajemy dodatkowe rules do nazwy użytkownika.
$form->addRule('username', __('Field required'), 'required');
$form->addRule('password', __('Field required'), 'required');
// 140327PR Dodane na potrzeby detekcji wygasania hasła
$form->registerRule('check_password_expired', 'callback', 'check_password_expired', 'Base_User_LoginCommon');
$form->addRule(array('username','password'), __('Hasło wygasło. Musisz stworzyć nowe. Kliknij na link poniżej: '.__('Recover password')), 'check_password_expired');
//140327PR Koniec
if($form->isSubmitted() && $form->validate()) {
$user = $form->exportValue('username');
$autologin = $form->exportValue('autologin');
//140327PR message po zalogowaniu się o wygasaniu hasła i konieczności jego zmiany jeśli zostało mniej niż 5 dni
$ilosc_dni = Base_User_LoginCommon::check_password_count_days($user);
if ($ilosc_dni>26){
$pozostalo = 30-$ilosc_dni;
Base_StatusBarCommon::message('UWAGA !!! <br> Twoje hasło wygaśnie za dni: '.$pozostalo. '<br>Zmień już je dzisiaj !!!','error');
}
//140327PR koniec
Base_User_LoginCommon::set_logged($user);
if($autologin)
Base_User_LoginCommon::new_autologin_id();
location(array());
Dodajemy to co jest pomiędzy znacznikami //140327PR i //140327PR koniec. Reszta kodu jest oryginalna, zostawiona aby łatwiej było go znaleźć w pliku.
oraz dodajemy 2 funkcje walidatora do pliku base/user/login/LoginCommon_0.php
//140327PR do walidacji wygasania hasła
public static function check_password_expired($x){
$username = $x[0];
$count_days = self::check_password_count_days($username);
if($count_days<=30) //login ok - ważny
return true;
else
return false;
}
public static function check_password_count_days ($username){
$ret = DB::Execute('SELECT ifnull(datediff(now(),p.time_stamp),999) as ilosc FROM user_login u JOIN user_password p ON u.id=p.user_login_id WHERE u.login=%s AND u.active=1', array($username));
while ($record=$ret->FetchRow()) {
$count_days = $record['ilosc'];
}
return $count_days;
}
//140327PR Koniec
Jak widać nie jest to jakoś wymyślne. Nie miałem czasu tego pieścić.
Jeśli ktoś jest zainteresowany - zapraszam do poprawiania tego i publicznej dyskusji.
Mam również pytanie (prośbę) do autorów epesi - nie można tego wrzucić do kolejnej wersji epesi? W Polsce jest to w wielu przypadkach niezbędne , a uchroni mnie to przed modyfikowaniem ręcznym każdej następnej wersji.
Proponuję (po niezbędnych poprawkach) wdrożyć powyższe, a do panelu administratora w "Domyślnym ustawieniu użytkownika"-> "Różne" dodać opcję np "Włącz zaawansowaną kontrolę hasła", która przełącza ze standardowego sposobu na zaproponowany przeze mnie.
Można także dodać opcję "co ile zmieniać hasło [dni]" i doprogramować to.
Można oczywiście dodać również formularz zmiany hasła na poziomie loginu, ale wydaje mi się, że zaproponowane rozwiązanie jest jednak bezpiczeniejsze (choć bardziej kłopotliwe dla usera).
I pewnie kilka innych rzeczy.
Pozdrawiam
PRaski
PS: Jeśli uważacie, że tutaj nie jest dobre miejsce na omawianie kodu - przenieście ten mail w bardziej odpowiednie miejsce forum.