From ea1f9accd18f549b559a849e9a2789e5db8f9eff Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Aug 2020 01:28:31 +0700 Subject: [PATCH 1/4] modified staff authorize --- auth-ldap/authentication.php | 223 +++++++++++++++++++++++++---------- auth-ldap/config.php | 13 ++ auth-ldap/plugin.php | 4 +- 3 files changed, 178 insertions(+), 62 deletions(-) diff --git a/auth-ldap/authentication.php b/auth-ldap/authentication.php index 48b27a1..a9954d9 100644 --- a/auth-ldap/authentication.php +++ b/auth-ldap/authentication.php @@ -25,6 +25,8 @@ class LDAPAuthentication { * http://www.kouti.com/tables/userattributes.htm (AD) * https://fsuid.fsu.edu/admin/lib/WinADLDAPAttributes.html (AD) */ + + static $schemas = array( 'msad' => array( 'user' => array( @@ -76,6 +78,14 @@ function getConfig() { return $this->config; } + function login($backend,$client){ + //fake login function, need to bypass autreg request + return true; + } + function supportsInteractiveAuthentication(){ + //set false to skip user create page and pass to automatic registration + return false; + } function autodiscover($domain, $dns=array()) { require_once(PEAR_DIR.'Net/DNS2.php'); // TODO: Lookup DNS server from hosts file if not set @@ -108,7 +118,7 @@ function autodiscover($domain, $dns=array()) { function getServers() { if (!($servers = $this->getConfig()->get('servers')) - || !($servers = preg_split('/\s+/', $servers))) { + || !($servers = preg_split('/\s+/', $servers))) { if ($domain = $this->getConfig()->get('domain')) { $dns = preg_split('/,?\s+/', $this->getConfig()->get('dns')); return $this->autodiscover($domain, array_filter($dns)); @@ -126,6 +136,7 @@ function getServers() { } function getConnection($force_reconnect=false) { + file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); static $connection = null; if ($connection && !$force_reconnect) @@ -147,6 +158,7 @@ function getConnection($force_reconnect=false) { $defaults['options'] += array( 'LDAP_OPT_PROTOCOL_VERSION' => 3, 'LDAP_OPT_REFERRALS' => 0, + 'LDAP_OPT_DEBUG_LEVEL'=>9 ); // Active Directory servers almost always use self-signed certs putenv('LDAPTLS_REQCERT=never'); @@ -167,6 +179,7 @@ function getConnection($force_reconnect=false) { * Binds to the directory under the search-user credentials configured */ function _bind($connection) { + file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); if ($dn = $this->getConfig()->get('bind_dn')) { $pw = Crypto::decrypt($this->getConfig()->get('bind_pw'), SECRET_SALT, $this->getConfig()->getNamespace()); @@ -182,6 +195,7 @@ function _bind($connection) { } function authenticate($username, $password=null) { + file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); // Thanks, http://stackoverflow.com/a/764651 // Binding with an empty password implies an anonymous bind which // will likely be successful and incorrect @@ -205,17 +219,17 @@ function authenticate($username, $password=null) { $dn = preg_replace_callback(':\{([^}]+)\}:', function($match) use ($username, $domain, $config) { switch ($match[1]) { - case 'username': - return $username; - case 'domain': - return $domain; - case 'search_base': - if (!$config->get('search_base')) - return 'dc=' . implode(',dc=', - explode('.', $config->get('domain'))); + case 'username': + return $username; + case 'domain': + return $domain; + case 'search_base': + if (!$config->get('search_base')) + return 'dc=' . implode(',dc=', + explode('.', $config->get('domain'))); // Fall through to default - default: - return $config->get($match[1]); + default: + return $config->get($match[1]); } }, $schema['dn'] @@ -257,14 +271,16 @@ function($match) use ($username, $domain, $config) { * server's advertised DSE information */ function getSchema($connection) { + file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); $schema = $this->getConfig()->get('schema'); if (!$schema || $schema == 'auto') { $dse = $connection->rootDse(array('supportedCapabilities')); // Microsoft Active Directory // http://www.alvestrand.no/objectid/1.2.840.113556.1.4.800.html if (($caps = $dse->getValue('supportedCapabilities')) - && in_array('1.2.840.113556.1.4.800', $caps)) { + && in_array('1.2.840.113556.1.4.800', $caps)) { $this->getConfig()->set('schema', 'msad'); + return 'msad'; } } @@ -276,9 +292,12 @@ function getSchema($connection) { } function lookup($lookup_dn, $bind=true) { + $c = $this->getConnection(); - if ($bind && !$this->_bind($c)) + if ($bind && !$this->_bind($c)) { + return null; + } $schema = static::$schemas[$this->getSchema($c)]; $schema = $schema['user']; @@ -292,14 +311,15 @@ function lookup($lookup_dn, $bind=true) { ))) ); $r = $c->search($lookup_dn, '(objectClass=*)', $opts); - if (PEAR::isError($r) || !$r->count()) - return null; + if (PEAR::isError($r) || !$r->count()){ + return null; + } return $this->_getUserInfoArray($r->current(), $schema); } function search($query) { - $c = $this->getConnection(); + $c = $this->getConnection(); // TODO: Include bind information $users = array(); if (!$this->_bind($c)) @@ -326,14 +346,14 @@ function search($query) { } function getSearchBase() { - $base = $this->getConfig()->get('search_base'); + $base = $this->getConfig()->get('search_base'); if (!$base && ($domain=$this->getConfig()->get('domain'))) $base = 'dc='.str_replace('.', ',dc=', $domain); return $base; } function _getValue($entry, $names) { - foreach (array_filter(splat($names)) as $n) + foreach (array_filter(splat($names)) as $n) // Support multi-value attributes foreach (splat($entry->getValue($n, 'all')) as $val) // Return the first non-bool-false value of the entries @@ -344,7 +364,7 @@ function _getValue($entry, $names) { function _getUserInfoArray($e, $schema) { // Detect first and last name if only full name is given if (!($first = $this->_getValue($e, $schema['first'])) - || !($last = $this->_getValue($e, $schema['last']))) { + || !($last = $this->_getValue($e, $schema['last']))) { $name = new PersonsName($this->_getValue($e, $schema['full'])); $first = $name->getFirst(); $last = $name->getLast(); @@ -366,49 +386,130 @@ function _getUserInfoArray($e, $schema) { function lookupAndSync($username, $dn) { switch ($this->type) { - case 'staff': - if (($user = StaffSession::lookup($username)) && $user->getId()) { - if (!$user instanceof StaffSession) { - // osTicket <= v1.9.7 or so - $user = new StaffSession($user->getId()); + case 'staff': + /* + * DEPRECATED + if (($user = StaffSession::lookup($username)) && $user->getId()) { + if (!$user instanceof StaffSession) { + + // osTicket <= v1.9.7 or so + $user = new StaffSession($user->getId()); + } + return $user; + } + */ + + $c = $this->getConnection(); + if ('msad' == $this->getSchema($c) && stripos($dn, ',dc=') === false) { + // The user login DN will be user@domain. We need an LDAP DN + // -- fetch the real DN which looks like `CN=blah,DC=` + // NOTE: Already bound, so no need to bind again + list($samid) = explode('@', $dn); + $r = $c->search( + $this->getSearchBase(), + //to check what user currently in staff group, we should check it by memberOf attribute + sprintf('(&(|(userPrincipalName=%s)(samAccountName=%s))(memberOf='.$this->getConfig()->get($this->type.'_dn').'))', $dn, $samid), + $opts); + if (!PEAR::isError($r) && $r->count()) { + $dn = $r->current()->dn(); + } + if (!($info = $this->lookup($dn, false))) { + //if search return nothing - put empty function response (abstract function will throw access error) + return; + } + //if all ok, modify response dict by adding copies of some fields (because response have different keys from which needed in next) + $info += ["dept_id"=>1,"role_id"=>1,"firstname"=>$info["first"],"lastname"=>$info["last"]]; + $acct = false; + $isUser = false; + //check if user already have Client type account - if true, we'll skip account creation step + $isUser = ClientAccount::lookupByUsername($username); + foreach (array($username, $info['username'], $info['email']) as $name) { + //try to find Staff type of account in osticket DB + if ($name && ($acct = Staff::lookup($name))) + break; + } + //if Client not found - we'll make it automatically (by built-in function) + if (!$isUser){ + //make new request class instance + $tmp = new ClientCreateRequest($this, $username, $info); + //make autoreg, in top of abstract class LDAP auth we made some fake functions to pass errors + $tmp->attemptAutoRegister(); + //take ClientSession from created user (need to add user into staff) + $acct = ClientAccount::lookupByUsername($username); + + } + + if (!$acct) { + //make request to rank up Client to Staff + $staff = Staff::create(); + // $errors is global var, so we need just send this link (no need to create ourself var) + if ($staff->update($info,$errors)) { + $type = array('type' => 'created'); + Signal::send('object.created', $staff, $type); + //trying to take new Staff user + if (($user = StaffSession::lookup($username)) && $user->getId()) { + if (!$user instanceof StaffSession) { + // osTicket <= v1.9.7 or so + $user = new StaffSession($user->getId()); + } + return $user; + } + } + else{ + //we got some problems on creating user, so we'll return empty response + return ; + } + + } + //if all found previously, just return Staff session + if (($user = StaffSession::lookup($username)) && $user->getId()) { + if (!$user instanceof StaffSession) { + // osTicket <= v1.9.7 or so + $user = new StaffSession($user->getId()); + } + return $user; + } + else{ + //no staff user found + return; + } + } + break; + case 'client': + file_put_contents('/var/www/html/output.txt',"Work as CLIENT"." | ",FILE_APPEND); + $c = $this->getConnection(); + if ('msad' == $this->getSchema($c) && stripos($dn, ',dc=') === false) { + // The user login DN will be user@domain. We need an LDAP DN + // -- fetch the real DN which looks like `CN=blah,DC=` + // NOTE: Already bound, so no need to bind again + list($samid) = explode('@', $dn); + $r = $c->search( + $this->getSearchBase(), + sprintf('(|(userPrincipalName=%s)(samAccountName=%s))', $dn, $samid), + $opts); + if (!PEAR::isError($r) && $r->count()) + $dn = $r->current()->dn(); } - return $user; - } - break; - case 'client': - $c = $this->getConnection(); - if ('msad' == $this->getSchema($c) && stripos($dn, ',dc=') === false) { - // The user login DN will be user@domain. We need an LDAP DN - // -- fetch the real DN which looks like `CN=blah,DC=` - // NOTE: Already bound, so no need to bind again - list($samid) = explode('@', $dn); - $r = $c->search( - $this->getSearchBase(), - sprintf('(|(userPrincipalName=%s)(samAccountName=%s))', $dn, $samid), - $opts); - if (!PEAR::isError($r) && $r->count()) - $dn = $r->current()->dn(); - } - - // Lookup all the information on the user. Try to get the email - // addresss as well as the username when looking up the user - // locally. - if (!($info = $this->lookup($dn, false))) - return; - - $acct = false; - foreach (array($username, $info['username'], $info['email']) as $name) { - if ($name && ($acct = ClientAccount::lookupByUsername($name))) - break; - } - if (!$acct) - return new ClientCreateRequest($this, $username, $info); - if (($client = new ClientSession(new EndUser($acct->getUser()))) + // Lookup all the information on the user. Try to get the email + // addresss as well as the username when looking up the user + // locally. + file_put_contents('/var/www/html/output.txt',"\nLOOKUP DN: ".$dn,FILE_APPEND); + if (!($info = $this->lookup($dn, false))) + return; + file_put_contents('/var/www/html/output.txt',"\nLDAP RESULT: ".$info['username'],FILE_APPEND); + $acct = false; + foreach (array($username, $info['username'], $info['email']) as $name) { + if ($name && ($acct = ClientAccount::lookupByUsername($name))) + break; + } + if (!$acct) + return new ClientCreateRequest($this, $username, $info); + if (($client = new ClientSession(new EndUser($acct->getUser()))) && !$client->getId()) - return; + return; - return $client; + return $client; } // TODO: Auto-create users, etc. @@ -416,7 +517,7 @@ function lookupAndSync($username, $dn) { } class StaffLDAPAuthentication extends StaffAuthenticationBackend - implements AuthDirectorySearch { + implements AuthDirectorySearch { static $name = /* trans */ "Active Directory or LDAP"; static $id = "ldap"; @@ -424,6 +525,7 @@ class StaffLDAPAuthentication extends StaffAuthenticationBackend function __construct($config) { $this->_ldap = new LDAPAuthentication($config); $this->config = $config; + } function authenticate($username, $password=false, $errors=array()) { @@ -462,6 +564,7 @@ class ClientLDAPAuthentication extends UserAuthenticationBackend { static $name = /* trans */ "Active Directory or LDAP"; static $id = "ldap.client"; + function __construct($config) { $this->_ldap = new LDAPAuthentication($config, 'client'); $this->config = $config; @@ -495,4 +598,4 @@ function bootstrap() { if ($config->get('auth-client')) UserAuthenticationBackend::register(new ClientLDAPAuthentication($config)); } -} +} \ No newline at end of file diff --git a/auth-ldap/config.php b/auth-ldap/config.php index 9ccfbf0..bb51326 100644 --- a/auth-ldap/config.php +++ b/auth-ldap/config.php @@ -96,6 +96,19 @@ function($self, $val) use ($__) { 'hint' => $__('Used when searching for users'), 'configuration' => array('size'=>70, 'length'=>120), )), + //added fields + 'staff_dn' => new TextboxField(array( + 'label'=>$__('Staff DN'), + 'hint'=> $__('Full LDAP Staff Context path'), + 'configuration' => array('size'=>70, 'length'=>200) + )), + //added fields + 'user_dn' => new TextboxField(array( + 'label'=>$__('User DN'), + 'hint'=> $__('Full LDAP User Context path'), + 'default'=> "CN=Users", + 'configuration' => array('size'=>70, 'length'=>200) + )), 'schema' => new ChoiceField(array( 'label' => $__('LDAP Schema'), 'hint' => $__('Layout of the user data in the LDAP server'), diff --git a/auth-ldap/plugin.php b/auth-ldap/plugin.php index 06a66a1..c19741d 100644 --- a/auth-ldap/plugin.php +++ b/auth-ldap/plugin.php @@ -2,9 +2,9 @@ set_include_path(get_include_path().PATH_SEPARATOR.dirname(__file__).'/include'); return array( 'id' => 'auth:ldap', # notrans - 'version' => '0.6.2', + 'version' => '0.6.2.1', 'name' => /* trans */ 'LDAP Authentication and Lookup', - 'author' => 'Jared Hancock', + 'author' => 'Jared Hancock & modified by kallibr44', 'description' => /* trans */ 'Provides a configurable authentication backend which works against Microsoft Active Directory and OpenLdap servers', From 7566a01e34cf63e7f3457e7a1c76a2cec7d614bf Mon Sep 17 00:00:00 2001 From: kallibr44 Date: Mon, 3 Aug 2020 01:34:00 +0700 Subject: [PATCH 2/4] Update authentication.php --- auth-ldap/authentication.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/auth-ldap/authentication.php b/auth-ldap/authentication.php index a9954d9..c2d7799 100644 --- a/auth-ldap/authentication.php +++ b/auth-ldap/authentication.php @@ -136,7 +136,6 @@ function getServers() { } function getConnection($force_reconnect=false) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); static $connection = null; if ($connection && !$force_reconnect) @@ -179,7 +178,6 @@ function getConnection($force_reconnect=false) { * Binds to the directory under the search-user credentials configured */ function _bind($connection) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); if ($dn = $this->getConfig()->get('bind_dn')) { $pw = Crypto::decrypt($this->getConfig()->get('bind_pw'), SECRET_SALT, $this->getConfig()->getNamespace()); @@ -195,7 +193,6 @@ function _bind($connection) { } function authenticate($username, $password=null) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); // Thanks, http://stackoverflow.com/a/764651 // Binding with an empty password implies an anonymous bind which // will likely be successful and incorrect @@ -271,7 +268,6 @@ function($match) use ($username, $domain, $config) { * server's advertised DSE information */ function getSchema($connection) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); $schema = $this->getConfig()->get('schema'); if (!$schema || $schema == 'auto') { $dse = $connection->rootDse(array('supportedCapabilities')); @@ -476,7 +472,6 @@ function lookupAndSync($username, $dn) { } break; case 'client': - file_put_contents('/var/www/html/output.txt',"Work as CLIENT"." | ",FILE_APPEND); $c = $this->getConnection(); if ('msad' == $this->getSchema($c) && stripos($dn, ',dc=') === false) { // The user login DN will be user@domain. We need an LDAP DN @@ -494,10 +489,8 @@ function lookupAndSync($username, $dn) { // Lookup all the information on the user. Try to get the email // addresss as well as the username when looking up the user // locally. - file_put_contents('/var/www/html/output.txt',"\nLOOKUP DN: ".$dn,FILE_APPEND); if (!($info = $this->lookup($dn, false))) return; - file_put_contents('/var/www/html/output.txt',"\nLDAP RESULT: ".$info['username'],FILE_APPEND); $acct = false; foreach (array($username, $info['username'], $info['email']) as $name) { if ($name && ($acct = ClientAccount::lookupByUsername($name))) @@ -598,4 +591,4 @@ function bootstrap() { if ($config->get('auth-client')) UserAuthenticationBackend::register(new ClientLDAPAuthentication($config)); } -} \ No newline at end of file +} From 991c804c3f361958d154df79827487a2cae41465 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Aug 2020 01:36:33 +0700 Subject: [PATCH 3/4] modified staff authorize --- auth-ldap/plugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth-ldap/plugin.php b/auth-ldap/plugin.php index c19741d..06a66a1 100644 --- a/auth-ldap/plugin.php +++ b/auth-ldap/plugin.php @@ -2,9 +2,9 @@ set_include_path(get_include_path().PATH_SEPARATOR.dirname(__file__).'/include'); return array( 'id' => 'auth:ldap', # notrans - 'version' => '0.6.2.1', + 'version' => '0.6.2', 'name' => /* trans */ 'LDAP Authentication and Lookup', - 'author' => 'Jared Hancock & modified by kallibr44', + 'author' => 'Jared Hancock', 'description' => /* trans */ 'Provides a configurable authentication backend which works against Microsoft Active Directory and OpenLdap servers', From 73c14ebd69ebab9eb8c6231beda075b8eadcb73a Mon Sep 17 00:00:00 2001 From: kallibr44 Date: Mon, 3 Aug 2020 01:38:29 +0700 Subject: [PATCH 4/4] modified auth --- auth-ldap/authentication.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/auth-ldap/authentication.php b/auth-ldap/authentication.php index a9954d9..e51fe9d 100644 --- a/auth-ldap/authentication.php +++ b/auth-ldap/authentication.php @@ -136,7 +136,6 @@ function getServers() { } function getConnection($force_reconnect=false) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); static $connection = null; if ($connection && !$force_reconnect) @@ -179,7 +178,6 @@ function getConnection($force_reconnect=false) { * Binds to the directory under the search-user credentials configured */ function _bind($connection) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); if ($dn = $this->getConfig()->get('bind_dn')) { $pw = Crypto::decrypt($this->getConfig()->get('bind_pw'), SECRET_SALT, $this->getConfig()->getNamespace()); @@ -195,7 +193,6 @@ function _bind($connection) { } function authenticate($username, $password=null) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); // Thanks, http://stackoverflow.com/a/764651 // Binding with an empty password implies an anonymous bind which // will likely be successful and incorrect @@ -271,7 +268,6 @@ function($match) use ($username, $domain, $config) { * server's advertised DSE information */ function getSchema($connection) { - file_put_contents('/var/www/html/output.txt',"\n".__FUNCTION__." | ",FILE_APPEND); $schema = $this->getConfig()->get('schema'); if (!$schema || $schema == 'auto') { $dse = $connection->rootDse(array('supportedCapabilities')); @@ -398,7 +394,7 @@ function lookupAndSync($username, $dn) { return $user; } */ - + //lower added by kallibr44 $c = $this->getConnection(); if ('msad' == $this->getSchema($c) && stripos($dn, ',dc=') === false) { // The user login DN will be user@domain. We need an LDAP DN @@ -476,7 +472,6 @@ function lookupAndSync($username, $dn) { } break; case 'client': - file_put_contents('/var/www/html/output.txt',"Work as CLIENT"." | ",FILE_APPEND); $c = $this->getConnection(); if ('msad' == $this->getSchema($c) && stripos($dn, ',dc=') === false) { // The user login DN will be user@domain. We need an LDAP DN @@ -494,10 +489,8 @@ function lookupAndSync($username, $dn) { // Lookup all the information on the user. Try to get the email // addresss as well as the username when looking up the user // locally. - file_put_contents('/var/www/html/output.txt',"\nLOOKUP DN: ".$dn,FILE_APPEND); if (!($info = $this->lookup($dn, false))) return; - file_put_contents('/var/www/html/output.txt',"\nLDAP RESULT: ".$info['username'],FILE_APPEND); $acct = false; foreach (array($username, $info['username'], $info['email']) as $name) { if ($name && ($acct = ClientAccount::lookupByUsername($name)))