From 2b8143b4fb8e7613238bd6c3778eaa795877ee8d Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 8 Jul 2020 10:12:29 +0300 Subject: [PATCH] dev --- application/Espo/Core/Api/Auth.php | 7 +- application/Espo/Core/Utils/Auth.php | 6 +- .../Espo/Core/Utils/Authentication/ApiKey.php | 36 +++-- .../Espo/Core/Utils/Authentication/Base.php | 67 --------- .../Espo/Core/Utils/Authentication/Espo.php | 43 ++++-- .../Espo/Core/Utils/Authentication/Hmac.php | 56 ++++--- .../Espo/Core/Utils/Authentication/LDAP.php | 142 +++++++++--------- .../Espo/Core/Utils/Authentication/Login.php | 14 +- 8 files changed, 172 insertions(+), 199 deletions(-) delete mode 100644 application/Espo/Core/Utils/Authentication/Base.php diff --git a/application/Espo/Core/Api/Auth.php b/application/Espo/Core/Api/Auth.php index 689611fc36..cc19ec25af 100644 --- a/application/Espo/Core/Api/Auth.php +++ b/application/Espo/Core/Api/Auth.php @@ -93,20 +93,19 @@ class Auth $authenticationMethod = null; - if ($request->hasHeader('Http-Espo-Authorization')) { - $espoAuthorizationHeader = $request->getHeader('Http-Espo-Authorization'); + if ($request->hasHeader('Espo-Authorization')) { + $espoAuthorizationHeader = $request->getHeader('Espo-Authorization'); list($username, $password) = explode(':', base64_decode($espoAuthorizationHeader), 2); } else { if ($request->hasHeader('X-Hmac-Authorization')) { $hmacAuthorizationHeader = $request->getHeader('X-Hmac-Authorization'); $authenticationMethod = 'Hmac'; - list($username, $password) = explode(':', base64_decode($hmacAuthorizationHeader), 2); + $username = explode(':', base64_decode($hmacAuthorizationHeader), 2)[0]; } else { $apiKeyHeader = $request->getHeader('X-Api-Key'); if ($apiKeyHeader) { $authenticationMethod = 'ApiKey'; $username = $apiKeyHeader; - $password = null; } } } diff --git a/application/Espo/Core/Utils/Auth.php b/application/Espo/Core/Utils/Auth.php index edf979017b..e16b0c0df5 100644 --- a/application/Espo/Core/Utils/Auth.php +++ b/application/Espo/Core/Utils/Auth.php @@ -148,7 +148,7 @@ class Auth $isByTokenOnly = false; if (!$authenticationMethod) { - if ($this->request->getHeader('Http-Espo-Authorization-By-Token') === 'true') { + if ($this->request->getHeader('Espo-Authorization-By-Token') === 'true') { $isByTokenOnly = true; } } @@ -162,7 +162,7 @@ class Auth } if (!$isByTokenOnly) { - $this->checkFailedAttemptsLimit($username); + $this->checkFailedAttemptsLimit(); } $authToken = null; @@ -225,7 +225,7 @@ class Auth $loginResultData = []; - $user = $authenticationImpl->login($username, $password, $authToken, $params, $this->request, $loginResultData); + $user = $authenticationImpl->login($username, $password, $authToken, $this->request, $params, $loginResultData); $authLogRecord = null; diff --git a/application/Espo/Core/Utils/Authentication/ApiKey.php b/application/Espo/Core/Utils/Authentication/ApiKey.php index ff0a60375d..61cfb42d26 100644 --- a/application/Espo/Core/Utils/Authentication/ApiKey.php +++ b/application/Espo/Core/Utils/Authentication/ApiKey.php @@ -29,25 +29,33 @@ namespace Espo\Core\Utils\Authentication; -use Espo\Core\Exceptions\Error; - -use Espo\Entities\AuthToken; +use Espo\Entities\{ + User, + AuthToken, +}; use Espo\Core\Api\Request; +use Espo\Core\ORM\EntityManager; -class ApiKey extends Base +class ApiKey implements Login { - public function login(string $username, ?string $password, ?AuthToken $authToken, array $params, Request $request) - { - $apiKey = $username; + protected $entityManager; - $user = $this->getEntityManager()->getRepository('User')->findOne([ - 'whereClause' => [ - 'type' => 'api', - 'apiKey' => $apiKey, - 'authMethod' => 'ApiKey', - ] - ]); + public function __construct(EntityManager $entityManager) + { + $this->entityManager = $entityManager; + } + + public function login( + ?string $username, ?string $password, ?AuthToken $authToken, Request $request, array $params, array &$resultData + ) : ?User { + $apiKey = $request->getHeader('X-Api-Key'); + + $user = $this->entityManager->getRepository('User')->where([ + 'type' => 'api', + 'apiKey' => $apiKey, + 'authMethod' => 'ApiKey', + ])->findOne(); return $user; } diff --git a/application/Espo/Core/Utils/Authentication/Base.php b/application/Espo/Core/Utils/Authentication/Base.php deleted file mode 100644 index 860209a99c..0000000000 --- a/application/Espo/Core/Utils/Authentication/Base.php +++ /dev/null @@ -1,67 +0,0 @@ -config = $config; - $this->entityManager = $entityManager; - } - - protected function getConfig() - { - return $this->config; - } - - protected function getEntityManager() - { - return $this->entityManager; - } - - protected function getPasswordHash() - { - if (!isset($this->passwordHash)) { - $this->passwordHash = new \Espo\Core\Utils\PasswordHash($this->config); - } - - return $this->passwordHash; - } -} diff --git a/application/Espo/Core/Utils/Authentication/Espo.php b/application/Espo/Core/Utils/Authentication/Espo.php index 199bd23399..b0fa29f666 100644 --- a/application/Espo/Core/Utils/Authentication/Espo.php +++ b/application/Espo/Core/Utils/Authentication/Espo.php @@ -29,31 +29,44 @@ namespace Espo\Core\Utils\Authentication; -use Espo\Core\Exceptions\Error; +use Espo\Entities\{ + User, + AuthToken, +}; -use Espo\Entities\AuthToken; +use Espo\Core\{ + ORM\EntityManager, + Api\Request, + Utils\PasswordHash, +}; -use Espo\Core\Api\Request; - -class Espo extends Base +class Espo implements Login { - public function login(string $username, ?string $password, ?AuthToken $authToken, array $params, Request $request) + protected $entityManager; + protected $passwordHash; + + public function __construct(EntityManager $entityManager, PasswordHash $passwordHash) { - if (!$password) return; + $this->entityManager = $entityManager; + $this->passwordHash = $passwordHash; + } + + public function login( + ?string $username, ?string $password, ?AuthToken $authToken, Request $request, array $params, array &$resultData + ) :?User { + if (!$password) return null; if ($authToken) { $hash = $authToken->get('hash'); } else { - $hash = $this->getPasswordHash()->hash($password); + $hash = $this->passwordHash->hash($password); } - $user = $this->getEntityManager()->getRepository('User')->findOne([ - 'whereClause' => [ - 'userName' => $username, - 'password' => $hash, - 'type!=' => ['api', 'system'], - ] - ]); + $user = $this->entityManager->getRepository('User')->where( [ + 'userName' => $username, + 'password' => $hash, + 'type!=' => ['api', 'system'], + ])->findOne(); if ($user && $authToken) { if ($user->id !== $authToken->get('userId')) { diff --git a/application/Espo/Core/Utils/Authentication/Hmac.php b/application/Espo/Core/Utils/Authentication/Hmac.php index 90e96a570c..9c6db1382a 100644 --- a/application/Espo/Core/Utils/Authentication/Hmac.php +++ b/application/Espo/Core/Utils/Authentication/Hmac.php @@ -29,35 +29,47 @@ namespace Espo\Core\Utils\Authentication; -use Espo\Core\Exceptions\Error; +use Espo\Entities\{ + User, + AuthToken, +}; -use Espo\Entities\AuthToken; +use Espo\Core\{ + ORM\EntityManager, + Api\Request, + Utils\Config, + Utils\ApiKey, +}; -use Espo\Core\Utils\ApiKey; - -use Espo\Core\Api\Request; - -class Hmac extends Base +class Hmac implements Login { - public function login(string $username, ?string $password, ?AuthToken $authToken, array $params, Request $request) + protected $entityManager; + protected $config; + + public function __construct(EntityManager $entityManager, Config $config) { - $apiKey = $username; - $hash = $password; + $this->entityManager = $entityManager; + $this->config = $config; + } - $user = $this->getEntityManager()->getRepository('User')->findOne([ - 'whereClause' => [ - 'type' => 'api', - 'apiKey' => $apiKey, - 'authMethod' => 'Hmac', - ] - ]); + public function login( + ?string $username, ?string $password, ?AuthToken $authToken, Request $request, array $params, array &$resultData + ) :?User { + $authString = base64_decode($request->getHeader('X-Hmac-Authorization')); - if (!$user) return; + list($apiKey, $hash) = explode(':', $authString, 2); + + $user = $this->entityManager->getRepository('User')->where([ + 'type' => 'api', + 'apiKey' => $apiKey, + 'authMethod' => 'Hmac', + ])->findOne(); + + if (!$user) return null; if ($user) { - $apiKeyUtil = new ApiKey($this->getConfig()); - $secretKey = $apiKeyUtil->getSecretKeyForUserId($user->id); - if (!$secretKey) return; + $secretKey = (new ApiKey($this->config))->getSecretKeyForUserId($user->id); + if (!$secretKey) return null; $string = $request->getMethod() . ' ' . $request->getResourcePath(); @@ -65,7 +77,7 @@ class Hmac extends Base return $user; } - return; + return null; } return $user; diff --git a/application/Espo/Core/Utils/Authentication/LDAP.php b/application/Espo/Core/Utils/Authentication/LDAP.php index 4739502498..d59e008dee 100644 --- a/application/Espo/Core/Utils/Authentication/LDAP.php +++ b/application/Espo/Core/Utils/Authentication/LDAP.php @@ -30,15 +30,22 @@ namespace Espo\Core\Utils\Authentication; use Espo\Core\Exceptions\Error; -use Espo\Core\Utils\Config; -use Espo\Core\ORM\EntityManager; -use Espo\Core\Utils\Auth; -use Espo\Entities\AuthToken; + +use Espo\Entities\{ + User, + AuthToken, +}; + +use Espo\Core\{ + ORM\EntityManager, + Api\Request, + Utils\Config, + Utils\PasswordHash, + Utils\Language, +}; use Espo\Core\Container; -use Espo\Core\Api\Request; - class LDAP extends Espo { private $utils; @@ -47,12 +54,17 @@ class LDAP extends Espo protected $config; protected $entityManager; + protected $passwordHash; protected $container; + protected $language; - public function __construct(Config $config, EntityManager $entityManager, Container $container) - { + public function __construct( + Config $config, EntityManager $entityManager, PasswordHash $passwordHash, Language $language, Container $container + ) { $this->config = $config; $this->entityManager = $entityManager; + $this->passwordHash = $passwordHash; + $this->language = $language; $this->container = $container; $this->utils = new LDAP\Utils($config); @@ -63,73 +75,38 @@ class LDAP extends Espo * * @var array */ - protected $ldapFieldMap = array( + protected $ldapFieldMap = [ 'userName' => 'userNameAttribute', 'firstName' => 'userFirstNameAttribute', 'lastName' => 'userLastNameAttribute', 'title' => 'userTitleAttribute', 'emailAddress' => 'userEmailAddressAttribute', 'phoneNumber' => 'userPhoneNumberAttribute', - ); + ]; /** * User field name => option name * * @var array */ - protected $userFieldMap = array( + protected $userFieldMap = [ 'teamsIds' => 'userTeamsIds', 'defaultTeamId' => 'userDefaultTeamId', - ); + ]; /** * User field name => option name * * @var array */ - protected $portalUserFieldMap = array( + protected $portalUserFieldMap = [ 'portalsIds' => 'portalUserPortalsIds', 'portalRolesIds' => 'portalUserRolesIds', - ); - - protected function getUtils() - { - return $this->utils; - } - - protected function getContainer() - { - return $this->container; - } - - protected function useSystemUser() - { - $systemUser = $this->getEntityManager()->getEntity('User', 'system'); - if (!$systemUser) { - throw new Error("System user is not found."); - } - - $this->getContainer()->set('user', $systemUser); - } - - protected function getLdapClient() - { - if (!isset($this->ldapClient)) { - $options = $this->getUtils()->getLdapClientOptions(); - - try { - $this->ldapClient = new LDAP\Client($options); - } catch (\Exception $e) { - $GLOBALS['log']->error('LDAP error: ' . $e->getMessage()); - } - } - - return $this->ldapClient; - } + ]; public function login( - string $username, ?string $password, ?AuthToken $authToken, array $params, Request $request - ) { + ?string $username, ?string $password, ?AuthToken $authToken, Request $request, array $params, array &$resultData + ) :?User { $isPortal = !empty($params['isPortal']); if ($authToken) { @@ -139,9 +116,9 @@ class LDAP extends Espo if (!$password || $username == '**logout') return; if ($isPortal) { - $useLdapAuthForPortalUser = $this->getUtils()->getOption('portalUserLdapAuth'); + $useLdapAuthForPortalUser = $this->utils->getOption('portalUserLdapAuth'); if (!$useLdapAuthForPortalUser) { - return parent::login($username, $password, $authToken, $params, $request); + return parent::login($username, $password, $authToken, $request, $params, $resultData); } } @@ -151,7 +128,7 @@ class LDAP extends Espo try { $ldapClient->bind(); } catch (\Exception $e) { - $options = $this->getUtils()->getLdapClientOptions(); + $options = $this->utils->getLdapClientOptions(); $GLOBALS['log']->error('LDAP: Could not connect to LDAP server ['.$options['host'].'], details: ' . $e->getMessage()); $adminUser = $this->adminLogin($username, $password); @@ -190,7 +167,7 @@ class LDAP extends Espo } } - $user = $this->getEntityManager()->getRepository('User')->findOne([ + $user = $this->entityManager->getRepository('User')->findOne([ 'whereClause' => [ 'userName' => $username, 'type!=' => ['api', 'system'] @@ -198,9 +175,9 @@ class LDAP extends Espo ]); if (!isset($user)) { - if (!$this->getUtils()->getOption('createEspoUser')) { + if (!$this->utils->getOption('createEspoUser')) { $this->useSystemUser(); - throw new Error($this->getContainer()->get('language')->translate('ldapUserInEspoNotFound', 'messages', 'User')); + throw new Error($this->language->translate('ldapUserInEspoNotFound', 'messages', 'User')); } $userData = $ldapClient->getEntry($userDn); @@ -210,6 +187,31 @@ class LDAP extends Espo return $user; } + protected function useSystemUser() + { + $systemUser = $this->entityManager->getEntity('User', 'system'); + if (!$systemUser) { + throw new Error("System user is not found."); + } + + $this->container->set('user', $systemUser); + } + + protected function getLdapClient() + { + if (!isset($this->ldapClient)) { + $options = $this->utils->getLdapClientOptions(); + + try { + $this->ldapClient = new LDAP\Client($options); + } catch (\Exception $e) { + $GLOBALS['log']->error('LDAP error: ' . $e->getMessage()); + } + } + + return $this->ldapClient; + } + /** * Login by authorization token * @@ -218,14 +220,14 @@ class LDAP extends Espo * * @return \Espo\Entities\User | null */ - protected function loginByToken($username, \Espo\Entities\AuthToken $authToken = null) + protected function loginByToken($username, AuthToken $authToken = null) { if (!isset($authToken)) { return null; } $userId = $authToken->get('userId'); - $user = $this->getEntityManager()->getEntity('User', $userId); + $user = $this->entityManager->getEntity('User', $userId); $tokenUsername = $user->get('userName'); if (strtolower($username) != strtolower($tokenUsername)) { @@ -236,11 +238,11 @@ class LDAP extends Espo return null; } - $user = $this->getEntityManager()->getRepository('User')->findOne(array( - 'whereClause' => array( + $user = $this->entityManager->getRepository('User')->findOne([ + 'whereClause' => [ 'userName' => $username, - ) - )); + ] + ]); return $user; } @@ -254,9 +256,9 @@ class LDAP extends Espo */ protected function adminLogin($username, $password) { - $hash = $this->getPasswordHash()->hash($password); + $hash = $this->passwordHash->hash($password); - $user = $this->getEntityManager()->getRepository('User')->findOne([ + $user = $this->entityManager->getRepository('User')->findOne([ 'whereClause' => [ 'userName' => $username, 'password' => $hash, @@ -307,11 +309,11 @@ class LDAP extends Espo $this->useSystemUser(); - $user = $this->getEntityManager()->getEntity('User'); + $user = $this->entityManager->getEntity('User'); $user->set($data); - $this->getEntityManager()->saveEntity($user); + $this->entityManager->saveEntity($user); - return $this->getEntityManager()->getEntity('User', $user->id); + return $this->entityManager->getEntity('User', $user->id); } /** @@ -324,7 +326,7 @@ class LDAP extends Espo protected function findLdapUserDnByUsername($username) { $ldapClient = $this->getLdapClient(); - $options = $this->getUtils()->getOptions(); + $options = $this->utils->getOptions(); $loginFilterString = ''; if (!empty($options['userLoginFilter'])) { @@ -368,7 +370,7 @@ class LDAP extends Espo */ protected function loadFields($type) { - $options = $this->getUtils()->getOptions(); + $options = $this->utils->getOptions(); $typeMap = $type . 'FieldMap'; diff --git a/application/Espo/Core/Utils/Authentication/Login.php b/application/Espo/Core/Utils/Authentication/Login.php index 55a91b84ac..6a217f6e13 100644 --- a/application/Espo/Core/Utils/Authentication/Login.php +++ b/application/Espo/Core/Utils/Authentication/Login.php @@ -29,10 +29,16 @@ namespace Espo\Core\Utils\Authentication; -/** - * @todo Add login method. - */ +use Espo\Entities\{ + User, + AuthToken, +}; + +use Espo\Core\Api\Request; + interface Login { - + public function login( + ?string $username, ?string $password, ?AuthToken $authToken, Request $request, array $params, array &$resultData + ) : ?User; }