mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 06:56:05 +00:00
oauth providers
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldProcessing\OAuthAccount;
|
||||
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Entities\OAuthAccount;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\OAuth\ConfigDataProvider;
|
||||
|
||||
/**
|
||||
* @implements Loader<OAuthAccount>
|
||||
*/
|
||||
class DataLoader implements Loader
|
||||
{
|
||||
public function __construct(
|
||||
private ConfigDataProvider $configDataProvider,
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
if (!$entity->get('providerId')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$provider = $entity->getProvider();
|
||||
|
||||
$scope = null;
|
||||
|
||||
if ($provider->getScopes()) {
|
||||
$scope = implode($provider->getScopeSeparator() ?? ' ', $provider->getScopes());
|
||||
}
|
||||
|
||||
$data = [
|
||||
'endpoint' => $provider->getAuthorizationEndpoint(),
|
||||
'clientId' => $provider->getClientId(),
|
||||
'redirectUri' => $this->configDataProvider->getRedirectUri(),
|
||||
'scope' => $scope,
|
||||
'prompt' => $provider->getAuthorizationPrompt(),
|
||||
'params' => $provider->getAuthorizationParams(),
|
||||
];
|
||||
|
||||
$entity->set('data', $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldProcessing\OAuthProvider;
|
||||
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Entities\OAuthProvider;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\OAuth\ConfigDataProvider;
|
||||
|
||||
/**
|
||||
* @implements Loader<OAuthProvider>
|
||||
*/
|
||||
class AuthorizationRedirectUriLoader implements Loader
|
||||
{
|
||||
public function __construct(
|
||||
private ConfigDataProvider $configDataProvider,
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
$entity->set('authorizationRedirectUri', $this->configDataProvider->getRedirectUri());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\OAuthProvider;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
use Espo\Core\Utils\Crypt;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class GeneralFilter implements Filter
|
||||
{
|
||||
private const ATTR_CLIENT_SECRET = 'clientSecret';
|
||||
|
||||
public function __construct(private Crypt $crypt) {}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
$this->processClientSecret($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function processClientSecret(Data $data): void
|
||||
{
|
||||
$value = $data->get(self::ATTR_CLIENT_SECRET);
|
||||
|
||||
if ($value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$data->set(self::ATTR_CLIENT_SECRET, $this->crypt->encrypt($value));
|
||||
}
|
||||
}
|
||||
47
application/Espo/Controllers/OAuthAccount.php
Normal file
47
application/Espo/Controllers/OAuthAccount.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Controllers\RecordBase;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class OAuthAccount extends RecordBase
|
||||
{
|
||||
protected function checkAccess(): bool
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
47
application/Espo/Controllers/OAuthProvider.php
Normal file
47
application/Espo/Controllers/OAuthProvider.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Controllers\Record;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class OAuthProvider extends Record
|
||||
{
|
||||
protected function checkAccess(): bool
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Formula\Functions\ExtGroup\OauthGroup;
|
||||
|
||||
use Espo\Core\Formula\EvaluatedArgumentList;
|
||||
use Espo\Core\Formula\Exceptions\BadArgumentType;
|
||||
use Espo\Core\Formula\Exceptions\Error;
|
||||
use Espo\Core\Formula\Exceptions\TooFewArguments;
|
||||
use Espo\Core\Formula\Func;
|
||||
use Espo\Tools\OAuth\Exceptions\AccountNotFound;
|
||||
use Espo\Tools\OAuth\Exceptions\NoToken;
|
||||
use Espo\Tools\OAuth\Exceptions\ProviderNotAvailable;
|
||||
use Espo\Tools\OAuth\Exceptions\TokenObtainingFailure;
|
||||
use Espo\Tools\OAuth\TokensProvider;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class GetAccessTokenType implements Func
|
||||
{
|
||||
public function __construct(
|
||||
private TokensProvider $provider,
|
||||
) {}
|
||||
|
||||
public function process(EvaluatedArgumentList $arguments): string
|
||||
{
|
||||
if (count($arguments) < 1) {
|
||||
throw TooFewArguments::create(1);
|
||||
}
|
||||
|
||||
$id = $arguments[0];
|
||||
|
||||
if (!is_string($id)) {
|
||||
throw BadArgumentType::create(1, 'string');
|
||||
}
|
||||
|
||||
try {
|
||||
$tokens = $this->provider->get($arguments[0]);
|
||||
} catch (AccountNotFound|NoToken|ProviderNotAvailable|TokenObtainingFailure $e) {
|
||||
$message = "Could not obtain access token for OAuth account $id.";
|
||||
|
||||
throw new Error($message, 500, $e);
|
||||
}
|
||||
|
||||
$accessToken = $tokens->getAccessToken();
|
||||
|
||||
if (!$accessToken) {
|
||||
throw new Error("No access token.");
|
||||
}
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
}
|
||||
83
application/Espo/Entities/OAuthAccount.php
Normal file
83
application/Espo/Entities/OAuthAccount.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
/** @noinspection PhpMultipleClassDeclarationsInspection */
|
||||
|
||||
namespace Espo\Entities;
|
||||
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\ORM\Entity;
|
||||
use ValueError;
|
||||
|
||||
class OAuthAccount extends Entity
|
||||
{
|
||||
public const ENTITY_TYPE = 'OAuthAccount';
|
||||
|
||||
public function getProvider(): OAuthProvider
|
||||
{
|
||||
$provider = $this->relations->getOne('provider');
|
||||
|
||||
if (!$provider instanceof OAuthProvider) {
|
||||
throw new ValueError("No provider.");
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
public function getAccessToken(): ?string
|
||||
{
|
||||
return $this->get('accessToken');
|
||||
}
|
||||
|
||||
public function getRefreshToken(): ?string
|
||||
{
|
||||
return $this->get('refreshToken');
|
||||
}
|
||||
|
||||
public function getExpiresAt(): ?DateTime
|
||||
{
|
||||
/** @var ?DateTime */
|
||||
return $this->getValueObject('expiresAt');
|
||||
}
|
||||
|
||||
public function setAccessToken(?string $accessToken): self
|
||||
{
|
||||
return $this->set('accessToken', $accessToken);
|
||||
}
|
||||
|
||||
public function setRefreshToken(?string $refreshToken): self
|
||||
{
|
||||
return $this->set('refreshToken', $refreshToken);
|
||||
}
|
||||
|
||||
public function setExpiresAt(?DateTime $expiresAt): self
|
||||
{
|
||||
return $this->setValueObject('expiresAt', $expiresAt);
|
||||
}
|
||||
}
|
||||
118
application/Espo/Entities/OAuthProvider.php
Normal file
118
application/Espo/Entities/OAuthProvider.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
/** @noinspection PhpMultipleClassDeclarationsInspection */
|
||||
|
||||
namespace Espo\Entities;
|
||||
|
||||
use Espo\Core\ORM\Entity;
|
||||
use stdClass;
|
||||
use ValueError;
|
||||
|
||||
class OAuthProvider extends Entity
|
||||
{
|
||||
public const ENTITY_TYPE = 'OAuthProvider';
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->get('isActive');
|
||||
}
|
||||
|
||||
public function getClientId(): string
|
||||
{
|
||||
$value = $this->get('clientId');
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new ValueError("No client ID.");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getClientSecret(): string
|
||||
{
|
||||
$value = $this->get('clientSecret');
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new ValueError("No client secret.");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getTokenEndpoint(): string
|
||||
{
|
||||
$value = $this->get('tokenEndpoint');
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new ValueError("No token endpoint.");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getAuthorizationEndpoint(): string
|
||||
{
|
||||
$value = $this->get('authorizationEndpoint');
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new ValueError("No authorization endpoint.");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getResourceEndpoint(): ?string
|
||||
{
|
||||
return $this->get('resourceEndpoint');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopes(): array
|
||||
{
|
||||
return $this->get('scopes') ?? [];
|
||||
}
|
||||
|
||||
public function getScopeSeparator(): ?string
|
||||
{
|
||||
return $this->get('scopeSeparator');
|
||||
}
|
||||
|
||||
public function getAuthorizationPrompt(): string
|
||||
{
|
||||
return $this->get('authorizationPrompt');
|
||||
}
|
||||
|
||||
public function getAuthorizationParams(): ?stdClass
|
||||
{
|
||||
return $this->get('authorizationParams') ?? null;
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,7 @@
|
||||
"Group Email Folders": "Group Email Folders",
|
||||
"Authentication Providers": "Authentication Providers",
|
||||
"Address Countries": "Address Countries",
|
||||
"OAuth Providers": "OAuth Providers",
|
||||
"Success": "Success",
|
||||
"Fail": "Fail",
|
||||
"Configuration Instructions": "Configuration Instructions",
|
||||
@@ -318,7 +319,8 @@
|
||||
"sms": "SMS settings.",
|
||||
"pdfTemplates": "Templates for printing to PDF.",
|
||||
"formulaSandbox": "Write and test formula scripts.",
|
||||
"addressCountries": "Countries available for address fields."
|
||||
"addressCountries": "Countries available for address fields.",
|
||||
"oAuthProviders": "OAuth providers for integrations."
|
||||
},
|
||||
"keywords": {
|
||||
"settings": "system",
|
||||
|
||||
@@ -64,7 +64,9 @@
|
||||
"AuthenticationProvider": "Authentication Provider",
|
||||
"GlobalStream": "Global Stream",
|
||||
"AddressCountry": "Address Country",
|
||||
"AppSecret": "App Secret"
|
||||
"AppSecret": "App Secret",
|
||||
"OAuthProvider": "OAuth Provider",
|
||||
"OAuthAccount": "OAuth Account"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Note": "Notes",
|
||||
@@ -119,7 +121,9 @@
|
||||
"AuthenticationProvider": "Authentication Providers",
|
||||
"GlobalStream": "Global Stream",
|
||||
"AddressCountry": "Address Countries",
|
||||
"AppSecret": "App Secrets"
|
||||
"AppSecret": "App Secrets",
|
||||
"OAuthProvider": "OAuth Providers",
|
||||
"OAuthAccount": "OAuth Accounts"
|
||||
},
|
||||
"labels": {
|
||||
"Previous Page": "Previous Page",
|
||||
|
||||
16
application/Espo/Resources/i18n/en_US/OAuthAccount.json
Normal file
16
application/Espo/Resources/i18n/en_US/OAuthAccount.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create OAuthAccount": "Create OAuth Account",
|
||||
"Connection": "Connection"
|
||||
},
|
||||
"fields": {
|
||||
"provider": "Provider",
|
||||
"hasAccessToken": "Has Access Token",
|
||||
"user": "User",
|
||||
"providerIsActive": "Provider is Active",
|
||||
"data": "Data"
|
||||
},
|
||||
"links": {
|
||||
"provider": "Provider"
|
||||
}
|
||||
}
|
||||
25
application/Espo/Resources/i18n/en_US/OAuthProvider.json
Normal file
25
application/Espo/Resources/i18n/en_US/OAuthProvider.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create OAuthProvider": "Create OAuth Provider"
|
||||
},
|
||||
"fields": {
|
||||
"isActive": "Is Active",
|
||||
"clientId": "Client ID",
|
||||
"clientSecret": "Client Secret",
|
||||
"authorizationEndpoint": "Authorization Endpoint",
|
||||
"tokenEndpoint": "Token Endpoint",
|
||||
"resourceEndpoint": "Resource Endpoint",
|
||||
"authorizationRedirectUri": "Authorization Redirect URI",
|
||||
"scopes": "Scopes",
|
||||
"scopeSeparator": "Scope Separator",
|
||||
"hasAccessToken": "Has Access Token",
|
||||
"authorizationPrompt": "Authorization Prompt",
|
||||
"authorizationParams": "Authorization Params"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Accounts"
|
||||
},
|
||||
"tooltips": {
|
||||
"authorizationParams": "Additional query parameters to be sent to the authorization endpoint. Specified in JSON format."
|
||||
}
|
||||
}
|
||||
13
application/Espo/Resources/layouts/OAuthAccount/detail.json
Normal file
13
application/Espo/Resources/layouts/OAuthAccount/detail.json
Normal file
@@ -0,0 +1,13 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "name"}, {"name": "provider"}],
|
||||
[{"name": "user"}, {"name": "hasAccessToken"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "description"}]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "name"}],
|
||||
[{"name": "provider"}],
|
||||
[{"name": "user"}, {"name": "hasAccessToken"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "description"}]
|
||||
]
|
||||
}
|
||||
]
|
||||
10
application/Espo/Resources/layouts/OAuthAccount/list.json
Normal file
10
application/Espo/Resources/layouts/OAuthAccount/list.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"width": 50
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "hasAccessToken",
|
||||
"width": 50
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"width": 50
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"accounts": {
|
||||
"index": 0
|
||||
}
|
||||
}
|
||||
27
application/Espo/Resources/layouts/OAuthProvider/detail.json
Normal file
27
application/Espo/Resources/layouts/OAuthProvider/detail.json
Normal file
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "name"}, {"name": "isActive"}],
|
||||
[{"name": "clientId"}, {"name": "clientSecret"}],
|
||||
[{"name": "authorizationRedirectUri"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "authorizationEndpoint"}, {"name": "tokenEndpoint"}],
|
||||
[{"name": "resourceEndpoint"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "scopes"}],
|
||||
[{"name": "scopeSeparator"}, false],
|
||||
[{"name": "authorizationParams"}, {"name": "authorizationPrompt"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "description"}]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "name"}, {"name": "isActive"}]
|
||||
]
|
||||
}
|
||||
]
|
||||
10
application/Espo/Resources/layouts/OAuthProvider/list.json
Normal file
10
application/Espo/Resources/layouts/OAuthProvider/list.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "isActive",
|
||||
"width": 50
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "isActive",
|
||||
"width": 50
|
||||
}
|
||||
]
|
||||
@@ -350,6 +350,12 @@
|
||||
"iconClass": "fas fa-key",
|
||||
"description": "appSecrets"
|
||||
},
|
||||
{
|
||||
"url": "#Admin/oAuthProviders",
|
||||
"label": "OAuth Providers",
|
||||
"iconClass": "fas fa-sign-in-alt",
|
||||
"description": "oAuthProviders"
|
||||
},
|
||||
{
|
||||
"url": "#Admin/appLog",
|
||||
"label": "App Log",
|
||||
|
||||
@@ -625,6 +625,12 @@
|
||||
"insertText": "ext\\acl\\getPermissionLevel(USER_ID, PERMISSION)",
|
||||
"returnType": "string",
|
||||
"unsafe": true
|
||||
},
|
||||
{
|
||||
"name": "ext\\oauth\\getAccessToken",
|
||||
"insertText": "ext\\oauth\\getAccessToken(ID)",
|
||||
"returnType": "string",
|
||||
"unsafe": true
|
||||
}
|
||||
],
|
||||
"functionClassNameMap": {
|
||||
@@ -637,6 +643,7 @@
|
||||
"ext\\acl\\getLevel": "Espo\\Core\\Formula\\Functions\\ExtGroup\\AclGroup\\GetLevelType",
|
||||
"ext\\acl\\getPermissionLevel": "Espo\\Core\\Formula\\Functions\\ExtGroup\\AclGroup\\GetPermissionLevelType",
|
||||
"util\\base64Encode": "Espo\\Core\\Formula\\Functions\\UtilGroup\\Base64EncodeType",
|
||||
"util\\base64Decode": "Espo\\Core\\Formula\\Functions\\UtilGroup\\Base64DecodeType"
|
||||
"util\\base64Decode": "Espo\\Core\\Formula\\Functions\\UtilGroup\\Base64DecodeType",
|
||||
"ext\\oauth\\getAccessToken": "Espo\\Core\\Formula\\Functions\\ExtGroup\\OauthGroup\\GetAccessTokenType"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,11 @@
|
||||
"exportsTo": "ace.require.define.modules",
|
||||
"exportsAs": "ace/mode/javascript"
|
||||
},
|
||||
"ace-mode-json": {
|
||||
"path": "client/lib/ace-mode-json.js",
|
||||
"exportsTo": "ace.require.define.modules",
|
||||
"exportsAs": "ace/mode/json"
|
||||
},
|
||||
"ace-ext-language_tools": {
|
||||
"path": "client/lib/ace-ext-language_tools.js",
|
||||
"exportsTo": "ace.require.define.modules",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"controller": "controllers/record",
|
||||
"sidePanels": {
|
||||
"detail": [
|
||||
{
|
||||
"name": "connection",
|
||||
"label": "Connection",
|
||||
"view": "views/o-auth-account/records/panels/connection",
|
||||
"notRefreshable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"controller": "controllers/record",
|
||||
"relationshipPanels": {
|
||||
"accounts": {
|
||||
"layout": "listForProvider",
|
||||
"selectDisabled": true,
|
||||
"unlinkDisabled": true
|
||||
}
|
||||
},
|
||||
"dynamicLogic": {
|
||||
"fields": {
|
||||
"authorizationRedirectUri": {
|
||||
"visible": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isNotEmpty",
|
||||
"attribute": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"clientId": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "isActive"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"clientSecret": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "isActive"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"authorizationEndpoint": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "isActive"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"tokenEndpoint": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "isActive"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"fields": {
|
||||
"accessToken": {
|
||||
"forbidden": true
|
||||
},
|
||||
"refreshToken": {
|
||||
"forbidden": true
|
||||
},
|
||||
"expiresAt": {
|
||||
"forbidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fields": {
|
||||
"clientSecret": {
|
||||
"internal": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"maxLength": 100
|
||||
},
|
||||
"provider": {
|
||||
"type": "link",
|
||||
"required": true,
|
||||
"readOnlyAfterCreate": true
|
||||
},
|
||||
"user": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"hasAccessToken": {
|
||||
"type": "bool",
|
||||
"readOnly": true,
|
||||
"notStorable": true,
|
||||
"orderDisabled": true,
|
||||
"directAccessDisabled": true,
|
||||
"select": {
|
||||
"select": "IS_NOT_NULL:(accessToken)"
|
||||
}
|
||||
},
|
||||
"providerIsActive": {
|
||||
"type": "foreign",
|
||||
"link": "provider",
|
||||
"field": "isActive"
|
||||
},
|
||||
"data": {
|
||||
"type": "jsonObject",
|
||||
"notStorable": true,
|
||||
"directAccessDisabled": true,
|
||||
"readOnly": true
|
||||
},
|
||||
"accessToken": {
|
||||
"type": "password",
|
||||
"readOnly": true,
|
||||
"dbType": "text"
|
||||
},
|
||||
"refreshToken": {
|
||||
"type": "password",
|
||||
"readOnly": true,
|
||||
"dbType": "text"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"expiresAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"provider": {
|
||||
"type": "belongsTo",
|
||||
"entity": "OAuthProvider",
|
||||
"foreign": "accounts"
|
||||
},
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"maxLength": 100
|
||||
},
|
||||
"isActive": {
|
||||
"type": "bool",
|
||||
"default": true
|
||||
},
|
||||
"clientId": {
|
||||
"type": "varchar",
|
||||
"maxLength": 150
|
||||
},
|
||||
"clientSecret": {
|
||||
"type": "password",
|
||||
"maxLength": 512,
|
||||
"dbType": "text"
|
||||
},
|
||||
"authorizationEndpoint": {
|
||||
"type": "url",
|
||||
"maxLength": 512,
|
||||
"dbType": "text",
|
||||
"strip": false
|
||||
},
|
||||
"tokenEndpoint": {
|
||||
"type": "url",
|
||||
"maxLength": 512,
|
||||
"dbType": "text",
|
||||
"strip": false
|
||||
},
|
||||
"resourceEndpoint": {
|
||||
"type": "url",
|
||||
"maxLength": 512,
|
||||
"dbType": "text",
|
||||
"strip": false
|
||||
},
|
||||
"authorizationRedirectUri": {
|
||||
"type": "url",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"copyToClipboard": true,
|
||||
"directAccessDisabled": true
|
||||
},
|
||||
"authorizationPrompt": {
|
||||
"type": "enum",
|
||||
"default": "none",
|
||||
"options": [
|
||||
"none",
|
||||
"consent",
|
||||
"login",
|
||||
"select_account"
|
||||
],
|
||||
"maxLength": 14
|
||||
},
|
||||
"scopes": {
|
||||
"type": "array",
|
||||
"noEmptyString": true,
|
||||
"allowCustomOptions": true,
|
||||
"storeArrayValues": false,
|
||||
"displayAsList": true
|
||||
},
|
||||
"authorizationParams": {
|
||||
"type": "jsonObject",
|
||||
"view": "views/o-auth-provider/fields/authorization-params",
|
||||
"tooltip": true
|
||||
},
|
||||
"scopeSeparator": {
|
||||
"type": "varchar",
|
||||
"maxLength": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"accounts": {
|
||||
"type": "hasMany",
|
||||
"entity": "OAuthAccount",
|
||||
"foreign": "provider",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"readLoaderClassNameList": [
|
||||
"Espo\\Classes\\FieldProcessing\\OAuthAccount\\DataLoader"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"readLoaderClassNameList": [
|
||||
"Espo\\Classes\\FieldProcessing\\OAuthProvider\\AuthorizationRedirectUriLoader"
|
||||
],
|
||||
"createInputFilterClassNameList": [
|
||||
"Espo\\Classes\\Record\\OAuthProvider\\GeneralFilter"
|
||||
],
|
||||
"updateInputFilterClassNameList": [
|
||||
"Espo\\Classes\\Record\\OAuthProvider\\GeneralFilter"
|
||||
],
|
||||
"duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"entity": true
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"entity": true,
|
||||
"duplicateCheckFieldList": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
@@ -485,6 +485,16 @@
|
||||
},
|
||||
"noAuth": true
|
||||
},
|
||||
{
|
||||
"method": "post",
|
||||
"route": "/OAuth/:id/connection",
|
||||
"actionClassName": "Espo\\Tools\\OAuth\\Api\\PostConnection"
|
||||
},
|
||||
{
|
||||
"method": "delete",
|
||||
"route": "/OAuth/:id/connection",
|
||||
"actionClassName": "Espo\\Tools\\OAuth\\Api\\DeleteConnection"
|
||||
},
|
||||
{
|
||||
"route": "/:controller/:id",
|
||||
"method": "get",
|
||||
|
||||
61
application/Espo/Tools/OAuth/Api/DeleteConnection.php
Normal file
61
application/Espo/Tools/OAuth/Api/DeleteConnection.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Api;
|
||||
|
||||
use Espo\Core\Api\Action;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\ResponseComposer;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Record\EntityProvider;
|
||||
use Espo\Entities\OAuthAccount;
|
||||
use Espo\Tools\OAuth\ConnectionService;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class DeleteConnection implements Action
|
||||
{
|
||||
public function __construct(
|
||||
private ConnectionService $service,
|
||||
private EntityProvider $entityProvider,
|
||||
) {}
|
||||
|
||||
public function process(Request $request): Response
|
||||
{
|
||||
$id = $request->getRouteParam('id') ?? throw new BadRequest();
|
||||
|
||||
$account = $this->entityProvider->getByClass(OAuthAccount::class, $id);
|
||||
|
||||
$this->service->disconnect($account);
|
||||
|
||||
return ResponseComposer::json(true);
|
||||
}
|
||||
}
|
||||
66
application/Espo/Tools/OAuth/Api/PostConnection.php
Normal file
66
application/Espo/Tools/OAuth/Api/PostConnection.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Api;
|
||||
|
||||
use Espo\Core\Api\Action;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\ResponseComposer;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Record\EntityProvider;
|
||||
use Espo\Entities\OAuthAccount;
|
||||
use Espo\Tools\OAuth\ConnectionService;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class PostConnection implements Action
|
||||
{
|
||||
public function __construct(
|
||||
private ConnectionService $service,
|
||||
private EntityProvider $entityProvider,
|
||||
) {}
|
||||
|
||||
public function process(Request $request): Response
|
||||
{
|
||||
$id = $request->getRouteParam('id') ?? throw new BadRequest();
|
||||
$code = $request->getParsedBody()->code ?? null;
|
||||
|
||||
if (!is_string($code)) {
|
||||
throw new BadRequest("No code.");
|
||||
}
|
||||
|
||||
$account = $this->entityProvider->getByClass(OAuthAccount::class, $id);
|
||||
|
||||
$this->service->connect($account, $code);
|
||||
|
||||
return ResponseComposer::json(true);
|
||||
}
|
||||
}
|
||||
44
application/Espo/Tools/OAuth/ConfigDataProvider.php
Normal file
44
application/Espo/Tools/OAuth/ConfigDataProvider.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth;
|
||||
|
||||
use Espo\Core\Utils\Config\ApplicationConfig;
|
||||
|
||||
class ConfigDataProvider
|
||||
{
|
||||
public function __construct(
|
||||
private ApplicationConfig $config,
|
||||
) {}
|
||||
|
||||
public function getRedirectUri(): string
|
||||
{
|
||||
return $this->config->getSiteUrl() . '/oauth/callback';
|
||||
}
|
||||
}
|
||||
82
application/Espo/Tools/OAuth/ConnectionService.php
Normal file
82
application/Espo/Tools/OAuth/ConnectionService.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Entities\OAuthAccount;
|
||||
use Espo\ORM\EntityManager;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
|
||||
class ConnectionService
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private GenericProviderFactory $genericProviderFactory,
|
||||
private TokenSetter $tokenSetter,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws Error
|
||||
*/
|
||||
public function connect(OAuthAccount $account, string $code): void
|
||||
{
|
||||
$provider = $account->getProvider();
|
||||
|
||||
if (!$provider->isActive()) {
|
||||
throw new Forbidden("Provider is not active.");
|
||||
}
|
||||
|
||||
$genericProvider = $this->genericProviderFactory->create($provider);
|
||||
|
||||
try {
|
||||
$tokens = $genericProvider->getAccessToken('authorization_code', ['code' => $code]);
|
||||
} catch (GuzzleException $e) {
|
||||
throw new Error("Token request error.", 500, $e);
|
||||
} catch (IdentityProviderException $e) {
|
||||
throw new Error("Token request response error.", 500, $e);
|
||||
}
|
||||
|
||||
$this->tokenSetter->set($account, $tokens);
|
||||
|
||||
$this->entityManager->saveEntity($account);
|
||||
}
|
||||
|
||||
public function disconnect(OAuthAccount $account): void
|
||||
{
|
||||
$account->setAccessToken(null);
|
||||
$account->setRefreshToken(null);
|
||||
$account->setExpiresAt(null);
|
||||
|
||||
$this->entityManager->saveEntity($account);
|
||||
}
|
||||
}
|
||||
33
application/Espo/Tools/OAuth/Exceptions/AccountNotFound.php
Normal file
33
application/Espo/Tools/OAuth/Exceptions/AccountNotFound.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Exceptions;
|
||||
|
||||
class AccountNotFound extends OAuthException
|
||||
{}
|
||||
33
application/Espo/Tools/OAuth/Exceptions/NoToken.php
Normal file
33
application/Espo/Tools/OAuth/Exceptions/NoToken.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Exceptions;
|
||||
|
||||
class NoToken extends OAuthException
|
||||
{}
|
||||
35
application/Espo/Tools/OAuth/Exceptions/OAuthException.php
Normal file
35
application/Espo/Tools/OAuth/Exceptions/OAuthException.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class OAuthException extends Exception
|
||||
{}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Exceptions;
|
||||
|
||||
class ProviderNotAvailable extends OAuthException
|
||||
{}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth\Exceptions;
|
||||
|
||||
class TokenObtainingFailure extends OAuthException
|
||||
{}
|
||||
60
application/Espo/Tools/OAuth/GenericProviderFactory.php
Normal file
60
application/Espo/Tools/OAuth/GenericProviderFactory.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth;
|
||||
|
||||
use Espo\Core\Utils\Crypt;
|
||||
use Espo\Entities\OAuthProvider;
|
||||
use League\OAuth2\Client\Provider\GenericProvider;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class GenericProviderFactory
|
||||
{
|
||||
public function __construct(
|
||||
private ConfigDataProvider $configDataProvider,
|
||||
private Crypt $crypt,
|
||||
) {}
|
||||
|
||||
public function create(OAuthProvider $provider): GenericProvider
|
||||
{
|
||||
$secret = $this->crypt->decrypt($provider->getClientSecret());
|
||||
|
||||
return new GenericProvider([
|
||||
'clientId' => $provider->getClientId(),
|
||||
'clientSecret' => $secret,
|
||||
'redirectUri' => $this->configDataProvider->getRedirectUri(),
|
||||
'urlAccessToken' => $provider->getTokenEndpoint(),
|
||||
|
||||
'urlAuthorize' => 'dummy',
|
||||
'urlResourceOwnerDetails' => 'dummy',
|
||||
]);
|
||||
}
|
||||
}
|
||||
62
application/Espo/Tools/OAuth/TokenSetter.php
Normal file
62
application/Espo/Tools/OAuth/TokenSetter.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth;
|
||||
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Utils\Crypt;
|
||||
use Espo\Entities\OAuthAccount;
|
||||
use League\OAuth2\Client\Token\AccessTokenInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class TokenSetter
|
||||
{
|
||||
public function __construct(
|
||||
private Crypt $crypt,
|
||||
) {}
|
||||
|
||||
public function set(OAuthAccount $account, AccessTokenInterface $tokens): void
|
||||
{
|
||||
$accessToken = $this->crypt->encrypt($tokens->getToken());
|
||||
|
||||
$refreshToken = $tokens->getRefreshToken() ?
|
||||
$this->crypt->encrypt($tokens->getRefreshToken()) :
|
||||
null;
|
||||
|
||||
$expires = $tokens->getExpires() !== null ?
|
||||
DateTime::fromTimestamp($tokens->getExpires()) :
|
||||
null;
|
||||
|
||||
$account->setAccessToken($accessToken);
|
||||
$account->setRefreshToken($refreshToken);
|
||||
$account->setExpiresAt($expires);
|
||||
}
|
||||
}
|
||||
62
application/Espo/Tools/OAuth/Tokens.php
Normal file
62
application/Espo/Tools/OAuth/Tokens.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth;
|
||||
|
||||
use Espo\Core\Field\DateTime;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* @immutable
|
||||
*/
|
||||
class Tokens
|
||||
{
|
||||
public function __construct(
|
||||
#[SensitiveParameter]
|
||||
private string $accessToken,
|
||||
#[SensitiveParameter]
|
||||
private ?string $refreshToken,
|
||||
private ?DateTime $expiresAt,
|
||||
) {}
|
||||
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function getRefreshToken(): ?string
|
||||
{
|
||||
return $this->refreshToken;
|
||||
}
|
||||
|
||||
public function getExpiresAt(): ?DateTime
|
||||
{
|
||||
return $this->expiresAt;
|
||||
}
|
||||
}
|
||||
177
application/Espo/Tools/OAuth/TokensProvider.php
Normal file
177
application/Espo/Tools/OAuth/TokensProvider.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OAuth;
|
||||
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Utils\Crypt;
|
||||
use Espo\Entities\OAuthAccount;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
use Espo\Tools\OAuth\Exceptions\NoToken;
|
||||
use Espo\Tools\OAuth\Exceptions\ProviderNotAvailable;
|
||||
use Espo\Tools\OAuth\Exceptions\AccountNotFound;
|
||||
use Espo\Tools\OAuth\Exceptions\TokenObtainingFailure;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use LogicException;
|
||||
|
||||
class TokensProvider
|
||||
{
|
||||
private const EXPIRATION_LEAD_TIME = 60;
|
||||
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private GenericProviderFactory $genericProviderFactory,
|
||||
private TokenSetter $tokenSetter,
|
||||
private Crypt $crypt,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws AccountNotFound
|
||||
* @throws ProviderNotAvailable
|
||||
* @throws NoToken
|
||||
* @throws TokenObtainingFailure
|
||||
*/
|
||||
public function get(string $id): Tokens
|
||||
{
|
||||
$account = $this->fetch($id);
|
||||
|
||||
if (
|
||||
$account->getRefreshToken() &&
|
||||
$account->getExpiresAt() &&
|
||||
$account->getExpiresAt()->isGreaterThan(
|
||||
DateTime::createNow()->addSeconds(- self::EXPIRATION_LEAD_TIME)
|
||||
)
|
||||
) {
|
||||
$this->refresh($account);
|
||||
}
|
||||
|
||||
if (!$account->getAccessToken()) {
|
||||
throw new NoToken();
|
||||
}
|
||||
|
||||
$accessToken = $this->crypt->decrypt($account->getAccessToken());
|
||||
|
||||
$refreshToken = $account->getRefreshToken() ?
|
||||
$this->crypt->decrypt($account->getRefreshToken()) :
|
||||
null;
|
||||
|
||||
return new Tokens(
|
||||
accessToken: $accessToken,
|
||||
refreshToken: $refreshToken,
|
||||
expiresAt: $account->getExpiresAt(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ProviderNotAvailable
|
||||
* @throws AccountNotFound
|
||||
* @throws NoToken
|
||||
*/
|
||||
private function fetch(string $id): OAuthAccount
|
||||
{
|
||||
// Ensuring the token is not being refreshed.
|
||||
$this->entityManager->getTransactionManager()->start();
|
||||
|
||||
$account = $this->entityManager
|
||||
->getRDBRepositoryByClass(OAuthAccount::class)
|
||||
->clone(
|
||||
SelectBuilder::create()
|
||||
->from(OAuthAccount::ENTITY_TYPE)
|
||||
->forShare()
|
||||
->build()
|
||||
)
|
||||
->where([Attribute::ID => $id])
|
||||
->findOne();
|
||||
|
||||
$this->entityManager->getTransactionManager()->commit();
|
||||
|
||||
if (!$account) {
|
||||
throw new AccountNotFound();
|
||||
}
|
||||
|
||||
if (!$account->getProvider()->isActive()) {
|
||||
throw new ProviderNotAvailable();
|
||||
}
|
||||
|
||||
if (!$account->getAccessToken()) {
|
||||
throw new NoToken();
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TokenObtainingFailure
|
||||
* @noinspection PhpDocRedundantThrowsInspection
|
||||
*/
|
||||
private function refresh(OAuthAccount $account): void
|
||||
{
|
||||
$this->entityManager
|
||||
->getTransactionManager()
|
||||
->run(function () use ($account) {
|
||||
$this->refreshInTransaction($account);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TokenObtainingFailure
|
||||
*/
|
||||
private function refreshInTransaction(OAuthAccount $account): void
|
||||
{
|
||||
$refreshToken = $account->getRefreshToken();
|
||||
|
||||
if (!$refreshToken) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$refreshToken = $this->crypt->decrypt($refreshToken);
|
||||
|
||||
$this->entityManager
|
||||
->getRDBRepositoryByClass(OAuthAccount::class)
|
||||
->forUpdate()
|
||||
->sth()
|
||||
->where([Attribute::ID => $account->getId()])
|
||||
->find();
|
||||
|
||||
$genericProvider = $this->genericProviderFactory->create($account->getProvider());
|
||||
|
||||
try {
|
||||
$tokens = $genericProvider->getAccessToken('refresh_token', ['refresh_token' => $refreshToken]);
|
||||
} catch (GuzzleException|IdentityProviderException $e) {
|
||||
throw new TokenObtainingFailure($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$this->tokenSetter->set($account, $tokens);
|
||||
|
||||
$this->entityManager->saveEntity($account);
|
||||
}
|
||||
}
|
||||
@@ -398,6 +398,11 @@ class AdminController extends Controller {
|
||||
this.getRouter().dispatch('AppSecret', 'list', {fromAdmin: true});
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
actionOAuthProviders() {
|
||||
this.getRouter().dispatch('OAuthProvider', 'list', {fromAdmin: true});
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
actionJobs() {
|
||||
this.collectionFactory.create('Job', collection => {
|
||||
|
||||
307
client/src/views/o-auth-account/records/panels/connection.js
Normal file
307
client/src/views/o-auth-account/records/panels/connection.js
Normal file
@@ -0,0 +1,307 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
import SidePanelView from 'views/record/panels/side';
|
||||
|
||||
export default class OAuthAccountConnectionPanelView extends SidePanelView {
|
||||
|
||||
// language=Handlebars
|
||||
templateContent = `
|
||||
{{#if hasDisconnect}}
|
||||
<div class="margin-bottom">
|
||||
<span
|
||||
class="label label-success label-md"
|
||||
>{{translate 'Connected' scope='ExternalAccount'}}</span>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
data-action="disconnect"
|
||||
>{{translate 'Disconnect' scope='ExternalAccount'}}</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasConnect}}
|
||||
<div class="margin-bottom">
|
||||
<span
|
||||
class="label label-default label-md"
|
||||
>{{translate 'Disconnected' scope='ExternalAccount'}}</span>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
data-action="connect"
|
||||
>{{translate 'Connect' scope='ExternalAccount'}}</button>
|
||||
{{/if}}
|
||||
`
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
inProcess = false
|
||||
|
||||
data() {
|
||||
const isSet = this.model.attributes.hasAccessToken !== undefined;
|
||||
|
||||
const hasDisconnect = !this.inProcess &&
|
||||
isSet &&
|
||||
this.model.attributes.hasAccessToken;
|
||||
|
||||
const hasConnect =
|
||||
!this.inProcess &&
|
||||
isSet &&
|
||||
!this.model.attributes.hasAccessToken &&
|
||||
this.model.attributes.providerIsActive;
|
||||
|
||||
// noinspection JSValidateTypes
|
||||
return {
|
||||
hasDisconnect,
|
||||
hasConnect,
|
||||
}
|
||||
}
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
this.listenTo(this.model, 'sync', () => this.reRender());
|
||||
|
||||
this.addActionHandler('connect', () => this.actionConnect());
|
||||
this.addActionHandler('disconnect', () => this.actionDisconnect());
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async actionDisconnect() {
|
||||
this.inProcess = true;
|
||||
|
||||
await this.reRender();
|
||||
|
||||
Espo.Ui.notify(' ... ');
|
||||
|
||||
await Espo.Ajax.deleteRequest(`OAuth/${this.model.id}/connection`);
|
||||
|
||||
await this.model.fetch();
|
||||
|
||||
Espo.Ui.notify();
|
||||
|
||||
this.inProcess = false;
|
||||
|
||||
await this.reRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async actionConnect() {
|
||||
const data = this.model.attributes.data || {};
|
||||
|
||||
const endpoint = data.endpoint;
|
||||
const redirectUri = data.redirectUri;
|
||||
const clientId = data.clientId;
|
||||
const scope = data.scope;
|
||||
const prompt = data.prompt;
|
||||
const params = data.params;
|
||||
|
||||
const proxy = window.open('about:blank', 'ConnectWithOAuth', 'location=0,status=0,width=800,height=800');
|
||||
|
||||
const info = await this.processWithData({
|
||||
endpoint,
|
||||
redirectUri,
|
||||
clientId,
|
||||
scope,
|
||||
prompt,
|
||||
params,
|
||||
}, proxy);
|
||||
|
||||
this.inProcess = true;
|
||||
|
||||
await this.reRender()
|
||||
|
||||
Espo.Ui.notify(' ... ');
|
||||
|
||||
try {
|
||||
await Espo.Ajax.postRequest(`OAuth/${this.model.id}/connection`, {code: info.code});
|
||||
} catch (e) {
|
||||
this.inProcess = false;
|
||||
|
||||
await this.reRender();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.model.fetch();
|
||||
|
||||
Espo.Ui.notify();
|
||||
|
||||
this.inProcess = false;
|
||||
|
||||
await this.reRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {{
|
||||
* endpoint: string,
|
||||
* clientId: string,
|
||||
* redirectUri: string,
|
||||
* scope: string|null,
|
||||
* prompt: string,
|
||||
* params: Record|null,
|
||||
* }} data
|
||||
* @param {WindowProxy} proxy
|
||||
* @return {Promise<{code: string}>}
|
||||
*/
|
||||
processWithData(data, proxy) {
|
||||
const state = undefined;
|
||||
|
||||
const params = {
|
||||
client_id: data.clientId,
|
||||
redirect_uri: data.redirectUri,
|
||||
response_type: 'code',
|
||||
prompt: data.prompt,
|
||||
};
|
||||
|
||||
if (data.scope) {
|
||||
params.scope = data.scope;
|
||||
}
|
||||
|
||||
if (data.params) {
|
||||
for (const name in data.params) {
|
||||
params[name] = data.params[name];
|
||||
}
|
||||
}
|
||||
|
||||
const partList = Object.entries(params)
|
||||
.map(([key, value]) => {
|
||||
return key + '=' + encodeURIComponent(value);
|
||||
});
|
||||
|
||||
const url = data.endpoint + '?' + partList.join('&');
|
||||
|
||||
return this.processWindow(url, state, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} url
|
||||
* @param {string} state
|
||||
* @param {WindowProxy} proxy
|
||||
* @return {Promise<{code: string}>}
|
||||
*/
|
||||
processWindow(url, state, proxy) {
|
||||
proxy.location.href = url;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const fail = () => {
|
||||
window.clearInterval(interval);
|
||||
|
||||
if (!proxy.closed) {
|
||||
proxy.close();
|
||||
}
|
||||
|
||||
reject();
|
||||
};
|
||||
|
||||
const interval = window.setInterval(() => {
|
||||
if (proxy.closed) {
|
||||
fail();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let url;
|
||||
|
||||
try {
|
||||
url = proxy.location.href;
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedData = this.parseWindowUrl(url);
|
||||
|
||||
if (!parsedData) {
|
||||
fail();
|
||||
Espo.Ui.error('Could not parse URL', true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((parsedData.error || parsedData.code) && state && parsedData.state !== state) {
|
||||
fail();
|
||||
Espo.Ui.error('State mismatch', true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsedData.error) {
|
||||
fail();
|
||||
Espo.Ui.error(parsedData.errorDescription || this.translate('Error'), true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsedData.code) {
|
||||
window.clearInterval(interval);
|
||||
proxy.close();
|
||||
|
||||
resolve({
|
||||
code: parsedData.code,
|
||||
});
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} url
|
||||
* @return {?{
|
||||
* code: ?string,
|
||||
* state: ?string,
|
||||
* error: ?string,
|
||||
* errorDescription: ?string,
|
||||
* }}
|
||||
*/
|
||||
parseWindowUrl(url) {
|
||||
try {
|
||||
const params = new URL(url).searchParams;
|
||||
|
||||
return {
|
||||
code: params.get('code'),
|
||||
state: params.get('state'),
|
||||
error: params.get('error'),
|
||||
errorDescription: params.get('errorDescription'),
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
234
client/src/views/o-auth-provider/fields/authorization-params.js
Normal file
234
client/src/views/o-auth-provider/fields/authorization-params.js
Normal file
@@ -0,0 +1,234 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
import BaseFieldView from 'views/fields/base';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* edit: import('ace-builds').edit,
|
||||
* require: import('ace-builds').require,
|
||||
* }}
|
||||
*/
|
||||
let ace;
|
||||
|
||||
export default class OAuthProviderAuthorizationParamsFieldView extends BaseFieldView {
|
||||
|
||||
// language=Handlebars
|
||||
detailTemplateContent = `
|
||||
{{#if isNotEmpty}}
|
||||
<div id="{{containerId}}">{{value}}</div>
|
||||
{{else}}
|
||||
{{#if isSet}}
|
||||
<span class="none-value">{{translate 'None'}}</span>
|
||||
{{else}}
|
||||
<span class="loading-value"></span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
`
|
||||
|
||||
// language=Handlebars
|
||||
editTemplateContent = `
|
||||
<div id="{{containerId}}">{{value}}</div>
|
||||
`
|
||||
|
||||
height = 46
|
||||
maxLineDetailCount = 80
|
||||
maxLineEditCount = 200
|
||||
|
||||
data() {
|
||||
const data = super.data();
|
||||
|
||||
const value = this.model.attributes[this.name];
|
||||
|
||||
data.containerId = this.containerId;
|
||||
data.isNotEmpty = value != null;
|
||||
data.isSet = value !== undefined;
|
||||
|
||||
try {
|
||||
data.value = value ? JSON.stringify(value, null, ' ') : null;
|
||||
} catch (e) {
|
||||
data.value = null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
this.height = this.options.height || this.params.height || this.height;
|
||||
|
||||
this.maxLineDetailCount =
|
||||
this.options.maxLineDetailCount ||
|
||||
this.params.maxLineDetailCount ||
|
||||
this.maxLineDetailCount;
|
||||
|
||||
this.maxLineEditCount =
|
||||
this.options.maxLineEditCount ||
|
||||
this.params.maxLineEditCount ||
|
||||
this.maxLineEditCount;
|
||||
|
||||
this.containerId = 'editor-' + Math.floor((Math.random() * 10000) + 1).toString();
|
||||
|
||||
if (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL) {
|
||||
this.wait(
|
||||
this.requireAce()
|
||||
);
|
||||
}
|
||||
|
||||
this.on('remove', () => {
|
||||
if (this.editor) {
|
||||
this.editor.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
this.validations.push(() => this.validateJson());
|
||||
}
|
||||
|
||||
requireAce() {
|
||||
return Espo.loader.requirePromise('lib!ace')
|
||||
.then(lib => {
|
||||
ace = lib;
|
||||
|
||||
const list = [
|
||||
Espo.loader.requirePromise('lib!ace-ext-language_tools'),
|
||||
Espo.loader.requirePromise('lib!ace-mode-json'),
|
||||
];
|
||||
|
||||
if (this.getThemeManager().getParam('isDark')) {
|
||||
list.push(
|
||||
Espo.loader.requirePromise('lib!ace-theme-tomorrow_night')
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.all(list);
|
||||
});
|
||||
}
|
||||
|
||||
afterRender() {
|
||||
super.afterRender();
|
||||
|
||||
this.$editor = this.$el.find('#' + this.containerId);
|
||||
|
||||
if (
|
||||
this.$editor.length &&
|
||||
(
|
||||
this.mode === this.MODE_EDIT ||
|
||||
this.mode === this.MODE_DETAIL ||
|
||||
this.mode === this.MODE_LIST
|
||||
)
|
||||
) {
|
||||
this.$editor.css('fontSize', 'var(--font-size-base)');
|
||||
|
||||
if (this.mode === this.MODE_EDIT) {
|
||||
this.$editor.css('minHeight', this.height + 'px');
|
||||
}
|
||||
|
||||
const editor = this.editor = ace.edit(this.containerId);
|
||||
|
||||
editor.setOptions({fontFamily: 'var(--font-family-monospace)'});
|
||||
editor.setFontSize('var(--font-size-base)');
|
||||
editor.container.style.lineHeight = 'var(--line-height-computed)';
|
||||
editor.renderer.updateFontSize();
|
||||
|
||||
editor.setOptions({
|
||||
maxLines: this.mode === this.MODE_EDIT ? this.maxLineEditCount : this.maxLineDetailCount,
|
||||
enableLiveAutocompletion: true,
|
||||
});
|
||||
|
||||
if (this.getThemeManager().getParam('isDark')) {
|
||||
editor.setOptions({
|
||||
theme: 'ace/theme/tomorrow_night',
|
||||
});
|
||||
}
|
||||
|
||||
if (this.isEditMode()) {
|
||||
editor.getSession().on('change', () => {
|
||||
this.trigger('change', {ui: true});
|
||||
});
|
||||
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
}
|
||||
|
||||
if (this.isReadMode()) {
|
||||
editor.setReadOnly(true);
|
||||
editor.renderer.$cursorLayer.element.style.display = 'none';
|
||||
editor.renderer.setShowGutter(false);
|
||||
}
|
||||
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.getSession().setUseWorker(false);
|
||||
editor.commands.removeCommand('find');
|
||||
editor.setHighlightActiveLine(false);
|
||||
|
||||
const Mode = ace.require('ace/mode/json').Mode;
|
||||
|
||||
editor.session.setMode(new Mode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {boolean}
|
||||
*/
|
||||
validateJson() {
|
||||
const raw = this.editor.getValue();
|
||||
|
||||
if (!raw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
JSON.parse(raw);
|
||||
} catch (e) {
|
||||
const message = this.translate('Not valid');
|
||||
|
||||
this.showValidationMessage(message, '.ace_editor');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fetch() {
|
||||
let value = null;
|
||||
|
||||
const raw = this.editor.getValue();
|
||||
|
||||
if (!raw) {
|
||||
return {[this.name]: null};
|
||||
}
|
||||
|
||||
try {
|
||||
value = JSON.parse(raw);
|
||||
} catch (e) {}
|
||||
|
||||
return {[this.name]: value};
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,8 @@
|
||||
"chillerlan/php-qrcode": "^4.4",
|
||||
"ext-ctype": "*",
|
||||
"lasserafn/php-initial-avatar-generator": "^4.4",
|
||||
"tholu/php-cidr-match": "^0.4"
|
||||
"tholu/php-cidr-match": "^0.4",
|
||||
"league/oauth2-client": "^2.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
|
||||
278
composer.lock
generated
278
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c71fbf8961a0a000e8e902a0bf78bd6c",
|
||||
"content-hash": "c9e886d22a6f5af3773701b37aa6cf97",
|
||||
"packages": [
|
||||
{
|
||||
"name": "async-aws/core",
|
||||
@@ -1481,6 +1481,215 @@
|
||||
},
|
||||
"time": "2024-11-04T11:18:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.9.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
|
||||
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
|
||||
"guzzlehttp/psr7": "^2.7.0",
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-client-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"ext-curl": "*",
|
||||
"guzzle/client-integration-tests": "3.0.2",
|
||||
"php-http/message-factory": "^1.1",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Required for CURL handler support",
|
||||
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Jeremy Lindblom",
|
||||
"email": "jeremeamia@gmail.com",
|
||||
"homepage": "https://github.com/jeremeamia"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"psr-18",
|
||||
"psr-7",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-24T11:22:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "2.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-17T10:06:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.7.0",
|
||||
@@ -2750,6 +2959,71 @@
|
||||
],
|
||||
"time": "2024-09-21T08:32:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-client",
|
||||
"version": "2.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-client.git",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
|
||||
"php": "^7.1 || >=8.0.0 <8.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.5",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.4",
|
||||
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "^3.11"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"homepage": "https://github.com/shadowhand",
|
||||
"role": "Contributor"
|
||||
}
|
||||
],
|
||||
"description": "OAuth 2.0 Client Library",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"SSO",
|
||||
"authorization",
|
||||
"identity",
|
||||
"idp",
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"single sign on"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth2-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1"
|
||||
},
|
||||
"time": "2025-02-26T04:37:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
"version": "3.1.2",
|
||||
@@ -8988,5 +9262,5 @@
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -196,6 +196,10 @@
|
||||
"src": "node_modules/ace-builds/src-noconflict/mode-html.js",
|
||||
"dest": "client/lib/ace-mode-html.js"
|
||||
},
|
||||
{
|
||||
"src": "node_modules/ace-builds/src-noconflict/mode-json.js",
|
||||
"dest": "client/lib/ace-mode-json.js"
|
||||
},
|
||||
{
|
||||
"src": "node_modules/ace-builds/src-noconflict/mode-handlebars.js",
|
||||
"dest": "client/lib/ace-mode-handlebars.js"
|
||||
|
||||
41
public/oauth/callback/index.php
Normal file
41
public/oauth/callback/index.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
include "../../../bootstrap.php";
|
||||
|
||||
use Espo\Core\Application;
|
||||
use Espo\Core\Application\Runner\Params;
|
||||
use Espo\Core\ApplicationRunners\EntryPoint;
|
||||
|
||||
$app = new Application();
|
||||
|
||||
$app->run(
|
||||
EntryPoint::class,
|
||||
Params::create()->with('entryPoint', 'oauthCallback')
|
||||
);
|
||||
Reference in New Issue
Block a user