Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi;
|
||||
|
||||
use ApplicationCore;
|
||||
use Xentral\Components\Http\Request;
|
||||
use Xentral\Components\HttpClient\HttpClientFactory;
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
use Xentral\Modules\GoogleApi\Client\GoogleApiClientFactory;
|
||||
use Xentral\Modules\GoogleApi\Service\GoogleAccountGateway;
|
||||
use Xentral\Modules\GoogleApi\Service\GoogleAccountService;
|
||||
use Xentral\Modules\GoogleApi\Service\GoogleAuthorizationService;
|
||||
use Xentral\Modules\GoogleApi\Service\GoogleCredentialsService;
|
||||
use Xentral\Modules\GoogleApi\Wrapper\CompanyConfigWrapper;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices(): array
|
||||
{
|
||||
return [
|
||||
'GoogleCredentialsService' => 'onInitGoogleCredentialsService',
|
||||
'GoogleAccountGateway' => 'onInitGoogleAccountGateway',
|
||||
'GoogleAccountService' => 'onInitGoogleAccountService',
|
||||
'GoogleAuthorizationService' => 'onInitGoogleAuthorizationService',
|
||||
'GoogleApiClientFactory' => 'onInitGoogleApiClientFactory',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return GoogleCredentialsService
|
||||
*/
|
||||
public static function onInitGoogleCredentialsService(ContainerInterface $container): GoogleCredentialsService
|
||||
{
|
||||
return new GoogleCredentialsService(
|
||||
self::onInitCompanyConfigWrapper($container)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return GoogleAccountGateway
|
||||
*/
|
||||
public static function onInitGoogleAccountGateway(ContainerInterface $container): GoogleAccountGateway
|
||||
{
|
||||
return new GoogleAccountGateway($container->get('Database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return GoogleAccountService
|
||||
*/
|
||||
public static function onInitGoogleAccountService(ContainerInterface $container): GoogleAccountService
|
||||
{
|
||||
return new GoogleAccountService(
|
||||
$container->get('GoogleAccountGateway'),
|
||||
$container->get('Database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return GoogleAuthorizationService
|
||||
*/
|
||||
public static function onInitGoogleAuthorizationService(ContainerInterface $container): GoogleAuthorizationService
|
||||
{
|
||||
/** @var GoogleCredentialsService $credentialService */
|
||||
$credentialService = $container->get('GoogleCredentialsService');
|
||||
/** @var Request $request */
|
||||
$request = $container->get('Request');
|
||||
/** @var HttpClientFactory $clientFactory */
|
||||
$clientFactory = $container->get('HttpClientFactory');
|
||||
$httpClient = $clientFactory->createClient();
|
||||
return new GoogleAuthorizationService(
|
||||
$container->get('GoogleAccountGateway'),
|
||||
$container->get('GoogleAccountService'),
|
||||
$httpClient,
|
||||
$credentialService->getCredentials(),
|
||||
$request->getBaseUrl()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return GoogleApiClientFactory
|
||||
*/
|
||||
public static function onInitGoogleApiClientFactory(ContainerInterface $container): GoogleApiClientFactory
|
||||
{
|
||||
return new GoogleApiClientFactory(
|
||||
$container->get('GoogleAccountGateway'),
|
||||
$container->get('GoogleAuthorizationService'),
|
||||
$container->get('HttpClientFactory')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return CompanyConfigWrapper
|
||||
*/
|
||||
private static function onInitCompanyConfigWrapper(ContainerInterface $container): CompanyConfigWrapper
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
|
||||
return new CompanyConfigWrapper($app->erp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Client;
|
||||
|
||||
use Xentral\Components\HttpClient\Exception\TransferErrorExceptionInterface;
|
||||
use Xentral\Components\HttpClient\HttpClientInterface;
|
||||
use Xentral\Components\HttpClient\Request\ClientRequest;
|
||||
use Xentral\Components\Logger\LoggerAwareTrait;
|
||||
use Xentral\Components\Util\StringUtil;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountData;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleApiRequestException;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleApiResponseException;
|
||||
|
||||
final class GoogleApiClient implements GoolgeApiClientInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/** @var HttpClientInterface $httpClient */
|
||||
private $httpClient;
|
||||
|
||||
/** @var GoogleAccountData $account */
|
||||
private $account;
|
||||
|
||||
/**
|
||||
* @param HttpClientInterface $client
|
||||
* @param GoogleAccountData $account
|
||||
*/
|
||||
public function __construct(HttpClientInterface $client, GoogleAccountData $account)
|
||||
{
|
||||
$this->httpClient = $client;
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function getAccount(): GoogleAccountData
|
||||
{
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @param array $data
|
||||
* @param array $headers
|
||||
*
|
||||
* @throws GoogleApiRequestException
|
||||
* @throws GoogleApiResponseException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sendRequest(
|
||||
string $method,
|
||||
string $uri,
|
||||
array $data = null,
|
||||
array $headers = []
|
||||
): array {
|
||||
$requestBody = null;
|
||||
if ($data !== null && is_array($data) && count($data) > 0) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$requestBody = json_encode($data);
|
||||
}
|
||||
|
||||
$request = new ClientRequest($method, $uri, $headers, $requestBody);
|
||||
try {
|
||||
$response = $this->httpClient->sendRequest($request);
|
||||
$this->logger->debug(
|
||||
'Google API request succeeded: {uri}',
|
||||
['uri' => $request->getUri(), 'request' => $request, 'response' => $response]
|
||||
);
|
||||
} catch (TransferErrorExceptionInterface $e) {
|
||||
$this->logger->warning(
|
||||
'Google API request failed: {uri} ERROR {code}',
|
||||
[
|
||||
'uri' => $request->getUri(),
|
||||
'code' => $e->getCode(),
|
||||
'request' => $request,
|
||||
'response' => $e->getResponse(),
|
||||
]
|
||||
);
|
||||
throw new GoogleApiRequestException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$contentType = mb_strtolower($response->getHeaderLine('content-type'));
|
||||
$responseBody = $response->getBody()->getContents();
|
||||
if ($responseBody !== '' && StringUtil::startsWith($contentType, 'application/json')) {
|
||||
$result = json_decode($responseBody, true);
|
||||
}
|
||||
|
||||
if ($result === false || $result === null || !is_array($result)) {
|
||||
throw new GoogleApiResponseException('Wrong format in JSON response.');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Client;
|
||||
|
||||
use Xentral\Components\HttpClient\HttpClientFactory;
|
||||
use Xentral\Components\HttpClient\RequestOptions;
|
||||
use Xentral\Components\Logger\LoggerAwareTrait;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountNotFoundException;
|
||||
use Xentral\Modules\GoogleApi\Exception\NoAccessTokenException;
|
||||
use Xentral\Modules\GoogleApi\Exception\NoRefreshTokenException;
|
||||
use Xentral\Modules\GoogleApi\Service\GoogleAccountGateway;
|
||||
use Xentral\Modules\GoogleApi\Service\GoogleAuthorizationService;
|
||||
|
||||
final class GoogleApiClientFactory
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/** @var GoogleAccountGateway $gateway */
|
||||
private $gateway;
|
||||
|
||||
/** @var GoogleAuthorizationService $authorizer */
|
||||
private $auth;
|
||||
|
||||
/** @var HttpClientFactory $clientFactory */
|
||||
private $clientFactory;
|
||||
|
||||
/**
|
||||
* @param GoogleAccountGateway $gateway
|
||||
* @param GoogleAuthorizationService $auth
|
||||
* @param HttpClientFactory $clientFactory
|
||||
*/
|
||||
public function __construct(
|
||||
GoogleAccountGateway $gateway,
|
||||
GoogleAuthorizationService $auth,
|
||||
HttpClientFactory $clientFactory
|
||||
)
|
||||
{
|
||||
$this->gateway = $gateway;
|
||||
$this->auth = $auth;
|
||||
$this->clientFactory = $clientFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws GoogleAccountNotFoundException
|
||||
* @throws NoRefreshTokenException
|
||||
*
|
||||
* @return GoogleApiClient
|
||||
*/
|
||||
public function createClient(int $userId): GoogleApiClient
|
||||
{
|
||||
$account = $this->gateway->getAccountByUser($userId);
|
||||
try{
|
||||
$token = $this->gateway->getAccessToken($account->getId());
|
||||
} catch (NoAccessTokenException $e) {
|
||||
$token = null;
|
||||
}
|
||||
if ($token === null || $token->getTimeToLive() < 10) {
|
||||
$token = $this->auth->refreshAccessToken($account);
|
||||
}
|
||||
$options = new RequestOptions();
|
||||
$options->setHeader(
|
||||
'Authorization',
|
||||
sprintf('Bearer %s', $token->getToken())
|
||||
);
|
||||
$options->setHeader('Accept', 'application/json');
|
||||
$httpClient = $this->clientFactory->createClient($options);
|
||||
$client = new GoogleApiClient($httpClient, $account);
|
||||
$client->setLogger($this->logger);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Client;
|
||||
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountData;
|
||||
|
||||
interface GoolgeApiClientInterface
|
||||
{
|
||||
/**
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function getAccount(): GoogleAccountData;
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @param array|null $data
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sendRequest(string $method, string $uri, array $data = null, array $headers = []): array;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Data;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use Xentral\Modules\GoogleApi\Exception\InvalidArgumentException;
|
||||
|
||||
final class GoogleAccessTokenData
|
||||
{
|
||||
/** @var int $accountId */
|
||||
private $accountId;
|
||||
|
||||
/** @var string $token */
|
||||
private $token;
|
||||
|
||||
/** @var DateTimeInterface $expirationDate */
|
||||
private $expirationDate;
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param string $token
|
||||
* @param DateTimeInterface $expirationDate
|
||||
*/
|
||||
public function __construct(
|
||||
int $accountId,
|
||||
string $token,
|
||||
DateTimeInterface $expirationDate
|
||||
) {
|
||||
$this->accountId = $accountId;
|
||||
$this->token = $token;
|
||||
$this->expirationDate = $expirationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return GoogleAccessTokenData
|
||||
*/
|
||||
public static function fromDbState($data): GoogleAccessTokenData
|
||||
{
|
||||
if (!isset($data['google_account_id'], $data['token'], $data['expires'])) {
|
||||
throw new InvalidArgumentException('Invalid Token Data.');
|
||||
}
|
||||
$expires = DateTime::createFromFormat('Y-m-d H:i:s', $data['expires']);
|
||||
|
||||
return new static($data['google_account_id'], $data['token'], $expires);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$expiration = null;
|
||||
if ($this->expirationDate !== null) {
|
||||
$expiration = $this->getExpirationDate()->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
return [
|
||||
'google_account_id' => $this->getAccountId(),
|
||||
'token' => $this->getToken(),
|
||||
'expires' => $expiration,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeToLive(): int
|
||||
{
|
||||
$now = new DateTime();
|
||||
$diff = $this->expirationDate->getTimestamp() - $now->getTimestamp();
|
||||
if ($diff < 0) {
|
||||
$diff = 0;
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAccountId(): int
|
||||
{
|
||||
return $this->accountId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getToken(): string
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTimeInterface
|
||||
*/
|
||||
public function getExpirationDate(): DateTimeInterface
|
||||
{
|
||||
return $this->expirationDate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Data;
|
||||
|
||||
use Xentral\Modules\GoogleApi\Exception\InvalidArgumentException;
|
||||
|
||||
final class GoogleAccountData
|
||||
{
|
||||
/** @var int */
|
||||
private $id;
|
||||
|
||||
/** @var int */
|
||||
private $userId;
|
||||
|
||||
/** @var string $refreshToken */
|
||||
private $refreshToken;
|
||||
|
||||
/** @var string $identifier */
|
||||
private $identifier;
|
||||
|
||||
/**
|
||||
* @param int|null $id
|
||||
* @param int $userId
|
||||
* @param string|null $identifier
|
||||
* @param string|null $refreshToken
|
||||
*/
|
||||
public function __construct(
|
||||
?int $id,
|
||||
int $userId,
|
||||
?string $identifier,
|
||||
string $refreshToken = null
|
||||
) {
|
||||
$this->id = $id;
|
||||
$this->userId = $userId;
|
||||
$this->identifier = $identifier;
|
||||
$this->refreshToken = $refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $dataSet
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public static function fromDbState($dataSet): GoogleAccountData
|
||||
{
|
||||
if (!isset($dataSet['user_id'], $dataSet['id'])) {
|
||||
throw new InvalidArgumentException('Invalid or incomplete Dataset.');
|
||||
}
|
||||
|
||||
return new self(
|
||||
$dataSet['id'],
|
||||
$dataSet['user_id'],
|
||||
$dataSet['identifier'],
|
||||
$dataSet['refresh_token']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'user_id' => $this->getUserId(),
|
||||
'identifier' => $this->getIdentifier(),
|
||||
'refresh_token' => $this->getRefreshToken(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUserId(): int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRefreshToken(): ?string
|
||||
{
|
||||
return $this->refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIdentifier(): ?string
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Data;
|
||||
|
||||
use Countable;
|
||||
|
||||
final class GoogleAccountPropertyCollection implements Countable
|
||||
{
|
||||
/** @var GoogleAccountPropertyValue[] $properties */
|
||||
private $properties;
|
||||
|
||||
/**
|
||||
* @param GoogleAccountPropertyValue[] $properties
|
||||
*/
|
||||
public function __construct(array $properties = [])
|
||||
{
|
||||
$this->properties = [];
|
||||
foreach ($properties as $property) {
|
||||
$this->add($property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->properties) && $this->properties[$key] !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get(string $key): ?string
|
||||
{
|
||||
if (!array_key_exists($key, $this->properties) || $this->properties[$key] === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->properties[$key]->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets property immutable
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
*
|
||||
* @return GoogleAccountPropertyCollection
|
||||
*/
|
||||
public function set(string $key, string $value): GoogleAccountPropertyCollection
|
||||
{
|
||||
if ($this->has($key)) {
|
||||
$property = new GoogleAccountPropertyValue(
|
||||
$this->properties[$key]->getId(),
|
||||
$this->properties[$key]->getAccountId(),
|
||||
$this->properties[$key]->getKey(),
|
||||
$value
|
||||
);
|
||||
} else {
|
||||
$property = new GoogleAccountPropertyValue(null, null, $key, $value);
|
||||
}
|
||||
$properties = clone($this);
|
||||
$properties->properties[$key] = $property;
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes property immutable
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return GoogleAccountPropertyCollection
|
||||
*/
|
||||
public function remove(string $key): GoogleAccountPropertyCollection
|
||||
{
|
||||
if (!array_key_exists($key, $this->properties)) {
|
||||
return $this;
|
||||
}
|
||||
$properties = clone($this);
|
||||
$properties->properties[$key] = null;
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GoogleAccountPropertyValue[]
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets properties as Key => Value array
|
||||
*
|
||||
* @return array [key => value]
|
||||
*/
|
||||
public function getkeyValueMap(): array
|
||||
{
|
||||
$array = [];
|
||||
foreach ($this->properties as $key => $obj) {
|
||||
if ($obj !== null) {
|
||||
$array[$key] = $obj->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->properties as $property) {
|
||||
if ($property !== null) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __debugInfo(): ?array
|
||||
{
|
||||
return $this->getkeyValueMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccountPropertyValue $property
|
||||
*/
|
||||
private function add(GoogleAccountPropertyValue $property): void
|
||||
{
|
||||
$this->properties[$property->getKey()] = $property;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Data;
|
||||
|
||||
use Xentral\Modules\GoogleApi\Exception\InvalidArgumentException;
|
||||
|
||||
final class GoogleAccountPropertyValue
|
||||
{
|
||||
/** @var string $key */
|
||||
private $key;
|
||||
|
||||
/** @var string $value */
|
||||
private $value;
|
||||
|
||||
/** @var int|null */
|
||||
private $id;
|
||||
|
||||
/** @var int|null */
|
||||
private $accountId;
|
||||
|
||||
/**
|
||||
* @param int|null $id
|
||||
* @param int $accountId
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct(?int $id, ?int $accountId, string $key, string $value)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->accountId = $accountId;
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return GoogleAccountPropertyValue
|
||||
*/
|
||||
public static function fromDbState(array $data): GoogleAccountPropertyValue
|
||||
{
|
||||
if (!isset($data['id'], $data['google_account_id'], $data['varname'], $data['value'])) {
|
||||
throw new InvalidArgumentException('Invalid or incomplete Dataset.');
|
||||
}
|
||||
|
||||
return new static($data['id'], $data['google_account_id'], $data['varname'], $data['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'google_account_id' => $this->getAccountId(),
|
||||
'varname' => $this->getKey(),
|
||||
'value' => $this->getValue()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getAccountId(): ?int
|
||||
{
|
||||
return $this->accountId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Data;
|
||||
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleCredentialsException;
|
||||
|
||||
final class GoogleCredentialsData
|
||||
{
|
||||
/** @var string|null $clientId */
|
||||
private $clientId;
|
||||
|
||||
/** @var string|null $clientSecret */
|
||||
private $clientSecret;
|
||||
|
||||
/** @var string|null $redirectUri */
|
||||
private $redirectUri;
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
* @param string $clientSecret
|
||||
* @param string $redirectUri
|
||||
*/
|
||||
public function __construct(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $redirectUri
|
||||
) {
|
||||
$this->clientId = $clientId;
|
||||
$this->clientSecret = $clientSecret;
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClientId(): ?string
|
||||
{
|
||||
return $this->clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClientSecret(): ?string
|
||||
{
|
||||
return $this->clientSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri(): ?string
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws GoogleCredentialsException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function validate(): void
|
||||
{
|
||||
if (empty($this->getClientId())) {
|
||||
throw new GoogleCredentialsException('Google client-id not set.');
|
||||
}
|
||||
if (empty($this->getClientSecret())) {
|
||||
throw new GoogleCredentialsException('Google client secret not set.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Data;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Xentral\Modules\GoogleApi\Exception\InvalidArgumentException;
|
||||
|
||||
final class GoogleTokenResponseData
|
||||
{
|
||||
/** @var string $accessToken */
|
||||
private $accessToken;
|
||||
|
||||
/** @var string[] $scopes */
|
||||
private $scopes;
|
||||
|
||||
/** @var string|null $tokenType */
|
||||
private $tokenType;
|
||||
|
||||
/** @var string|null $refreshToken */
|
||||
private $refreshToken;
|
||||
|
||||
/** @var DateTimeImmutable $expirationDate */
|
||||
private $expirationDate;
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
* @param int $expiresIn
|
||||
* @param array $scopes
|
||||
* @param string|null $tokenType
|
||||
* @param string|null $refreshToken
|
||||
*/
|
||||
private function __construct(
|
||||
string $accessToken,
|
||||
int $expiresIn,
|
||||
array $scopes,
|
||||
string $tokenType,
|
||||
?string $refreshToken = null
|
||||
) {
|
||||
$this->accessToken = $accessToken;
|
||||
$this->scopes = $scopes;
|
||||
$this->tokenType = $tokenType;
|
||||
$this->refreshToken = $refreshToken;
|
||||
$this->setExpiration($expiresIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return GoogleTokenResponseData
|
||||
*/
|
||||
public static function createfromResponseArray(array $data): GoogleTokenResponseData
|
||||
{
|
||||
if (!isset($data['access_token'], $data['expires_in'], $data['scope'], $data['token_type'])) {
|
||||
throw new InvalidArgumentException('Invalid token response.');
|
||||
}
|
||||
$scopes = explode(' ', $data['scope']);
|
||||
$obj = new static(
|
||||
$data['access_token'],
|
||||
$data['expires_in'],
|
||||
$scopes,
|
||||
$data['token_type']
|
||||
);
|
||||
if (array_key_exists('token_type', $data)) {
|
||||
$obj->tokenType = $data['token_type'];
|
||||
}
|
||||
if (array_key_exists('refresh_token', $data)) {
|
||||
$obj->refreshToken = $data['refresh_token'];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRefreshToken(): bool
|
||||
{
|
||||
return !empty($this->refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTimeInterface
|
||||
*/
|
||||
public function getExpirationDate(): DateTimeInterface
|
||||
{
|
||||
return $this->expirationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopes(): array
|
||||
{
|
||||
return $this->scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTokenType(): string
|
||||
{
|
||||
return $this->tokenType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRefreshToken(): ?string
|
||||
{
|
||||
return $this->refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $expiresIn
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setExpiration(int $expiresIn): void
|
||||
{
|
||||
$this->expirationDate = new DateTimeImmutable('now');
|
||||
$interval = new DateInterval(sprintf('PT%sS', $expiresIn));
|
||||
$this->expirationDate = $this->expirationDate->add($interval);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class AuthorizationExpiredException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class CsrfViolationException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleAccountAlreadyExistsException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleAccountDeleteException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleAccountException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleAccountGatewayException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleAccountNotFoundException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ModuleExceptionInterface;
|
||||
|
||||
interface GoogleApiExceptionInterface extends ModuleExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleApiRequestException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleApiResponseException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class GoogleCredentialsException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use InvalidArgumentException as SplInvalidArgumentException;
|
||||
|
||||
class InvalidArgumentException extends SplInvalidArgumentException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class NoAccessTokenException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class NoRefreshTokenException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class UserConsentException extends RuntimeException implements GoogleApiExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi;
|
||||
|
||||
final class GoogleScope
|
||||
{
|
||||
/** @var string CLOUDPRINT */
|
||||
public const CLOUDPRINT = 'https://www.googleapis.com/auth/cloudprint';
|
||||
|
||||
/** @var string CALENDAR */
|
||||
public const CALENDAR = 'https://www.googleapis.com/auth/calendar';
|
||||
|
||||
/** @var string MAIL */
|
||||
public const MAIL = 'https://mail.google.com/';
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Service;
|
||||
|
||||
use Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Components\Database\SqlQuery\SelectQuery;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccessTokenData;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountPropertyValue;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountPropertyCollection;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountData;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountGatewayException;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountNotFoundException;
|
||||
use Xentral\Modules\GoogleApi\Exception\NoAccessTokenException;
|
||||
use Xentral\Modules\GoogleApi\GoogleScope;
|
||||
|
||||
final class GoogleAccountGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function existsAccount(int $id): bool
|
||||
{
|
||||
$sql = 'SELECT ga.id FROM `google_account` AS `ga` WHERE ga.id = :id';
|
||||
$resultId = $this->db->fetchValue($sql, ['id' => $id]);
|
||||
|
||||
return $resultId === $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @throws GoogleAccountNotFoundException
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function getAccount(int $id): GoogleAccountData
|
||||
{
|
||||
$query = $this->buildAccountQuery()
|
||||
->where('ga.id = :id');
|
||||
$values = ['id' => $id];
|
||||
$account = $this->queryAccount($query, $values);
|
||||
if ($account === null) {
|
||||
throw new GoogleAccountNotFoundException('Google Account not Available.');
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws GoogleAccountNotFoundException
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function getAccountByUser(int $userId): GoogleAccountData
|
||||
{
|
||||
$query = $this->buildAccountQuery()
|
||||
->where('ga.user_id = :user_id');
|
||||
$values = ['user_id' => $userId];
|
||||
$account = $this->queryAccount($query, $values);
|
||||
if ($account === null) {
|
||||
throw new GoogleAccountNotFoundException(
|
||||
sprintf('No Google account found for user "%s"', $userId)
|
||||
);
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*
|
||||
* @throws GoogleAccountNotFoundException
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function getAccountByGmailAddress(string $email): GoogleAccountData
|
||||
{
|
||||
try {
|
||||
$query = $this->buildAccountQuery()
|
||||
->join('', 'google_account_property AS gp', 'ga.id = gp.google_account_id')
|
||||
->where("gp.varname = :varname AND gp.value = :email");
|
||||
$values = ['varname' => 'gmail_address', 'email' => $email];
|
||||
$account = $this->queryAccount($query, $values);
|
||||
} catch (Exception $e) {
|
||||
throw new GoogleAccountNotFoundException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
if ($account === null) {
|
||||
throw new GoogleAccountNotFoundException(
|
||||
sprintf('No Google Account found for email address "%s"', $email)
|
||||
);
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets all accounts that have a certain scope
|
||||
*
|
||||
* @param string $scope
|
||||
*
|
||||
* @return GoogleAccountData[]|array
|
||||
*/
|
||||
public function getAccountsByScope(string $scope): array
|
||||
{
|
||||
try {
|
||||
$query = $this->buildAccountQuery()
|
||||
->join('', 'google_account_scope AS gs', 'ga.id = gs.google_account_id')
|
||||
->where('gs.scope = :scope');
|
||||
$values = ['scope' => $scope];
|
||||
$rows = $this->db->fetchAll($query->getStatement(), $values);
|
||||
$accounts = [];
|
||||
foreach ($rows as $row) {
|
||||
$accounts[] = GoogleAccountData::fromDbState($row);
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
} catch (Exception $e) {
|
||||
throw new GoogleAccountGatewayException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $addressId
|
||||
*
|
||||
* @throws GoogleAccountGatewayException
|
||||
*
|
||||
* @return GoogleAccountData|null
|
||||
*/
|
||||
public function tryGetAccountByAddress(int $addressId): ?GoogleAccountData
|
||||
{
|
||||
try {
|
||||
$query = $this->buildAccountQuery()
|
||||
->join('', 'user AS u', 'ga.user_id = u.id')
|
||||
->where('u.adresse = :address_id');
|
||||
$values = ['address_id' => $addressId];
|
||||
|
||||
return $this->queryAccount($query, $values);
|
||||
} catch (Exception $e) {
|
||||
throw new GoogleAccountGatewayException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
*
|
||||
* @throws NoAccessTokenException
|
||||
*
|
||||
* @return GoogleAccessTokenData
|
||||
*/
|
||||
public function getAccessToken(int $accountId): GoogleAccessTokenData
|
||||
{
|
||||
$query = $this->buildTokenQuery()
|
||||
->where('gt.google_account_id = :account_id');
|
||||
$values = ['account_id' => $accountId];
|
||||
$token = $this->queryToken($query, $values);
|
||||
if ($token === null) {
|
||||
throw new NoAccessTokenException(
|
||||
sprintf('No access token for account "%s" available.', $accountId)
|
||||
);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param string $scope
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAccountScope(int $accountId, string $scope): bool
|
||||
{
|
||||
$scopes = $this->getScopes($accountId);
|
||||
if (in_array($scope, $scopes, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return string[] available scopes of the account
|
||||
*/
|
||||
public function getScopes(int $accountId): array
|
||||
{
|
||||
$sql = 'SELECT gs.id, gs.scope FROM `google_account_scope` AS `gs` WHERE gs.google_account_id = :account_id';
|
||||
$pairs = $this->db->fetchPairs($sql, ['account_id' => $accountId]);
|
||||
if (!is_array($pairs) || count($pairs) === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values($pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be removed after Dec 31. 2020
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function getCloudPrintAccount(): GoogleAccountData
|
||||
{
|
||||
$accounts = $this->getAccountsByScope(GoogleScope::CLOUDPRINT);
|
||||
if (count($accounts) < 1) {
|
||||
throw new GoogleAccountNotFoundException('No cloud printing account available.');
|
||||
}
|
||||
|
||||
return $accounts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return GoogleAccountPropertyCollection
|
||||
*/
|
||||
public function getAccountProperties(int $accountId): GoogleAccountPropertyCollection
|
||||
{
|
||||
$sql = 'SELECT gp.id, gp.google_account_id, gp.varname, gp.value
|
||||
FROM `google_account_property` AS `gp`
|
||||
WHERE gp.google_account_id = :account_id';
|
||||
$result = $this->db->fetchAll($sql, ['account_id'=> $accountId]);
|
||||
if (!is_array($result) || count($result) === 0) {
|
||||
return new GoogleAccountPropertyCollection([]);
|
||||
}
|
||||
$properties = [];
|
||||
foreach ($result as $row) {
|
||||
$properties[] = GoogleAccountPropertyValue::fromDbState($row);
|
||||
}
|
||||
|
||||
return new GoogleAccountPropertyCollection($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SelectQuery
|
||||
*/
|
||||
private function buildAccountQuery(): SelectQuery
|
||||
{
|
||||
return $this->db->select()
|
||||
->cols(
|
||||
[
|
||||
'ga.id',
|
||||
'ga.user_id',
|
||||
'ga.refresh_token',
|
||||
'ga.identifier',
|
||||
]
|
||||
)
|
||||
->from('google_account AS ga');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SelectQuery $query
|
||||
* @param array $bindValues
|
||||
*
|
||||
* @return GoogleAccountData|null
|
||||
*/
|
||||
private function queryAccount(SelectQuery $query, $bindValues = []): ?GoogleAccountData
|
||||
{
|
||||
$sql = $query->getStatement();
|
||||
$resultSet = $this->db->fetchRow($sql, $bindValues);
|
||||
if (empty($resultSet)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return GoogleAccountData::fromDbState($resultSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SelectQuery
|
||||
*/
|
||||
private function buildTokenQuery(): SelectQuery
|
||||
{
|
||||
return $this->db->select()
|
||||
->cols(
|
||||
[
|
||||
'gt.google_account_id',
|
||||
'gt.token',
|
||||
'gt.expires',
|
||||
]
|
||||
)
|
||||
->from('google_access_token AS gt');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SelectQuery $query
|
||||
* @param array $bindValues
|
||||
*
|
||||
* @return GoogleAccessTokenData|null
|
||||
*/
|
||||
private function queryToken(SelectQuery $query, $bindValues = []): ?GoogleAccessTokenData
|
||||
{
|
||||
$sql = $query->getStatement();
|
||||
$resultSet = $this->db->fetchRow($sql, $bindValues);
|
||||
if (empty($resultSet)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return GoogleAccessTokenData::fromDbState($resultSet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Service;
|
||||
|
||||
use Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccessTokenData;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountPropertyValue;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountPropertyCollection;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountData;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountAlreadyExistsException;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountDeleteException;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountNotFoundException;
|
||||
use Xentral\Modules\GoogleApi\Exception\InvalidArgumentException;
|
||||
|
||||
final class GoogleAccountService
|
||||
{
|
||||
/** @var GoogleAccountGateway $gateway */
|
||||
private $gateway;
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param GoogleAccountGateway $gateway
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(GoogleAccountGateway $gateway, Database $database)
|
||||
{
|
||||
$this->gateway = $gateway;
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param string|null $identifier
|
||||
* @param string|null $refreshToken
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws GoogleAccountAlreadyExistsException
|
||||
* @throws GoogleAccountNotFoundException
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function createAccount(int $userId, ?string $identifier, string $refreshToken = null): GoogleAccountData
|
||||
{
|
||||
if ($userId < 1) {
|
||||
throw new InvalidArgumentException('Cannot create Google Account without User Id.');
|
||||
}
|
||||
try {
|
||||
$this->gateway->getAccountByUser($userId);
|
||||
throw new GoogleAccountAlreadyExistsException('A Google account already exists for this user.');
|
||||
} catch (GoogleAccountNotFoundException $e) {
|
||||
}
|
||||
|
||||
$account = new GoogleAccountData(null, $userId, $identifier, $refreshToken);
|
||||
$id = $this->insertAccount($account);
|
||||
|
||||
return $this->gateway->getAccount($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccountData $account
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function saveAccount(GoogleAccountData $account): int
|
||||
{
|
||||
if ($account->getId() === null || !$this->gateway->existsAccount($account->getId())) {
|
||||
return $this->insertAccount($account);
|
||||
}
|
||||
|
||||
return $this->updateAccount($account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the google user account entry and all associated tokens, scopes and properties
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @throws GoogleAccountDeleteException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAccount(int $id): void
|
||||
{
|
||||
$this->db->beginTransaction();
|
||||
try {
|
||||
$queries = [
|
||||
'DELETE FROM `google_account` WHERE `id` = :id',
|
||||
'DELETE FROM `google_access_token` WHERE `google_account_id` = :id',
|
||||
'DELETE FROM `google_account_property` WHERE `google_account_id` = :id',
|
||||
'DELETE FROM `google_account_scope` WHERE `google_account_id` = :id',
|
||||
];
|
||||
foreach ($queries as $sql) {
|
||||
$this->db->perform($sql, ['id' => $id]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->db->rollBack();
|
||||
throw new GoogleAccountDeleteException('Could not Delete Google Account', $e->getCode(), $e);
|
||||
}
|
||||
$this->db->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccessTokenData $token
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveAccessToken(GoogleAccessTokenData $token): void
|
||||
{
|
||||
$values = $token->toArray();
|
||||
$update = 'UPDATE `google_access_token`
|
||||
SET `token` = :token, `expires` = :expires
|
||||
WHERE `google_account_id` = :google_account_id';
|
||||
$affected = $this->db->fetchAffected($update, $values);
|
||||
if ($affected > 0) {
|
||||
return;
|
||||
}
|
||||
$insert = 'INSERT INTO `google_access_token` (`google_account_id`, `token`, `expires`)
|
||||
VALUES (:google_account_id, :token, :expires)';
|
||||
$this->db->perform($insert, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccessTokenData $token
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAccessToken(GoogleAccessTokenData $token): void
|
||||
{
|
||||
$sql = 'DELETE FROM `google_access_token` WHERE `google_account_id` = :google_account_id';
|
||||
$this->db->perform($sql, $token->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param GoogleAccountPropertyCollection $properties
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveAccountProperties(int $accountId, GoogleAccountPropertyCollection $properties): void
|
||||
{
|
||||
foreach ($properties->getAll() as $key => $property) {
|
||||
if ($property === null) {
|
||||
$this->deleteProperty($accountId, $key);
|
||||
continue;
|
||||
}
|
||||
if ($property->getId() === null) {
|
||||
$this->insertProperty($accountId, $property);
|
||||
continue;
|
||||
}
|
||||
$this->updateProperty($accountId, $property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param string $scope
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveAccountScope(int $accountId, string $scope): void
|
||||
{
|
||||
$existingScopes = $this->gateway->getScopes($accountId);
|
||||
if (in_array($scope, $existingScopes, true)) {
|
||||
return;
|
||||
}
|
||||
$sql = 'INSERT INTO `google_account_scope` (`google_account_id`, `scope`)
|
||||
VALUES (:account_id, :scope)';
|
||||
$this->db->perform($sql, ['account_id' => $accountId, 'scope' => $scope]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAccountScopes(int $accountId): void
|
||||
{
|
||||
$sql = 'DELETE FROM `google_account_scope` WHERE `google_account_id` = :account_id';
|
||||
$this->db->perform($sql, ['account_id' => $accountId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccountData $account
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function insertAccount(GoogleAccountData $account): int
|
||||
{
|
||||
$sql = 'INSERT INTO `google_account` (`user_id`, `refresh_token`, `identifier`) VALUES
|
||||
(:user_id, :refresh_token, :identifier)';
|
||||
$values = $account->toArray();
|
||||
$this->db->perform($sql, $values);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccountData $account
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function updateAccount(GoogleAccountData $account): int
|
||||
{
|
||||
$sql = 'UPDATE `google_account` SET
|
||||
`user_id` = :user_id,
|
||||
`identifier` = :identifier,
|
||||
`refresh_token` = :refresh_token
|
||||
WHERE `id` = :id';
|
||||
$values = $account->toArray();
|
||||
$this->db->perform($sql, $values);
|
||||
|
||||
return $account->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param GoogleAccountPropertyValue $property
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function insertProperty(int $accountId, GoogleAccountPropertyValue $property): void
|
||||
{
|
||||
$sql = 'INSERT INTO `google_account_property` (`google_account_id`, `varname`, `value`)
|
||||
VALUES (:account_id, :varname, :value)';
|
||||
$values = $property->toArray();
|
||||
$values['account_id'] = $accountId;
|
||||
$this->db->perform($sql, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param GoogleAccountPropertyValue $property
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function updateProperty(int $accountId, GoogleAccountPropertyValue $property): void
|
||||
{
|
||||
$sql = 'UPDATE `google_account_property`
|
||||
SET `google_account_id` = :account_id, `varname` = :varname, `value` = :value
|
||||
WHERE `id` = :id';
|
||||
$values = $property->toArray();
|
||||
$values['account_id'] = $accountId;
|
||||
$this->db->perform($sql, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param string $varname
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function deleteProperty(int $accountId, string $varname): void
|
||||
{
|
||||
$sql = 'DELETE FROM `google_account_property`
|
||||
WHERE `google_account_id` = :account_id AND `varname` = :varname';
|
||||
$values = ['account_id' => $accountId, 'varname' => $varname];
|
||||
$this->db->perform($sql, $values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Service;
|
||||
|
||||
use Exception;
|
||||
use Xentral\Components\Http\RedirectResponse;
|
||||
use Xentral\Components\Http\Request;
|
||||
use Xentral\Components\Http\Session\Session;
|
||||
use Xentral\Components\HttpClient\Exception\ClientErrorException;
|
||||
use Xentral\Components\HttpClient\Exception\ServerErrorException;
|
||||
use Xentral\Components\HttpClient\Exception\TransferErrorExceptionInterface;
|
||||
use Xentral\Components\HttpClient\HttpClient;
|
||||
use Xentral\Components\HttpClient\HttpClientInterface;
|
||||
use Xentral\Components\HttpClient\Request\ClientRequest;
|
||||
use Xentral\Components\Logger\LoggerAwareTrait;
|
||||
use Xentral\Components\Util\StringUtil;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccessTokenData;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleAccountData;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleCredentialsData;
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleTokenResponseData;
|
||||
use Xentral\Modules\GoogleApi\Exception\AuthorizationExpiredException;
|
||||
use Xentral\Modules\GoogleApi\Exception\CsrfViolationException;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleAccountNotFoundException;
|
||||
use Xentral\Modules\GoogleApi\Exception\GoogleCredentialsException;
|
||||
use Xentral\Modules\GoogleApi\Exception\InvalidArgumentException;
|
||||
use Xentral\Modules\GoogleApi\Exception\NoAccessTokenException;
|
||||
use Xentral\Modules\GoogleApi\Exception\NoRefreshTokenException;
|
||||
use Xentral\Modules\GoogleApi\Exception\UserConsentException;
|
||||
|
||||
final class GoogleAuthorizationService
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/** @var string SESSION_SEGMENT */
|
||||
private const SESSION_SEGMENT = 'googleapiauth';
|
||||
|
||||
/** @var string CSRF_KEY */
|
||||
private const CSRF_KEY = 'google_user_authorization';
|
||||
|
||||
/** @var string SESSION_KEY_URI */
|
||||
private const SESSION_KEY_URI = 'uri_after_authorization';
|
||||
|
||||
/** @var string URL_AUTHORIZATION_CODE */
|
||||
private const URL_AUTHORIZATION_CODE = 'https://accounts.google.com/o/oauth2/auth';
|
||||
|
||||
/** @var string URL_TOKEN_FETCH */
|
||||
private const URL_TOKEN_FETCH = 'https://accounts.google.com/o/oauth2/token';
|
||||
|
||||
/** @var string URL_TOKEN_REFRESH */
|
||||
private const URL_TOKEN_REFRESH = 'https://www.googleapis.com/oauth2/v3/token';
|
||||
|
||||
/** @var string URL_TOKEN_REVOKE */
|
||||
private const URL_TOKEN_REVOKE = 'https://accounts.google.com/o/oauth2/revoke';
|
||||
|
||||
/** @var GoogleAccountGateway $gateway */
|
||||
private $gateway;
|
||||
|
||||
/** @var GoogleAccountService $service */
|
||||
private $service;
|
||||
|
||||
/** @var HttpClient $httpClient */
|
||||
private $httpClient;
|
||||
|
||||
/** @var string $baseUrl */
|
||||
private $baseUrl;
|
||||
|
||||
/** @var GoogleCredentialsData $credentials */
|
||||
private $credentials;
|
||||
|
||||
/**
|
||||
* @param GoogleAccountGateway $gateway
|
||||
* @param GoogleAccountService $service
|
||||
* @param HttpClientInterface $httpClient
|
||||
* @param GoogleCredentialsData $credentials
|
||||
* @param string $requestBaseUrl
|
||||
*/
|
||||
public function __construct(
|
||||
GoogleAccountGateway $gateway,
|
||||
GoogleAccountService $service,
|
||||
HttpClientInterface $httpClient,
|
||||
GoogleCredentialsData $credentials,
|
||||
string $requestBaseUrl
|
||||
) {
|
||||
$this->gateway = $gateway;
|
||||
$this->service = $service;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->baseUrl = $requestBaseUrl;
|
||||
$this->credentials = $credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Session $session
|
||||
* @param string[] $scopes
|
||||
* @param string $uriAfterRedirect
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws GoogleCredentialsException
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function requestScopeAuthorization(
|
||||
Session $session,
|
||||
array $scopes = [],
|
||||
string $uriAfterRedirect = 'index.php?module=welcome&action=settings'
|
||||
): RedirectResponse {
|
||||
if (count($scopes) === 0) {
|
||||
throw new InvalidArgumentException('No scopes for Google authorization defined.');
|
||||
}
|
||||
$this->credentials->validate();
|
||||
$clientId = $this->credentials->getClientId();
|
||||
$session->setValue(self::SESSION_SEGMENT, self::SESSION_KEY_URI, $uriAfterRedirect);
|
||||
$csrfToken = $session->createCsrfToken(self::CSRF_KEY);
|
||||
$redirectUri = $this->credentials->getRedirectUri();
|
||||
if ($redirectUri === null || $redirectUri === '') {
|
||||
$redirectUri = $this->getDefaultRedirectUri();
|
||||
}
|
||||
$scopeParam = implode(' ', $scopes);
|
||||
$queryParams = [
|
||||
'client_id' => $clientId,
|
||||
'redirect_uri' => $redirectUri,
|
||||
'response_type' => 'code',
|
||||
'scope' => $scopeParam,
|
||||
'access_type' => 'offline',
|
||||
'include_granted_scopes' => 'true',
|
||||
'state' => $csrfToken,
|
||||
];
|
||||
$url = sprintf('%s?%s', self::URL_AUTHORIZATION_CODE, http_build_query($queryParams));
|
||||
|
||||
return RedirectResponse::createFromUrl($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Session $session
|
||||
* @param Request $request
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function authorizationCallback(Session $session, Request $request, int $userId): RedirectResponse
|
||||
{
|
||||
$code = $request->get->get('code');
|
||||
$scopes = explode(' ', $request->get->get('scope', ''));
|
||||
$error = $request->get->get('error');
|
||||
$csrfToken = $request->get->get('state');
|
||||
if (
|
||||
$csrfToken === null
|
||||
|| !$session->isCsrfTokenValid(self::CSRF_KEY, $csrfToken, true)
|
||||
) {
|
||||
throw new CsrfViolationException('Invalid CSRF token in authorization.');
|
||||
}
|
||||
|
||||
// error in callback means the user declined access
|
||||
if ($error !== null) {
|
||||
$this->logger->error(
|
||||
'User consent rejected by "user_id={user}" original error: "{error}"',
|
||||
['user_id' => $userId, 'error' => $error]
|
||||
);
|
||||
throw new UserConsentException($error);
|
||||
}
|
||||
|
||||
// find/create account
|
||||
try {
|
||||
$account = $this->gateway->getAccountByUser($userId);
|
||||
} catch (GoogleAccountNotFoundException $e) {
|
||||
$account = $this->service->createAccount($userId, null);
|
||||
}
|
||||
|
||||
// store granted scopes
|
||||
$this->service->deleteAccountScopes($account->getId());
|
||||
foreach ($scopes as $scope) {
|
||||
$this->service->saveAccountScope($account->getId(), $scope);
|
||||
}
|
||||
|
||||
// fetch and save refresh token
|
||||
$array = $this->fetchTokenByAuthCode($code);
|
||||
$tokenResponse = GoogleTokenResponseData::createfromResponseArray($array);
|
||||
if ($tokenResponse->hasRefreshToken()) {
|
||||
$account = new GoogleAccountData(
|
||||
$account->getId(),
|
||||
$account->getUserId(),
|
||||
$account->getIdentifier(),
|
||||
$tokenResponse->getRefreshToken()
|
||||
);
|
||||
$this->service->saveAccount($account);
|
||||
}
|
||||
|
||||
// cache access token
|
||||
$accessToken = new GoogleAccessTokenData(
|
||||
$account->getId(),
|
||||
$tokenResponse->getAccessToken(),
|
||||
$tokenResponse->getExpirationDate()
|
||||
);
|
||||
$this->service->saveAccessToken($accessToken);
|
||||
|
||||
// read redirect uri from session
|
||||
$redirectUri = $session->getValue(
|
||||
self::SESSION_SEGMENT,
|
||||
self::SESSION_KEY_URI,
|
||||
'index.php?module=googleapi&action=edit',
|
||||
true
|
||||
);
|
||||
|
||||
return RedirectResponse::createFromUrl($redirectUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccountData $account
|
||||
*
|
||||
* @throws NoRefreshTokenException
|
||||
* @throws GoogleCredentialsException
|
||||
* @throws AuthorizationExpiredException
|
||||
*
|
||||
* @return GoogleAccessTokenData
|
||||
*/
|
||||
public function refreshAccessToken(GoogleAccountData $account): GoogleAccessTokenData
|
||||
{
|
||||
$this->credentials->validate();
|
||||
$refresh_token = $account->getRefreshToken();
|
||||
if ($refresh_token === null) {
|
||||
$this->logger->warning(
|
||||
'User "id={user_id} has no Google refresh token.',
|
||||
['user_id' => $account->getUserId()]
|
||||
);
|
||||
try {
|
||||
$refresh_token = $this->gateway->getAccessToken($account->getId())->getToken();
|
||||
} catch (NoAccessTokenException $e) {
|
||||
throw new NoRefreshTokenException('Account not authorized.');
|
||||
}
|
||||
}
|
||||
$postData = [
|
||||
'refresh_token' => $refresh_token,
|
||||
'client_id' => $this->credentials->getClientId(),
|
||||
'client_secret' => $this->credentials->getClientSecret(),
|
||||
'grant_type' => 'refresh_token',
|
||||
];
|
||||
try {
|
||||
$array = $this->apiRequest('POST', self::URL_TOKEN_REFRESH, $postData);
|
||||
} catch (ClientErrorException $e) {
|
||||
$this->logger->error(
|
||||
'Fetching new Google access token failed. Repeat the Authorization process!',
|
||||
['exception' => $e]
|
||||
);
|
||||
$this->revokeAuthorization($account);
|
||||
throw new AuthorizationExpiredException(
|
||||
'Failed to fetch access token. Try to repeat the Google authorization process.',
|
||||
$e->getCode(),
|
||||
$e
|
||||
);
|
||||
}
|
||||
$tokenResponse = GoogleTokenResponseData::createfromResponseArray($array);
|
||||
$accessToken = new GoogleAccessTokenData(
|
||||
$account->getId(),
|
||||
$tokenResponse->getAccessToken(),
|
||||
$tokenResponse->getExpirationDate()
|
||||
);
|
||||
$this->service->saveAccessToken($accessToken);
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleAccountData $account
|
||||
*
|
||||
* @return GoogleAccountData
|
||||
*/
|
||||
public function revokeAuthorization(GoogleAccountData $account): GoogleAccountData
|
||||
{
|
||||
try {
|
||||
$accessToken = $this->gateway->getAccessToken($account->getId());
|
||||
$this->revokeToken($accessToken->getToken());
|
||||
$this->service->deleteAccessToken($accessToken);
|
||||
} catch (NoAccessTokenException $e) {
|
||||
}
|
||||
if ($account->getRefreshToken() !== null) {
|
||||
$this->revokeToken($account->getRefreshToken());
|
||||
$account = new GoogleAccountData(
|
||||
$account->getId(),
|
||||
$account->getUserId(),
|
||||
$account->getIdentifier(),
|
||||
null
|
||||
);
|
||||
$this->service->saveAccount($account);
|
||||
}
|
||||
$this->service->deleteAccountScopes($account->getId());
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultRedirectUri(): string
|
||||
{
|
||||
return sprintf('%s/index.php?module=googleapi&action=redirect', $this->baseUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token refresh_token or access_token
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
public function revokeToken(string $token): bool
|
||||
{
|
||||
$url = sprintf('%s?token=%s', self::URL_TOKEN_REVOKE, $token);
|
||||
try {
|
||||
$this->apiRequest('GET', $url, null, []);
|
||||
} catch (ClientErrorException $e) {
|
||||
return true;
|
||||
} catch (ServerErrorException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authorizationCode
|
||||
*
|
||||
* @throws GoogleCredentialsException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function fetchTokenByAuthCode(string $authorizationCode): array
|
||||
{
|
||||
$redirectUri = $this->credentials->getRedirectUri();
|
||||
if (empty($redirectUri)) {
|
||||
$redirectUri = $this->getDefaultRedirectUri();
|
||||
}
|
||||
$this->credentials->validate();
|
||||
$postData = [
|
||||
'code' => $authorizationCode,
|
||||
'client_id' => $this->credentials->getClientId(),
|
||||
'client_secret' => $this->credentials->getClientSecret(),
|
||||
'redirect_uri' => $redirectUri,
|
||||
'grant_type' => 'authorization_code',
|
||||
];
|
||||
|
||||
return $this->apiRequest('POST', self::URL_TOKEN_FETCH, $postData, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array|null $data
|
||||
* @param array $headers
|
||||
*
|
||||
* @throws ClientErrorException
|
||||
* @throws ServerErrorException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function apiRequest($method, $url, $data = null, $headers = []): array
|
||||
{
|
||||
$requestBody = null;
|
||||
if ($data !== null) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$requestBody = json_encode($data);
|
||||
}
|
||||
|
||||
$request = new ClientRequest($method, $url, $headers, $requestBody);
|
||||
try {
|
||||
$response = $this->httpClient->sendRequest($request);
|
||||
$this->logger->debug(
|
||||
'Google authorization request succeeded: {uri}',
|
||||
['uri' => $request->getUri(), 'request' => $request, 'response' => $response]
|
||||
);
|
||||
} catch (TransferErrorExceptionInterface $e) {
|
||||
$code = $e->getCode();
|
||||
$this->logger->warning(
|
||||
'Google authorization request failed: {uri} ERROR {code}',
|
||||
[
|
||||
'uri' => $request->getUri(),
|
||||
'code' => $code,
|
||||
'request' => $request,
|
||||
'response' => $e->getResponse(),
|
||||
]
|
||||
);
|
||||
if ($code > 399 && $code < 500) {
|
||||
throw new ClientErrorException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
if ($code > 499 && $code < 600) {
|
||||
throw new ServerErrorException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
$contentType = $response->getHeaderLine('content-type');
|
||||
$responseBody = $response->getBody()->getContents();
|
||||
$result = [];
|
||||
if ($responseBody !== '' && StringUtil::startsWith($contentType, 'application/json')) {
|
||||
$result = json_decode($responseBody, true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Service;
|
||||
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleCredentialsData;
|
||||
use Xentral\Modules\GoogleApi\Wrapper\CompanyConfigWrapper;
|
||||
|
||||
final class GoogleCredentialsService implements GoogleCredentialsServiceInterface
|
||||
{
|
||||
/** @var CompanyConfigWrapper $config */
|
||||
private $config;
|
||||
|
||||
public function __construct(CompanyConfigWrapper $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GoogleCredentialsData
|
||||
*/
|
||||
public function getCredentials(): GoogleCredentialsData
|
||||
{
|
||||
$clientID = $this->config->get(self::KEY_CLIENT_ID);
|
||||
$secret = $this->config->get(self::KEY_CLIENT_SECRET);
|
||||
$uri = $this->config->get(self::KEY_REDIRECT_URI);
|
||||
|
||||
return new GoogleCredentialsData($clientID, $secret, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function existCredentials(): bool
|
||||
{
|
||||
$clientID = $this->config->get(self::KEY_CLIENT_ID);
|
||||
$secret = $this->config->get(self::KEY_CLIENT_SECRET);
|
||||
|
||||
return (is_string($clientID) && $clientID !== '') && (is_string($secret) && $secret !== '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GoogleCredentialsData $account
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveCredentials(GoogleCredentialsData $account): void
|
||||
{
|
||||
$this->config->set(self::KEY_CLIENT_ID, $account->getClientId());
|
||||
$this->config->set(self::KEY_CLIENT_SECRET, $account->getClientSecret());
|
||||
$this->config->set(self::KEY_REDIRECT_URI, $account->getRedirectUri());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCredentials(): void
|
||||
{
|
||||
$this->config->set(self::KEY_CLIENT_ID, null);
|
||||
$this->config->set(self::KEY_CLIENT_SECRET, null);
|
||||
$this->config->set(self::KEY_REDIRECT_URI, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Service;
|
||||
|
||||
use Xentral\Modules\GoogleApi\Data\GoogleCredentialsData;
|
||||
|
||||
interface GoogleCredentialsServiceInterface
|
||||
{
|
||||
/** @var string KEY_CLIENT_ID */
|
||||
public const KEY_CLIENT_ID = 'googleapi_client_id';
|
||||
|
||||
/** @var string KEY_CLIENT_SECRET */
|
||||
public const KEY_CLIENT_SECRET = 'googleapi_client_secret';
|
||||
|
||||
/** @var string KEY_REDIRECT_URI */
|
||||
public const KEY_REDIRECT_URI = 'googleapi_redirect_uri';
|
||||
|
||||
/**
|
||||
* @return GoogleCredentialsData
|
||||
*/
|
||||
public function getCredentials(): GoogleCredentialsData;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function existCredentials(): bool;
|
||||
|
||||
/**
|
||||
* @param GoogleCredentialsData $account
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveCredentials(GoogleCredentialsData $account): void;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCredentials(): void;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\GoogleApi\Wrapper;
|
||||
|
||||
use erpAPI;
|
||||
|
||||
/**
|
||||
* Anti-Corruption-Layer für erp::GetKonfiguration und erp::SetKonfigurationValue
|
||||
*/
|
||||
final class CompanyConfigWrapper
|
||||
{
|
||||
/** @var erpAPI $erp */
|
||||
private $erp;
|
||||
|
||||
/**
|
||||
* @param erpAPI $erp
|
||||
*/
|
||||
public function __construct(erpAPI $erp)
|
||||
{
|
||||
$this->erp = $erp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $name)
|
||||
{
|
||||
return $this->erp->GetKonfiguration($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set(string $name, $value)
|
||||
{
|
||||
$this->erp->SetKonfigurationValue($name, $value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
div .actionpane {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.btnGreenNew[disabled] {
|
||||
background-color: lightgrey !important;
|
||||
}
|
||||
|
||||
a.btnGreenNew.button {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Für die Bedienung der Modul-Oberfläche
|
||||
*/
|
||||
var GoogleApiUI = (function ($) {
|
||||
'use strict';
|
||||
|
||||
var me = {
|
||||
|
||||
isInitialized: false,
|
||||
|
||||
storage: {
|
||||
$table: null,
|
||||
dataTable: null
|
||||
},
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
init: function () {
|
||||
if (me.isInitialized === true) {
|
||||
return;
|
||||
}
|
||||
me.storage.$table = $('#googleapi_list');
|
||||
me.storage.dataTable = me.storage.$table.dataTable();
|
||||
me.registerEvents();
|
||||
|
||||
me.isInitialized = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
registerEvents: function () {
|
||||
|
||||
$(document).on('click', '.googleapi-delete', function (e) {
|
||||
e.preventDefault();
|
||||
var fieldId = $(this).data('googleapi-id');
|
||||
me.deleteItem(fieldId);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} fieldId
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
deleteItem: function (fieldId) {
|
||||
var confirmValue = confirm('Wirklich löschen?');
|
||||
if (confirmValue === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: 'index.php?module=googleapi&action=delete',
|
||||
data: {
|
||||
id: fieldId
|
||||
},
|
||||
method: 'post',
|
||||
dataType: 'json',
|
||||
beforeSend: function () {
|
||||
App.loading.open();
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.success === true) {
|
||||
me.reloadDataTable();
|
||||
}
|
||||
if (data.success === false) {
|
||||
alert('Unbekannter Fehler beim Löschen.');
|
||||
}
|
||||
App.loading.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
reloadDataTable: function () {
|
||||
me.storage.dataTable.api().ajax.reload();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init: me.init,
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
GoogleApiUI.init();
|
||||
});
|
||||
Reference in New Issue
Block a user