refactoring

This commit is contained in:
Yuri Kuznetsov
2020-07-11 12:24:35 +03:00
parent da7ac548c5
commit bb29373e16
24 changed files with 121 additions and 81 deletions
+5 -3
View File
@@ -31,10 +31,12 @@ namespace Espo\Controllers;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Utils\Auth;
use Espo\Core\Authentication\Authentication;
use Espo\Core\Di;
use Espo\Core\Api\Request;
use StdClass;
class App implements
Di\ServiceFactoryAware,
@@ -48,12 +50,12 @@ class App implements
return $this->serviceFactory->create('App')->getUserData();
}
public function postActionDestroyAuthToken(array $params, \StdClass $data, Request $request)
public function postActionDestroyAuthToken(array $params, StdClass $data, Request $request)
{
if (empty($data->token)) {
throw new BadRequest();
}
$auth = $this->injectableFactory->create(Auth::class);
$auth = $this->injectableFactory->create(Authentication::class);
return $auth->destroyAuthToken($data->token, $request);
}
}
+3
View File
@@ -32,6 +32,9 @@ namespace Espo\Core\Acl;
use Espo\Entities\User;
use Espo\ORM\Entity;
/**
* @todo Add methods.
*/
interface EntityAcl
{
+3
View File
@@ -32,6 +32,9 @@ namespace Espo\Core\Acl;
use Espo\Entities\User;
use Espo\ORM\Entity;
/**
* @todo Add methods.
*/
interface ScopeAcl
{
+2 -1
View File
@@ -40,7 +40,8 @@ use Espo\Core\Utils\FieldManagerUtil;
use Espo\Core\Utils\File\Manager as FileManager;
/**
* A table is generated for each user. It's resulted from merging of multitple roles.
* A table is generated for each user. Multiple roles are merged into a single table.
* This table is used for access checking.
*/
class Table
{
+15 -13
View File
@@ -33,20 +33,22 @@ use Exception;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Utils\Auth as AuthUtil;
use Espo\Core\Authentication\Authentication;
use Espo\Core\{
Api\Request,
Api\Response,
};
use StdClass;
/**
* Determines which auth method to use. Fetches a username and password from headers and server parameters.
* Then tries to log in.
*/
class Auth
{
protected $auth;
protected $authentication;
protected $authRequired;
@@ -54,9 +56,9 @@ class Auth
private $isResolvedUseNoAuth = false;
public function __construct(AuthUtil $auth, bool $authRequired = true, bool $isEntryPoint = false)
public function __construct(Authentication $authentication, bool $authRequired = true, bool $isEntryPoint = false)
{
$this->auth = $auth;
$this->authentication = $authentication;
$this->authRequired = $authRequired;
$this->isEntryPoint = $isEntryPoint;
}
@@ -137,7 +139,7 @@ class Auth
if (!$this->authRequired) {
if (!$this->isEntryPoint && $hasAuthData) {
try {
$isAuthenticated = $this->auth->login($username, $password, $request, $authenticationMethod);
$isAuthenticated = $this->authentication->login($username, $password, $request, $authenticationMethod);
} catch (Exception $e) {
$this->processException($response, $e);
return;
@@ -153,7 +155,7 @@ class Auth
if ($hasAuthData) {
try {
$authResult = $this->auth->login($username, $password, $request, $authenticationMethod);
$authResult = $this->authentication->login($username, $password, $request, $authenticationMethod);
} catch (Exception $e) {
$this->processException($response, $e);
}
@@ -182,24 +184,24 @@ class Auth
return explode(':', $string, 2);
}
protected function handleAuthResult(Response $response, array $authResult)
protected function handleAuthResult(Response $response, StdClass $authResult)
{
$status = $authResult['status'];
$status = $authResult->status;
if ($status === AuthUtil::STATUS_SUCCESS) {
if ($status === Authentication::STATUS_SUCCESS) {
$this->resolve();
return;
}
if ($status === AuthUtil::STATUS_SECOND_STEP_REQUIRED) {
if ($status === Authentication::STATUS_SECOND_STEP_REQUIRED) {
$response->setStatus(401);
$response->setHeader('X-Status-Reason', 'second-step-required');
$bodyData = [
'status' => $status,
'message' => $authResult['message'] ?? null,
'view' => $authResult['view'] ?? null,
'token' => $authResult['token'] ?? null,
'message' => $authResult->message ?? null,
'view' => $authResult->view ?? null,
'token' => $authResult->token ?? null,
];
$response->writeBody(json_encode($bodyData));
}
+6 -1
View File
@@ -34,6 +34,11 @@ use Espo\Core\{
Api\Response,
};
use Throwable;
/**
* Processes an error output. If an exception occured, it will be passed to here.
*/
class ErrorOutput
{
protected $errorDescriptions = [
@@ -62,7 +67,7 @@ class ErrorOutput
public function process(
Response $response,
\Throwable $exception,
Throwable $exception,
bool $toPrint = false,
?array $route = null,
?array $routeParams = null
@@ -33,6 +33,9 @@ use Espo\Core\Acl;
use Espo\Core\SelectManagerFactory;
use Espo\ORM\EntityManager;
/**
* Returns a list of entity types for which a PDF template exists.
*/
class TemplateEntityTypeList
{
protected $acl;
+5 -5
View File
@@ -38,12 +38,12 @@ use Espo\Core\{
InjectableFactory,
EntryPointManager,
CronManager,
Authentication\Authentication,
Api\Auth as ApiAuth,
Api\ErrorOutput as ApiErrorOutput,
Api\RequestWrapper,
Api\ResponseWrapper,
Api\RouteProcessor,
Utils\Auth,
Utils\Route,
Utils\Autoload,
Utils\Config,
@@ -131,7 +131,7 @@ class Application
try {
$authRequired = !($item['noAuth'] ?? false);
$apiAuth = new ApiAuth($this->createAuth(), $authRequired);
$apiAuth = new ApiAuth($this->createAuthentication(), $authRequired);
$apiAuth->process($requestWrapped, $responseWrapped);
if (!$apiAuth->isResolved()) {
@@ -200,7 +200,7 @@ class Application
$responseWrapped = new ResponseWrapper($handler->handle($request));
try {
$apiAuth = new ApiAuth($this->createAuth($authNotStrict), $authRequired, true);
$apiAuth = new ApiAuth($this->createAuthentication($authNotStrict), $authRequired, true);
$apiAuth->process($requestWrapped, $responseWrapped);
@@ -391,9 +391,9 @@ class Application
return $slim;
}
protected function createAuth(bool $allowAnyAccess = false) : Auth
protected function createAuthentication(bool $allowAnyAccess = false) : Authentication
{
return $this->getInjectableFactory()->createWith(Auth::class, [
return $this->getInjectableFactory()->createWith(Authentication::class, [
'allowAnyAccess' => $allowAnyAccess,
]);
}
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Utils;
namespace Espo\Core\Authentication;
use Espo\Core\Exceptions\{
Error,
@@ -42,10 +42,10 @@ use Espo\Entities\{
};
use Espo\Core\Authentication\{
Login,
TwoFA\CodeVerify as TwoFACodeVerify,
Utils\AuthenticationFactory,
TwoFA\Utils\Factory as Auth2FAFactory,
Login\Login,
TwoFactor\Methods\CodeVerify as TwoFACodeVerify,
LoginFactory,
TwoFactor\Factory as TwoFAFactory,
};
use Espo\Core\Api\Request;
@@ -58,10 +58,12 @@ use Espo\Core\{
ORM\EntityManager,
};
use StdClass;
/**
* Handles authentication. The entry point of the auth process.
*/
class Auth
class Authentication
{
const FAILED_ATTEMPTS_PERIOD = '60 seconds';
@@ -79,7 +81,7 @@ class Auth
protected $config;
protected $metadata;
protected $entityManager;
protected $authenticationFactory;
protected $authLoginFactory;
protected $auth2FAFactory;
public function __construct(
@@ -89,8 +91,8 @@ class Auth
Config $config,
Metadata $metadata,
EntityManager $entityManager,
AuthenticationFactory $authenticationFactory,
Auth2FAFactory $auth2FAFactory
LoginFactory $authLoginFactory,
TwoFAFactory $auth2FAFactory
) {
$this->allowAnyAccess = $allowAnyAccess;
@@ -99,7 +101,7 @@ class Auth
$this->config = $config;
$this->metadata = $metadata;
$this->entityManager = $entityManager;
$this->authenticationFactory = $authenticationFactory;
$this->authLoginFactory = $authLoginFactory;
$this->auth2FAFactory = $auth2FAFactory;
}
@@ -110,7 +112,7 @@ class Auth
protected function getAuthenticationImpl(string $method) : Login
{
return $this->authenticationFactory->create($method);
return $this->authLoginFactory->create($method);
}
protected function get2FAImpl(string $method) : TwoFACodeVerify
@@ -143,7 +145,7 @@ class Auth
*/
public function login(
?string $username, ?string $password = null, Request $request, ?string $authenticationMethod = null
) : ?array {
) : ?StdClass {
$isByTokenOnly = false;
if ($authenticationMethod) {
@@ -303,7 +305,7 @@ class Auth
return null;
}
} else {
$loginResultData = $twoFAImpl->getLoginData($user2FA);
$loginResultData = (array) $twoFAImpl->getLoginData($user2FA);
$secondStepRequired = true;
}
}
@@ -369,7 +371,7 @@ class Auth
}
if ($secondStepRequired) {
return [
return (object) [
'status' => self::STATUS_SECOND_STEP_REQUIRED,
'message' => $loginResultData['message'] ?? null,
'token' => $loginResultData['token'] ?? null,
@@ -377,7 +379,7 @@ class Auth
];
}
return [
return (object) [
'status' => self::STATUS_SUCCESS,
];
}
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication;
namespace Espo\Core\Authentication\Login;
use Espo\Entities\{
User,
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication;
namespace Espo\Core\Authentication\Login;
use Espo\Entities\{
User,
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication;
namespace Espo\Core\Authentication\Login;
use Espo\Entities\{
User,
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication;
namespace Espo\Core\Authentication\Login;
use Espo\Core\Exceptions\Error;
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication;
namespace Espo\Core\Authentication\Login;
use Espo\Entities\{
User,
@@ -36,8 +36,17 @@ use Espo\Entities\{
use Espo\Core\Api\Request;
/**
* Performs credentials checking. For the basic authorization a username & password are used.
* For other authorization methods credentials must be fetched from the request.
*/
interface Login
{
/**
* Check credentials.
*
* @return User if credentials are correct. NULL otherwise.
*/
public function login(
?string $username,
?string $password,
@@ -27,13 +27,13 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\Utils;
namespace Espo\Core\Authentication;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Metadata;
use Espo\Core\Authentication\Login;
use Espo\Core\Authentication\Login\Login;
class AuthenticationFactory
class LoginFactory
{
protected $injectableFactory;
protected $metadata;
@@ -51,9 +51,9 @@ class AuthenticationFactory
if (!$className) {
$sanitizedName = preg_replace('/[^a-zA-Z0-9]+/', '', $method);
$className = "Espo\\Custom\\Core\\Authentication\\" . $sanitizedName;
$className = "Espo\\Custom\\Core\\Authentication\\Login\\" . $sanitizedName;
if (!class_exists($className)) {
$className = "Espo\\Core\\Authentication\\" . $sanitizedName;
$className = "Espo\\Core\\Authentication\\Login\\" . $sanitizedName;
}
}
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA\Utils;
namespace Espo\Core\Authentication\TwoFactor;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Metadata;
@@ -52,9 +52,9 @@ class Factory
if (!$className) {
$sanitizedName = preg_replace('/[^a-zA-Z0-9]+/', '', $method);
$className = "Espo\\Custom\\Core\\\Authentication\\TwoFA\\" . $sanitizedName;
$className = "Espo\\Custom\\Core\\\Authentication\\TwoFactor\\Methods\\" . $sanitizedName;
if (!class_exists($className)) {
$className = "Espo\\Core\\Authentication\\TwoFA\\" . $sanitizedName;
$className = "Espo\\Core\\Authentication\\TwoFactor\\Methods\\" . $sanitizedName;
}
}
@@ -27,13 +27,15 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA;
namespace Espo\Core\Authentication\TwoFactor\Methods;
use Espo\Entities\User;
use StdClass;
interface CodeVerify
{
public function verifyCode(User $user, string $code) : bool;
public function getLoginData(User $user) : array;
public function getLoginData(User $user) : StdClass;
}
@@ -27,12 +27,14 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA;
namespace Espo\Core\Authentication\TwoFactor\Methods;
use Espo\Entities\User;
use Espo\ORM\EntityManager;
use Espo\Core\Authentication\TwoFA\Utils\Totp as TotpUtils;
use Espo\Core\Authentication\TwoFactor\Utils\Totp as TotpUtils;
use StdClass;
class Totp implements CodeVerify
{
@@ -60,9 +62,9 @@ class Totp implements CodeVerify
return $this->totp->verifyCode($secret, $code);
}
public function getLoginData(User $user) : array
public function getLoginData(User $user) : StdClass
{
return [
return (object) [
'message' => 'enterTotpCode',
];
}
@@ -27,13 +27,15 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA\User;
namespace Espo\Core\Authentication\TwoFactor\User;
use Espo\Entities\UserData;
use StdClass;
interface CodeVerify
{
public function generateData(UserData $userData, object $data, string $userName) : object;
public function generateData(UserData $userData, StdClass $data, string $userName) : StdClass;
public function verify(UserData $userData, string $code) : bool;
}
@@ -27,12 +27,14 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA\User;
namespace Espo\Core\Authentication\TwoFactor\User;
use Espo\Entities\UserData;
use Espo\Core\Authentication\TwoFA\Utils\Totp as TotpUtils;
use Espo\Core\Authentication\TwoFactor\Utils\Totp as TotpUtils;
use Espo\Core\Utils\Config;
use StdClass;
class Totp implements CodeVerify
{
protected $entityManager;
@@ -44,7 +46,7 @@ class Totp implements CodeVerify
$this->config = $config;
}
public function generateData(UserData $userData, object $data, string $userName) : object
public function generateData(UserData $userData, StdClass $data, string $userName) : StdClass
{
$secret = $this->totp->createSecret();
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA\Utils;
namespace Espo\Core\Authentication\TwoFactor;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Metadata;
@@ -52,9 +52,9 @@ class UserFactory
if (!$className) {
$sanitizedName = preg_replace('/[^a-zA-Z0-9]+/', '', $method);
$className = "Espo\\Custom\\Core\\Authentication\\TwoFA\\User\\" . $sanitizedName;
$className = "Espo\\Custom\\Core\\Authentication\\TwoFactor\\User\\" . $sanitizedName;
if (!class_exists($className)) {
$className = "Espo\\Core\\Authentication\\TwoFA\\User\\" . $sanitizedName;
$className = "Espo\\Core\\Authentication\\TwoFactor\\User\\" . $sanitizedName;
}
}
@@ -27,7 +27,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Authentication\TwoFA\Utils;
namespace Espo\Core\Authentication\TwoFactor\Utils;
use RobThree\Auth\TwoFactorAuth;
@@ -80,17 +80,17 @@
"hasher": {
"className": "Espo\\Core\\Utils\\Hasher"
},
"authenticationFactory": {
"className": "Espo\\Core\\Authentication\\Utils\\AuthenticationFactory"
"authLoginFactory": {
"className": "Espo\\Core\\Authentication\\LoginFactory"
},
"auth2FAFactory": {
"className": "Espo\\Core\\Authentication\\TwoFA\\Utils\\Factory"
"className": "Espo\\Core\\Authentication\\TwoFactor\\Factory"
},
"auth2FAUserFactory": {
"className": "Espo\\Core\\Authentication\\TwoFA\\Utils\\UserFactory"
"className": "Espo\\Core\\Authentication\\TwoFactor\\UserFactory"
},
"totp": {
"className": "Espo\\Core\\Authentication\\TwoFA\\Utils\\Totp"
"className": "Espo\\Core\\Authentication\\TwoFactor\\Utils\\Totp"
},
"emailFilterManager": {
"className": "Espo\\Core\\Utils\\EmailFilterManager"
+12 -10
View File
@@ -44,8 +44,10 @@ use Espo\Core\{
Utils\Config,
};
use Espo\Core\Authentication\Utils\AuthenticationFactory;
use Espo\Core\Authentication\TwoFA\Utils\UserFactory as Auth2FAUserFactory;
use Espo\Core\Authentication\LoginFactory;
use Espo\Core\Authentication\TwoFactor\UserFactory as TwoFactorUserFactory;
use StdClass;
class UserSecurity
{
@@ -53,7 +55,7 @@ class UserSecurity
protected $user;
protected $metadata;
protected $config;
protected $authenticationFactory;
protected $authLoginFactory;
protected $auth2FAUserFactory;
public function __construct(
@@ -61,18 +63,18 @@ class UserSecurity
User $user,
Metadata $metadata,
Config $config,
AuthenticationFactory $authenticationFactory,
Auth2FAUserFactory $auth2FAUserFactory
LoginFactory $authLoginFactory,
TwoFactorUserFactory $auth2FAUserFactory
) {
$this->entityManager = $entityManager;
$this->user = $user;
$this->metadata = $metadata;
$this->config = $config;
$this->authenticationFactory = $authenticationFactory;
$this->authLoginFactory = $authLoginFactory;
$this->auth2FAUserFactory = $auth2FAUserFactory;
}
public function read(string $id) : object
public function read(string $id) : StdClass
{
if (!$this->user->isAdmin() && $id !== $this->user->id) throw new Forbidden();
@@ -89,7 +91,7 @@ class UserSecurity
];
}
public function generate2FAData(string $id, object $data) : object
public function generate2FAData(string $id, StdClass $data) : StdClass
{
if (!$this->user->isAdmin() && $id !== $this->user->id) throw new Forbidden();
@@ -128,7 +130,7 @@ class UserSecurity
return $generatedData;
}
public function update(string $id, object $data) : object
public function update(string $id, StdClass $data) : StdClass
{
if (!$this->user->isAdmin() && $id !== $this->user->id) throw new Forbidden();
@@ -204,7 +206,7 @@ class UserSecurity
{
$method = $this->config->get('authenticationMethod', 'Espo');
$auth = $this->authenticationFactory->create($method);
$auth = $this->authLoginFactory->create($method);
$user = $this->entityManager->getRepository('User')->where([
'id' => $id,