LDAP changes

This commit is contained in:
Taras Machyshyn
2014-07-11 13:57:03 +03:00
parent f8675bd45c
commit 0d06770e80
3 changed files with 285 additions and 70 deletions
@@ -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;
}
}
@@ -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;
}
}