* Login/Password: handle forced password change on login page

r53022: visualize not existing ability to unset "must change password on next login", by making it readonly
r53024: change "password about to expire in N days" warning into an once per login info-message (was a redirect to password change)
This commit is contained in:
Ralf Becker 2015-07-01 17:54:18 +00:00
parent 55248b0d18
commit 058ef3e89f
15 changed files with 310 additions and 204 deletions

View File

@ -86,6 +86,12 @@ class admin_account
$readonlys = array(); $readonlys = array();
// at least ADS does not allow to unset it and SQL backend does not implement it either
if ($account['mustchangepassword'])
{
$readonlys['mustchangepassword'] = true;
}
if ($deny_edit) if ($deny_edit)
{ {
foreach(array_keys($account) as $key) foreach(array_keys($account) as $key)

View File

@ -901,7 +901,7 @@ td.et2_required {
border: 1px solid #E1E16D; border: 1px solid #E1E16D;
color: #000000; color: #000000;
font-size: 11px; font-size: 11px;
height: 15px; min-height: 15px;
padding: 4px 10px; padding: 4px 10px;
} }
.error p { .error p {

View File

@ -132,11 +132,13 @@ else
return lang('Sorry, your login has expired'); return lang('Sorry, your login has expired');
case 4: case 4:
return lang('Cookies are required to login to this site'); return lang('Cookies are required to login to this site');
case 5: case egw_session::CD_BAD_LOGIN_OR_PASSWORD:
return lang('Bad login or password'); return lang('Bad login or password');
case 98: case egw_session::CD_FORCE_PASSWORD_CHANGE:
return lang('You must change your password!');
case egw_session::CD_ACCOUNT_EXPIRED:
return lang('Account is expired'); return lang('Account is expired');
case 99: case egw_session::CD_BLOCKED:
return lang('Blocked, too many attempts'); return lang('Blocked, too many attempts');
case 10: case 10:
$GLOBALS['egw']->session->egw_setcookie('sessionid'); $GLOBALS['egw']->session->egw_setcookie('sessionid');
@ -272,9 +274,32 @@ else
$login .= '@'.$GLOBALS['egw_info']['server']['default_domain']; $login .= '@'.$GLOBALS['egw_info']['server']['default_domain'];
} }
} }
$GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($login,$passwd,$passwd_type); $GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($login, $passwd,
$passwd_type, false, true, true); // true = let session fail on forced password change
if(!isset($GLOBALS['sessionid']) || ! $GLOBALS['sessionid']) if (!$GLOBALS['sessionid'] && $GLOBALS['egw']->session->cd_reason == egw_session::CD_FORCE_PASSWORD_CHANGE)
{
if (isset($_POST['new_passwd']))
{
if (($errors = preferences_password::do_change($passwd, $_POST['new_passwd'], $_POST['new_passwd2'])))
{
$force_password_change = implode("\n", $errors);
}
else
{
$GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($login,$_POST['new_passwd'],$passwd_type);
}
}
else
{
$force_password_change = $GLOBALS['egw']->session->reason;
}
}
if (isset($force_password_change))
{
// will show new login-screen incl. new password field below
}
elseif (!isset($GLOBALS['sessionid']) || ! $GLOBALS['sessionid'])
{ {
$GLOBALS['egw']->session->egw_setcookie('eGW_remember','',0,'/'); $GLOBALS['egw']->session->egw_setcookie('eGW_remember','',0,'/');
egw::redirect_link('/login.php?cd=' . $GLOBALS['egw']->session->cd_reason); egw::redirect_link('/login.php?cd=' . $GLOBALS['egw']->session->cd_reason);
@ -363,12 +388,10 @@ else
} }
} }
} }
else // show login screen
{
if(isset($_COOKIE['last_loginid'])) if(isset($_COOKIE['last_loginid']))
{ {
$accounts =& CreateObject('phpgwapi.accounts'); $prefs = new preferences($GLOBALS['egw']->accounts->name2id($_COOKIE['last_loginid']));
$prefs =& CreateObject('phpgwapi.preferences', $accounts->name2id($_COOKIE['last_loginid']));
if($prefs->account_id) if($prefs->account_id)
{ {
@ -409,7 +432,6 @@ else
// remove the global var since the lang loginscreen message and its fallback (en) is empty or not set // remove the global var since the lang loginscreen message and its fallback (en) is empty or not set
unset($GLOBALS['loginscreenmessage']); unset($GLOBALS['loginscreenmessage']);
} }
}
foreach($_GET as $name => $value) foreach($_GET as $name => $value)
{ {
@ -424,5 +446,5 @@ else
$extra_vars = '?' . substr($extra_vars,1); $extra_vars = '?' . substr($extra_vars,1);
} }
$GLOBALS['egw']->framework->login_screen($extra_vars); $GLOBALS['egw']->framework->login_screen($extra_vars, $force_password_change);
} }

View File

@ -59,19 +59,19 @@ class auth
* check if users are supposed to change their password every x sdays, then check if password is of old 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. * 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& $message =null on return false: message why password needs to be changed
* @param string $class to know where you are/ or where you want to go * @return boolean true: all good, false: password change required, null: password expires in N days
* @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='') static function check_password_change(&$message=null)
{ {
// dont check anything for anonymous sessions/ users that are flagged as anonymous // dont check anything for anonymous sessions/ users that are flagged as anonymous
if (is_object($GLOBALS['egw']->session) && $GLOBALS['egw']->session->session_flags == 'A') return true; if (is_object($GLOBALS['egw']->session) && $GLOBALS['egw']->session->session_flags == 'A') return true;
// some statics (and initialisation to make information and timecalculation a) more readable in conditions b) persistent per request // some statics (and initialisation to make information and timecalculation a) more readable in conditions b) persistent per request
// if user has to be warned about an upcomming passwordchange, remember for the session, that he was informed // if user has to be warned about an upcomming passwordchange, remember for the session, that he was informed
static $UserKnowsAboutPwdChange=null; static $UserKnowsAboutPwdChange=null;
if (is_null($UserKnowsAboutPwdChange)) $UserKnowsAboutPwdChange =& egw_cache::getSession('phpgwapi','auth_UserKnowsAboutPwdChange'); if (is_null($UserKnowsAboutPwdChange)) $UserKnowsAboutPwdChange =& egw_cache::getSession('phpgwapi','auth_UserKnowsAboutPwdChange');
// retrieve the timestamp regarding the last change of the password from auth system and store it with the session // retrieve the timestamp regarding the last change of the password from auth system and store it with the session
static $alpwchange_val=null; static $alpwchange_val=null;
static $pwdTsChecked=null; static $pwdTsChecked=null;
@ -87,7 +87,7 @@ class auth
// on the other side, if your auth system does not require an forcedPasswordChange, you will not be asked. // on the other side, if your auth system does not require an forcedPasswordChange, you will not be asked.
if (method_exists($backend,'getLastPwdChange')) if (method_exists($backend,'getLastPwdChange'))
{ {
$alpwchange_val = $backend->getLastPwdChange($GLOBALS['egw_info']['user']['account_lid']); $alpwchange_val = $backend->getLastPwdChange($GLOBALS['egw']->session->account_lid);
$pwdTsChecked = true; $pwdTsChecked = true;
} }
// if your authsystem does not provide that information, its likely, that you cannot change your password there, // if your authsystem does not provide that information, its likely, that you cannot change your password there,
@ -101,19 +101,13 @@ class auth
} }
static $passwordAgeBorder=null; static $passwordAgeBorder=null;
static $daysLeftUntilChangeReq=null; static $daysLeftUntilChangeReq=null;
// some debug output and develop options to move the horizons and warn levels around
//$GLOBALS['egw_info']['server']['change_pwd_every_x_days'] =35;
//$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change']=5;
//echo egw_time::to('now','ts').'<br>';
//echo "User changed password at:".egw_time::to($GLOBALS['egw_info']['user'][$alpwchange]).'<br>';
//echo "User password is ".((egw_time::to('now','ts')-$GLOBALS['egw_info']['user'][$alpwchange])/86400)." days old<br>";
//echo "Users must change passwords every ".$GLOBALS['egw_info']['server']['change_pwd_every_x_days'].' days ('.($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400).') seconds.<br>';
//error_log(__METHOD__.__LINE__.'#'.$alpwchange_val.'# is null:'.is_null($alpwchange_val).'# is empty:'.empty($alpwchange_val).'# is set:'.isset($alpwchange_val));
//echo egw_time::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400).'<br>';
// if neither timestamp isset return true, nothing to do (exept this means the password is too old) // if neither timestamp isset return true, nothing to do (exept this means the password is too old)
if (is_null($alpwchange_val) && if (is_null($alpwchange_val) &&
empty($GLOBALS['egw_info']['server']['change_pwd_every_x_days']) empty($GLOBALS['egw_info']['server']['change_pwd_every_x_days']))
) return true; {
return true;
}
if (is_null($passwordAgeBorder) && $GLOBALS['egw_info']['server']['change_pwd_every_x_days']) if (is_null($passwordAgeBorder) && $GLOBALS['egw_info']['server']['change_pwd_every_x_days'])
{ {
$passwordAgeBorder = (egw_time::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400)); $passwordAgeBorder = (egw_time::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400));
@ -123,60 +117,55 @@ class auth
// maxage - passwordage = days left until change is required // maxage - passwordage = days left until change is required
$daysLeftUntilChangeReq = ($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] - ((egw_time::to('now','ts')-($alpwchange_val?$alpwchange_val:0))/86400)); $daysLeftUntilChangeReq = ($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] - ((egw_time::to('now','ts')-($alpwchange_val?$alpwchange_val:0))/86400));
} }
//echo "Warn about the upcomming change ".$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'].' days before that time is reached<br>'; if ($alpwchange_val == 0 || // admin requested password change
//$result = $GLOBALS['egw_info']['server']['change_pwd_every_x_days'] - $daysLeftUntilChangeReq; $passwordAgeBorder > $alpwchange_val || // change password every N days policy requests change
//echo $GLOBALS['egw_info']['server']['change_pwd_every_x_days'].' - '.$daysLeftUntilChangeReq.'='. $result.'<br>'; // user should be warned N days in advance about change and is not yet
if (!($app == 'preferences' && $class == 'preferences_password' && $method == 'change') && $GLOBALS['egw_info']['server']['change_pwd_every_x_days'] &&
( $GLOBALS['egw_info']['user']['apps']['preferences'] &&
($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] &&
($GLOBALS['egw_info']['user']['apps']['preferences'] || $GLOBALS['egw_info']['user']['apps']['password']) &&
(
($passwordAgeBorder > $alpwchange_val) ||
(
$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] && $GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] &&
$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] > $daysLeftUntilChangeReq $GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] > $daysLeftUntilChangeReq &&
) $UserKnowsAboutPwdChange !== true)
)
) || $alpwchange_val==0
)
)
{
if ($GLOBALS['egw']->acl->check('nopasswordchange', 1, 'preferences')) return true; // user has no rights to change password
if ($UserKnowsAboutPwdChange === true && !($passwordAgeBorder > $alpwchange_val || $alpwchange_val==0)) return true; // user has already been informed about the upcomming password expiration
if (!is_null($alpwchange_val))
{ {
if ($alpwchange_val == 0) if ($alpwchange_val == 0)
{ {
$message = lang('an admin required that you must change your password upon login.'); $message = lang('An admin required that you must change your password upon login.');
}
elseif (($passwordAgeBorder < $alpwchange_val) ||
(
$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] &&
$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] > $daysLeftUntilChangeReq &&
$daysLeftUntilChangeReq > 0
)
)
{
$UserKnowsAboutPwdChange = true;
$message = lang('your password is about to expire in %1 days, you may change your password now',round($daysLeftUntilChangeReq));
} }
elseif ($passwordAgeBorder > $alpwchange_val && $alpwchange_val > 0) elseif ($passwordAgeBorder > $alpwchange_val && $alpwchange_val > 0)
{ {
error_log(__METHOD__.' Password of '.$GLOBALS['egw_info']['user']['account_lid'].' ('.$GLOBALS['egw_info']['user']['account_fullname'].') is of old age.'.array2string(array( error_log(__METHOD__.' Password of '.$GLOBALS['egw_info']['user']['account_lid'].' ('.$GLOBALS['egw_info']['user']['account_fullname'].') is of old age.'.array2string(array(
'ts'=> $alpwchange_val, 'ts'=> $alpwchange_val,
'date'=>egw_time::to($alpwchange_val)))); 'date'=>egw_time::to($alpwchange_val))));
$message = lang('it has been more then %1 days since you changed your password',$GLOBALS['egw_info']['server']['change_pwd_every_x_days']); $message = lang('It has been more then %1 days since you changed your password',$GLOBALS['egw_info']['server']['change_pwd_every_x_days']);
} }
egw::redirect_link('/index.php',array( else
'menuaction' => 'preferences.preferences_password.change', {
'message' => $message, // login page does not inform user about passwords about to expire
'nopopup' => true, if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' &&
)); ($GLOBALS['egw_info']['flags']['currentapp'] != 'home' ||
strpos($_SERVER['SCRIPT_NAME'], '/home/') !== false))
{
$UserKnowsAboutPwdChange = true;
} }
$message = lang('Your password is about to expire in %1 days, you may change your password now',round($daysLeftUntilChangeReq));
// user has no rights to change password --> do NOT warn, as only forced check ignores rights
if ($GLOBALS['egw']->acl->check('nopasswordchange', 1, 'preferences')) return true;
return null;
}
return false;
} }
return true; return true;
} }
/**
* Retired password check method called all over the place
*
* @deprecated use check_password_change
*/
static function check_password_age()
{
return true; // no change
}
/** /**
* fetch the last pwd change for the user * fetch the last pwd change for the user
* *
@ -234,7 +223,7 @@ class auth
} }
if (($ret = $this->backend->change_password($old_passwd, $new_passwd, $account_id))) if (($ret = $this->backend->change_password($old_passwd, $new_passwd, $account_id)))
{ {
if ($account_id == $GLOBALS['egw_info']['user']['account_id']) if ($account_id == $GLOBALS['egw']->session->account_id)
{ {
// need to change current users password in session // need to change current users password in session
egw_cache::setSession('phpgwapi', 'password', base64_encode($new_passwd)); egw_cache::setSession('phpgwapi', 'password', base64_encode($new_passwd));

View File

@ -200,7 +200,7 @@ class auth_sql implements auth_backend
{ {
$admin = True; $admin = True;
// Don't allow password changes for other accounts when using XML-RPC // Don't allow password changes for other accounts when using XML-RPC
if(!$account_id || $GLOBALS['egw_info']['flags']['currentapp'] == 'login') if(!$account_id)
{ {
$admin = False; $admin = False;
$account_id = $GLOBALS['egw_info']['user']['account_id']; $account_id = $GLOBALS['egw_info']['user']['account_id'];

View File

@ -572,11 +572,12 @@ abstract class egw_framework
abstract function footer(); abstract function footer();
/** /**
* displays a login screen * Displays the login screen
* *
* @param string $extra_vars for login url * @param string $extra_vars for login url
* @param string $change_passwd =null string with message to render input fields for password change
*/ */
function login_screen($extra_vars) function login_screen($extra_vars, $change_passwd=null)
{ {
self::csp_frame_src_attrs(array()); // array() no external frame-sources self::csp_frame_src_attrs(array()); // array() no external frame-sources
@ -587,8 +588,32 @@ abstract class egw_framework
$tmpl->set_var('lang_message',$GLOBALS['loginscreenmessage']); $tmpl->set_var('lang_message',$GLOBALS['loginscreenmessage']);
// hide change-password fields, if not requested
if (!$change_passwd)
{
$tmpl->set_block('login_form','change_password');
$tmpl->set_var('change_password', '');
$tmpl->set_var('lang_password',lang('password'));
$tmpl->set_var('cd',check_logoutcode($_GET['cd']));
$tmpl->set_var('cd_class', isset($_GET['cd']) && $_GET['cd'] != 1 ? 'error' : '');
$last_loginid = $_COOKIE['last_loginid']; $last_loginid = $_COOKIE['last_loginid'];
$last_domain = $_COOKIE['last_domain'];
$tmpl->set_var('passwd', '');
$tmpl->set_var('autofocus_login', 'autofocus');
}
else
{
$tmpl->set_var('lang_password',lang('Old password'));
$tmpl->set_var('lang_new_password',lang('New password'));
$tmpl->set_var('lang_repeat_password',lang('Repeat password'));
$tmpl->set_var('cd', $change_passwd);
$tmpl->set_var('cd_class', 'error');
$last_loginid = $_POST['login'];
$last_domain = $_POST['domain'];
$tmpl->set_var('passwd', $_POST['passwd']);
$tmpl->set_var('autofocus_login', '');
$tmpl->set_var('autofocus_new_passwd', 'autofocus');
}
if($GLOBALS['egw_info']['server']['show_domain_selectbox']) if($GLOBALS['egw_info']['server']['show_domain_selectbox'])
{ {
foreach(array_keys($GLOBALS['egw_domain']) as $domain) foreach(array_keys($GLOBALS['egw_domain']) as $domain)
@ -597,7 +622,7 @@ abstract class egw_framework
} }
$tmpl->set_var(array( $tmpl->set_var(array(
'lang_domain' => lang('domain'), 'lang_domain' => lang('domain'),
'select_domain' => html::select('logindomain',$_COOKIE['last_domain'],$domains,true,'tabindex="2"',0,false), 'select_domain' => html::select('logindomain',$last_domain,$domains,true,'tabindex="2"',0,false),
)); ));
} }
else else
@ -612,9 +637,9 @@ abstract class egw_framework
reset($GLOBALS['egw_domain']); reset($GLOBALS['egw_domain']);
list($default_domain) = each($GLOBALS['egw_domain']); list($default_domain) = each($GLOBALS['egw_domain']);
if($_COOKIE['last_domain'] != $default_domain && !empty($_COOKIE['last_domain'])) if(!empty ($last_domain) && $last_domain != $default_domain)
{ {
$last_loginid .= '@' . $_COOKIE['last_domain']; $last_loginid .= '@' . $last_domain;
} }
} }
} }
@ -654,12 +679,10 @@ abstract class egw_framework
} }
$tmpl->set_var('login_url', $GLOBALS['egw_info']['server']['webserver_url'] . '/login.php' . $extra_vars); $tmpl->set_var('login_url', $GLOBALS['egw_info']['server']['webserver_url'] . '/login.php' . $extra_vars);
$tmpl->set_var('version',$GLOBALS['egw_info']['server']['versions']['phpgwapi']); $tmpl->set_var('version', $GLOBALS['egw_info']['server']['versions']['phpgwapi']);
$tmpl->set_var('cd',check_logoutcode($_GET['cd'])); $tmpl->set_var('login', $last_loginid);
$tmpl->set_var('cookie',$last_loginid);
$tmpl->set_var('lang_username',lang('username')); $tmpl->set_var('lang_username',lang('username'));
$tmpl->set_var('lang_password',lang('password'));
$tmpl->set_var('lang_login',lang('login')); $tmpl->set_var('lang_login',lang('login'));
$tmpl->set_var('website_title', $GLOBALS['egw_info']['server']['site_title']); $tmpl->set_var('website_title', $GLOBALS['egw_info']['server']['site_title']);
@ -825,6 +848,14 @@ abstract class egw_framework
*/ */
protected function _get_header(array $extra=array()) protected function _get_header(array $extra=array())
{ {
// display password expires in N days message once per session
$message = null;
if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' &&
auth::check_password_change($message) !== true)
{
self::message($message, 'info');
}
// get used language code (with a little xss check, if someone tries to sneak something in) // get used language code (with a little xss check, if someone tries to sneak something in)
if (preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$GLOBALS['egw_info']['user']['preferences']['common']['lang'])) if (preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$GLOBALS['egw_info']['user']['preferences']['common']['lang']))
{ {

View File

@ -166,6 +166,24 @@ class egw_session
*/ */
var $required_files; var $required_files;
/**
* Nummeric code why session creation failed
*
* @var int
*/
var $cd_reason;
const CD_BAD_LOGIN_OR_PASSWORD = 5;
const CD_FORCE_PASSWORD_CHANGE = 97;
const CD_ACCOUNT_EXPIRED = 98;
const CD_BLOCKED = 99; // to many failed attempts to loing
/**
* Verbose reason why session creation failed
*
* @var string
*/
var $reason;
/** /**
* Constructor just loads up some defaults from cookies * Constructor just loads up some defaults from cookies
* *
@ -422,9 +440,10 @@ class egw_session
* @param string $passwd_type type of password being used, ie plaintext, md5, sha1 * @param string $passwd_type type of password being used, ie plaintext, md5, sha1
* @param boolean $no_session =false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support * @param boolean $no_session =false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support
* @param boolean $auth_check =true if false, the user is loged in without checking his password (eg. for single sign on), default = true * @param boolean $auth_check =true if false, the user is loged in without checking his password (eg. for single sign on), default = true
* @param boolean $fail_on_forced_password_change =false true: do NOT create session, if password change requested
* @return string session id * @return string session id
*/ */
function create($login,$passwd = '',$passwd_type = '',$no_session=false,$auth_check=true) function create($login,$passwd = '',$passwd_type = '',$no_session=false,$auth_check=true,$fail_on_forced_password_change=false)
{ {
if (is_array($login)) if (is_array($login))
{ {
@ -492,7 +511,7 @@ class egw_session
$this->account_id && $GLOBALS['egw']->accounts->get_type($this->account_id) == 'g') $this->account_id && $GLOBALS['egw']->accounts->get_type($this->account_id) == 'g')
{ {
$this->reason = $blocked ? 'blocked, too many attempts' : 'bad login or password'; $this->reason = $blocked ? 'blocked, too many attempts' : 'bad login or password';
$this->cd_reason = $blocked ? 99 : 5; $this->cd_reason = $blocked ? self::CD_BLOCKED : self::CD_BAD_LOGIN_OR_PASSWORD;
// we dont log anon users as it would block the website // we dont log anon users as it would block the website
if (!$GLOBALS['egw']->acl->get_specific_rights_for_account($this->account_id,'anonymous','phpgwapi')) if (!$GLOBALS['egw']->acl->get_specific_rights_for_account($this->account_id,'anonymous','phpgwapi'))
@ -502,6 +521,11 @@ class egw_session
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)"); if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)");
return false; return false;
} }
if ($fail_on_forced_password_change && auth::check_password_change($this->reason) === false)
{
$this->cd_reason = self::CD_FORCE_PASSWORD_CHANGE;
return false;
}
if (!$this->account_id && $GLOBALS['egw_info']['server']['auto_create_acct']) if (!$this->account_id && $GLOBALS['egw_info']['server']['auto_create_acct'])
{ {
if ($GLOBALS['egw_info']['server']['auto_create_acct'] == 'lowercase') if ($GLOBALS['egw_info']['server']['auto_create_acct'] == 'lowercase')
@ -543,7 +567,7 @@ class egw_session
if ($GLOBALS['egw']->accounts->is_expired($GLOBALS['egw_info']['user'])) if ($GLOBALS['egw']->accounts->is_expired($GLOBALS['egw_info']['user']))
{ {
$this->reason = 'account is expired'; $this->reason = 'account is expired';
$this->cd_reason = 98; $this->cd_reason = self::CD_ACCOUNT_EXPIRED;
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)"); if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)");
return false; return false;

View File

@ -70,7 +70,7 @@ alphabet common de a,ä,b,c,d,e,f,g,h,i,j,k,l,m,n,o,ö,p,q,r,s,t,u,ü,v,w,x,y,z
alt common de Alt alt common de Alt
alternate style-sheet: common de Alternatives Style-sheet alternate style-sheet: common de Alternatives Style-sheet
american samoa common de AMERICANISCH SAMOA american samoa common de AMERICANISCH SAMOA
an admin required that you must change your password upon login. common de Sie werden hiermit aufgefordert Ihr Passwort zu ändern. (Dies wurde von einem Administrator veranlasst.) an admin required that you must change your password upon login. common de Sie werden hiermit aufgefordert Ihr Passwort zu ändern. Dies wurde von einem Administrator veranlasst.
an error happened common de Ein Fehler ist aufgetreten. an error happened common de Ein Fehler ist aufgetreten.
an existing and by the webserver readable directory enables the image browser and upload. common de Ein existierendes, und vom Webserver lesbares Verzeichnis, schaltet den Bild Browser und Upload ein. an existing and by the webserver readable directory enables the image browser and upload. common de Ein existierendes, und vom Webserver lesbares Verzeichnis, schaltet den Bild Browser und Upload ein.
and common de und and common de und
@ -940,7 +940,7 @@ your message has been sent common de Ihre Nachricht wurde versendet
your password does not have required strength of %1 character classes and minimum length of %2 characters. common de Ihr Passwort hat nicht die benötigte Qualität von %1 Zeichenklassen und Mindestanzahl von %2 Zeichen. your password does not have required strength of %1 character classes and minimum length of %2 characters. common de Ihr Passwort hat nicht die benötigte Qualität von %1 Zeichenklassen und Mindestanzahl von %2 Zeichen.
your password does not have required strength: common de Ihr Passwort hat nicht die erforderliche Stärke: your password does not have required strength: common de Ihr Passwort hat nicht die erforderliche Stärke:
your password failed the following criteria: common de Ihr Passwort entspricht nicht den folgenden Kriterien: your password failed the following criteria: common de Ihr Passwort entspricht nicht den folgenden Kriterien:
your password is about to expire in %1 days, you may change your password now common de Ihr Passwort läuft in %1 Tagen ab. Sie können nun hier Ihr Passwort ändern, oder warten bis Sie es ändern müssen. your password is about to expire in %1 days, you may change your password now common de Ihr Passwort läuft in %1 Tagen ab. Sie können jetzt Ihr Passwort ändern, oder warten bis Sie es ändern müssen.
your password might not match the password policy. common de Ihr Passwort könnte den Richtlinien nicht entsprechen. your password might not match the password policy. common de Ihr Passwort könnte den Richtlinien nicht entsprechen.
your search returned %1 matchs common de Ihre Suche ergab %1 Treffer your search returned %1 matchs common de Ihre Suche ergab %1 Treffer
your search returned 1 match common de Ihre Suche ergab einen Treffer your search returned 1 match common de Ihre Suche ergab einen Treffer

View File

@ -5,12 +5,16 @@
</div> </div>
<div id="centerBox"> <div id="centerBox">
<div id="loginScreenMessage">{lang_message}</div> <div id="loginScreenMessage">{lang_message}</div>
<div id="loginCdMessage">{cd}</div>
<form name="login_form" method="post" action="{login_url}"> <form name="login_form" method="post" action="{login_url}">
<table class="divLoginbox divSideboxEntry" cellspacing="0" cellpadding="2" border="0" align="center"> <table class="divLoginbox divSideboxEntry" cellspacing="0" cellpadding="2" border="0" align="center">
<tr class="divLoginboxHeader"> <tr class="divLoginboxHeader">
<td colspan="3">{website_title}</td> <td colspan="3">{website_title}</td>
</tr> </tr>
<tr>
<td colspan="3">
<div id="loginCdMessage" class="{cd_class}">{cd}</div>
</td>
</tr>
<tr> <tr>
<td colspan="2" height="20"> <td colspan="2" height="20">
<input type="hidden" name="passwd_type" value="text" /> <input type="hidden" name="passwd_type" value="text" />
@ -40,16 +44,26 @@
<!-- END remember_me_selection --> <!-- END remember_me_selection -->
<tr> <tr>
<td align="right">{lang_username}:&nbsp;</td> <td align="right">{lang_username}:&nbsp;</td>
<td><input name="login" tabindex="4" value="{cookie}" size="30" autofocus /></td> <td><input name="login" tabindex="4" value="{login}" size="30" autofocus /></td>
</tr> </tr>
<tr> <tr>
<td align="right">{lang_password}:&nbsp;</td> <td align="right">{lang_password}:&nbsp;</td>
<td><input name="passwd" tabindex="5" type="password" size="30" /></td> <td><input name="passwd" tabindex="5" value="{passwd}" type="password" size="30" /></td>
</tr> </tr>
<!-- BEGIN change_password -->
<tr>
<td align="right">{lang_new_password}:&nbsp;</td>
<td><input name="new_passwd" tabindex="6" type="password" size="30" /></td>
</tr>
<tr>
<td align="right">{lang_repeat_password}:&nbsp;</td>
<td><input name="new_passwd2" tabindex="7" type="password" size="30" /></td>
</tr>
<!-- END change_password -->
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td> <td>
<input tabindex="6" type="submit" value=" {lang_login} " name="submitit" /> <input tabindex="8" type="submit" value=" {lang_login} " name="submitit" />
</td> </td>
</tr> </tr>
<!-- BEGIN registration --> <!-- BEGIN registration -->

View File

@ -201,7 +201,7 @@ input[type=image]
.divLoginbox .divLoginbox
{ {
position:relative; position:relative;
width: 370px; width: 400px;
border: #9c9c9c 1px solid; border: #9c9c9c 1px solid;
} }
@ -483,6 +483,7 @@ body {
padding-bottom: 10px; padding-bottom: 10px;
color: red; color: red;
font-style: italic; font-style: italic;
margin: 7px 7px 0 0;
} }
.divLoginboxHeader { .divLoginboxHeader {
text-align: center; text-align: center;

View File

@ -2275,14 +2275,11 @@ body {
border-top-left-radius: 10px; border-top-left-radius: 10px;
color: red; color: red;
text-align: center; text-align: center;
padding-top: 1em; padding: 1em;
white-space: pre-wrap;
} }
#loginMainDiv div#centerBox #loginCdMessage span { #loginMainDiv div#centerBox #loginCdMessage.error {
padding: 0.5em; font-weight: bold;
font-size: 1.2em;
color: #189800;
text-shadow: -1px -1px 0px #101010, 1px 1px 0px #505050;
width: 100%;
} }
#loginMainDiv div#centerBox form { #loginMainDiv div#centerBox form {
margin: 1em; margin: 1em;

View File

@ -163,8 +163,9 @@ div#egw_fw_header, div.egw_fw_ui_category:hover,#loginMainDiv,#loginMainDiv #div
* Reimplemented to remove site_title from login box and display it as loginscreenmessage, if none set. * Reimplemented to remove site_title from login box and display it as loginscreenmessage, if none set.
* *
* @param string $extra_vars for login url * @param string $extra_vars for login url
* @param string $change_passwd =null string with message to render input fields for password change
*/ */
function login_screen($extra_vars) function login_screen($extra_vars, $change_passwd=null)
{ {
if (empty($GLOBALS['loginscreenmessage'])) if (empty($GLOBALS['loginscreenmessage']))
{ {
@ -172,6 +173,6 @@ div#egw_fw_header, div.egw_fw_ui_category:hover,#loginMainDiv,#loginMainDiv #div
} }
unset($GLOBALS['egw_info']['server']['site_title']); unset($GLOBALS['egw_info']['server']['site_title']);
return parent::login_screen($extra_vars); return parent::login_screen($extra_vars, $change_passwd);
} }
} }

View File

@ -98,18 +98,11 @@
.border_radius_button_lefttop; .border_radius_button_lefttop;
color: red; color: red;
text-align: center; text-align: center;
padding-top: 1em; padding: 1em;
white-space: pre-wrap;
// Text der Meldung
span {
padding: 0.5em;
font-size: 1.2em;
// .background-color-hint;
color: @color_hint;
text-shadow: -1px -1px 0px #101010, 1px 1px 0px #505050;
width: 100%;
} }
#loginCdMessage.error {
font-weight: bold;
} }

View File

@ -8,7 +8,7 @@
</div> </div>
<div id="centerBox"> <div id="centerBox">
<div id="loginScreenMessage">{lang_message}</div> <div id="loginScreenMessage">{lang_message}</div>
<div id="loginCdMessage">{cd}</div> <div id="loginCdMessage" class="{cd_class}">{cd}</div>
<form name="login_form" method="post" action="{login_url}"> <form name="login_form" method="post" action="{login_url}">
<table class="divLoginbox divSideboxEntry" cellspacing="0" cellpadding="2" border="0" align="center"> <table class="divLoginbox divSideboxEntry" cellspacing="0" cellpadding="2" border="0" align="center">
<tr class="divLoginboxHeader"> <tr class="divLoginboxHeader">
@ -43,16 +43,26 @@
<!-- END remember_me_selection --> <!-- END remember_me_selection -->
<tr> <tr>
<td align="right">{lang_username}:&nbsp;</td> <td align="right">{lang_username}:&nbsp;</td>
<td><input name="login" tabindex="4" value="{cookie}" size="30" autofocus/></td> <td><input name="login" tabindex="4" value="{login}" size="30" {autofocus_login}/></td>
</tr> </tr>
<tr> <tr>
<td align="right">{lang_password}:&nbsp;</td> <td align="right">{lang_password}:&nbsp;</td>
<td><input name="passwd" tabindex="5" type="password" size="30" /></td> <td><input name="passwd" tabindex="5" value="{passwd}" type="password" size="30" /></td>
</tr> </tr>
<!-- BEGIN change_password -->
<tr>
<td align="right">{lang_new_password}:&nbsp;</td>
<td><input name="new_passwd" tabindex="6" type="password" size="30" {autofocus_new_passwd}/></td>
</tr>
<tr>
<td align="right">{lang_repeat_password}:&nbsp;</td>
<td><input name="new_passwd2" tabindex="7" type="password" size="30" /></td>
</tr>
<!-- END change_password -->
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td> <td>
<input tabindex="6" type="submit" value=" {lang_login} " name="submitit" /> <input tabindex="8" type="submit" value=" {lang_login} " name="submitit" />
</td> </td>
</tr> </tr>
<!-- BEGIN registration --> <!-- BEGIN registration -->

View File

@ -36,43 +36,8 @@ class preferences_password
{ {
if ($content['button']['change']) if ($content['button']['change'])
{ {
$o_passwd = $GLOBALS['egw_info']['user']['passwd']; if (($errors = self::do_change($content['o_passwd_2'], $content['n_passwd'], $content['n_passwd_2'])))
if($o_passwd != $content['o_passwd_2'])
{ {
$errors[] = lang('The old password is not correct');
}
if($content['n_passwd'] != $content['n_passwd_2'])
{
$errors[] = lang('The two passwords are not the same');
}
if($o_passwd == $content['n_passwd'])
{
$errors[] = lang('Old password and new password are the same. This is invalid. You must enter a new password');
}
if(!$content['n_passwd'])
{
$errors[] = lang('You must enter a password');
}
// allow auth backends or configured password strenght to throw exceptions and display there message
if (!$errors)
{
try {
$passwd_changed = $GLOBALS['egw']->auth->change_password($o_passwd, $content['n_passwd'],
$GLOBALS['egw_info']['user']['account_id']);
}
catch (Exception $e) {
$errors[] = $e->getMessage();
}
}
if(!$passwd_changed)
{
if (!$errors) // if we have no specific error, add general message
{
$errors[] = lang('Failed to change password.');
}
egw_framework::message(implode("\n", $errors), 'error'); egw_framework::message(implode("\n", $errors), 'error');
$content = array(); $content = array();
} }
@ -81,7 +46,6 @@ class preferences_password
egw_framework::refresh_opener(lang('Password changed'), 'preferences'); egw_framework::refresh_opener(lang('Password changed'), 'preferences');
egw_framework::window_close(); egw_framework::window_close();
} }
} }
} }
@ -90,4 +54,58 @@ class preferences_password
$tmpl->exec('preferences.preferences_password.change', $content,array(),array(),array(),2); $tmpl->exec('preferences.preferences_password.change', $content,array(),array(),array(),2);
} }
/**
* Do some basic checks and then change password
*
* @param string $old_passwd
* @param string $new_passwd
* @param string $new_passwd2
* @return array with already translated errors
*/
public static function do_change($old_passwd, $new_passwd, $new_passwd2)
{
if ($GLOBALS['egw_info']['flags']['currentapp'] != 'preferences')
{
translation::add_app('preferences');
}
$errors = array();
if (isset($GLOBALS['egw_info']['user']['passwd']) &&
$old_passwd !== $GLOBALS['egw_info']['user']['passwd'])
{
$errors[] = lang('The old password is not correct');
}
if ($new_passwd != $new_passwd2)
{
$errors[] = lang('The two passwords are not the same');
}
if ($old_passwd !== false && $old_passwd == $new_passwd)
{
$errors[] = lang('Old password and new password are the same. This is invalid. You must enter a new password');
}
if (!$new_passwd)
{
$errors[] = lang('You must enter a password');
}
// allow auth backends or configured password strenght to throw exceptions and display there message
if (!$errors)
{
try {
if (!$GLOBALS['egw']->auth->change_password($old_passwd, $new_passwd,
$GLOBALS['egw']->session->account_id))
{
// if we have no specific error, add general message
$errors[] = lang('Failed to change password.');
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
}
}
return $errors;
}
} }