greentiger
greentiger

Reputation: 85

Discovering nested groups using LDAP with PHP

I have an LDAP authentication system that can find and retrieve the memberOf attribute from AD but I'm not sure how to go about getting nested group memberships. I'm aware of the memberOf:1.2.840.113556.1.4.1941: extension but unsure how to adapt it to my code. My code that is working is below (it just doesn't do nested groups). Can someone with more experience help?

IN CONFIG.PHP

$adbase = "some.domain.com";
$adtree = "OU=All Departments,DC=some,DC=domain,DC=com";
$group_admins = "Test App Admins";
$group_staff = "Test App Staff";

IN RUN.PHP

function extract_unit($string, $start, $end) {
    $pos = stripos($string, $start);
    $str = substr($string, $pos);
    $str_two = substr($str, strlen($start));
    $second_pos = stripos($str_two, $end);
    $str_three = substr($str_two, 0, $second_pos);
    $unit = trim($str_three); // remove whitespaces
    return $unit;
    }

extract($_POST);

if($action == "logon") {
    $ldap = ldap_connect($adbase);
    $userID = "$user@$adbase";
    $bind = @ldap_bind($ldap, $userID, $pass);

    if($bind == true) {
        // assign session variables
        $_SESSION['username'] = $user;

        // get group memberships
        $results = ldap_search($ldap, $adtree, "(samaccountname=$user)",array("memberof", "mail"));
        $entries = ldap_get_entries($ldap, $results);

        // get group count (first entry) and group listings
        $beat = 0; $str = ""; foreach($entries[0]['memberof'] as $temp) {
            if($beat == 0) {
                $count = $temp;
                }
            else {
                $temp = extract_unit($temp, "CN=", ",OU=");
                $str .= "$temp, ";
                $groups[] = $temp;
                }
            $beat++;
            }
        // return the mail address, stackoverflow.com/questions/16224720/searching-for-email-address-ldap-active-directory
        $mail = $entries[0]["mail"][0];
        // assign session variables
        $_SESSION['mail'] = $mail;

        // groups defined in config.php
        $client = "You are logged in as a client.";
        if(in_array($group_admins, $groups)) {
            $_SESSION['admin'] = true;
            $client = "You are logged in as an administrator.";
            }
        else {
            $_SESSION['admin'] = false;
            }
        // groups defined in config.php
        if(in_array($group_staff, $groups)) {
            $_SESSION['staff'] = true;
            $client = "You are logged in as staff.";
            }
        else {
            $_SESSION['staff'] = false;
            }

        // if it returns true user was found
        print "<h2>Signed in as $user</h2>\n";
        print "$client<br>\n";
        print "<img src=\"./images/next.png\"> <a href=\"./?d=welcome\">Continue ...</a>\n";
        print "<!-- count is $count -->";
        print "<!-- groups are $str -->";
        print "<!-- mail is $mail -->";
        }
    else {
        print "<h2>Login attempt failed</h2>\n";
        print "<img src=\"./images/logon.png\"> <a href=\"./?d=user/logon\">You may try again ...</a>\n";
        }
    }

Upvotes: 1

Views: 3616

Answers (2)

greentiger
greentiger

Reputation: 85

I ended up getting to work but I had to take a different path.

This is the code that I finally got PHP LDAP authentication working, with nested groups. This assumes the username is being checked to see if it exists in a group or nested group. If you take out (samaccountname=$user) then it will return a list of all the users in that group but it will take considerably longer (less than a second to like 30+ seconds).

/*
$adBase like "some.domain.com"
$ldBase like "OU=Users,DC=some,DC=domain,DC=com"
$admins like "CN=Test Admins,OU=Groups"
$staff like "CN=Test Staff,OU=Groups"
$ldaplink like "ldap://ldap.domain.com/o=domain.com??sub?"
*/

$ldap   = ldap_connect($adBase);
$bind   = @ldap_bind($ldap, "$user@$adBase", $pass);

$admins = $ldAdmin.",".$ldBase;
$staffs = $ldStaff.",".$ldBase;
$filtrA = "(&(objectClass=user)(samaccountname=$user)(memberof:1.2.840.113556.1.4.1941:=$admins))";
$filtrS = "(&(objectClass=user)(samaccountname=$user)(memberof:1.2.840.113556.1.4.1941:=$staffs))";

if($bind) {
    // default message if not found in either group
    $client = "You are logged in as a client.";

    // get basic info first; stackoverflow.com/questions/1622472...
    $result = ldap_search($ldap, $ldBase, "(samaccountname=$user)", array("mail"));
    $entries = ldap_get_entries($ldap, $result);
    $mail = $entries[0]["mail"][0];
        $_SESSION['username'] = $user;
        $_SESSION['mail'] = $mail;


    #region check admin/nested group
    $link1A = ldap_search($ldap, $ldBase, $filtrA, array("name"));
    $link2A = ldap_get_entries($ldap, $link1A);
    @$nameA = $link2A[0]["name"][0]; // throws error if not defined but that's what we're testing thus the @

    if($nameA == true) {
        // user was found in administrators or in nested group
        $_SESSION['admin'] = true;
        $client = "You are logged in as an administrator.";
        }
    else {
        $_SESSION['admin'] = false;
        }
    #endregion

    #region check staff/nested group
    $link1S = ldap_search($ldap, $ldBase, $filtrS, array("name"));
    $link2S = ldap_get_entries($ldap, $link1S);
    @$nameS = $link2S[0]["name"][0]; // throws error if not defined but that's what we're testing thus the @

    if($nameS == true) {
        // user was found in staff or in nested group
        $_SESSION['staff'] = true;
        $client = "You are logged in as staff.";
        }
    else {
        $_SESSION['staff'] = false;
        }
    #endregion

    // if it returns true user was found
    print "<h2>Signed in as $user</h2>\n";
    print "$client<br>\n";
    print "<img src=\"./images/next.png\"> <a href=\"./?d=tem/welcome\">Continue ...</a>\n";
    print "<!-- mail is $mail -->";
    }
else {
    print "<h2>Login attempt failed</h2>\n";
    print "<img src=\"./images/logon.png\"> <a href=\"./?d=user/logon\">You may try again ...</a>\n";
    }

Upvotes: 1

jwilleke
jwilleke

Reputation: 11026

You need to construct a query using the EXTENSIBLE MATCH filter syntax similar to:

(memberOf:1.2.840.113556.1.4.1941:=CN=GroupOne,OU=Security Groups,OU=Groups,DC=YOURDOMAIN,DC=NET)

Which RESOLVES ALL MEMBERS (INCLUDING NESTED) SECURITY GROUPS (REQUIRES AT LEAST WINDOWS 2003 SP2)

Upvotes: 0

Related Questions