We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 5821
    • 5 Posts
    Hello everyone,

    I’ve been lurking around for a long time (and on a few other support forums for other CMS projects), and I’ve finally decided to speak up. Before I start rambling, let me state that I have a functional grasp of PHP and MYSQL, and the site I refer to will be a LAMP stack (if any of that helps), Here is my dilemma. I have been charged by my institution to develop a management plan and proposal for the new website we will be developing. I have decided that we will use modx for this project, since it pretty much does EXACTLY what we need it to do... with one caveat:

    One of the requirements this site needs to fulfill is the authentication for webusers (who will only be affiliated with our institution) must be done SECURELY against our LDAP, or if all else fails, our IMAP mail server. (basically so our users don’t have to have a million different account credentials to keep track of).

    I am considering using the IMAP authentication plugin in the repository, and am not concerned with secure communication of user credentials (passwords) from the webserver to the IMAP server. What I am concerned about is communication of passwords from the user’s web browser to the webserver/IMAP Auth plugin. My initial thought that this could be remedied by putting the site up as https://, but I really don’t want the WHOLE site under SSL. I guess what I am wondering is if it is possible for there to be a link on the main page of our modx site that goes to a separate page/virtual host that is SSL’d. At that point, the user would enter their user info, then, once authenticated, redirect them back to our modx site with access to the pages that would not be available to unauthenticated users.

    Phew! I really hope that was some semblance of coherent. Please, anyone with some suggestions throw me a rope. I really appreciate it!

    Thank You!
    Corvias
      • 25663 MODX Staff
      • 12,272 Posts
      This should be entirely doable (or easily solvable), and Adam has his school (Bryan College) authenticating to an IMAP repository. smiley I’ll let those with direct experience speak to the details.
        Ryan Thrash, MODX Co-Founder
        Follow me on Twitter at @rthrash or catch my occasional unofficial thoughts at thrash.me
        • 15982
        • 21 Posts
        Below is some code that we use for another PHP product to authenticate our users active a MS Active Directory. it is a plugin to a calendering system that authenticates our users to use the calendar.

        The problem with AD is that it does not follow the LDAP specification that expects a unique user ID number for each user, instead AD uses a unique "name" for each user. This causes problems when trying to use it in traditional mySQL database based authentication. This code creates a unique user ID number based on the samAccountName used in AD to uniquely identify a user.

        I am not a ’coder’ (more of a hacker, coder wannabe) so I don’t understand the code fully and I it won’t work out of the box for MODx, but I think it is a good starting point for someone to build an authentication plugin/module for MODx using an LDAP server as the user source. It works for us in another scenario. PM me if you want to know what Calendar it is.

        Have fun

        PS I provide no support or help, its just FYI, hope it helps

        Dean

        <?php
        
        #################################
        #
        ### LDAP AUTH MODULE VERSION 1.1
        #
        #################################
        
        global $ldap_opts;
        
        #################################
        #
        ### LDAP OPTIONS
        #
        #################################
        
        # LDAP Host //Set your LDAP Server address here
        $ldap_opts['host'] = 'ldap://xxx.xxx.xxx.xxx.xxx.xx';
        
        # LDAP Protocol version, 3 is most common //3 works for MS Active Directory
        $ldap_opts['version'] = 3;
        
        # LDAP anonymous dn and password. This should
        # have access to connect and list users //In AD you need put domain then username ie mydomain\\username
        $ldap_opts['anon_dn']  = 'xxx\\xx';
        $ldap_opts['anon_pw']  = 'xxxx';
        
        $ldap_opts['debug'] = 0;
        
        # DN Suffix //Can use multiple OU's to get users seperate each with a comma
        $ldap_opts['suffix'] = array(
                'OU=Teachers, OU=Staff, DC=internal, DC=xxx, DC=xxx, DC=xxx, DC=xx',
        	'OU=Network_Managers, DC=internal, DC=xxx, DC=xxx, DC=xxx, DC=xx'
                );
        
        # Default admin/superuser userid // This is the name of the user who will be the super admin (in AD it is samAcccountName) ie ssmith
        $ldap_opts['admin'] = "xxxx";
        
        # attributes translations
        # 'name' = user's full name
        # 'uid' = unique id number of user.
        #       Set to __autogen creates uids. Some
        #       LDAP dbs do not have uids. MS AD et. al. Getting
        #       this from your LDAP db is faster if it exists.
        # 'userid' = login user id
        # 'email' = email address
        $ldap_opts['attribs'] = array(
                'name' => 'givenname',
                'uid' => '__autogen',
                'userid' => 'name',
                'email' => 'mail',
                'dn' => 'dn'
        );
        
        #############################
        #
        ### END OPTIONS
        #
        #############################
        
        #########################################################
        #
        # FUNCTIONS REQUIRERD BY ALL AUTH MODULES
        #
        # The following naming conventions are used:
        #
        # userid	- the login userid.
        # id		- the unique id of a user (uid)
        # user name	- the common name of the user
        #
        # E.g. A user named (user name) "John Smith" may have
        # a userid of "jsmith" and an id of 234
        #
        #
        # -----------------------------------------------------
        #
        # _ex_auth_login(&$user_object)
        #
        #    rerturn true or false based on authentication.
        #
        # Where $user_object is a reference to a user. Auth
        # against $user_object->userid and $user_object->pass
        #
        #
        # ----------------------------------------------------
        #
        # _ex_auth_get_user(&$user_object)
        #
        #   set values in $user_object, return value is ignored
        #
        # $user_object as passed will have either, userid, or
        # id set. This function MUST set:
        #
        #  $user_object->id	- the unique id for this user
        #			- must be a number > 0
        #
        #  $user_object->userid - the userid of this user,
        #			- e.g. jsmith
        #
        #  this function may optionally and is encurated to set:
        #
        #  $user_object->name	- the common name of ther user
        #			- e.g. Joe Smith
        #
        #  $user_object->email	- the primary e-mail address of
        #			- this user
        #
        #  if no user is found, set all of the user's attributes
        #  to null. 
        #
        # --------------------------------------------------
        #
        # _ex_auth_get_users_by_id($ids, $associative = false)
        #
        #  return an array of users that have ids in $ids
        #
        #  This should return an array of 'key' => values for id,
        #  userid, name, and email.
        #
        #  $.g. $users = array();
        #
        #  for each user in ($ids)
        #
        #     $user[] = array('name' => $uname, 'userid' => $userid, 'id' => $uid, 'email' => $email);
        #
        #  return $users;
        #
        #  if $associative is true, the array returned must
        #  have the users' ids as it's key. E.g.
        #
        #  for each user in ($ids)
        #
        #     $user[$uid] = array('name' => $uname, 'userid' => $userid, 'id' => $uid, 'email' => $email);
        #
        #  return $users;
        #
        #
        # ------------------------------------------------
        #
        #  _ex_auth_search_users($key = false, $search_str, $anchor_start = false, $associative = false)
        #
        #  search and return an array of users and their attributes.
        #  the attributes that must be, or rmay be set follow the same
        #  rules as _ex_auth_get_user
        #
        #  where $key is the field to search:
        #
        #       "name"		- search users' names
        #	"userid"	- search userids
        #	false (default)	- search both userids and users' names
        #
        #  $search_str is the string to search, this contains
        #  	no wildcard characters. If your authentication
        #	method requires wildcards, this function is
        #	expected to add them
        #
        #  $anchor_start if this is true, a wildcard character
        #  	is only appended to the end of the string. If
        #	false (default) prepend and append wildcards	
        #  
        #  $associative indicates that the function must return
        #	an array of associative arrays with the user's
        #	id as the key. E.g. a searrch that returned 2
        #	users may set by:
        #
        #	-- for each user returned from search..
        #	   $users[$user['id']] = array('userid' => $user['unique_id'],'name' => $user['name']);
        #
        #       return $users;
        #
        # -------------------------------------------------
        #
        # _ex_auth_list_chars($key)
        #
        # search and return a list characters that there are
        # user entrires for. If your installation consists of 2
        # users; bob and joe, this function would return an array
        # containing 'b' and 'j'. The characters retruned MUST be
        # lower case.
        #
        # $key is the field to search:
        #
        #	"name"		- search users' names
        #	"userid"	- search userid
        #
        #
        # If your installation is full of users that range from
        # a - z, you may just return an array containing a-z
        # using:
        #
        # return array_map("strtolower", $GLOBALS['_cal_alphabet']);
        #
        #########################################################
        
        if($ldap_opts['debug']) {
        
           echo("LDAP fields:<br>");
        
           foreach(array_keys($ldap_opts['attribs']) as $atr)
              echo("{$atr}: {$ldap_opts['attribs'][$atr]}<br>");
        
        
           echo("<br>");
        }
        ######################################
        #
        ### AUTHENTICATE A USER
        #
        #####################################
        function _ex_auth_login(&$u)
        {
        
           global $ldap_opts;
        
           $auth = ldap_connect($ldap_opts['host']);
        
           # SET VERSION
           #################
           if($ldap_opts['version'] == 3) ldap_set_option($auth, LDAP_OPT_PROTOCOL_VERSION, 3);
        
           $login = ldap_bind($auth, $u->dn, $u->pass);
        
           if(!$login) {
              return 0;
           }
        
           return 1;
        
        }
        
        
        #######################
        #
        ### SET A SINGLE USER
        #
        ########################
        
        function _ex_auth_get_user(&$u)
        {
        
           global $ldap_opts, $_cal_sql, $_cal_dbpref;;
        
           $ldap_opts['conn_id'] or _ex_auth_ldap_connect();
        
           # GET ALL VALUES FOR USER
           ###########################
           if($u->id) $filter = $ldap_opts['attribs']['uid'] ."=". $u->id;
           else if($u->userid) $filter = $ldap_opts['attribs']['userid'] ."=". $u->userid;
           else return 1;
        
        
           # AUTOGEN ID?
           ##################
           if($ldap_opts['attribs']['uid'] == '__autogen' && $u->id) {
        
              # GET USERID FROM THYME
              ########################
              list($uinf) = $_cal_sql->query("select id, userid from {$_cal_dbpref}Users where
                        id = {$u->id}");
        
               # alter filter
               ###############
               $filter = $ldap_opts['attribs']['userid'] ."=". $uinf['userid'];
        
           }
        
        
           #$filter = "({$filter}) (OU=Network_Managers | OU=Teachers)";
        
           if($ldap_opts['debug']) {
              echo("Using filter: {$filter}<br>");
           }
        
           if(is_array($ldap_opts['suffix'])) {
        
              for($i = 0; $i < count($ldap_opts['suffix']); $i++) {
        
                 if($ldap_opts['debug']) {
                    echo("Suffix: {$ldap_opts['suffix'][$i]}<br>");
                 }
        
                 $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'][$i], $filter, array_values($ldap_opts['attribs']), 0, 1);
        
                 if($sr && @ldap_count_entries($ldap_opts['conn_id'], $sr)) {
                    $i = count($ldap_opts['suffix']);
                 }
        
              }
        
           } else {
        
              $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'], $filter, array_values($ldap_opts['attribs']), 0, 1);
        
              if(!$sr) return 0;
           }
        
        
           if(!@ldap_count_entries($ldap_opts['conn_id'], $sr)) {
              $u->email = $u->name = $u->id = $u->userid = null;
              return 0;
           }
        
           $info = ldap_get_entries($ldap_opts['conn_id'], $sr);
        
           $u->email = $info[0][$ldap_opts['attribs']['email']][0];
           $u->name = $info[0][$ldap_opts['attribs']['name']][0];
           $u->userid = $info[0][$ldap_opts['attribs']['userid']][0];
           $u->dn = $info[0][$ldap_opts['attribs']['dn']];
        
           # AUTOGEN ID?
           ##################
           if($ldap_opts['attribs']['uid'] == '__autogen') {
        
              if($u->id) $info[0][$ldap_opts['attribs']['uid']][0] = $u->id;
              else $info[0][$ldap_opts['attribs']['uid']][0] = _ex_auth_mk_uid($u->userid);
        
           }
        
           $u->id = $info[0][$ldap_opts['attribs']['uid']][0];
        
           if($u->userid == $ldap_opts['admin']) $u->admin = 1;
        
           if($ldap_opts['debug'] && !$u->id) {
        
              echo("Failed to get user info.<br><br>");
              echo("<pre>passed:\n");
              print_r($u);
              echo("\nReturned from LDAP:\n");
              print_r($info[0]);
              echo("</pre>\n");
              exit;
        
           }
        
        
           return 1;
        
        
        }
        
        
        ###############################################
        #
        ### GET USERS BY ID
        #
        ###############################################
        
        function _ex_auth_get_users_by_id($ids, $associative = false)
        {
        
           global $ldap_opts;
        
           $ldap_opts['conn_id'] or _ex_auth_ldap_connect();
        
           if(!is_array($ids) || count($ids) < 1) return array();
        
           $filter = "(|(". $ldap_opts['attribs']['uid'] ."=". join(")(".$ldap_opts['attribs']['uid'] ."=", $ids) ."))";
        
           # AUTOGEN UIDS
           ###################
           if($ldap_opts['attribs']['uid'] == '__autogen') {
        
              # will need these later
              $userids = _ex_auth_get_userids($ids);
        
              $filter = "(|(". $ldap_opts['attribs']['userid'] ."=". join(")(".$ldap_opts['attribs']['userid'] ."=", $userids) ."))";
        
        
           }
        
           $users = $info = array();
        
           # GET ALL ENTRIES
           #####################
           if(is_array($ldap_opts['suffix'])) {
        
              for($i = 0; $i < count($ldap_opts['suffix']); $i++) {
        
                 $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'][$i],$filter,
                        array_values($ldap_opts['attribs']), 0, 0);
        
                 if(ldap_count_entries($ldap_opts['conn_id'], $sr)) {
                    $info = array_merge($info, ldap_get_entries($ldap_opts['conn_id'], $sr));
                 }
              }
        
           } else {
        
              $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'],$filter,
                        array_values($ldap_opts['attribs']), 0, 0);
        
              if(!ldap_count_entries($ldap_opts['conn_id'], $sr)) {
                 return array();
              }
        
              $info = ldap_get_entries($ldap_opts['conn_id'], $sr);
        
              if($info['count'] < 1) return array();
        
           }
        
           $info['count'] = count($info) - 1;
        
           if(is_array($userids)) $userids = array_flip($userids);
        
           # GRAB EACH USER
           ###################
           for ($i=0; $i<$info["count"]; $i++) {
        
              # POPULATE UIDS
              #################
              if($ldap_opts['attribs']['uid'] == '__autogen') {
        
                 $info[$i][$ldap_opts['attribs']['uid']][0] = $userids[$info[$i][$ldap_opts['attribs']['userid']][0]];
        
              }
        
              if($associative) {
        
                 $users[$info[$i][$ldap_opts['attribs']['uid']][0]] = array(
                        'name' => $info[$i][$ldap_opts['attribs']['name']][0],
                        'userid' => $info[$i][$ldap_opts['attribs']['userid']][0],
                        'email' => $info[$i][$ldap_opts['attribs']['email']][0],
                        'dn' => $info[0][$ldap_opts['attribs']['dn']]);
        
              } else {
                 $users[] = array('id' => $info[$i][$ldap_opts['attribs']['uid']][0],
                        'name' => $info[$i][$ldap_opts['attribs']['name']][0],
                        'userid' => $info[$i][$ldap_opts['attribs']['userid']][0],
                        'email' => $info[$i][$ldap_opts['attrirbs']['email']][0],
                        'dn' => $info[0][$ldap_opts['attribs']['dn']]);
              }
        
           }
        
          return $users;
        
        
        }
        
        
        ################################################
        #
        ### RETURN A LIST OF USERS THAT MATCH A QUERY
        #
        ################################################
        
        function _ex_auth_search_users($key = false, $search_str, $anchor_start = false, $associative = false)
        {
        
           global $ldap_opts;
        
           $ldap_opts['conn_id'] or _ex_auth_ldap_connect();
        
           # SEARCH OPTIONS
           #######################
           $search_str = ($anchor_start  ? "" : "*") . $search_str . ($search_str ? "*" : "");
        
           switch($key)
           {
              case 'name':
                 $filter = $ldap_opts['attribs']['name'] . "={$search_str}";
                 break;
        
              case 'userid':
                 $filter = $ldap_opts['attribs']['userid'] ."={$search_str}";
                 break;
        
              default:
                 $filter = "(|({$ldap_opts['attribs']['userid']}={$search_str})({$ldap_opts['attribs']['name']}={$search_str}))";
        
           }
        
           $info = array();
        
           # GET ALL ENTRIES
           #####################
           if(is_array($ldap_opts['suffix'])) {
        
              for($i = 0; $i < count($ldap_opts['suffix']); $i++) {
        
                 $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'][$i],$filter,
                        array_values($ldap_opts['attribs']), 0, 0);
        
                 if(ldap_count_entries($ldap_opts['conn_id'], $sr)) {
                    $info = array_merge($info, ldap_get_entries($ldap_opts['conn_id'], $sr));
                 }
        
              }
        
           } else {
        
              $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'],$filter,
                        array_values($ldap_opts['attribs']), 0, 0);
        
              if(!ldap_count_entries($ldap_opts['conn_id'], $sr)) {
                 return array();
              }
        
              $info = ldap_get_entries($ldap_opts['conn_id'], $sr);
        
              if($info['count'] < 1) return array();
        
           }
        
           $info['count'] = count($info) - 1;
        
           # AUTOGEN UIDS
           ################
           if($ldap_opts['attribs']['uid'] == '__autogen') {
        
              $userids = array();
        
              # we have to get each userid
              for($i = 0; $i < $info['count']; $i++)
                $userids[] = $info[$i][$ldap_opts['attribs']['userid']][0];
        
              # will need these later
              $userids = _ex_auth_mk_uids($userids);
        
           }
        
           # GRAB EACH USER
           ###################
           for ($i=0; $i<$info["count"]; $i++) {
        
              if($ldap_opts['attribs']['uid'] == '__autogen') {
                 $info[$i][$ldap_opts['attribs']['uid']][0] = $userids[$info[$i][$ldap_opts['attribs']['userid']][0]];
        
              }
        
              if($associative) {
                 $users[$info[$i][$ldap_opts['attribs']['uid']][0]] = array('name' => $info[$i][$ldap_opts['attribs']['name']][0],
                        'userid' => $info[$i][$ldap_opts['attribs']['userid']][0],
                        'email' => $info[$i][$ldap_opts['attribs']['email']][0],
                        'dn' => $info[$i][$ldap_opts['attribs']['dn']]);
        
              } else {
                 $users[] = array('id' => $info[$i][$ldap_opts['attribs']['uid']][0],
                        'name' => $info[$i][$ldap_opts['attribs']['name']][0],
                        'userid' => $info[$i][$ldap_opts['attribs']['userid']][0],
                        'email' => $info[$i][$ldap_opts['attribs']['email']][0],
                        'dn' => $info[$i][$ldap_opts['attribs']['dn']]);
              }
        
           }
        
          return $users;
        
        
        }
        
        
        #############################################################
        #
        ### RETURN A LIST OF CHARACTERS THERE ENTRIES FOR
        #
        ############################################################
        
        # if you have a large LDAP database and this is expensive,
        # you may simply return an array consisting of a-z as keys
        # return array( 'a' => 1, 'b' => 1, 'c' => 1, 'd' => 1 ... etc..)
        
        function _ex_auth_list_chars($key)
        {
        
           #return array_flip(array_map("strtolower", $GLOBALS['_cal_alphabet']));
        
           global $ldap_opts;
        
           $ldap_opts['conn_id'] or _ex_auth_ldap_connect();
        
           $chars = array();
        
           # SELECT USERNAME OR COMMON NAME
           $key = ($key == 'name' ? $ldap_opts['attribs']['name'] : $ldap_opts['attribs']['userid']);
        
           # GET ENTRIES FOR EACH CHAR
           #############################
           foreach($GLOBALS['_cal_alphabet'] as $c) {
        
              $info = array();
        
              if(is_array($ldap_opts['suffix'])) {
        
                 for($i = 0; $i < count($ldap_opts['suffix']); $i++) {
        
                    $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'][$i],
                        "($key=$c*)", array($key), 0, 0);
        
                    if(ldap_count_entries($ldap_opts['conn_id'], $sr)) {
        
                       $info = ldap_get_entries($ldap_opts['conn_id'], $sr);
        
                       # stop when we get 1
                       $i = count($ldap_opts['suffix']);
                    }
                 }
        
              } else {
        
                 $sr = ldap_search($ldap_opts['conn_id'], $ldap_opts['suffix'],
                        "($key=$c*)", array($key), 0, 0);
        
                 if(!ldap_count_entries($ldap_opts['conn_id'], $sr)) {
                    $info = ldap_get_entries($ldap_opts['conn_id'], $sr);
                 }
              }
        
              if(!(count($info) -1)) continue;
        
              $chars[substr(strtolower($info[0][$key][0]),0,1)] = 1;
           }
           return $chars;
        
        }
        
        
        #################################
        #
        ### INTERNAL CONNECT FUNCTION
        #
        ################################
        function _ex_auth_ldap_connect()
        {
        
           global $ldap_opts;
        
           $ldap_opts['conn_id'] = @ldap_connect($ldap_opts['host']);
        
           # SET VERSION
           #################
           if($ldap_opts['version'] == 3)
              @ldap_set_option($ldap_opts['conn_id'], LDAP_OPT_PROTOCOL_VERSION, 3);
        
           $auth = @ldap_bind($ldap_opts['conn_id'], $ldap_opts['anon_dn'], $ldap_opts['anon_pw']);
        
           if(!$auth) {
              return 0;
           }
        
        
        }
        
        #####################################
        #
        ### UID GENERATION
        #
        #####################################
        
        function _ex_auth_mk_uids($usernames) {
        
           global $_cal_sql, $_cal_dbpref;
        
           $uinf = $_cal_sql->query("select id, userid from {$_cal_dbpref}Users
                where userid in ('". join("','",
                array_map(array('_cal_sql','escape_string'),$usernames)) ."')", true);
        
           # userids in $usernames, but not in $uinf
           ##########################################
           $create = array_diff($usernames, $uinf);
        
           if(!count($create)) return array_flip($uinf);
        
           # there are userids to create
           ###############################
           foreach($create as $u) {
        
              $_cal_sql->query("insert into {$_cal_dbpref}Users (userid) values ('".
                        $_cal_sql->escape_string($u) ."')");
        
           }
        
           # now get them ...
           ##################
           $new = $_cal_sql->query("select userid, id from {$_cal_dbpref}Users
                where userid in ('". join("','",
                array_map(array('_cal_sql','escape_string'),$create)) ."')", true);
        
           return array_merge(array_flip($uinf), $new);
        
        
        }
        
        
        function _ex_auth_mk_uid($username) {
        
           global $_cal_sql, $_cal_dbpref;
        
           list($uinf) = $_cal_sql->query("select userid, id from {$_cal_dbpref}Users
                where userid = '". $_cal_sql->escape_string(strtolower($username)) ."'");
        
           if($uinf['id']) return $uinf['id'];
        
           # does not exist, create it
           #############################
           $uid = $_cal_sql->query("insert into {$_cal_dbpref}Users (userid) values ('".
                        $_cal_sql->escape_string(strtolower($username)) ."')");
        
           # DBs that return last insert id
           ###################################
           if(_CAL_SQL_DRIVER_ == 'mysql' || _CAL_SQL_DRIVER_ == 'mssql') return $uid;
        
           list($uinf) = $_cal_sql->query("select userid, id from {$_cal_dbpref}Users
                where userid = '". $_cal_sql->escape_string($username) ."'");
        
           if($uinf['id']) return $uinf['id'];
        
        
        }
        
        function _ex_auth_get_userids($ids) {
        
           global $_cal_sql, $_cal_dbpref;
        
           return $_cal_sql->query("select id, userid from {$_cal_dbpref}Users where id in (".
                join(",", $ids) .")", true);
        
        }
        
        ?>

          Personal Blog: www.deanernst.com
          Family Blog: www.ernstfamilyonline.com