diff --git a/lam/HISTORY b/lam/HISTORY index 1bb72b64..5b172506 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -1,5 +1,6 @@ December 2017 - PHP 5.6 and Internet Explorer 11 or later required + - Account status also shows expired accounts 19.09.2017 6.1 diff --git a/lam/lib/modules/shadowAccount.inc b/lam/lib/modules/shadowAccount.inc index 7a2b9c53..a9e7b1ce 100644 --- a/lam/lib/modules/shadowAccount.inc +++ b/lam/lib/modules/shadowAccount.inc @@ -787,6 +787,29 @@ class shadowAccount extends baseModule implements passwordService { return ($time < $now); } + /** + * Returns if the given password is expired. + * + * @param array $attrs LDAP attributes + * @return bool expired + */ + public static function isPasswordExpired($attrs) { + $attrs = array_change_key_case($attrs, CASE_LOWER); + if (empty($attrs['shadowlastchange'][0]) || empty($attrs['shadowmax'][0])) { + return false; + } + if (($attrs['shadowlastchange'][0] < 1) || ($attrs['shadowmax'][0] < 1)) { + return; + } + $time = new DateTime('@' . $attrs['shadowlastchange'][0] * 24 * 3600, new DateTimeZone('UTC')); + $time = $time->add(new DateInterval('P' . $attrs['shadowmax'][0] . 'D')); + if (!empty($attrs['shadowinactive'][0]) && ($attrs['shadowinactive'][0] > 0)) { + $time = $time->add(new DateInterval('P' . $attrs['shadowinactive'][0] . 'D')); + } + $now = new DateTime(null, getTimeZone()); + return ($time < $now); + } + } if (interface_exists('\LAM\JOB\Job', false)) { diff --git a/lam/lib/types/user.inc b/lam/lib/types/user.inc index 02b04cba..53d6a36b 100644 --- a/lam/lib/types/user.inc +++ b/lam/lib/types/user.inc @@ -355,6 +355,9 @@ class user extends baseType { if (shadowAccount::isAccountExpired($shadowAttrs)) { $expiredLabels[] = _('Shadow') . ': ' . _('Account expiration'); } + elseif (shadowAccount::isPasswordExpired($shadowAttrs)) { + $expiredLabels[] = _('Shadow') . ': ' . _('Password expiration'); + } } if (!empty($expiredLabels)) { $expiredTip = ''; @@ -921,6 +924,9 @@ class lamUserList extends lamList { $attrs[] = 'nsAccountLock'; $attrs[] = 'accountUnlockTime'; $attrs[] = 'shadowExpire'; + $attrs[] = 'shadowLastChange'; + $attrs[] = 'shadowMax'; + $attrs[] = 'shadowInactive'; $attrs[] = 'objectClass'; } return $attrs; @@ -954,7 +960,8 @@ class lamUserList extends lamList { || ($ppolicyAvailable && !$ppolicyLocked) || ($windowsAvailable && !$windowsLocked); $shadowExpired = shadowAccount::isAccountExpired($this->entries[$i]); - $expired = $shadowExpired; + $shadowPasswordExpired = shadowAccount::isPasswordExpired($this->entries[$i]); + $expired = $shadowExpired || $shadowPasswordExpired; $status = self::FILTER_UNLOCKED; if ($expired) { $status = self::FILTER_EXPIRED; @@ -1006,7 +1013,8 @@ class lamUserList extends lamList { && (!$ppolicyAvailable || $ppolicyLocked) && (!$windowsAvailable || $windowsLocked); $shadowExpired = shadowAccount::isAccountExpired($attrs); - $expired = $shadowExpired; + $shadowPasswordExpired = shadowAccount::isPasswordExpired($attrs); + $expired = $shadowExpired || $shadowPasswordExpired; $icon = 'unlocked.png'; if ($expired) { $icon = 'expired.png'; @@ -1024,6 +1032,9 @@ class lamUserList extends lamList { if ($shadowExpired) { $tipContent .= ''; } + elseif ($shadowPasswordExpired) { + $tipContent .= ''; + } // Unix if ($unixAvailable) { $unixIcon = 'unlocked.png'; diff --git a/lam/tests/lib/modules/shadowAccountTest.php b/lam/tests/lib/modules/shadowAccountTest.php index f46a0f3c..43b9c458 100644 --- a/lam/tests/lib/modules/shadowAccountTest.php +++ b/lam/tests/lib/modules/shadowAccountTest.php @@ -61,6 +61,58 @@ $this->assertTrue(shadowAccount::isAccountExpired($attrs)); } + public function test_isPasswordExpired_noAttr() { + $attrs = array('objectClass' => array('shadowAccount')); + + $this->assertFalse(shadowAccount::isPasswordExpired($attrs)); + } + + public function test_isPasswordExpired_notExpired() { + $change = intval(time() / (24*3600)) - 10; + $attrs = array( + 'objectClass' => array('shadowAccount'), + 'shadoWlastCHange' => array(0 => $change), + 'shadowmax' => array(0 => '14'), + ); + + $this->assertFalse(shadowAccount::isPasswordExpired($attrs)); + } + + public function test_isPasswordExpired_expired() { + $change = intval(time() / (24*3600)) - 10; + $attrs = array( + 'objectClass' => array('shadowAccount'), + 'shadoWlastCHange' => array(0 => $change), + 'shadowmax' => array(0 => '7'), + ); + + $this->assertTrue(shadowAccount::isPasswordExpired($attrs)); + } + + public function test_isPasswordExpired_notExpiredInactiveSet() { + $change = intval(time() / (24*3600)) - 10; + $attrs = array( + 'objectClass' => array('shadowAccount'), + 'shadoWlastCHange' => array(0 => $change), + 'shadowmax' => array(0 => '7'), + 'shaDowinactIVe' => array(0 => '14'), + ); + + $this->assertFalse(shadowAccount::isPasswordExpired($attrs)); + } + + public function test_isPasswordExpired_expiredInactiveSet() { + $change = intval(time() / (24*3600)) - 10; + $attrs = array( + 'objectClass' => array('shadowAccount'), + 'shadoWlastCHange' => array(0 => $change), + 'shadowmax' => array(0 => '7'), + 'shaDowinactIVe' => array(0 => '2'), + ); + + $this->assertTrue(shadowAccount::isPasswordExpired($attrs)); + } + } if (is_readable('lam/lib/passwordExpirationJob.inc')) {
' . _('Shadow') . ': ' . _('Account expiration') . '  
' . _('Shadow') . ': ' . _('Password expiration') . '