From a36f3e1e356be5b14513eb9e8cd5aa0a4a81652d Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Fri, 16 Oct 2015 09:40:17 +0200 Subject: [PATCH 1/9] Initial commit --- .../authentication/active_directory.inc.php | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 html/includes/authentication/active_directory.inc.php diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php new file mode 100644 index 000000000..cb6753191 --- /dev/null +++ b/html/includes/authentication/active_directory.inc.php @@ -0,0 +1,263 @@ + 1) { + putenv('LDAPTLS_REQCERT=never'); +}; + +function authenticate($username, $password) { + global $config, $ds; + + if ($username && $ds) { + // bind with sAMAccountName instead of full LDAP DN + if (ldap_bind($ds, "{$username}@{$config['auth_ad_domain']}", $password)) { + return 1; + } else { + return 0; + } + } else { + echo ldap_error($ds); + } + + return 0; + +} + +function reauthenticate($sess_id, $token) { + return 0; +} + + +function passwordscanchange($username='') { + return 0; +} + + +function changepassword($username, $newpassword) { + return 0; +} + + +function auth_usermanagement() { + return 0; +} + + +function adduser($username, $password, $level, $email='', $realname='', $can_modify_passwd='1') { + return 0; +} + + +function user_exists($username) { + global $config, $ds; + + $search = ldap_search($ds, $config['auth_ad_base_dn'], + "(samaccountname={$username})",array('samaccountname')); + $entries = ldap_get_entries($ds, $search); + + + if ($entries['count']) { + return 1; + } + + return 0; + +} + + +function get_userlevel($username) { + global $config, $ds; + + $userlevel = 0; + + // Find all defined groups $username is in + $search = ldap_search($ds, $config['auth_ad_base_dn'], + "(samaccountname={$username})", array('memberOf')); + $entries = ldap_get_entries($ds, $search); + + // Loop the list and find the highest level + foreach ($entries[0]['memberof'] as $entry) { + $group_cn = get_cn($entry); + if ($config['auth_ad_groups'][$group_cn]['level'] > $userlevel) { + $userlevel = $config['auth_ad_groups'][$group_cn]['level']; + } + } + + return $userlevel; + +} + + +function get_userid($username) { + global $config, $ds; + + $attributes = array('objectsid'); + $result = ldap_search($ds, $config['auth_ad_base_dn'], + "(samaccountname={$username})", $attributes); + $entries = ldap_get_entries($ds, $search); + + if ($entries['count']) { + return preg_replace('/.*-(\d+)$/','$1',sid_from_ldap($entries[0]['objectsid'][0])); + } + + return -1; + +} + + +function deluser($username) { + // Not supported + return 0; + +} + + +function get_userlist() { + global $config, $ds; + $userlist = array(); + + $ldap_groups = get_group_list(); + + foreach($ldap_groups as $ldap_group) { + $group_cn = get_cn($ldap_group); + $search = ldap_search($ds, $config['auth_ad_base_dn'], "(cn={$group_cn})", array('member')); + $entries = ldap_get_entries($ds, $search); + + foreach($entries[0]['member'] as $member) { + $member_cn = get_cn($member); + $search = ldap_search($ds, $config['auth_ad_base_dn'], "(cn={$member_cn})", + array('sAMAccountname', 'displayName', 'objectSID', 'mail')); + $results = ldap_get_entries($ds, $search); + foreach($results as $result) { + if(isset($result['samaccountname'][0])) { + $userid = preg_replace('/.*-(\d+)$/','$1', + sid_from_ldap($result[0]['objectsid'][0])); + $username = $result['samaccountname'][0]; + + // don't make duplicates, user may be member of more than one group + $userhash[$username] = array( + 'realname' => $result['displayName'][0], + 'user_id' => $userid, + 'email' => $result['mail'][0] + ); + } + } + } + } + + foreach($userhash[] as $key) { + $userlist[] = array( + 'username' => $key, + 'realname' => $userhash[$key]['realname'], + 'user_id' => $userhash[$key]['user_id'], + 'email' => $userhash[$key]['email'] + ); + } + + return $userlist; +} + + +function can_update_users() { + // not supported so return 0 + return 0; + +} + + +function get_user($user_id) { + // not supported so return 0 + return 0; + +} + + +function update_user($user_id, $realname, $level, $can_modify_passwd, $email) { + // not supported so return 0 + return 0; + +} + + +function get_fullname($username) { + global $config, $ds; + + $attributes = array('name'); + $result = ldap_search($ds, $config['auth_ad_base_dn'], + "(samaccountname={$username})", $attributes); + $entries = ldap_get_entries($ds, $result); + if ($entries['count'] > 0) { + $membername = $entries[0]['name'][0]; + } else { + $membername = $username; + } + + return $membername; +} + + +function get_group_list() { + global $config; + + $ldap_groups = array(); + + // show all Active Directory Users by default + $default_group = 'Users'; + + if (isset($config['auth_ad_group'])) { + if ($config['auth_ad_group'] !== $default_group) { + $ldap_groups[] = $config['auth_ad_group']; + } + } + + if (!isset($config['auth_ad_groups']) && !isset($config['auth_ad_group'])) { + $ldap_groups[] = get_dn($default_group); + } + + foreach ($config['auth_ad_groups'] as $key => $value) { + $ldap_groups[] = get_dn($key); + } + + return $ldap_groups; + +} + +function get_dn($samaccountname) { + global $config, $ds; + + + $attributes = array('dn'); + $result = ldap_search($ds, $config['auth_ad_base_dn'], + "(samaccountname={$samaccountname})", $attributes); + $entries = ldap_get_entries($ds, $result); + if ($entries['count'] > 0) { + return $entries[0]['dn']; + } else { + return ''; + } +} + +function get_cn($dn) { + preg_match('/[^,]*/', $dn, $matches, PREG_OFFSET_CAPTURE, 3); + return $matches[0][0]; +} + +function sid_from_ldap($sid) +{ + $sidHex = unpack('H*hex', $sid)['hex']; + $subAuths = unpack('H2/H2/n/N/V*', $sid); + $revLevel = hexdec(substr($sidHex, 0, 2)); + $authIdent = hexdec(substr($sidHex, 4, 12)); + return 'S-'.$revLevel.'-'.$authIdent.'-'.implode('-', $subAuths); +} From f87360bc0fe29031b0254108eafeb1ecc9969c18 Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Thu, 22 Oct 2015 09:13:42 +0200 Subject: [PATCH 2/9] Added actual documentation for active_directory auth --- doc/Extensions/Authentication.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/Extensions/Authentication.md b/doc/Extensions/Authentication.md index 048a08f13..eee0915b9 100644 --- a/doc/Extensions/Authentication.md +++ b/doc/Extensions/Authentication.md @@ -9,7 +9,9 @@ Here we will provide configuration details for these modules. - LDAP: ldap - HTTP Auth: http-auth +- Active Directory: active_directory + +- HTTP Auth: http-auth #### User levels @@ -99,3 +101,20 @@ $config['auth_ldap_groupmemberattr'] = "memberUid"; ``` Replace {id} with the unique ID provided by Jumpcloud. + +#### Active Directory Authentication + +Config option: `active_directory` + +This is similar to LDAP Authentication. Install __php_ldap__ for CentOS/RHEL or __php5-ldap__ for Debian/Ubuntu. + +If you have issues with secure LDAP try setting `$config['auth_ad_dont_check_certificates']` to `1`. + +``` +$config['auth_ad_url'] = "ldaps://your-domain.controll.er"; +$config['auth_ad_dont_check_certificates'] = 1; // or 0 +$config['auth_ad_domain'] = "your-domain.com"; +$config['auth_ad_base_dn'] = "dc=your-domain,dc=com"; +$config['auth_ad_groups']['admin']['level'] = 10; +$config['auth_ad_groups']['pfy']['level'] = 7; +``` From 93b57040c51011b64dbaba3f6b9b5c6b81ea7cb9 Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Thu, 22 Oct 2015 09:16:20 +0200 Subject: [PATCH 3/9] Authenticate against active directory --- .../authentication/active_directory.inc.php | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php index cb6753191..dc4d11eb3 100644 --- a/html/includes/authentication/active_directory.inc.php +++ b/html/includes/authentication/active_directory.inc.php @@ -11,8 +11,8 @@ ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); // disable certificate checking if required -if (isset($config['auth_ad_dont_check_certificates'] && - $config['auth_ad_dont_check_certificates'] > 1) { +if (isset($config['auth_ad_dont_check_certificates']) && + $config['auth_ad_dont_check_certificates'] > 0) { putenv('LDAPTLS_REQCERT=never'); }; @@ -31,30 +31,34 @@ function authenticate($username, $password) { } return 0; - } function reauthenticate($sess_id, $token) { + // not supported so return 0 return 0; } function passwordscanchange($username='') { + // not supported so return 0 return 0; } function changepassword($username, $newpassword) { + // not supported so return 0 return 0; } function auth_usermanagement() { + // not supported so return 0 return 0; } function adduser($username, $password, $level, $email='', $realname='', $can_modify_passwd='1') { + // not supported so return 0 return 0; } @@ -72,7 +76,6 @@ function user_exists($username) { } return 0; - } @@ -95,7 +98,6 @@ function get_userlevel($username) { } return $userlevel; - } @@ -112,14 +114,12 @@ function get_userid($username) { } return -1; - } function deluser($username) { - // Not supported + // not supported so return 0 return 0; - } @@ -142,11 +142,10 @@ function get_userlist() { foreach($results as $result) { if(isset($result['samaccountname'][0])) { $userid = preg_replace('/.*-(\d+)$/','$1', - sid_from_ldap($result[0]['objectsid'][0])); - $username = $result['samaccountname'][0]; + sid_from_ldap($result['objectsid'][0])); // don't make duplicates, user may be member of more than one group - $userhash[$username] = array( + $userhash[$result['samaccountname'][0]] = array( 'realname' => $result['displayName'][0], 'user_id' => $userid, 'email' => $result['mail'][0] @@ -156,7 +155,7 @@ function get_userlist() { } } - foreach($userhash[] as $key) { + foreach(array_keys($userhash) as $key) { $userlist[] = array( 'username' => $key, 'realname' => $userhash[$key]['realname'], @@ -172,14 +171,12 @@ function get_userlist() { function can_update_users() { // not supported so return 0 return 0; - } function get_user($user_id) { // not supported so return 0 return 0; - } @@ -255,7 +252,7 @@ function get_cn($dn) { function sid_from_ldap($sid) { - $sidHex = unpack('H*hex', $sid)['hex']; + $sidHex = unpack('H*hex', $sid); $subAuths = unpack('H2/H2/n/N/V*', $sid); $revLevel = hexdec(substr($sidHex, 0, 2)); $authIdent = hexdec(substr($sidHex, 4, 12)); From 779c90b1fd026f57e899419ae43b46912be82b6d Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Fri, 30 Oct 2015 17:17:55 +0100 Subject: [PATCH 4/9] Checking for groups now --- .../authentication/active_directory.inc.php | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php index dc4d11eb3..316017855 100644 --- a/html/includes/authentication/active_directory.inc.php +++ b/html/includes/authentication/active_directory.inc.php @@ -22,7 +22,29 @@ function authenticate($username, $password) { if ($username && $ds) { // bind with sAMAccountName instead of full LDAP DN if (ldap_bind($ds, "{$username}@{$config['auth_ad_domain']}", $password)) { - return 1; + // group membership in one of the configured groups is required + if (isset($config['auth_ad_require_groupmembership']) && + $config['auth_ad_require_groupmembership'] > 0) { + $search = ldap_search($ds, $config['auth_ad_base_dn'], + "(samaccountname={$username})", array('memberOf')); + $entries = ldap_get_entries($ds, $search); + + $user_authenticated = 0; + + foreach ($entries[0]['memberof'] as $entry) { + $group_cn = get_cn($entry); + if (isset($config['auth_ad_groups'][$group_cn]['level'])) { + // user is in one of the defined groups + $user_authenticated = 1; + } + } + + return $user_authenticated; + + } else { + // group membership is not required and user is valid + return 1; + }; } else { return 0; } From 8e0a95ab2b9b04fc2f4a02a29e91fd702fd8da6e Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Fri, 30 Oct 2015 17:22:46 +0100 Subject: [PATCH 5/9] Documented all config options --- doc/Extensions/Authentication.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/Extensions/Authentication.md b/doc/Extensions/Authentication.md index eee0915b9..d7c526d36 100644 --- a/doc/Extensions/Authentication.md +++ b/doc/Extensions/Authentication.md @@ -110,6 +110,12 @@ This is similar to LDAP Authentication. Install __php_ldap__ for CentOS/RHEL or If you have issues with secure LDAP try setting `$config['auth_ad_dont_check_certificates']` to `1`. +##### Require actual membership of the configured groups + +If you set ```$config['auth_ad_require_groupmembership']``` to 1, the authenticated user has to be a member of the specific group. Otherwise all users can authenticate, but are limited to user level 0 and only have access to shared dashboards. + +##### Sample configuration + ``` $config['auth_ad_url'] = "ldaps://your-domain.controll.er"; $config['auth_ad_dont_check_certificates'] = 1; // or 0 @@ -117,4 +123,5 @@ $config['auth_ad_domain'] = "your-domain.com"; $config['auth_ad_base_dn'] = "dc=your-domain,dc=com"; $config['auth_ad_groups']['admin']['level'] = 10; $config['auth_ad_groups']['pfy']['level'] = 7; +$config['auth_ad_require_groupmembership'] = 0; ``` From 0eeb4d2ef865cefc0101fe554dacae00ce55ab4d Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Tue, 10 Nov 2015 10:03:47 +0100 Subject: [PATCH 6/9] Fixed a bug from scrutinizer --- html/includes/authentication/active_directory.inc.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php index 316017855..5aea2e11b 100644 --- a/html/includes/authentication/active_directory.inc.php +++ b/html/includes/authentication/active_directory.inc.php @@ -127,7 +127,7 @@ function get_userid($username) { global $config, $ds; $attributes = array('objectsid'); - $result = ldap_search($ds, $config['auth_ad_base_dn'], + $search = ldap_search($ds, $config['auth_ad_base_dn'], "(samaccountname={$username})", $attributes); $entries = ldap_get_entries($ds, $search); @@ -148,6 +148,7 @@ function deluser($username) { function get_userlist() { global $config, $ds; $userlist = array(); + $userhash = array(); $ldap_groups = get_group_list(); From 2326061e6889c2f9f1a2cc281bb6c30d56bd3e4b Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Tue, 10 Nov 2015 10:38:42 +0100 Subject: [PATCH 7/9] Removed unused variables and updated coding style --- .../authentication/active_directory.inc.php | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php index 5aea2e11b..0773a108e 100644 --- a/html/includes/authentication/active_directory.inc.php +++ b/html/includes/authentication/active_directory.inc.php @@ -41,33 +41,36 @@ function authenticate($username, $password) { return $user_authenticated; - } else { + } + else { // group membership is not required and user is valid return 1; }; - } else { + } + else { return 0; } - } else { + } + else { echo ldap_error($ds); } return 0; } -function reauthenticate($sess_id, $token) { +function reauthenticate() { // not supported so return 0 return 0; } -function passwordscanchange($username='') { +function passwordscanchange() { // not supported so return 0 return 0; } -function changepassword($username, $newpassword) { +function changepassword() { // not supported so return 0 return 0; } @@ -79,7 +82,7 @@ function auth_usermanagement() { } -function adduser($username, $password, $level, $email='', $realname='', $can_modify_passwd='1') { +function adduser() { // not supported so return 0 return 0; } @@ -219,7 +222,8 @@ function get_fullname($username) { $entries = ldap_get_entries($ds, $result); if ($entries['count'] > 0) { $membername = $entries[0]['name'][0]; - } else { + } + else { $membername = $username; } @@ -263,7 +267,8 @@ function get_dn($samaccountname) { $entries = ldap_get_entries($ds, $result); if ($entries['count'] > 0) { return $entries[0]['dn']; - } else { + } + else { return ''; } } From 16df0fdd2e3882725d4081f06c87ec2e568b80bd Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Tue, 10 Nov 2015 10:39:42 +0100 Subject: [PATCH 8/9] Coding style --- html/includes/authentication/active_directory.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php index 0773a108e..659f5cba6 100644 --- a/html/includes/authentication/active_directory.inc.php +++ b/html/includes/authentication/active_directory.inc.php @@ -45,7 +45,7 @@ function authenticate($username, $password) { else { // group membership is not required and user is valid return 1; - }; + } } else { return 0; From d326869675f06d41fc161795c7f71270c8568b54 Mon Sep 17 00:00:00 2001 From: Falk Stern Date: Tue, 10 Nov 2015 10:47:50 +0100 Subject: [PATCH 9/9] Removed unused variables --- html/includes/authentication/active_directory.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/includes/authentication/active_directory.inc.php b/html/includes/authentication/active_directory.inc.php index 659f5cba6..03788f90d 100644 --- a/html/includes/authentication/active_directory.inc.php +++ b/html/includes/authentication/active_directory.inc.php @@ -142,7 +142,7 @@ function get_userid($username) { } -function deluser($username) { +function deluser() { // not supported so return 0 return 0; }