Skip to content

Commit

Permalink
Draft - Active Directory support (#156)
Browse files Browse the repository at this point in the history
* Switch to LTB LDAP 0.2

* Migration of send_mail function

* Migration of connect function

* Migration of ldap_get_mail_for_notification function

* Require autoload in index

* Migration of search function

* Use ldapInstance in smarty plugin

* Call ldap_bind to check password

* Fix typo

* Migrate notify_admin_by_mail function

* Function to convert AD date

* First mapping between OpenLDAP and AD

* Manage other AD specific attributes

* AD lockout time

* AD identifier

* Move OpenLDAP specific attributes

* Work on isLocked function

* Use new ltb-common Directory functions

* Hide special value of lockout date

* Use ltb-common password expiration functions

* Use functions to lock/unlock an account

* Use ltb-common function to modify password

* Use ltb-common resetAtNextConnection function

* Add feature to enable/disable account

* Remove composer.lock

* Use new lockDate function to remove OpenLDAP specific code

* Clean lock account code

* Use Directory interface for search locked account

* Use Directory interface for search expired passwords

* Use Directory interface for search idle accounts

* Use Directory interface for search will expire passwords

* Use password policy configuration from Directory interface

* Fix merge

* Fix merge

* Doc for OpenLDAP/AD
  • Loading branch information
coudot authored Sep 27, 2024
1 parent 9c1274d commit c8447be
Show file tree
Hide file tree
Showing 22 changed files with 480 additions and 354 deletions.
44 changes: 36 additions & 8 deletions conf/config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
# All the default values are kept here, you should not modify it but use
# config.inc.local.php file instead to override the settings from here.
#==============================================================================

# LDAP
$ldap_type = "openldap";
$ldap_url = "ldap://localhost";
$ldap_starttls = false;
$ldap_binddn = "cn=manager,dc=example,dc=com";
Expand All @@ -39,9 +41,12 @@
$ldap_lastauth_attribute = "authTimestamp";
#$ldap_network_timeout = 10;

# Override LDAP password policy configuration
#$ldap_lockout_duration = 3600; # 1 hour
#$ldap_password_max_age = 7889400; # 3 months

# How display attributes
$attributes_map = array(
'authtimestamp' => array( 'attribute' => 'authtimestamp', 'faclass' => 'lock', 'type' => 'date' ),
'businesscategory' => array( 'attribute' => 'businesscategory', 'faclass' => 'briefcase', 'type' => 'text' ),
'carlicense' => array( 'attribute' => 'carlicense', 'faclass' => 'car', 'type' => 'text' ),
'created' => array( 'attribute' => 'createtimestamp', 'faclass' => 'clock-o', 'type' => 'date' ),
Expand All @@ -52,7 +57,6 @@
'fax' => array( 'attribute' => 'facsimiletelephonenumber', 'faclass' => 'fax', 'type' => 'tel' ),
'firstname' => array( 'attribute' => 'givenname', 'faclass' => 'user-o', 'type' => 'text' ),
'fullname' => array( 'attribute' => 'cn', 'faclass' => 'user-circle', 'type' => 'text' ),
'identifier' => array( 'attribute' => 'uid', 'faclass' => 'user-o', 'type' => 'text' ),
'l' => array( 'attribute' => 'l', 'faclass' => 'globe', 'type' => 'text' ),
'lastname' => array( 'attribute' => 'sn', 'faclass' => 'user-o', 'type' => 'text' ),
'mail' => array( 'attribute' => 'mail', 'faclass' => 'envelope-o', 'type' => 'mailto' ),
Expand All @@ -66,16 +70,28 @@
'phone' => array( 'attribute' => 'telephonenumber', 'faclass' => 'phone', 'type' => 'tel' ),
'postaladdress' => array( 'attribute' => 'postaladdress', 'faclass' => 'map-marker', 'type' => 'address' ),
'postalcode' => array( 'attribute' => 'postalcode', 'faclass' => 'globe', 'type' => 'text' ),
'secretary' => array( 'attribute' => 'secretary', 'faclass' => 'user-circle-o', 'type' => 'dn_link' ),
'state' => array( 'attribute' => 'st', 'faclass' => 'globe', 'type' => 'text' ),
'street' => array( 'attribute' => 'street', 'faclass' => 'map-marker', 'type' => 'text' ),
'title' => array( 'attribute' => 'title', 'faclass' => 'certificate', 'type' => 'text' ),
);

# Directory specific attributes
$openldap_attributes_map = array(
'authtimestamp' => array( 'attribute' => 'authtimestamp', 'faclass' => 'lock', 'type' => 'date' ),
'identifier' => array( 'attribute' => 'uid', 'faclass' => 'user-o', 'type' => 'text' ),
'pwdaccountlockedtime' => array( 'attribute' => 'pwdaccountlockedtime', 'faclass' => 'lock', 'type' => 'date' ),
'pwdchangedtime' => array( 'attribute' => 'pwdchangedtime', 'faclass' => 'lock', 'type' => 'date' ),
'pwdfailuretime' => array( 'attribute' => 'pwdfailuretime', 'faclass' => 'lock', 'type' => 'date' ),
'pwdlastsuccess' => array( 'attribute' => 'pwdlastsuccess', 'faclass' => 'lock', 'type' => 'date' ),
'pwdpolicysubentry' => array( 'attribute' => 'pwdpolicysubentry', 'faclass' => 'lock', 'type' => 'ppolicy_dn' ),
'pwdreset' => array( 'attribute' => 'pwdreset', 'faclass' => 'lock', 'type' => 'boolean' ),
'secretary' => array( 'attribute' => 'secretary', 'faclass' => 'user-circle-o', 'type' => 'dn_link' ),
'state' => array( 'attribute' => 'st', 'faclass' => 'globe', 'type' => 'text' ),
'street' => array( 'attribute' => 'street', 'faclass' => 'map-marker', 'type' => 'text' ),
'title' => array( 'attribute' => 'title', 'faclass' => 'certificate', 'type' => 'text' )
);
$activedirectory_attributes_map = array(
'authtimestamp' => array( 'attribute' => 'lastlogon', 'faclass' => 'lock', 'type' => 'ad_date' ),
'identifier' => array( 'attribute' => 'samaccountname', 'faclass' => 'user-o', 'type' => 'text' ),
'pwdaccountlockedtime' => array( 'attribute' => 'lockouttime', 'faclass' => 'lock', 'type' => 'ad_date' ),
'pwdchangedtime' => array( 'attribute' => 'pwdlastset', 'faclass' => 'lock', 'type' => 'ad_date' ),
'pwdfailuretime' => array( 'attribute' => 'badpasswordtime', 'faclass' => 'lock', 'type' => 'ad_date' ),
);

# Search
Expand All @@ -95,29 +111,41 @@
$display_items = array('identifier', 'firstname', 'lastname', 'title', 'businesscategory', 'employeenumber', 'employeetype', 'mail', 'mailquota', 'phone', 'mobile', 'fax', 'postaladdress', 'street', 'postalcode', 'l', 'state', 'organizationalunit', 'organization', 'manager', 'secretary' );
$display_title = "fullname";
$display_show_undefined = false;
$display_password_items = array('pwdchangedtime', 'pwdreset', 'pwdaccountlockedtime', 'pwdfailuretime','pwdpolicysubentry', 'authtimestamp', 'pwdlastsuccess', 'created', 'modified');
$display_password_items = array('pwdchangedtime', 'pwdfailuretime','pwdpolicysubentry', 'authtimestamp', 'pwdlastsuccess', 'created', 'modified');
$display_password_expiration_date = true;

# Features

$use_checkpassword = true;

$use_resetpassword = true;
$use_resetpassword_resetchoice = true;
$resetpassword_reset_default = true;

$show_lockstatus = true;
$use_unlockaccount = true;
$use_unlockcomment = false;
$use_unlockcomment_required = false;
$use_lockaccount = true;

$use_lockcomment = false;
$use_lockcomment_required = false;

$show_expirestatus = true;

$use_searchlocked = true;

$use_searchexpired = true;

$use_searchwillexpire = true;
$willexpiredays = 14;

$use_searchidle = true;
$idledays = 60;

$use_enableaccount = false;
$use_disableaccount = false;
$show_enablestatus = false;

# Local password policy
# This is applied before directory password policy
Expand Down
33 changes: 33 additions & 0 deletions docs/enableaccount.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Enable and disable account
==========================

Show enabled status
-------------------

Service Desk will display if account is enabled or not. To allow this feature:

.. code-block:: php
$show_enablestatus = true;
Enable account
--------------

This feature allows to enable the account. The button is only displayed if the account is disabled.

To enable this feature:

.. code-block:: php
$use_enableaccount = true;
Disable account
---------------

This feature allows to disable the account. It is only displayed if the account is enabled.

To enable this feature:

.. code-block:: php
$use_disableaccount = true;
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ LDAP Tool Box Service Desk documentation
checkpassword.rst
resetpassword.rst
lockaccount.rst
enableaccount.rst
hook.rst
dashboards.rst
configuration-mail.rst
Expand Down
22 changes: 21 additions & 1 deletion docs/ldap-parameters.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
LDAP parameters
===============

Type of directory
-----------------

You can define the type of LDAP directory (``openldap`` or ``activedirectory``). The default value is ``openldap``.

.. code-block:: php
$ldap_type = "openldap";
.. tip:: Other configuration parameters could be impacted by this choice, check their documentation.

Server address
--------------

Expand Down Expand Up @@ -40,7 +51,7 @@ Configure DN and password in ``$ldap_bindn`` and ``$ldap_bindpw``:
$ldap_binddn = "cn=manager,dc=example,dc=com";
$ldap_bindpw = "secret";
.. tip:: You can use the LDAP admin account or any service account. The account needs to read users, password policy entries and write ``userPassword`` and ``pwdReset`` attributes in user entries. Note that using the LDAP admin account will bypass any password policy like minimal size or password history when reseting the password.
.. tip:: You can use the LDAP admin account or any service account. The account needs to read users, password policy entries and write password and some other related attributes in user entries. On OpenLDAP, using the LDAP admin account will bypass any password policy like minimal size or password history when reseting the password.

LDAP Base
---------
Expand Down Expand Up @@ -106,6 +117,13 @@ Set ``$ldap_default_ppolicy`` value if a default policy is configured in your LD
.. tip:: Password policy is first searched in ``pwdPolicySubentry`` attribute of user entry, then fallback to default policy.

You can override some policies, like lockout duration or password maximal age:

.. code-block:: php
$ldap_lockout_duration = 3600; # 1 hour
$ldap_password_max_age = 7889400; # 3 months
Last authentication attribute
-----------------------------

Expand All @@ -114,3 +132,5 @@ The last authentication date can be stored in different attributes depending on
.. code-block:: php
$ldap_lastauth_attribute = "pwdLastSuccess";
.. tip:: This attribute is automatically configured for Active Directory.
4 changes: 2 additions & 2 deletions docs/lockaccount.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Lock account
============
Lock and unlock account
=======================

Show lock status
----------------
Expand Down
44 changes: 44 additions & 0 deletions htdocs/disableaccount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/*
* Disable account in LDAP directory
*/

$result = "";
$dn = "";
$password = "";

if (isset($_POST["dn"]) and $_POST["dn"]) {
$dn = $_POST["dn"];
} else {
$result = "dnrequired";
}

if (!$use_disableaccount) {
$result = "actionforbidden";
}

if ($result === "") {

require_once("../conf/config.inc.php");
require __DIR__ . '/../vendor/autoload.php';

# Connect to LDAP
$ldap_connection = $ldapInstance->connect();

$ldap = $ldap_connection[0];
$result = $ldap_connection[1];

if ($ldap) {
if ( $directory->disableAccount($ldap, $dn) ) {
$result = "accountdisabled";
} else {
$result = "ldaperror";
}
}
}

if ($audit_log_file) {
auditlog($audit_log_file, $dn, $audit_admin, "disableaccount", $result);
}

header('Location: index.php?page=display&dn='.$dn.'&disableaccountresult='.$result);
50 changes: 29 additions & 21 deletions htdocs/display.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
$prehookresult= "";
$posthookresult= "";
$ldapExpirationDate="";
$canLockAccount="";
$isAccountEnabled = "";
$lockDate = "";

if (isset($_GET["dn"]) and $_GET["dn"]) {
$dn = $_GET["dn"];
Expand Down Expand Up @@ -63,16 +66,14 @@

# Search attributes
$attributes = array();
$search_items = array_merge( $display_items, $display_password_items);
$search_items = array_merge($display_items, $display_password_items);
foreach( $search_items as $item ) {
$attributes[] = $attributes_map[$item]['attribute'];
}
$attributes[] = $attributes_map[$display_title]['attribute'];
$attributes[] = "pwdPolicySubentry";

# Search entry
$search = ldap_read($ldap, $dn, $ldap_user_filter, $attributes);

$errno = ldap_errno($ldap);

if ( $errno ) {
Expand All @@ -93,22 +94,28 @@
$entry[0][$attr] = $values;
}

# Include default password policy
if ( !$entry[0]['pwdpolicysubentry'] and $ldap_default_ppolicy) {
$entry[0]['pwdpolicysubentry'][] = $ldap_default_ppolicy;
}
# Get password policy configuration
$pwdPolicyConfiguration = $directory->getPwdPolicyConfiguration($ldap, $dn, $ldap_default_ppolicy);
if ($ldap_lockout_duration) { $pwdPolicyConfiguration['lockout_duration'] = $ldap_lockout_durantion; }
if ($ldap_password_max_age) { $pwdPolicyConfiguration['password_max_age'] = $ldap_password_max_age; }

if ($display_edit_link) {
# Replace {dn} in URL
$edit_link = str_replace("{dn}", urlencode($dn), $display_edit_link);
}

# Search user active password policy
$pwdPolicy = "";
if (isset($entry[0]['pwdpolicysubentry'][0])) {
$pwdPolicy = $entry[0]['pwdpolicysubentry'][0];
} elseif (isset($ldap_default_ppolicy)) {
$pwdPolicy = $ldap_default_ppolicy;
$lockDate = $directory->getLockDate($ldap, $dn);
$unlockDate = $directory->getUnlockDate($ldap, $dn, $pwdPolicyConfiguration);
$isLocked = $directory->isLocked($ldap, $dn, $pwdPolicyConfiguration);
$canLockAccount = $pwdPolicyConfiguration["lockout_enabled"];

$expirationDate = $directory->getPasswordExpirationDate($ldap, $dn, $pwdPolicyConfiguration);
$isExpired = $directory->isPasswordExpired($ldap, $dn, $pwdPolicyConfiguration);

$resetAtNextConnection = $directory->resetAtNextConnection($ldap, $dn);

if ($show_enablestatus) {
$isAccountEnabled = $directory->isAccountEnabled($ldap, $dn);
}

$isLocked = false;
Expand Down Expand Up @@ -196,9 +203,11 @@
$smarty->assign("show_undef", $display_show_undefined);

$smarty->assign("isLocked", $isLocked);
$smarty->assign("lockDate", $lockDate);
$smarty->assign("unlockDate", $unlockDate);
$smarty->assign("isExpired", $isExpired);
$smarty->assign("ldapExpirationDate", $ldapExpirationDate);
$smarty->assign("ldapExpirationDate", $expirationDate ? $expirationDate->getTimestamp(): NULL);
$smarty->assign("resetAtNextConnection", $resetAtNextConnection);

$smarty->assign("edit_link", $edit_link);

Expand All @@ -208,13 +217,12 @@
$smarty->assign("accountlockresult", $accountlockresult);
$smarty->assign("prehookresult", $prehookresult);
$smarty->assign("posthookresult", $posthookresult);
if ($pwdLockout == false) $smarty->assign("use_lockaccount", $pwdLockout);
if(isset($messages[$resetpasswordresult]))
{
$smarty->assign('msg_resetpasswordresult',$messages[$resetpasswordresult]);
}
else
{
if ($canLockAccount == false) { $smarty->assign("use_lockaccount", $canLockAccount); }
$smarty->assign("isAccountEnabled", $isAccountEnabled);
if (isset($messages[$resetpasswordresult])) {
$smarty->assign('msg_resetpasswordresult', $messages[$resetpasswordresult]);
} else {
$smarty->assign('msg_resetpasswordresult','');
}

?>
44 changes: 44 additions & 0 deletions htdocs/enableaccount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/*
* Enable account in LDAP directory
*/

$result = "";
$dn = "";
$password = "";

if (isset($_POST["dn"]) and $_POST["dn"]) {
$dn = $_POST["dn"];
} else {
$result = "dnrequired";
}

if (!$use_enableaccount) {
$result = "actionforbidden";
}

if ($result === "") {

require_once("../conf/config.inc.php");
require __DIR__ . '/../vendor/autoload.php';

# Connect to LDAP
$ldap_connection = $ldapInstance->connect();

$ldap = $ldap_connection[0];
$result = $ldap_connection[1];

if ($ldap) {
if ( $directory->enableAccount($ldap, $dn) ) {
$result = "accountenabled";
} else {
$result = "ldaperror";
}
}
}

if ($audit_log_file) {
auditlog($audit_log_file, $dn, $audit_admin, "enableaccount", $result);
}

header('Location: index.php?page=display&dn='.$dn.'&enableaccountresult='.$result);
Loading

0 comments on commit c8447be

Please sign in to comment.