Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend
This commit is contained in:
@@ -28,16 +28,18 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Authentication;
|
||||
use Espo\Core\Exceptions\Error,
|
||||
Espo\Core\Utils\Config,
|
||||
Espo\Core\ORM\EntityManager,
|
||||
Espo\Core\Utils\Auth;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Auth;
|
||||
use Zend\Ldap\Ldap as LdapClient;
|
||||
|
||||
class LDAP extends Base
|
||||
{
|
||||
private $utils;
|
||||
|
||||
private $zendLdap;
|
||||
private $ldapClient;
|
||||
|
||||
/**
|
||||
* Espo => LDAP name
|
||||
@@ -50,27 +52,17 @@ class LDAP extends Base
|
||||
{
|
||||
parent::__construct($config, $entityManager, $auth);
|
||||
|
||||
$this->fields = array(
|
||||
'userName' => $this->getConfig()->get('ldapUserNameAttribute'),
|
||||
'firstName' => $this->getConfig()->get('ldapUserFirstNameAttribute'),
|
||||
'lastName' => $this->getConfig()->get('ldapUserLastNameAttribute'),
|
||||
'title' => $this->getConfig()->get('ldapUserTitleAttribute'),
|
||||
'emailAddress' => $this->getConfig()->get('ldapUserEmailAddressAttribute'),
|
||||
'phoneNumber' => $this->getConfig()->get('ldapUserPhoneNumberAttribute')
|
||||
);
|
||||
|
||||
$this->utils = new LDAP\Utils($config);
|
||||
|
||||
try {
|
||||
$this->zendLdap = new LDAP\LDAP();
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('LDAP Authentication error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function getZendLdap()
|
||||
{
|
||||
return $this->zendLdap;
|
||||
$options = $this->getUtils()->getOptions();
|
||||
$this->fields = array(
|
||||
'userName' => $options['userNameAttribute'],
|
||||
'firstName' => $options['userTitleAttribute'],
|
||||
'lastName' => $options['userFirstNameAttribute'],
|
||||
'title' => $options['userLastNameAttribute'],
|
||||
'emailAddress' => $options['userEmailAddressAttribute'],
|
||||
'phoneNumber' => $options['userPhoneNumberAttribute']
|
||||
);
|
||||
}
|
||||
|
||||
protected function getUtils()
|
||||
@@ -78,6 +70,21 @@ class LDAP extends Base
|
||||
return $this->utils;
|
||||
}
|
||||
|
||||
protected function getLdapClient()
|
||||
{
|
||||
if (!isset($this->ldapClient)) {
|
||||
$options = $this->getUtils()->getLdapClientOptions();
|
||||
|
||||
try {
|
||||
$this->ldapClient = new LdapClient($options);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('LDAP error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->ldapClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP login
|
||||
*
|
||||
@@ -92,32 +99,35 @@ class LDAP extends Base
|
||||
return $this->loginByToken($username, $authToken);
|
||||
}
|
||||
|
||||
$options = $this->getUtils()->getZendOptions();
|
||||
$ldapClient = $this->getLdapClient();
|
||||
|
||||
$ldap = $this->getZendLdap();
|
||||
$ldap = $ldap->setOptions($options);
|
||||
//login LDAP admin user (ldapUsername, ldapPassword)
|
||||
try {
|
||||
$ldapClient->bind();
|
||||
} catch (\Exception $e) {
|
||||
$options = $this->getUtils()->getLdapClientOptions();
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$options['username'].'], details: ' . $e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
$userDn = $this->findLdapUserDnByUsername($username);
|
||||
$GLOBALS['log']->debug('Found DN for ['.$username.']: ['.$userDn.'].');
|
||||
if (!isset($userDn)) {
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$username.'], details: user is not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$ldap->bind($username, $password);
|
||||
|
||||
$dn = $ldap->getDn($username);
|
||||
$GLOBALS['log']->debug('Found DN for ['.$username.']: ['.$dn.']');
|
||||
|
||||
$loginFilter = $this->getUtils()->getOption('userLoginFilter');
|
||||
$GLOBALS['log']->debug('Found loginFilter: ['.$loginFilter.']');
|
||||
|
||||
$userData = $ldap->searchByLoginFilter($loginFilter, $dn, 3);
|
||||
$GLOBALS['log']->debug('Found userData ... ');
|
||||
|
||||
} catch (\Zend\Ldap\Exception\LdapException $zle) {
|
||||
$ldapClient->bind($userDn, $password);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
$admin = $this->adminLogin($username, $password);
|
||||
if (!isset($admin)) {
|
||||
$GLOBALS['log']->error('LDAP Authentication: ' . $zle->getMessage());
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$username.'], details: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
$GLOBALS['log']->info('LDAP Authentication: Administrator login by username ['.$username.']');
|
||||
$GLOBALS['log']->info('LDAP: Administrator ['.$username.'] was logged in by Espo method.');
|
||||
}
|
||||
|
||||
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
|
||||
@@ -129,6 +139,8 @@ class LDAP extends Base
|
||||
$isCreateUser = $this->getUtils()->getOption('createEspoUser');
|
||||
if (!isset($user) && $isCreateUser) {
|
||||
$this->getAuth()->useNoAuth(); /** Required to fix Acl "isFetched()" error */
|
||||
|
||||
$userData = $ldapClient->getEntry($userDn);
|
||||
$user = $this->createUser($userData);
|
||||
}
|
||||
|
||||
@@ -200,11 +212,12 @@ class LDAP extends Base
|
||||
$data = array();
|
||||
|
||||
// show full array of the LDAP user
|
||||
$GLOBALS['log']->debug(print_r($userData, true));
|
||||
$GLOBALS['log']->debug('LDAP: user data: ' .print_r($userData, true));
|
||||
|
||||
foreach ($this->fields as $espo => $ldap) {
|
||||
$ldap = strtolower($ldap);
|
||||
if (isset($userData[$ldap][0])) {
|
||||
$GLOBALS['log']->debug('LDAP, createUser: ['.$espo.'] - ['.$userData[$ldap][0].']');
|
||||
$GLOBALS['log']->debug('LDAP: Create a user wtih ['.$espo.'] = ['.$userData[$ldap][0].'].');
|
||||
$data[$espo] = $userData[$ldap][0];
|
||||
}
|
||||
}
|
||||
@@ -217,4 +230,23 @@ class LDAP extends Base
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find LDAP user DN by his username
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @return string | null
|
||||
*/
|
||||
protected function findLdapUserDnByUsername($username)
|
||||
{
|
||||
$ldapClient = $this->getLdapClient();
|
||||
$options = $this->getUtils()->getOptions();
|
||||
|
||||
$result = $ldapClient->search('(&(objectClass=user)('.$options['userNameAttribute'].'='.$username.'))', null, LdapClient::SEARCH_SCOPE_ONE);
|
||||
|
||||
foreach ($result as $item) {
|
||||
return $item["dn"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 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/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Authentication\LDAP;
|
||||
|
||||
class LDAP extends \Zend\Ldap\Ldap
|
||||
{
|
||||
protected $userNameAttribute = 'CN';
|
||||
|
||||
/**
|
||||
* Set user name attribute name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUserNameAttributeName($name)
|
||||
{
|
||||
$this->userNameAttribute = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DN depends on options, ex. "cn=test,ou=People,dc=maxcrc,dc=com"
|
||||
*
|
||||
* @return string DN format
|
||||
*/
|
||||
public function getDn($acctname)
|
||||
{
|
||||
return $this->getAccountDn($acctname, \Zend\Ldap\Ldap::ACCTNAME_FORM_DN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix a bug, ex. CN=Alice Baker,CN=Users,DC=example,DC=com
|
||||
*
|
||||
* @param string $acctname
|
||||
* @return string - Account DN
|
||||
*/
|
||||
protected function getAccountDn($acctname)
|
||||
{
|
||||
$baseDn = $this->getBaseDn();
|
||||
|
||||
if ($this->getBindRequiresDn() && isset($baseDn)) {
|
||||
try {
|
||||
return parent::getAccountDn($acctname);
|
||||
} catch (\Zend\Ldap\Exception\LdapException $zle) {
|
||||
/*if ($zle->getCode() != \Zend\Ldap\Exception\LdapException::LDAP_NO_SUCH_OBJECT) {
|
||||
throw $zle;
|
||||
}*/
|
||||
}
|
||||
|
||||
$acctname = $this->userNameAttribute . '=' . \Zend\Ldap\Filter\AbstractFilter::escapeValue($acctname) . ',' . $baseDn;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,12 @@ class Utils
|
||||
'tryUsernameSplit' => 'ldapTryUsernameSplit',
|
||||
'networkTimeout' => 'ldapNetworkTimeout',
|
||||
'createEspoUser' => 'ldapCreateEspoUser',
|
||||
'userLoginFilter' => 'ldapUserLoginFilter',
|
||||
'userNameAttribute' => 'ldapUserNameAttribute',
|
||||
'userTitleAttribute' => 'ldapUserTitleAttribute',
|
||||
'userFirstNameAttribute' => 'ldapUserFirstNameAttribute',
|
||||
'userLastNameAttribute' => 'ldapUserLastNameAttribute',
|
||||
'userEmailAddressAttribute' => 'ldapUserEmailAddressAttribute',
|
||||
'userPhoneNumberAttribute' => 'ldapUserPhoneNumberAttribute',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -66,8 +71,13 @@ class Utils
|
||||
* @var array
|
||||
*/
|
||||
protected $permittedEspoOptions = array(
|
||||
'createEspoUser' => false,
|
||||
'userLoginFilter' => null,
|
||||
'createEspoUser',
|
||||
'userNameAttribute',
|
||||
'userTitleAttribute',
|
||||
'userFirstNameAttribute',
|
||||
'userLastNameAttribute',
|
||||
'userEmailAddressAttribute',
|
||||
'userPhoneNumberAttribute',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -148,12 +158,10 @@ class Utils
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getZendOptions()
|
||||
public function getLdapClientOptions()
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
$espoOptions = array_keys($this->permittedEspoOptions);
|
||||
|
||||
$zendOptions = array_diff_key($options, array_flip($espoOptions));
|
||||
$zendOptions = array_diff_key($options, array_flip($this->permittedEspoOptions));
|
||||
|
||||
return $zendOptions;
|
||||
}
|
||||
|
||||
@@ -152,11 +152,5 @@ return array (
|
||||
],
|
||||
"calendarEntityList" => ["Meeting", "Call", "Task"],
|
||||
'isInstalled' => false,
|
||||
'ldapUserNameAttribute' => 'samaccountname',
|
||||
'ldapUserFirstNameAttribute' => 'givenname',
|
||||
'ldapUserLastNameAttribute' => 'sn',
|
||||
'ldapUserTitleAttribute' => 'title',
|
||||
'ldapUserEmailAddressAttribute' => 'mail',
|
||||
'ldapUserPhoneNumberAttribute' => 'telephonenumber',
|
||||
);
|
||||
|
||||
|
||||
@@ -139,5 +139,11 @@ return array ( 'defaultPermissions' =>
|
||||
'authTokenMaxIdleTime'
|
||||
),
|
||||
'isInstalled' => false,
|
||||
'ldapUserNameAttribute' => 'sAMAccountName',
|
||||
'ldapUserFirstNameAttribute' => 'givenName',
|
||||
'ldapUserLastNameAttribute' => 'sn',
|
||||
'ldapUserTitleAttribute' => 'title',
|
||||
'ldapUserEmailAddressAttribute' => 'mail',
|
||||
'ldapUserPhoneNumberAttribute' => 'telephoneNumber',
|
||||
);
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
"ldapHost": "Host",
|
||||
"ldapPort": "Port",
|
||||
"ldapAuth": "Auth",
|
||||
"ldapUsername": "Username",
|
||||
"ldapUsername": "Full User DN",
|
||||
"ldapPassword": "Password",
|
||||
"ldapBindRequiresDn": "Bind Requires Dn",
|
||||
"ldapBaseDn": "Base Dn",
|
||||
"ldapBindRequiresDn": "Bind Requires DN",
|
||||
"ldapBaseDn": "Base DN",
|
||||
"ldapAccountCanonicalForm": "Account Canonical Form",
|
||||
"ldapAccountDomainName": "Account Domain Name",
|
||||
"ldapTryUsernameSplit": "Try Username Split",
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
"rows": [
|
||||
[{"name": "ldapHost"}, {"name": "ldapPort"}],
|
||||
[{"name": "ldapAuth"}, {"name": "ldapSecurity"}],
|
||||
[{"name": "ldapUsername"}, false],
|
||||
[{"name": "ldapUsername", "fullWidth": true}],
|
||||
[{"name": "ldapPassword"}, false],
|
||||
[{"name": "ldapBindRequiresDn"}, {"name": "ldapUserLoginFilter"}],
|
||||
[{"name": "ldapBaseDn"}, false],
|
||||
[{"name": "ldapAccountCanonicalForm"}, false],
|
||||
[{"name": "ldapUserNameAttribute"}, false],
|
||||
[{"name": "ldapAccountCanonicalForm"}, {"name": "ldapBindRequiresDn"}],
|
||||
[{"name": "ldapBaseDn", "fullWidth": true}],
|
||||
[{"name": "ldapAccountDomainName"}, {"name": "ldapAccountDomainNameShort"}],
|
||||
[{"name": "ldapTryUsernameSplit"}, {"name": "ldapOptReferrals"}],
|
||||
[{"name": "ldapCreateEspoUser"}, false],
|
||||
[{"name": "ldapUserNameAttribute"}, {"name": "ldapUserTitleAttribute"}],
|
||||
[{"name": "ldapUserFirstNameAttribute"}, {"name": "ldapUserLastNameAttribute"}],
|
||||
[{"name": "ldapUserTitleAttribute"}, false],
|
||||
[{"name": "ldapUserEmailAddressAttribute"}, {"name": "ldapUserPhoneNumberAttribute"}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -154,9 +154,7 @@
|
||||
"required": true
|
||||
},
|
||||
"ldapPort": {
|
||||
"type": "int",
|
||||
"max": 999999,
|
||||
"min": 0,
|
||||
"type": "varchar",
|
||||
"default": 389
|
||||
},
|
||||
"ldapSecurity": {
|
||||
@@ -179,9 +177,6 @@
|
||||
"ldapBaseDn": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"ldapUserLoginFilter": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"ldapAccountCanonicalForm": {
|
||||
"type": "enum",
|
||||
"options": ["Dn", "Username", "Backslash", "Principal"]
|
||||
@@ -206,22 +201,28 @@
|
||||
"default": true
|
||||
},
|
||||
"ldapUserNameAttribute": {
|
||||
"type": "varchar"
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"ldapUserFirstNameAttribute": {
|
||||
"type": "varchar"
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"ldapUserLastNameAttribute": {
|
||||
"type": "varchar"
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"ldapUserTitleAttribute": {
|
||||
"type": "varchar"
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"ldapUserEmailAddressAttribute": {
|
||||
"type": "varchar"
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"ldapUserPhoneNumberAttribute": {
|
||||
"type": "varchar"
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"exportDisabled": {
|
||||
"type": "bool",
|
||||
|
||||
@@ -76,14 +76,14 @@ Espo.define('views/admin/authentication', 'views/settings/record/edit', function
|
||||
true: [
|
||||
{
|
||||
action: 'show',
|
||||
fields: ['ldapUserNameAttribute', 'ldapUserTitleAttribute', 'ldapUserFirstNameAttribute', 'ldapUserLastNameAttribute', 'ldapUserEmailAddressAttribute', 'ldapUserPhoneNumberAttribute']
|
||||
fields: ['ldapUserTitleAttribute', 'ldapUserFirstNameAttribute', 'ldapUserLastNameAttribute', 'ldapUserEmailAddressAttribute', 'ldapUserPhoneNumberAttribute']
|
||||
}
|
||||
]
|
||||
},
|
||||
default: [
|
||||
{
|
||||
action: 'hide',
|
||||
fields: ['ldapUserNameAttribute', 'ldapUserTitleAttribute', 'ldapUserFirstNameAttribute', 'ldapUserLastNameAttribute', 'ldapUserEmailAddressAttribute', 'ldapUserPhoneNumberAttribute']
|
||||
fields: ['ldapUserTitleAttribute', 'ldapUserFirstNameAttribute', 'ldapUserLastNameAttribute', 'ldapUserEmailAddressAttribute', 'ldapUserPhoneNumberAttribute']
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user