Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend

This commit is contained in:
yuri
2016-08-01 11:12:51 +03:00
9 changed files with 119 additions and 219 deletions
@@ -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",
+2 -2
View File
@@ -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']
}
]
},