Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
@@ -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;
}