From 8e454e9ae7ae3fe0f62cd7037fb3e2f9bb436d0f Mon Sep 17 00:00:00 2001 From: John Kingsnorth Date: Fri, 23 Dec 2022 00:29:40 +0000 Subject: [PATCH] dev/core#2559 Support tenancy in OAuth providers --- .../CRM/OAuth/DAO/OAuthClient.php | 32 +++- .../CRM/OAuth/DAO/OAuthContactToken.php | 2 +- .../CRM/OAuth/DAO/OAuthSysToken.php | 2 +- ext/oauth-client/CRM/OAuth/Upgrader.php | 12 ++ .../Civi/OAuth/CiviGenericProvider.php | 47 ++++++ .../Civi/OAuth/OAuthLeagueFacade.php | 1 + .../ang/oauthClientCreateHelp.aff.html | 4 +- .../ang/oauthClientCreator.aff.html | 13 +- .../ang/oauthClientEditor.aff.html | 13 +- ext/oauth-client/oauth_client.civix.php | 10 +- .../providers/ms-exchange.dist.json | 7 +- ext/oauth-client/sql/auto_install.sql | 146 +++++++----------- ext/oauth-client/sql/auto_uninstall.sql | 6 +- .../Civi/OAuth/CiviGenericProviderTest.php | 98 ++++++++++++ .../xml/schema/CRM/OAuth/OAuthClient.xml | 12 ++ 15 files changed, 291 insertions(+), 114 deletions(-) create mode 100644 ext/oauth-client/tests/phpunit/Civi/OAuth/CiviGenericProviderTest.php diff --git a/ext/oauth-client/CRM/OAuth/DAO/OAuthClient.php b/ext/oauth-client/CRM/OAuth/DAO/OAuthClient.php index 8ef1e32107..eb8826a22f 100644 --- a/ext/oauth-client/CRM/OAuth/DAO/OAuthClient.php +++ b/ext/oauth-client/CRM/OAuth/DAO/OAuthClient.php @@ -6,7 +6,7 @@ * * Generated from oauth-client/xml/schema/CRM/OAuth/OAuthClient.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6d11e5e77e49b52267e60124c13d9fa4) + * (GenCodeChecksum:547088257f723e251fbec42e97c710bb) */ use CRM_OAuth_ExtensionUtil as E; @@ -58,6 +58,15 @@ class CRM_OAuth_DAO_OAuthClient extends CRM_Core_DAO { */ public $guid; + /** + * Tenant ID + * + * @var string|null + * (SQL type: varchar(128)) + * Note that values will be retrieved from the database as a string. + */ + public $tenant; + /** * Client Secret * @@ -175,6 +184,23 @@ class CRM_OAuth_DAO_OAuthClient extends CRM_Core_DAO { 'localizable' => 0, 'add' => '5.32', ], + 'tenant' => [ + 'name' => 'tenant', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => E::ts('Tenant ID'), + 'description' => E::ts('Tenant ID'), + 'maxlength' => 128, + 'size' => CRM_Utils_Type::HUGE, + 'where' => 'civicrm_oauth_client.tenant', + 'permission' => [ + 'manage OAuth client', + ], + 'table_name' => 'civicrm_oauth_client', + 'entity' => 'OAuthClient', + 'bao' => 'CRM_OAuth_DAO_OAuthClient', + 'localizable' => 0, + 'add' => '5.57', + ], 'secret' => [ 'name' => 'secret', 'type' => CRM_Utils_Type::T_TEXT, @@ -218,6 +244,10 @@ class CRM_OAuth_DAO_OAuthClient extends CRM_Core_DAO { 'entity' => 'OAuthClient', 'bao' => 'CRM_OAuth_DAO_OAuthClient', 'localizable' => 0, + 'html' => [ + 'type' => 'CheckBox', + 'label' => E::ts("Enabled"), + ], 'add' => '5.32', ], 'created_date' => [ diff --git a/ext/oauth-client/CRM/OAuth/DAO/OAuthContactToken.php b/ext/oauth-client/CRM/OAuth/DAO/OAuthContactToken.php index 65305eb68e..1031036c9e 100644 --- a/ext/oauth-client/CRM/OAuth/DAO/OAuthContactToken.php +++ b/ext/oauth-client/CRM/OAuth/DAO/OAuthContactToken.php @@ -6,7 +6,7 @@ * * Generated from oauth-client/xml/schema/CRM/OAuth/OAuthContactToken.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1246756a94c72807dc6cb4bc2c70bb7e) + * (GenCodeChecksum:e94388515cadc3bb149a0339ef42acdb) */ use CRM_OAuth_ExtensionUtil as E; diff --git a/ext/oauth-client/CRM/OAuth/DAO/OAuthSysToken.php b/ext/oauth-client/CRM/OAuth/DAO/OAuthSysToken.php index ee08c34ada..9d3070cbf5 100644 --- a/ext/oauth-client/CRM/OAuth/DAO/OAuthSysToken.php +++ b/ext/oauth-client/CRM/OAuth/DAO/OAuthSysToken.php @@ -6,7 +6,7 @@ * * Generated from oauth-client/xml/schema/CRM/OAuth/OAuthSysToken.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:14387fad3c51ab994575b3cd759e6229) + * (GenCodeChecksum:5e1f78f265ffdd9101b9c5f76ed947f2) */ use CRM_OAuth_ExtensionUtil as E; diff --git a/ext/oauth-client/CRM/OAuth/Upgrader.php b/ext/oauth-client/CRM/OAuth/Upgrader.php index 9b38f5c40e..df5fdd0eb5 100644 --- a/ext/oauth-client/CRM/OAuth/Upgrader.php +++ b/ext/oauth-client/CRM/OAuth/Upgrader.php @@ -36,4 +36,16 @@ class CRM_OAuth_Upgrader extends CRM_OAuth_Upgrader_Base { return TRUE; } + /** + * Add support for tenancy + * + * @return bool + * @throws Exception + */ + public function upgrade_5581(): bool { + $this->ctx->log->info('Applying oauth-client update 5581. Adding tenant column to the civicrm_oauth_client table.'); + $this->addTask('Add Tenant ID to civicrm_oauth_client', 'addColumn', 'civicrm_oauth_client', 'tenant', 'varchar(128) NULL COMMENT "Tenant ID" AFTER guid'); + return TRUE; + } + } diff --git a/ext/oauth-client/Civi/OAuth/CiviGenericProvider.php b/ext/oauth-client/Civi/OAuth/CiviGenericProvider.php index 8bff0ffb38..6edd596a0b 100644 --- a/ext/oauth-client/Civi/OAuth/CiviGenericProvider.php +++ b/ext/oauth-client/Civi/OAuth/CiviGenericProvider.php @@ -17,9 +17,16 @@ use League\OAuth2\Client\Token\AccessToken; * and proprietary scopes+URLs. To use this, set the the option: * * "urlResourceOwnerDetails": "{{use_id_token}}", + * - Allow support for {{tenant}} token in provider URLs, if the provider has + * the 'tenancy' option set to TRUE (eg: ms-exchange). */ class CiviGenericProvider extends \League\OAuth2\Client\Provider\GenericProvider { + /** + * @var string + */ + protected $tenant; + protected function getAuthorizationParameters(array $options) { $newOptions = parent::getAuthorizationParameters($options); if (!isset($options['approval_prompt'])) { @@ -30,6 +37,46 @@ class CiviGenericProvider extends \League\OAuth2\Client\Provider\GenericProvider return $newOptions; } + /** + * Returns the base URL for authorizing a client. + * + * Eg. https://oauth.service.com/authorize + * + * @return string + */ + public function getBaseAuthorizationUrl() { + $url = parent::getBaseAuthorizationUrl(); + return $this->replaceTenantToken($url); + } + + /** + * Returns the base URL for requesting an access token. + * + * Eg. https://oauth.service.com/token + * + * @param array $params + * @return string + */ + public function getBaseAccessTokenUrl(array $params) { + $url = parent::getBaseAccessTokenUrl($params); + return $this->replaceTenantToken($url); + } + + /** + * Replace {{tenant}} in the endpoint URLs with 'common' for consumer accounts + * or the tenancy ID for dedicated services. + * + * @param string $str URL to replace + * @return string + */ + private function replaceTenantToken($str) { + if (strpos($str, '{{tenant}}') !== FALSE) { + $tenant = !empty($this->tenant) ? $this->tenant : 'common'; + $str = str_replace('{{tenant}}', $tenant, $str); + } + return $str; + } + /** * Requests resource owner details. * diff --git a/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php b/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php index 01fd7d4064..27d0c18fcb 100644 --- a/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php +++ b/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php @@ -34,6 +34,7 @@ class OAuthLeagueFacade { $localOptions = []; $localOptions['clientId'] = $clientDef['guid']; + $localOptions['tenant'] = !empty($clientDef['tenant']) ? $clientDef['tenant'] : ''; $localOptions['clientSecret'] = $clientDef['secret']; // NOTE: If we ever have frontend users, this may need to change. $localOptions['redirectUri'] = \CRM_OAuth_BAO_OAuthClient::getRedirectUri(); diff --git a/ext/oauth-client/ang/oauthClientCreateHelp.aff.html b/ext/oauth-client/ang/oauthClientCreateHelp.aff.html index ab9a8b19d2..bf774c7b99 100644 --- a/ext/oauth-client/ang/oauthClientCreateHelp.aff.html +++ b/ext/oauth-client/ang/oauthClientCreateHelp.aff.html @@ -25,8 +25,8 @@
  • - {{ts('Determine the client credentials ("Client ID" and "Client Secret").')}} + {{ts('Determine the client credentials, including a client ID and secret.')}}
  • -

    {{ts('Finally, copy the client credentials below:')}}

    +

    {{ts('Finally, enter the client credentials below.')}}

    diff --git a/ext/oauth-client/ang/oauthClientCreator.aff.html b/ext/oauth-client/ang/oauthClientCreator.aff.html index 15e7443575..810cfd9ee3 100644 --- a/ext/oauth-client/ang/oauthClientCreator.aff.html +++ b/ext/oauth-client/ang/oauthClientCreator.aff.html @@ -3,12 +3,17 @@
    - - + + +
    +
    + + +
    {{ts('Optional. Leave blank for consumer accounts. If you have a dedicated instance of Microsoft 365, and your application is not multi-tenancy, then enter your tenant ID here.')}}
    - - + +
    diff --git a/ext/oauth-client/ang/oauthClientEditor.aff.html b/ext/oauth-client/ang/oauthClientEditor.aff.html index 7588460ebc..359a24cda0 100644 --- a/ext/oauth-client/ang/oauthClientEditor.aff.html +++ b/ext/oauth-client/ang/oauthClientEditor.aff.html @@ -11,12 +11,17 @@
    - - + + +
    +
    + + +

    {{ts('Optional. Leave blank for consumer accounts. If you have a dedicated instance of Microsoft 365, and your application is not multi-tenancy, then enter your tenant ID here.')}}

    - - + +
    diff --git a/ext/oauth-client/oauth_client.civix.php b/ext/oauth-client/oauth_client.civix.php index e83d3f14ba..3d9ae5d3f5 100644 --- a/ext/oauth-client/oauth_client.civix.php +++ b/ext/oauth-client/oauth_client.civix.php @@ -24,7 +24,7 @@ class CRM_OAuth_ExtensionUtil { * Translated text. * @see ts */ - public static function ts($text, $params = []): string { + public static function ts($text, $params = []) { if (!array_key_exists('domain', $params)) { $params['domain'] = [self::LONG_NAME, NULL]; } @@ -41,7 +41,7 @@ class CRM_OAuth_ExtensionUtil { * Ex: 'http://example.org/sites/default/ext/org.example.foo'. * Ex: 'http://example.org/sites/default/ext/org.example.foo/css/foo.css'. */ - public static function url($file = NULL): string { + public static function url($file = NULL) { if ($file === NULL) { return rtrim(CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME), '/'); } @@ -138,7 +138,7 @@ function _oauth_client_civix_civicrm_postInstall() { * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall */ -function _oauth_client_civix_civicrm_uninstall(): void { +function _oauth_client_civix_civicrm_uninstall() { _oauth_client_civix_civicrm_config(); if ($upgrader = _oauth_client_civix_upgrader()) { $upgrader->onUninstall(); @@ -150,7 +150,7 @@ function _oauth_client_civix_civicrm_uninstall(): void { * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable */ -function _oauth_client_civix_civicrm_enable(): void { +function _oauth_client_civix_civicrm_enable() { _oauth_client_civix_civicrm_config(); if ($upgrader = _oauth_client_civix_upgrader()) { if (is_callable([$upgrader, 'onEnable'])) { @@ -165,7 +165,7 @@ function _oauth_client_civix_civicrm_enable(): void { * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable * @return mixed */ -function _oauth_client_civix_civicrm_disable(): void { +function _oauth_client_civix_civicrm_disable() { _oauth_client_civix_civicrm_config(); if ($upgrader = _oauth_client_civix_upgrader()) { if (is_callable([$upgrader, 'onDisable'])) { diff --git a/ext/oauth-client/providers/ms-exchange.dist.json b/ext/oauth-client/providers/ms-exchange.dist.json index b496c5cb01..9065e2cbe9 100644 --- a/ext/oauth-client/providers/ms-exchange.dist.json +++ b/ext/oauth-client/providers/ms-exchange.dist.json @@ -1,8 +1,8 @@ { "title": "Microsoft Exchange Online", "options": { - "urlAuthorize": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", - "urlAccessToken": "https://login.microsoftonline.com/common/oauth2/v2.0/token", + "urlAuthorize": "https://login.microsoftonline.com/{{tenant}}/oauth2/v2.0/authorize", + "urlAccessToken": "https://login.microsoftonline.com/{{tenant}}/oauth2/v2.0/token", "urlResourceOwnerDetails": "{{use_id_token}}", "scopeSeparator": " ", "scopes": [ @@ -12,7 +12,8 @@ "openid", "email", "offline_access" - ] + ], + "tenancy": true }, "mailSettingsTemplate": { "name": "{{token.resource_owner.email}}", diff --git a/ext/oauth-client/sql/auto_install.sql b/ext/oauth-client/sql/auto_install.sql index dbb478e5c0..7c27e6ce55 100644 --- a/ext/oauth-client/sql/auto_install.sql +++ b/ext/oauth-client/sql/auto_install.sql @@ -9,21 +9,9 @@ -- Generated from schema.tpl -- DO NOT EDIT. Generated by CRM_Core_CodeGen -- - --- +--------------------------------------------------------------------+ --- | Copyright CiviCRM LLC. All rights reserved. | --- | | --- | This work is published under the GNU AGPLv3 license with some | --- | permitted exceptions and without any warranty. For full license | --- | and copyright information, see https://civicrm.org/licensing | --- +--------------------------------------------------------------------+ --- --- Generated from drop.tpl --- DO NOT EDIT. Generated by CRM_Core_CodeGen --- -- /******************************************************* -- * --- * Clean up the existing tables +-- * Clean up the existing tables - this section generated from drop.tpl -- * -- *******************************************************/ @@ -46,28 +34,20 @@ SET FOREIGN_KEY_CHECKS=1; -- * -- *******************************************************/ CREATE TABLE `civicrm_oauth_client` ( - - - `id` int unsigned AUTO_INCREMENT COMMENT 'Internal Client ID', - `provider` varchar(128) NOT NULL COMMENT 'Provider', - `guid` varchar(128) NOT NULL COMMENT 'Client ID', - `secret` text COMMENT 'Client Secret', - `options` text COMMENT 'Extra override options for the service (JSON)', - `is_active` tinyint NOT NULL DEFAULT 1 COMMENT 'Is the client currently enabled?', - `created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the client was created.', - `modified_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When the client was created or modified.' -, - PRIMARY KEY (`id`) - - , INDEX `UI_provider`( - provider - ) - , INDEX `UI_guid`( - guid - ) - - -) ENGINE=InnoDB ; + `id` int unsigned AUTO_INCREMENT COMMENT 'Internal Client ID', + `provider` varchar(128) NOT NULL COMMENT 'Provider', + `guid` varchar(128) NOT NULL COMMENT 'Client ID', + `tenant` varchar(128) COMMENT 'Tenant ID', + `secret` text COMMENT 'Client Secret', + `options` text COMMENT 'Extra override options for the service (JSON)', + `is_active` tinyint NOT NULL DEFAULT 1 COMMENT 'Is the client currently enabled?', + `created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the client was created.', + `modified_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When the client was created or modified.', + PRIMARY KEY (`id`), + INDEX `UI_provider`(provider), + INDEX `UI_guid`(guid) +) +ENGINE=InnoDB; -- /******************************************************* -- * @@ -75,33 +55,28 @@ CREATE TABLE `civicrm_oauth_client` ( -- * -- *******************************************************/ CREATE TABLE `civicrm_oauth_contact_token` ( - - - `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Token ID', - `tag` varchar(128) COMMENT 'The tag specifies how this token will be used.', - `client_id` int unsigned COMMENT 'Client ID', - `contact_id` int unsigned COMMENT 'Contact ID', - `grant_type` varchar(31) COMMENT 'Ex: authorization_code', - `scopes` text COMMENT 'List of scopes addressed by this token', - `token_type` varchar(128) COMMENT 'Ex: Bearer or MAC', - `access_token` text COMMENT 'Token to present when accessing resources', - `expires` int unsigned DEFAULT 0 COMMENT 'Expiration time for the access_token (seconds since epoch)', - `refresh_token` text COMMENT 'Token to present when refreshing the access_token', - `resource_owner_name` varchar(128) COMMENT 'Identifier for the resource owner. Structure varies by service.', - `resource_owner` text COMMENT 'Cached details describing the resource owner', - `error` text COMMENT '?? copied from OAuthSysToken', - `raw` text COMMENT 'The token response data, per AccessToken::jsonSerialize', - `created_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the token was created.', - `modified_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When the token was created or modified.' -, - PRIMARY KEY (`id`) - - , INDEX `UI_tag`( - tag - ) - -, CONSTRAINT FK_civicrm_oauth_contact_token_client_id FOREIGN KEY (`client_id`) REFERENCES `civicrm_oauth_client`(`id`) ON DELETE CASCADE, CONSTRAINT FK_civicrm_oauth_contact_token_contact_id FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE -) ENGINE=InnoDB ; + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Token ID', + `tag` varchar(128) COMMENT 'The tag specifies how this token will be used.', + `client_id` int unsigned COMMENT 'Client ID', + `contact_id` int unsigned COMMENT 'Contact ID', + `grant_type` varchar(31) COMMENT 'Ex: authorization_code', + `scopes` text COMMENT 'List of scopes addressed by this token', + `token_type` varchar(128) COMMENT 'Ex: Bearer or MAC', + `access_token` text COMMENT 'Token to present when accessing resources', + `expires` int unsigned DEFAULT 0 COMMENT 'Expiration time for the access_token (seconds since epoch)', + `refresh_token` text COMMENT 'Token to present when refreshing the access_token', + `resource_owner_name` varchar(128) COMMENT 'Identifier for the resource owner. Structure varies by service.', + `resource_owner` text COMMENT 'Cached details describing the resource owner', + `error` text COMMENT '?? copied from OAuthSysToken', + `raw` text COMMENT 'The token response data, per AccessToken::jsonSerialize', + `created_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the token was created.', + `modified_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When the token was created or modified.', + PRIMARY KEY (`id`), + INDEX `UI_tag`(tag), + CONSTRAINT FK_civicrm_oauth_contact_token_client_id FOREIGN KEY (`client_id`) REFERENCES `civicrm_oauth_client`(`id`) ON DELETE CASCADE, + CONSTRAINT FK_civicrm_oauth_contact_token_contact_id FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE +) +ENGINE=InnoDB; -- /******************************************************* -- * @@ -109,30 +84,23 @@ CREATE TABLE `civicrm_oauth_contact_token` ( -- * -- *******************************************************/ CREATE TABLE `civicrm_oauth_systoken` ( - - - `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Token ID', - `tag` varchar(128) COMMENT 'The tag specifies how this token will be used.', - `client_id` int unsigned COMMENT 'Client ID', - `grant_type` varchar(31) COMMENT 'Ex: authorization_code', - `scopes` text COMMENT 'List of scopes addressed by this token', - `token_type` varchar(128) COMMENT 'Ex: Bearer or MAC', - `access_token` text COMMENT 'Token to present when accessing resources', - `expires` int unsigned DEFAULT 0 COMMENT 'Expiration time for the access_token (seconds since epoch)', - `refresh_token` text COMMENT 'Token to present when refreshing the access_token', - `resource_owner_name` varchar(128) COMMENT 'Identifier for the resource owner. Structure varies by service.', - `resource_owner` text COMMENT 'Cached details describing the resource owner', - `error` text COMMENT 'List of scopes addressed by this token', - `raw` text COMMENT 'The token response data, per AccessToken::jsonSerialize', - `created_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the token was created.', - `modified_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When the token was created or modified.' -, - PRIMARY KEY (`id`) - - , INDEX `UI_tag`( - tag - ) - -, CONSTRAINT FK_civicrm_oauth_systoken_client_id FOREIGN KEY (`client_id`) REFERENCES `civicrm_oauth_client`(`id`) ON DELETE CASCADE -) ENGINE=InnoDB ; - + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Token ID', + `tag` varchar(128) COMMENT 'The tag specifies how this token will be used.', + `client_id` int unsigned COMMENT 'Client ID', + `grant_type` varchar(31) COMMENT 'Ex: authorization_code', + `scopes` text COMMENT 'List of scopes addressed by this token', + `token_type` varchar(128) COMMENT 'Ex: Bearer or MAC', + `access_token` text COMMENT 'Token to present when accessing resources', + `expires` int unsigned DEFAULT 0 COMMENT 'Expiration time for the access_token (seconds since epoch)', + `refresh_token` text COMMENT 'Token to present when refreshing the access_token', + `resource_owner_name` varchar(128) COMMENT 'Identifier for the resource owner. Structure varies by service.', + `resource_owner` text COMMENT 'Cached details describing the resource owner', + `error` text COMMENT 'List of scopes addressed by this token', + `raw` text COMMENT 'The token response data, per AccessToken::jsonSerialize', + `created_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the token was created.', + `modified_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When the token was created or modified.', + PRIMARY KEY (`id`), + INDEX `UI_tag`(tag), + CONSTRAINT FK_civicrm_oauth_systoken_client_id FOREIGN KEY (`client_id`) REFERENCES `civicrm_oauth_client`(`id`) ON DELETE CASCADE +) +ENGINE=InnoDB; diff --git a/ext/oauth-client/sql/auto_uninstall.sql b/ext/oauth-client/sql/auto_uninstall.sql index a3c96fd566..dac2c6f766 100644 --- a/ext/oauth-client/sql/auto_uninstall.sql +++ b/ext/oauth-client/sql/auto_uninstall.sql @@ -8,11 +8,9 @@ -- -- Generated from drop.tpl -- DO NOT EDIT. Generated by CRM_Core_CodeGen --- --- /******************************************************* --- * --- * Clean up the existing tables +---- /******************************************************* -- * +-- * Clean up the existing tables-- * -- *******************************************************/ SET FOREIGN_KEY_CHECKS=0; diff --git a/ext/oauth-client/tests/phpunit/Civi/OAuth/CiviGenericProviderTest.php b/ext/oauth-client/tests/phpunit/Civi/OAuth/CiviGenericProviderTest.php new file mode 100644 index 0000000000..c9114b68e5 --- /dev/null +++ b/ext/oauth-client/tests/phpunit/Civi/OAuth/CiviGenericProviderTest.php @@ -0,0 +1,98 @@ +install('oauth-client')->apply(); + } + + public function setUp(): void { + $this->makeDummyProviders(); + $this->makeDummyClients(); + parent::setUp(); + } + + public function tearDown(): void { + parent::tearDown(); + } + + public function hook_civicrm_oauthProviders(&$providers) { + $providers = array_merge($providers, $this->providers); + } + + public function makeDummyProviders() { + $this->providers['no-tenancy'] = [ + 'name' => 'no-tenancy', + 'title' => 'Provider without tenancy', + 'options' => [ + 'urlAuthorize' => 'https://dummy/authorize', + 'urlAccessToken' => 'https://dummy/token', + 'urlResourceOwnerDetails' => '{{use_id_token}}', + 'scopes' => ['foo'], + ], + ]; + $this->providers['with-tenancy'] = [ + 'name' => 'with-tenancy', + 'title' => 'Provider with tenancy', + 'options' => [ + 'urlAuthorize' => 'https://dummy/{{tenant}}/authorize', + 'urlAccessToken' => 'https://dummy/{{tenant}}/token', + 'urlResourceOwnerDetails' => '{{use_id_token}}', + 'scopes' => ['foo'], + 'tenancy' => TRUE, + ], + ]; + } + + private function makeDummyClients() { + $this->clients['no-tenancy'] = \Civi\Api4\OAuthClient::create(FALSE)->setValues( + [ + 'provider' => 'no-tenancy', + 'guid' => 'example-client-guid-no-tenancy', + 'secret' => 'example-secret', + ] + )->execute()->single(); + $this->clients['with-tenancy'] = \Civi\Api4\OAuthClient::create(FALSE)->setValues( + [ + 'provider' => 'with-tenancy', + 'guid' => 'example-client-guid-with-tenancy', + 'secret' => 'example-secret', + 'tenant' => '123-456-789', + ] + )->execute()->single(); + } + + public function testTenancyInURLs() { + + // Test no tenancy settings + $provider = \Civi::service('oauth2.league')->createProvider($this->clients['no-tenancy']); + $computedAuthorizationURL = $provider->getBaseAuthorizationUrl(); + $this->assertEquals($this->providers['no-tenancy']['options']['urlAuthorize'], $computedAuthorizationURL); + $computedTokenURL = $provider->getBaseAccessTokenUrl([]); + $this->assertEquals($this->providers['no-tenancy']['options']['urlAccessToken'], $computedTokenURL); + + // Test tenancy token rewrite + $provider = \Civi::service('oauth2.league')->createProvider($this->clients['with-tenancy']); + $computedAuthorizationURL = $provider->getBaseAuthorizationUrl(); + $this->assertEquals('https://dummy/123-456-789/authorize', $computedAuthorizationURL); + $computedTokenURL = $provider->getBaseAccessTokenUrl([]); + $this->assertEquals('https://dummy/123-456-789/token', $computedTokenURL); + + } + +} diff --git a/ext/oauth-client/xml/schema/CRM/OAuth/OAuthClient.xml b/ext/oauth-client/xml/schema/CRM/OAuth/OAuthClient.xml index 467c1e4ecd..59864cca0c 100644 --- a/ext/oauth-client/xml/schema/CRM/OAuth/OAuthClient.xml +++ b/ext/oauth-client/xml/schema/CRM/OAuth/OAuthClient.xml @@ -48,6 +48,18 @@ 5.32 + + tenant + Tenant ID + varchar + 128 + Tenant ID + 5.57 + + manage OAuth client + + + secret Client Secret -- 2.25.1