LDAP changes
This commit is contained in:
+69
-12
@@ -22,10 +22,17 @@
|
||||
|
||||
namespace Espo\Core\Utils\Authentication;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error,
|
||||
Espo\Core\Utils\Config,
|
||||
Espo\Core\ORM\EntityManager,
|
||||
Espo\Core\Utils\Auth;
|
||||
|
||||
class Ldap extends Base
|
||||
class LDAP extends Base
|
||||
{
|
||||
private $utils;
|
||||
|
||||
private $zendLdap;
|
||||
|
||||
/**
|
||||
* Espo => LDAP name
|
||||
*
|
||||
@@ -40,6 +47,24 @@ class Ldap extends Base
|
||||
'phoneNumber' => 'telephonenumber',
|
||||
);
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager, Auth $auth)
|
||||
{
|
||||
parent::__construct($config, $entityManager, $auth);
|
||||
|
||||
$this->zendLdap = new LDAP\LDAP();
|
||||
$this->utils = new LDAP\Utils($config);
|
||||
}
|
||||
|
||||
protected function getZendLdap()
|
||||
{
|
||||
return $this->zendLdap;
|
||||
}
|
||||
|
||||
protected function getUtils()
|
||||
{
|
||||
return $this->utils;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LDAP login
|
||||
@@ -55,16 +80,28 @@ class Ldap extends Base
|
||||
return $this->loginByToken($username, $authToken);
|
||||
}
|
||||
|
||||
$options = $this->getConfig()->get('ldap');
|
||||
$ldap = new Ldap\Ldap($options);
|
||||
$options = $this->getUtils()->getZendOptions();
|
||||
|
||||
$ldap = $this->getZendLdap();
|
||||
$ldap = $ldap->setOptions($options);
|
||||
|
||||
try {
|
||||
$ldap->bind($username, $password);
|
||||
$ldapUsername = $ldap->getCanonicalAccountName($username);
|
||||
|
||||
$dn = $ldap->getDn($username);
|
||||
|
||||
$loginFilter = $this->getUtils()->getOption('userLoginFilter');
|
||||
$userData = $ldap->searchByLoginFilter($loginFilter, $dn, 3);
|
||||
|
||||
} catch (\Zend\Ldap\Exception\LdapException $zle) {
|
||||
$GLOBALS['log']->info('LDAP Authentication: ' . $zle->getMessage());
|
||||
return null;
|
||||
|
||||
$admin = $this->adminLogin($username, $password);
|
||||
if (!isset($admin)) {
|
||||
$GLOBALS['log']->info('LDAP Authentication: ' . $zle->getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
$GLOBALS['log']->info('LDAP Authentication: Administrator login by username ['.$username.']');
|
||||
}
|
||||
|
||||
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
|
||||
@@ -73,13 +110,9 @@ class Ldap extends Base
|
||||
),
|
||||
));
|
||||
|
||||
$isCreateUser = $ldap->getEspoOption('createEspoUser');
|
||||
$isCreateUser = $this->getUtils()->getOption('createEspoUser');
|
||||
if (!isset($user) && $isCreateUser) {
|
||||
|
||||
$this->getAuth()->useNoAuth(); /** Required to fix Acl "isFetched()" error */
|
||||
|
||||
$dn = $ldap->getDn($username);
|
||||
$userData = $ldap->getEntry($dn);
|
||||
$user = $this->createUser($userData);
|
||||
}
|
||||
|
||||
@@ -117,6 +150,28 @@ class Ldap extends Base
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login user with administrator rights
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return \Espo\Entities\User | null
|
||||
*/
|
||||
protected function adminLogin($username, $password)
|
||||
{
|
||||
$hash = md5($password);
|
||||
|
||||
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
|
||||
'whereClause' => array(
|
||||
'userName' => $username,
|
||||
'password' => $hash,
|
||||
'isAdmin' => 1
|
||||
),
|
||||
));
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Espo user with data gets from LDAP server
|
||||
*
|
||||
@@ -140,5 +195,7 @@ class Ldap extends Base
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+61
-58
@@ -20,68 +20,12 @@
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Authentication\Ldap;
|
||||
namespace Espo\Core\Utils\Authentication\LDAP;
|
||||
|
||||
class Ldap extends \Zend\Ldap\Ldap
|
||||
class LDAP extends \Zend\Ldap\Ldap
|
||||
{
|
||||
protected $usernameAttribute = 'cn';
|
||||
|
||||
protected $espoOptions = array();
|
||||
|
||||
/**
|
||||
* Permitted Espo Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $permittedEspoOptions = array(
|
||||
/** Default Options (Zend LDAP):
|
||||
'host' => null,
|
||||
'port' => 0,
|
||||
'useSsl' => false,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'bindRequiresDn' => false,
|
||||
'baseDn' => null,
|
||||
'accountCanonicalForm' => null,
|
||||
'accountDomainName' => null,
|
||||
'accountDomainNameShort' => null,
|
||||
'accountFilterFormat' => null,
|
||||
'allowEmptyPassword' => false,
|
||||
'useStartTls' => false,
|
||||
'optReferrals' => false,
|
||||
'tryUsernameSplit' => true,
|
||||
'networkTimeout' => null,*/
|
||||
|
||||
/** Espo Options */
|
||||
'createEspoUser' => false,
|
||||
);
|
||||
|
||||
|
||||
public function setOptions($options)
|
||||
{
|
||||
$espoOptionList = array_keys($this->permittedEspoOptions);
|
||||
|
||||
$this->espoOptions = array_intersect_key($options, array_flip($espoOptionList));
|
||||
$options = array_diff_key($options, array_flip($espoOptionList));
|
||||
|
||||
return parent::setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Espo Options
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $returns
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEspoOption($name, $returns = null)
|
||||
{
|
||||
if (isset($this->espoOptions[$name])) {
|
||||
return $this->espoOptions[$name];
|
||||
}
|
||||
|
||||
return $returns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DN depends on options, ex. "cn=test,ou=People,dc=maxcrc,dc=com"
|
||||
@@ -117,4 +61,63 @@ class Ldap extends \Zend\Ldap\Ldap
|
||||
|
||||
return parent::getAccountDn($acctname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search a user using userLoginFilter
|
||||
*
|
||||
* @param string $filter
|
||||
* @param string $basedn
|
||||
* @param int $scope
|
||||
* @param array $attributes
|
||||
* @return array
|
||||
*/
|
||||
public function searchByLoginFilter($filter, $basedn = null, $scope = self::SEARCH_SCOPE_SUB, array $attributes = array())
|
||||
{
|
||||
$filter = $this->getLoginFilter($filter);
|
||||
|
||||
$result = $this->search($filter, $basedn, $scope, $attributes);
|
||||
|
||||
if ($result->count() > 0) {
|
||||
return $result->getFirst();
|
||||
}
|
||||
|
||||
throw new \Zend\Ldap\Exception\LdapException($this, 'searching: ' . $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get login filter in LDAP format
|
||||
*
|
||||
* @param string $filter
|
||||
* @return string
|
||||
*/
|
||||
protected function getLoginFilter($filter)
|
||||
{
|
||||
$baseFilter = '(objectClass=*)';
|
||||
|
||||
if (!empty($filter)) {
|
||||
$baseFilter = '(&' . $baseFilter . $this->convertToFilterFormat($filter). ')';
|
||||
}
|
||||
|
||||
return $baseFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and convert filter item in LDAP format
|
||||
*
|
||||
* @param string $filter [description]
|
||||
* @return string
|
||||
*/
|
||||
protected function convertToFilterFormat($filter)
|
||||
{
|
||||
$filter = trim($filter);
|
||||
if (substr($filter, 0, 1) != '(') {
|
||||
$filter = '(' . $filter;
|
||||
}
|
||||
|
||||
if (substr($filter, -1) != ')') {
|
||||
$filter = $filter . ')';
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Authentication\LDAP;
|
||||
|
||||
use \Espo\Core\Utils\Config;
|
||||
|
||||
class Utils
|
||||
{
|
||||
private $config;
|
||||
|
||||
protected $options = null;
|
||||
|
||||
/**
|
||||
* Association between LDAP and Espo fields
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldMap = array(
|
||||
'host' => 'ldapHost',
|
||||
'port' => 'ldapPort',
|
||||
'useSsl' => 'ldapSecurity',
|
||||
'useStartTls' => 'ldapSecurity',
|
||||
'username' => 'ldapUsername',
|
||||
'password' => 'ldapPassword',
|
||||
'bindRequiresDn' => 'ldapBindRequiresDn',
|
||||
'baseDn' => 'ldapBaseDn',
|
||||
'accountCanonicalForm' => 'ldapAccountCanonicalForm',
|
||||
'accountDomainName' => 'ldapAccountDomainName',
|
||||
'accountDomainNameShort' => 'ldapAccountDomainNameShort',
|
||||
'accountFilterFormat' => 'ldapAccountFilterFormat',
|
||||
'optReferrals' => 'ldapOptReferrals',
|
||||
'tryUsernameSplit' => 'ldapTryUsernameSplit',
|
||||
'networkTimeout' => 'ldapNetworkTimeout',
|
||||
'createEspoUser' => 'ldapCreateEspoUser',
|
||||
'userLoginFilter' => 'ldapUserLoginFilter',
|
||||
);
|
||||
|
||||
/**
|
||||
* Permitted Espo Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $permittedEspoOptions = array(
|
||||
'createEspoUser' => false,
|
||||
'userLoginFilter' => null,
|
||||
);
|
||||
|
||||
/**
|
||||
* accountCanonicalForm Map between Espo and Zend value
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accountCanonicalFormMap = array(
|
||||
'Dn' => 1,
|
||||
'Username' => 2,
|
||||
'Backslash' => 3,
|
||||
'Principal' => 4,
|
||||
);
|
||||
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Options from espo config according to $this->fieldMap
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
if (isset($this->options)) {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach ($this->fieldMap as $ldapName => $espoName) {
|
||||
|
||||
$option = $this->getConfig()->get($espoName);
|
||||
if (isset($option)) {
|
||||
$options[$ldapName] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
/** peculiar fields */
|
||||
$options['useSsl'] = (bool) ($options['useSsl'] == 'SSL');
|
||||
$options['useStartTls'] = (bool) ($options['useStartTls'] == 'TLS');
|
||||
$options['accountCanonicalForm'] = $this->accountCanonicalFormMap[ $options['accountCanonicalForm'] ];
|
||||
|
||||
$this->options = $options;
|
||||
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an ldap option
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $returns Return value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOption($name, $returns = null)
|
||||
{
|
||||
if (isset($this->options)) {
|
||||
$this->getOptions();
|
||||
}
|
||||
|
||||
if (isset($this->options[$name])) {
|
||||
return $this->options[$name];
|
||||
}
|
||||
|
||||
return $returns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Zend options for using Zend\Ldap
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getZendOptions()
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
$espoOptions = array_keys($this->permittedEspoOptions);
|
||||
|
||||
$zendOptions = array_diff_key($options, array_flip($espoOptions));
|
||||
|
||||
return $zendOptions;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user