Merge pull request #39 from LDAPAccountManager/expireStatus

Expire status
This commit is contained in:
gruberroland 2017-10-19 19:46:10 +02:00 committed by GitHub
commit 54bf006d5c
9 changed files with 606 additions and 304 deletions

View File

@ -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

File diff suppressed because it is too large Load Diff

BIN
lam/graphics/expired.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1090,7 +1090,6 @@ class LAMConfig {
*/
public function set_Passwd($value) {
if (is_string($value)) {
mt_srand((microtime() * 1000000));
$rand = getRandomNumber();
$salt0 = substr(pack("h*", md5($rand)), 0, 8);
$salt = substr(pack("H*", sha1($salt0 . $value)), 0, 4);
@ -2410,7 +2409,6 @@ class LAMCfgMain {
* @param String $password new password
*/
public function setPassword($password) {
mt_srand((microtime() * 1000000));
$rand = getRandomNumber();
$salt0 = substr(pack("h*", md5($rand)), 0, 8);
$salt = substr(pack("H*", sha1($salt0 . $password)), 0, 4);

View File

@ -4,7 +4,7 @@ $Id$
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2006 Tilo Lutz
Copyright (C) 2007 - 2016 Roland Gruber
Copyright (C) 2007 - 2017 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -771,6 +771,45 @@ class shadowAccount extends baseModule implements passwordService {
);
}
/**
* Returns if the given account is expired.
*
* @param array $attrs LDAP attributes
* @return bool expired
*/
public static function isAccountExpired($attrs) {
$attrs = array_change_key_case($attrs, CASE_LOWER);
if (empty($attrs['shadowexpire'][0])) {
return false;
}
$time = new DateTime('@' . $attrs['shadowexpire'][0] * 24 * 3600, new DateTimeZone('UTC'));
$now = new DateTime(null, getTimeZone());
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)) {

View File

@ -3373,6 +3373,28 @@ class windowsUser extends baseModule implements passwordService {
return $replacements;
}
/**
* Returns if the given account is expired.
*
* @param array $attrs LDAP attributes
* @return bool expired
*/
public static function isAccountExpired($attrs) {
$attrs = array_change_key_case($attrs, CASE_LOWER);
if (empty($attrs['accountexpires'][0])) {
return false;
}
$value = $attrs['accountexpires'][0];
if ($value < 1) {
return false;
}
$seconds = substr($value, 0, -7);
$time = new DateTime('1601-01-01', new DateTimeZone('UTC'));
$time->add(new DateInterval('PT' . $seconds . 'S'));
$now = new DateTime(null, getTimeZone());
return ($time < $now);
}
}
if (interface_exists('\LAM\JOB\Job', false)) {

View File

@ -346,7 +346,35 @@ class user extends baseType {
if ($isEditable) {
$onClick = 'onclick="showConfirmationDialog(\'' . _('Change account status') . '\', \'' . _('Ok') . '\', \'' . _('Cancel') . '\', \'lam_accountStatusDialog\', \'inputForm\', \'lam_accountStatusResult\');"';
}
return $dialogDiv . '<a href="#"><img id="lam_accountStatus" alt="status" ' . $onClick . ' helptitle="' . _('Account status') . '" helpdata="' . $tipContent . '" height=16 width=16 src="../../graphics/' . $icon . '"></a>&nbsp;&nbsp;&nbsp;';
$dialogDiv .= '<a href="#"><img id="lam_accountStatus" alt="status" ' . $onClick . ' helptitle="' . _('Account status') . '" helpdata="' . $tipContent . '" height=16 width=16 src="../../graphics/' . $icon . '"></a>&nbsp;&nbsp;&nbsp;';
// expiration status
$expiredLabels = array();
$shadowModule = $container->getAccountModule('shadowAccount');
if ($shadowModule != null) {
$shadowAttrs = $shadowModule->getAttributes();
if (shadowAccount::isAccountExpired($shadowAttrs)) {
$expiredLabels[] = _('Shadow') . ': ' . _('Account expiration');
}
elseif (shadowAccount::isPasswordExpired($shadowAttrs)) {
$expiredLabels[] = _('Shadow') . ': ' . _('Password expiration');
}
}
$windowsModule = $container->getAccountModule('windowsUser');
if ($windowsModule != null) {
$windowsAttrs = $windowsModule->getAttributes();
if (windowsUser::isAccountExpired($windowsAttrs)) {
$expiredLabels[] = _('Windows') . ': ' . _('Account expiration');
}
}
if (!empty($expiredLabels)) {
$expiredTip = '<table border=0>';
foreach ($expiredLabels as $label) {
$expiredTip .= '<tr><td>' . $label . '</td><td><img src=&quot;../../graphics/expired.png&quot;/></td></tr>';
}
$expiredTip .= '</table>';
$dialogDiv .= '<img alt="expired" helptitle="' . _('Expired') . '" helpdata="' . $expiredTip . '" height=16 width=16 src="../../graphics/expired.png">&nbsp;&nbsp;&nbsp;';
}
return $dialogDiv;
}
/**
@ -606,6 +634,8 @@ class lamUserList extends lamList {
/** virtual attribute name for account status column */
const ATTR_ACCOUNT_STATUS = 'lam_virtual_account_status';
/** filter value for expired accounts */
const FILTER_EXPIRED = 1;
/** filter value for locked accounts */
const FILTER_LOCKED = 2;
/** filter value for partially locked accounts */
@ -854,7 +884,8 @@ class lamUserList extends lamList {
'' => '',
_('Unlocked') => self::FILTER_UNLOCKED,
_('Partially locked') => self::FILTER_SEMILOCKED,
_('Locked') => self::FILTER_LOCKED
_('Locked') => self::FILTER_LOCKED,
_('Expired') => self::FILTER_EXPIRED,
);
$filterInput = new htmlSelect('filter' . strtolower($attrName), $filterOptions, array($value));
$filterInput->setCSSClasses(array($this->type->getScope() . '-dark'));
@ -899,6 +930,11 @@ class lamUserList extends lamList {
$attrs[] = 'lockoutTime';
$attrs[] = 'nsAccountLock';
$attrs[] = 'accountUnlockTime';
$attrs[] = 'shadowExpire';
$attrs[] = 'shadowLastChange';
$attrs[] = 'shadowMax';
$attrs[] = 'shadowInactive';
$attrs[] = 'accountExpires';
$attrs[] = 'objectClass';
}
return $attrs;
@ -931,8 +967,15 @@ class lamUserList extends lamList {
|| ($sambaAvailable && !$sambaLocked)
|| ($ppolicyAvailable && !$ppolicyLocked)
|| ($windowsAvailable && !$windowsLocked);
$shadowExpired = shadowAccount::isAccountExpired($this->entries[$i]);
$shadowPasswordExpired = shadowAccount::isPasswordExpired($this->entries[$i]);
$windowsExpired = windowsUser::isAccountExpired($this->entries[$i]);
$expired = $shadowExpired || $shadowPasswordExpired || $windowsExpired;
$status = self::FILTER_UNLOCKED;
if ($hasLocked && $hasUnlocked) {
if ($expired) {
$status = self::FILTER_EXPIRED;
}
elseif ($hasLocked && $hasUnlocked) {
$status = self::FILTER_SEMILOCKED;
}
elseif (!$hasUnlocked && $hasLocked) {
@ -978,16 +1021,30 @@ class lamUserList extends lamList {
&& (!$sambaAvailable || $sambaLocked)
&& (!$ppolicyAvailable || $ppolicyLocked)
&& (!$windowsAvailable || $windowsLocked);
$shadowExpired = shadowAccount::isAccountExpired($attrs);
$shadowPasswordExpired = shadowAccount::isPasswordExpired($attrs);
$windowsExpired = windowsUser::isAccountExpired($attrs);
$expired = $shadowExpired || $shadowPasswordExpired || $windowsExpired;
$icon = 'unlocked.png';
if ($fullyLocked) {
if ($expired) {
$icon = 'expired.png';
}
elseif ($fullyLocked) {
$icon = 'lock.png';
}
elseif ($partiallyLocked) {
$icon = 'partiallyLocked.png';
}
// print icon and detail tooltips
if ($unixAvailable || $sambaAvailable || $ppolicyAvailable || $windowsAvailable || $is389dsDeactivated) {
if ($unixAvailable || $sambaAvailable || $ppolicyAvailable || $windowsAvailable || $is389dsDeactivated || $expired) {
$tipContent = '<table border=0>';
// Shadow expired
if ($shadowExpired) {
$tipContent .= '<tr><td>' . _('Shadow') . ': ' . _('Account expiration') . '&nbsp;&nbsp;</td><td><img height=16 width=16 src=&quot;../../graphics/expired.png&quot;></td></tr>';
}
elseif ($shadowPasswordExpired) {
$tipContent .= '<tr><td>' . _('Shadow') . ': ' . _('Password expiration') . '&nbsp;&nbsp;</td><td><img height=16 width=16 src=&quot;../../graphics/expired.png&quot;></td></tr>';
}
// Unix
if ($unixAvailable) {
$unixIcon = 'unlocked.png';
@ -1019,6 +1076,9 @@ class lamUserList extends lamList {
$windowsIcon = 'lock.png';
}
$tipContent .= '<tr><td>' . _('Windows') . '&nbsp;&nbsp;</td><td><img height=16 width=16 src=&quot;../../graphics/' . $windowsIcon . '&quot;></td></tr>';
if ($windowsExpired) {
$tipContent .= '<tr><td>' . _('Windows') . ': ' . _('Account expiration') . '&nbsp;&nbsp;</td><td><img height=16 width=16 src=&quot;../../graphics/expired.png&quot;></td></tr>';
}
}
if ($windowsAvailable && $windowsPasswordLocked) {
$tipContent .= '<tr><td>' . _('Locked till') . '&nbsp;&nbsp;</td><td>' . $windowsPasswordLockedTime->format('Y-m-d H:i:s') . '</td></tr>';
@ -1049,6 +1109,16 @@ class lamUserList extends lamList {
return (isset($attrs['objectclass']) && in_array_ignore_case('posixAccount', $attrs['objectclass']) && isset($attrs['userpassword'][0]));
}
/**
* Returns if the Shadow part exists.
*
* @param array $attrs LDAP attributes
* @return boolean Shadow part exists
*/
public static function isShadowAvailable(&$attrs) {
return (isset($attrs['objectclass']) && in_array_ignore_case('shadowAccount', $attrs['objectclass']));
}
/**
* Returns if the Unix part is locked.
*

View File

@ -3,7 +3,7 @@
$Id$
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2016 Roland Gruber
Copyright (C) 2016 - 2017 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,13 +21,102 @@
*/
if (is_readable('lam/lib/passwordExpirationJob.inc')) {
include_once 'lam/lib/baseModule.inc';
include_once 'lam/lib/modules.inc';
include_once 'lam/lib/passwordExpirationJob.inc';
if (is_readable('lam/lib/passwordExpirationJob.inc')) {
include_once 'lam/lib/passwordExpirationJob.inc';
}
include_once 'lam/lib/modules/shadowAccount.inc';
/**
* Checks the shadowAccount class.
*
* @author Roland Gruber
*/
class ShadowAccountTest extends PHPUnit_Framework_TestCase {
public function test_isAccountExpired_noAttr() {
$attrs = array('objectClass' => array('shadowAccount'));
$this->assertFalse(shadowAccount::isAccountExpired($attrs));
}
public function test_isAccountExpired_notExpired() {
$expire = intval(time() / (24*3600)) + 10000;
$attrs = array(
'objectClass' => array('shadowAccount'),
'sHadoweXpirE' => array(0 => $expire)
);
$this->assertFalse(shadowAccount::isAccountExpired($attrs));
}
public function test_isAccountExpired_expired() {
$expire = intval(time() / (24*3600)) - 10000;
$attrs = array(
'objectClass' => array('shadowAccount'),
'sHadoweXpirE' => array(0 => $expire)
);
$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')) {
/**
* Checks the shadow expire job.
*

View File

@ -0,0 +1,84 @@
<?php
/*
$Id$
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2017 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
include_once 'lam/lib/baseModule.inc';
include_once 'lam/lib/modules.inc';
include_once 'lam/lib/modules/windowsUser.inc';
/**
* Checks the windowsUser class.
*
* @author Roland Gruber
*/
class WindowsUserTest extends PHPUnit_Framework_TestCase {
public function test_isAccountExpired_noAttr() {
$attrs = array('objectClass' => array('user'));
$this->assertFalse(windowsUser::isAccountExpired($attrs));
}
public function test_isAccountExpired_notExpired() {
$expire = $this->getTimeStamp(14);
$attrs = array(
'objectClass' => array('user'),
'accounTExpIRes' => array(0 => $expire)
);
$this->assertFalse(windowsUser::isAccountExpired($attrs));
}
public function test_isAccountExpired_expired() {
$expire = $this->getTimeStamp(-14);
$attrs = array(
'objectClass' => array('user'),
'accounTExpIRes' => array(0 => $expire)
);
$this->assertTrue(windowsUser::isAccountExpired($attrs));
}
/**
* Returns the timestamp from now with given time difference.
*
* @param int $diff time difference in days
*/
private function getTimeStamp($diff) {
$timeBase = new DateTime('1601-01-01', getTimeZone());
$time = new DateTime(null, getTimeZone());
if ($diff > 0) {
$time->add(new DateInterval('P' . $diff . 'D'));
}
else {
$time->sub(new DateInterval('P' . abs($diff) . 'D'));
}
$timeDiff = $time->diff($timeBase);
$days = $timeDiff->format('%a');
$seconds = $days * 24 * 3600 - ($time->getOffset());
echo $seconds . ' ';
return $seconds . '0000000';
}
}
?>