oauth: handle token expiration; factory in client manager

This commit is contained in:
Yuri Kuznetsov
2020-06-06 18:12:39 +03:00
parent f319be61d2
commit 74f2d7f1ea
3 changed files with 70 additions and 14 deletions

View File

@@ -29,9 +29,11 @@
namespace Espo\Core\ExternalAccount;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\InjectableFactory;
class ClientManager
{
@@ -39,13 +41,17 @@ class ClientManager
protected $metadata;
protected $clientMap = array();
protected $clientMap = [];
public function __construct($entityManager, $metadata, $config)
protected $injectableFactory = null;
public function __construct(
$entityManager, $metadata, $config, ?InjectableFactory $injectableFactory = null)
{
$this->entityManager = $entityManager;
$this->metadata = $metadata;
$this->config = $config;
$this->injectableFactory = $injectableFactory;
}
protected function getMetadata()
@@ -88,14 +94,44 @@ class ClientManager
}
}
public function create($integration, $userId)
public function create(string $integration, string $userId)
{
$authMethod = $this->getMetadata()->get("integrations.{$integration}.authMethod");
$methodName = 'create' . ucfirst($authMethod);
return $this->$methodName($integration, $userId);
if (method_exists($this, $methodName)) {
return $this->$methodName($integration, $userId);
}
if (!$this->injectableFactory) {
throw new Error();
}
$integrationEntity = $this->getEntityManager()->getEntity('Integration', $integration);
$externalAccountEntity = $this->getEntityManager()->getEntity('ExternalAccount', $integration . '__' . $userId);
if (!$externalAccountEntity) {
throw new Error("External Account {$integration} not found for {$userId}");
}
if (!$integrationEntity->get('enabled')) return null;
if (!$externalAccountEntity->get('enabled')) return null;
$className = $this->getMetadata()->get("integrations.{$integration}.clientClassName");
$client = $this->injectableFactory->createByClassName($className);
$client->setup(
$userId,
$integrationEntity,
$externalAccountEntity
);
$this->addToClientMap($client, $integrationEntity, $externalAccountEntity, $userId);
return $client;
}
protected function createOAuth2($integration, $userId)
protected function createOAuth2(string $integration, string $userId)
{
$integrationEntity = $this->getEntityManager()->getEntity('Integration', $integration);
$externalAccountEntity = $this->getEntityManager()->getEntity('ExternalAccount', $integration . '__' . $userId);
@@ -122,7 +158,7 @@ class ClientManager
$oauth2Client = new \Espo\Core\ExternalAccount\OAuth2\Client();
$client = new $className($oauth2Client, array(
$client = new $className($oauth2Client, [
'endpoint' => $this->getMetadata()->get("integrations.{$integration}.params.endpoint"),
'tokenEndpoint' => $this->getMetadata()->get("integrations.{$integration}.params.tokenEndpoint"),
'clientId' => $integrationEntity->get('clientId'),
@@ -131,7 +167,8 @@ class ClientManager
'accessToken' => $externalAccountEntity->get('accessToken'),
'refreshToken' => $externalAccountEntity->get('refreshToken'),
'tokenType' => $externalAccountEntity->get('tokenType'),
), $this);
'expiresAt' => $externalAccountEntity->get('expiresAt'),
], $this);
$this->addToClientMap($client, $integrationEntity, $externalAccountEntity, $userId);
@@ -149,4 +186,3 @@ class ClientManager
);
}
}

View File

@@ -122,7 +122,6 @@ abstract class OAuth2Abstract implements IClient
if (isset($result['expires_in']) && is_numeric($result['expires_in'])) {
$data['expiresAt'] = (new \DateTime())
->modify('+' . $result['expires_in'] . ' seconds')
->modify('-1 seconds')
->format('Y-m-d H:i:s');
}
@@ -171,9 +170,28 @@ abstract class OAuth2Abstract implements IClient
}
}
protected function handleAccessTokenActuality()
{
if ($this->getParam('expiresAt')) {
try {
$dt = new \DateTime($this->getParam('expiresAt'));
$dt->modify('-10 seconds');
} catch (\Exception $e) {
return;
}
if ($dt->format('U') <= (new \DateTime())->format('U')) {
$GLOBALS['log']->debug("Oauth: Refreshing expired token for client {$this->clientId}");
$this->refreshToken();
}
}
}
public function request($url, $params = null, $httpMethod = Client::HTTP_METHOD_GET, $contentType = null, $allowRenew = true)
{
$httpHeaders = array();
$this->handleAccessTokenActuality();
$httpHeaders = [];
if (!empty($contentType)) {
$httpHeaders['Content-Type'] = $contentType;
switch ($contentType) {

View File

@@ -56,7 +56,9 @@ class ExternalAccount extends Record
throw new Error("{$integration} is disabled.");
}
$factory = new \Espo\Core\ExternalAccount\ClientManager($this->getEntityManager(), $this->getMetadata(), $this->getConfig());
$factory = new \Espo\Core\ExternalAccount\ClientManager(
$this->getEntityManager(), $this->getMetadata(), $this->getConfig(), $this->getInjection('injectableFactory')
);
return $factory->create($integration, $id);
}