mail/lib/Integration/GoogleIntegration.php

172 строки
5.5 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Mail\Integration;
use Exception;
use OCA\Mail\Account;
use OCA\Mail\AppInfo\Application;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\Security\ICrypto;
use Psr\Log\LoggerInterface;
use function json_decode;
use function json_encode;
class GoogleIntegration {
private ITimeFactory $timeFactory;
private IConfig $config;
private ICrypto $crypto;
private IClientService $clientService;
private IURLGenerator $urlGenerator;
private LoggerInterface $logger;
public function __construct(ITimeFactory $timeFactory,
IConfig $config,
ICrypto $crypto,
IClientService $clientService,
IURLGenerator $urlGenerator,
LoggerInterface $logger) {
$this->timeFactory = $timeFactory;
$this->clientService = $clientService;
$this->crypto = $crypto;
$this->config = $config;
$this->urlGenerator = $urlGenerator;
$this->logger = $logger;
}
public function configure(string $clientId, string $clientSecret): void {
$this->config->setAppValue(
Application::APP_ID,
'google_oauth_client_id',
$clientId
);
$this->config->setAppValue(
Application::APP_ID,
'google_oauth_client_secret',
$this->crypto->encrypt($clientSecret),
);
}
public function unlink() {
$this->config->deleteAppValue(
Application::APP_ID,
'google_oauth_client_id',
);
$this->config->deleteAppValue(
Application::APP_ID,
'google_oauth_client_secret',
);
}
public function getClientId(): ?string {
$value = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_id');
if ($value === '') {
return null;
}
return $value;
}
public function isGoogleOauthAccount(Account $account): bool {
return $account->getMailAccount()->getInboundHost() === 'imap.gmail.com'
&& $account->getMailAccount()->getAuthMethod() === 'xoauth2';
}
public function finishConnect(Account $account,
string $code): Account {
$clientId = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_id');
$encryptedClientSecret = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_secret');
if (empty($clientId) || empty($encryptedClientSecret)) {
// This is highly unexpected
$this->logger->critical('Can not finish Google account linking due to missing client secrets');
return $account;
}
$clientSecret = $this->crypto->decrypt($encryptedClientSecret);
$httpClient = $this->clientService->newClient();
try {
$response = $httpClient->post('https://oauth2.googleapis.com/token', [
'content-type' => 'application/json',
'body' => json_encode([
'client_id' => $clientId,
'client_secret' => $clientSecret,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->getRedirectUrl(),
'code' => $code,
], JSON_THROW_ON_ERROR)
]);
} catch (Exception $e) {
$this->logger->error('Could not link Google account: ' . $e->getMessage(), [
'exception' => $e,
]);
return $account;
}
$data = json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR);
$encryptedRefreshToken = $this->crypto->encrypt($data['refresh_token']);
$account->getMailAccount()->setOauthRefreshToken($encryptedRefreshToken);
$encryptedAccessToken = $this->crypto->encrypt($data['access_token']);
$account->getMailAccount()->setOauthAccessToken($encryptedAccessToken);
$account->getMailAccount()->setOauthTokenTtl($this->timeFactory->getTime() + $data['expires_in']);
return $account;
}
public function refresh(Account $account): Account {
if ($account->getMailAccount()->getOauthTokenTtl() === null || $account->getMailAccount()->getOauthRefreshToken() === null) {
// Account is not authorized yet
return $account;
}
// Only refresh if the token expires in the next minute
if ($this->timeFactory->getTime() <= ($account->getMailAccount()->getOauthTokenTtl() - 60)) {
// No need to refresh yet
return $account;
}
$clientId = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_id');
$encryptedClientSecret = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_secret');
if (empty($clientId) || empty($encryptedClientSecret)) {
// Nothing to do here
return $account;
}
$refreshToken = $this->crypto->decrypt($account->getMailAccount()->getOauthRefreshToken());
$clientSecret = $this->crypto->decrypt($encryptedClientSecret);
$httpClient = $this->clientService->newClient();
try {
$response = $httpClient->post('https://oauth2.googleapis.com/token', [
'content-type' => 'application/json',
'body' => json_encode([
'client_id' => $clientId,
'client_secret' => $clientSecret,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
], JSON_THROW_ON_ERROR)
]);
} catch (Exception $e) {
$this->logger->warning('Could not refresh oauth token: ' . $e->getMessage(), [
'exception' => $e,
]);
return $account;
}
$data = json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR);
$encryptedAccessToken = $this->crypto->encrypt($data['access_token']);
$account->getMailAccount()->setOauthAccessToken($encryptedAccessToken);
$account->getMailAccount()->setOauthTokenTtl($this->timeFactory->getTime() + $data['expires_in']);
return $account;
}
public function getRedirectUrl(): string {
return $this->urlGenerator->linkToRouteAbsolute('mail.googleIntegration.oauthRedirect');
}
}