diff --git a/admin/inc/class.boaccounts.inc.php b/admin/inc/class.boaccounts.inc.php index dd88bdb4ad..8c574be762 100755 --- a/admin/inc/class.boaccounts.inc.php +++ b/admin/inc/class.boaccounts.inc.php @@ -260,7 +260,7 @@ { return False; } - + //error_log(array2string($userData)); $accountPrefix = ''; if(isset($GLOBALS['egw_info']['server']['account_prefix'])) { @@ -441,6 +441,7 @@ /* stores the userdata */ function save_user($_userData) { + //error_log(__METHOD__.array2string($_userData)); $account =& CreateObject('phpgwapi.accounts',$_userData['account_id'],'u'); $account->update_data($_userData); $account->save_repository(); @@ -458,6 +459,13 @@ $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( 'location' => 'changepassword' ),False,True); // called for every app now, not only enabled ones) + if ($_userData['account_lastpwd_change']==0) + { + // change password sets the shadow_timestamp/account_lastpwd_change timestamp + // so we need to reset that to 0 as Admin required the change of password upon next login + unset($_userData['account_passwd']); + $this->save_user($_userData); + } } $apps =& CreateObject('phpgwapi.applications',(int)$_userData['account_id']); diff --git a/admin/inc/class.uiaccounts.inc.php b/admin/inc/class.uiaccounts.inc.php index 8e569db5c4..c5a633c2cb 100755 --- a/admin/inc/class.uiaccounts.inc.php +++ b/admin/inc/class.uiaccounts.inc.php @@ -573,6 +573,7 @@ 'account_groups' => $_POST['account_groups'], 'anonymous' => $_POST['anonymous'], 'changepassword' => $_POST['changepassword'], + 'mustchangepassword' => $_POST['mustchangepassword'], 'account_permissions' => $_POST['account_permissions'], 'homedirectory' => $_POST['homedirectory'], 'loginshell' => $_POST['loginshell'], @@ -580,7 +581,7 @@ 'account_email' => $email /* 'file_space' => $_POST['account_file_space_number'] . "-" . $_POST['account_file_space_type'] */ ); - + if ($userData['mustchangpassword']) $userData['account_lastpwd_change']=0; /* when does the account expire */ if ($_POST['expires'] !== '' && !$_POST['never_expires']) { @@ -863,6 +864,7 @@ 'account_primary_group' => $_POST['account_primary_group'], 'anonymous' => $_POST['anonymous'], 'changepassword' => $_POST['changepassword'], + 'mustchangepassword' => $_POST['mustchangepassword'], 'account_permissions' => $_POST['account_permissions'], 'homedirectory' => $_POST['homedirectory'], 'loginshell' => $_POST['loginshell'], @@ -870,6 +872,21 @@ 'account_email' => $email, /* 'file_space' => $_POST['account_file_space_number'] . "-" . $_POST['account_file_space_type'] */ ); + if ($userData['mustchangepassword']) + { + $userData['account_lastpwd_change']=0; + } + else + { + $accountid = $account_id; + settype($account_id,'integer'); + $account_id = (int)($_GET['account_id'] ? $_GET['account_id'] : $accountid); + + //echo $account_id.'#
'; + $prevVal = $GLOBALS['egw']->accounts->id2name($account_id,'account_lastpwd_change').'#
'; + //echo $prevVal.'#
'; // previous Value was force password change by admin + if ($prevVal==0) $userData['account_lastpwd_change']=egw_time::to('now','ts'); + } if($userData['account_primary_group'] && (!isset($userData['account_groups']) || !in_array($userData['account_primary_group'],$userData['account_groups']))) { $userData['account_groups'][] = (int)$userData['account_primary_group']; @@ -965,6 +982,7 @@ 'lang_groups' => lang('Groups'), 'lang_anonymous' => lang('Anonymous user (not shown in list sessions)'), 'lang_changepassword'=> lang('Can change password'), + 'lang_mustchangepassword'=> lang('Must change password upon next login'), 'lang_firstname' => lang('First Name'), 'lang_lastlogin' => lang('Last login'), 'lang_lastloginfrom' => lang('Last login from'), @@ -985,6 +1003,7 @@ $acl =& CreateObject('phpgwapi.acl',(int)$_GET['account_id']); $var['anonymous'] = $acl->check('anonymous',1,'phpgwapi') ? '  X' : ' '; $var['changepassword'] = !$acl->check('nopasswordchange',1,'preferences') ? '  X' : ' '; + $var['mustchangepassword']= $userData['account_lastpwd_change']==0 ? '  X' : ' '; unset($acl); if ($userData['status']) @@ -1286,6 +1305,7 @@ function create_edit_user($_account_id,$_userData='',$_errors='') { + //_debug_array($_userData); $GLOBALS['egw_info']['flags']['include_xajax'] = true; $jscal =& CreateObject('phpgwapi.jscalendar'); @@ -1341,6 +1361,7 @@ $acl->read_repository(); $userData['anonymous'] = $acl->check('anonymous',1,'phpgwapi'); $userData['changepassword'] = !$acl->check('nopasswordchange',1,'preferences'); + $userData['mustchangepassword'] = ($userData['account_lastpwd_change']==0?true:false); unset($acl); } else @@ -1351,6 +1372,7 @@ $userGroups = Array(); $userData['anonymous'] = False; $userData['changepassword'] = True; + $userData['mustchangepassword'] = false; } $allGroups = $account->get_list('groups'); } @@ -1380,6 +1402,7 @@ 'lang_firstname' => lang('First Name'), 'lang_anonymous' => lang('Anonymous User (not shown in list sessions)'), 'lang_changepassword' => lang('Can change password'), + 'lang_mustchangepassword'=> lang('Must change password upon next login'), 'lang_button' => ($_account_id?lang('Save'):lang('Add')), 'lang_passwds_unequal' => lang('The two passwords are not the same'), /* 'lang_file_space' => lang('File Space') */ @@ -1449,6 +1472,7 @@ 'loginshell' => $loginshell, 'anonymous' => '', 'changepassword' => '', + 'mustchangepassword' => '', 'account_status' => '', 'account_firstname' => '', 'account_lastname' => '', diff --git a/admin/templates/default/account_form.tpl b/admin/templates/default/account_form.tpl index 378e706f17..771b5d3191 100644 --- a/admin/templates/default/account_form.tpl +++ b/admin/templates/default/account_form.tpl @@ -61,6 +61,13 @@ function check_password(id) {password_fields} + + + {lang_mustchangepassword} + {mustchangepassword} + + + {lang_changepassword} diff --git a/admin/templates/default/config.tpl b/admin/templates/default/config.tpl index 71ba843d79..12f3a2fb71 100644 --- a/admin/templates/default/config.tpl +++ b/admin/templates/default/config.tpl @@ -231,6 +231,20 @@ + + {lang_Force_users_to_change_their_password_regularily?(empty_for_no,number_for_after_that_number_of_days}: + + + + + + + {lang_Force_password_strength_(1-5,_default_empty: no check against rules for a strong password)?}: + + + + + {lang_Admin_email_addresses_(comma-separated)_to_be_notified_about_the_blocking_(empty_for_no_notify)}: diff --git a/home/index.php b/home/index.php index 2759e9f6da..4b32c1e41e 100755 --- a/home/index.php +++ b/home/index.php @@ -19,8 +19,8 @@ */ $GLOBALS['egw_info'] = array( 'flags' => array( - 'noheader' => False, - 'nonavbar' => False, + 'noheader' => true,//False, + 'nonavbar' => true,//False, 'currentapp' => 'home', 'enable_network_class' => False, 'enable_contacts_class' => False, @@ -30,7 +30,9 @@ ); include('../header.inc.php'); - + auth::check_password_age('home','index'); + $GLOBALS['egw_info']['flags']['nonavbar']=false; + common::egw_header(); /* ** Initializing the template */ @@ -288,4 +290,3 @@ //$GLOBALS['egw']->common->debug_phpgw_info(); //$GLOBALS['egw']->common->debug_list_core_functions(); $GLOBALS['egw']->common->egw_footer(); -?> diff --git a/infolog/index.php b/infolog/index.php index e10420d3e6..059ad184f1 100644 --- a/infolog/index.php +++ b/infolog/index.php @@ -18,7 +18,7 @@ $GLOBALS['egw_info'] = array( ) ); include('../header.inc.php'); - +auth::check_password_age('infolog','index'); include_once(EGW_INCLUDE_ROOT.'/infolog/setup/setup.inc.php'); if ($setup_info['infolog']['version'] != $GLOBALS['egw_info']['apps']['infolog']['version']) { diff --git a/phpgwapi/inc/class.accounts_ldap.inc.php b/phpgwapi/inc/class.accounts_ldap.inc.php index 08fed7c1f0..779ae664e9 100644 --- a/phpgwapi/inc/class.accounts_ldap.inc.php +++ b/phpgwapi/inc/class.accounts_ldap.inc.php @@ -504,7 +504,7 @@ class accounts_ldap // shadowexpire is in days since 1970/01/01 (equivalent to a timestamp (int UTC!) / (24*60*60) 'account_status' => isset($data['shadowexpire']) && $data['shadowexpire'][0]*24*3600+$utc_diff < time() ? false : 'A', 'account_expires' => isset($data['shadowexpire']) && $data['shadowexpire'][0] ? $data['shadowexpire'][0]*24*3600+$utc_diff : -1, // LDAP date is in UTC - 'account_lastpasswd_change' => isset($data['shadowlastchange']) ? $data['shadowlastchange'][0]*24*3600+$utc_diff : null, + 'account_lastpwd_change' => isset($data['shadowlastchange']) ? $data['shadowlastchange'][0]*24*3600+$utc_diff : null, // lastlogin and lastlogin from are not availible via the shadowAccount object class // 'account_lastlogin' => $data['phpgwaccountlastlogin'][0], // 'account_lastloginfrom' => $data['phpgwaccountlastloginfrom'][0], @@ -591,7 +591,7 @@ class accounts_ldap unset($to_write['shadowexpire']); // gives protocoll error otherwise } - if ($data['account_lastpasswd_change']) $to_write['shadowlastchange'] = $data['lastpasswd_change']/(24*3600); + if ($data['account_lastpwd_change']) $to_write['shadowlastchange'] = $data['lastpwd_change']/(24*3600); // lastlogin and lastlogin from are not availible via the shadowAccount object class // $to_write['phpgwaccountlastlogin'] = $data['lastlogin']; diff --git a/phpgwapi/inc/class.auth.inc.php b/phpgwapi/inc/class.auth.inc.php index 462bb3a4ab..39cab5d3f4 100644 --- a/phpgwapi/inc/class.auth.inc.php +++ b/phpgwapi/inc/class.auth.inc.php @@ -55,6 +55,46 @@ class auth } } + /** + * check_password_age + * check if users are supposed to change their password every x sdays, then check if password is of old age + * or the devil-admin reset the users password and forced the user to change his password on next login. + * + * @param string $app to know where you are/ or where you want to go + * @param string $class to know where you are/ or where you want to go + * @param string $method to know where you are/ or where you want to go + * @return boolean true if check determined, that you passed the test, otherwise void, as we get redirected + */ + static function check_password_age($app='', $class='', $method='') + { + //echo egw_time::to('now','ts').'
'; + //echo $GLOBALS['egw_info']['user']['account_lastpwd_change'].'
'; + //echo ($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400).'
'; + //echo egw_time::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400).'
'; + if (!($app == 'preferences' && $class == 'uipassword' && $method=='change') && + (($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] && + ($GLOBALS['egw_info']['user']['apps']['preferences'] || $GLOBALS['egw_info']['user']['apps']['password']) && + egw_time::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400)>$GLOBALS['egw_info']['user']['account_lastpwd_change'] + ) || $GLOBALS['egw_info']['user']['account_lastpwd_change']==0) + ) + { + error_log(__METHOD__.' Password of '.$GLOBALS['egw_info']['user']['account_lid'].' ('.$GLOBALS['egw_info']['user']['account_fullname'].') is of old age.'.array2string(array( + 'ts'=>$GLOBALS['egw_info']['user']['account_lastpwd_change'], + 'date'=>egw_time::to($GLOBALS['egw_info']['user']['account_lastpwd_change'])))); + if ($GLOBALS['egw_info']['user']['account_lastpwd_change']==0) + { + $message = lang('an admin required that you must change your password upon login.'); + } + else + { + $message = lang('it has been more then %1 days since you changed your password',$GLOBALS['egw_info']['server']['change_pwd_every_x_days']); + } + if ($GLOBALS['egw_info']['user']['apps']['password']) egw::redirect_link('/preferences/password.php',array('message'=>$message)); + egw::redirect_link('/index.php',array('menuaction'=>'preferences.uipassword.change','message'=>$message)); + } + return true; + } + /** * password authentication against password stored in sql datababse * @@ -365,29 +405,58 @@ class auth * @author cornelius weiss * @return mixed false if password is considered "safe" or a string $message if "unsafe" */ - static function crackcheck($passwd) + static function crackcheck($passwd,$reqstrength=5) { if (!preg_match('/.{'. ($noc=7). ',}/',$passwd)) { - $message = lang('Password must have at least %1 characters',$noc). '
'; + $message[] = lang('Password must have at least %1 characters',$noc). '
'; + } + else + { + $strength++; } if(!preg_match('/(.*\d.*){'. ($non=1). ',}/',$passwd)) { - $message .= lang('Password must contain at least %1 numbers',$non). '
'; + $message[] = lang('Password must contain at least %1 numbers',$non). '
'; + } + else + { + $strength++; } if(!preg_match('/(.*[[:upper:]].*){'. ($nou=1). ',}/',$passwd)) { - $message .= lang('Password must contain at least %1 uppercase letters',$nou). '
'; + $message[] = lang('Password must contain at least %1 uppercase letters',$nou). '
'; + } + else + { + $strength++; } if(!preg_match('/(.*[[:lower:]].*){'. ($nol=1). ',}/',$passwd)) { - $message .= lang('Password must contain at least %1 lowercase letters',$nol). '
'; + $message[] = lang('Password must contain at least %1 lowercase letters',$nol). '
'; + } + else + { + $strength++; } if(!preg_match('/(.*[\\!"#$%&\'()*+,-.\/:;<=>?@\[\]\^_ {|}~`].*){'. ($nol=1). ',}/',$passwd)) { - $message .= lang('Password must contain at least %1 special characters',$nol). '
'; + $message[] = lang('Password must contain at least %1 special characters',$nol). '
'; } - return $message ? $message : false; + else + { + $strength++; + } + if (count($message)>0 && $reqstrength>$strength) + { + $outmessage = lang('Your Password does not meet the required strength.
You must meet %1 criteria. You met only %2 criteria.
Your Password failed the following criteria:',$reqstrength,$strength); + $outmessage .= '
'.implode(' ',$message); + } + else + { + $outmessage =false; + } + return $outmessage ? $outmessage : false; } /** diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php index 9db9daf307..c08aa05848 100644 --- a/phpgwapi/inc/class.egw_framework.inc.php +++ b/phpgwapi/inc/class.egw_framework.inc.php @@ -362,15 +362,15 @@ abstract class egw_framework $var['quick_add'] = $this->_get_quick_add(); $var['user_info'] = $this->_user_time_info(); - - if($GLOBALS['egw_info']['user']['lastpasswd_change'] == 0) + + if($GLOBALS['egw_info']['user']['account_lastpwd_change'] == 0) { $api_messages = lang('You are required to change your password during your first login').'
'. lang('Click this image on the navbar: %1',''); } - elseif($GLOBALS['egw_info']['user']['lastpasswd_change'] < time() - (86400*30)) + elseif($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] && $GLOBALS['egw_info']['user']['account_lastpwd_change'] < time() - (86400*$GLOBALS['egw_info']['server']['change_pwd_every_x_days'])) { - $api_messages = lang('it has been more then %1 days since you changed your password',30); + $api_messages = lang('it has been more then %1 days since you changed your password',$GLOBALS['egw_info']['server']['change_pwd_every_x_days']); } // This is gonna change diff --git a/preferences/inc/class.uipassword.inc.php b/preferences/inc/class.uipassword.inc.php index 054f50c16e..6158caaef6 100644 --- a/preferences/inc/class.uipassword.inc.php +++ b/preferences/inc/class.uipassword.inc.php @@ -23,6 +23,7 @@ class uipassword function change() { + //_debug_array($GLOBALS['egw_info']['user']); $n_passwd = $_POST['n_passwd']; $n_passwd_2 = $_POST['n_passwd_2']; $o_passwd_2 = $_POST['o_passwd_2']; @@ -72,11 +73,23 @@ class uipassword $errors[] = lang('The two passwords are not the same'); } + if($o_passwd == $n_passwd) + { + $errors[] = lang('Old password and new password are the same. This is invalid. You must enter a new password'); + } + if(!$n_passwd) { $errors[] = lang('You must enter a password'); } - if($GLOBALS['egw_info']['server']['check_save_passwd'] && $error_msg = $GLOBALS['egw']->auth->crackcheck($n_passwd)) + $strength = ($GLOBALS['egw_info']['server']['force_pwd_strength']?$GLOBALS['egw_info']['server']['force_pwd_strength']:false); + //error_log(__METHOD__.__LINE__.' Strength:'.$strength); + + if ($strength && $strength>5) $strength =5; + if ($strength && $strength<0) $strength = false; + if($GLOBALS['egw_info']['server']['check_save_passwd'] && $strength==false) $strength=5;//old behavior + //error_log(__METHOD__.__LINE__.' Strength:'.$strength); + if(($GLOBALS['egw_info']['server']['check_save_passwd'] || $strength) && $error_msg = $GLOBALS['egw']->auth->crackcheck($n_passwd,$strength)) { $errors[] = $error_msg; } @@ -104,7 +117,10 @@ class uipassword { $GLOBALS['egw']->session->appsession('password','phpgwapi',base64_encode($n_passwd)); $GLOBALS['egw_info']['user']['passwd'] = $n_passwd; + $GLOBALS['egw_info']['user']['account_lastpwd_change'] = egw_time::to('now','ts'); + accounts::cache_invalidate($GLOBALS['egw_info']['user']['account_id']); egw::invalidate_session_cache(); + //_debug_array( $GLOBALS['egw_info']['user']); $GLOBALS['hook_values']['account_id'] = $GLOBALS['egw_info']['user']['account_id']; $GLOBALS['hook_values']['old_passwd'] = $o_passwd; $GLOBALS['hook_values']['new_passwd'] = $n_passwd; diff --git a/preferences/index.php b/preferences/index.php index 266f314c86..aaaa443281 100755 --- a/preferences/index.php +++ b/preferences/index.php @@ -11,12 +11,16 @@ $GLOBALS['egw_info'] = array( 'flags' => array( + 'noheader' => true, + 'novavbar' => true, 'currentapp' => 'preferences', 'disable_Template_class' => True, ), ); include('../header.inc.php'); - +auth::check_password_age('preferences','index'); +$GLOBALS['egw_info']['flags']['nonavbar']=false; +common::egw_header(); $pref_tpl =& CreateObject('phpgwapi.Template',EGW_APP_TPL); $templates = Array( 'pref' => 'index.tpl'