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 .= '' . _('Shadow') . ': ' . _('Account expiration') . ' | |
';
}
+ elseif ($shadowPasswordExpired) {
+ $tipContent .= '' . _('Shadow') . ': ' . _('Password expiration') . ' | |
';
+ }
// 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')) {