diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c738164c..acb8e7a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -104,7 +104,7 @@ vendor/bin/phpcs --standard=web/modules/contrib/apigee_edge/phpcs.xml.dist web/m git push -u origin patch-2:patch-2 ``` -## Running tests +## Set up environment variables Before you could start testing this module some environment variables needs to be set on your system. These variables are: @@ -124,6 +124,23 @@ You can set these environment variables multiple ways, either by defining them with `export` or `set` in the terminal or creating a copy of the `core/phpunit.xml.dist` file as `core/phpunit.xml` and specifying them in that file. +### Notes for testing using a Hybrid organization + +If testing with a Hybrid organization, only the following three environment variables are required: + +* `APIGEE_EDGE_INSTANCE_TYPE`: should be `hybrid`. +* `APIGEE_EDGE_ORGANIZATION` +* `APIGEE_EDGE_ACCOUNT_JSON_KEY`: the JSON encoded GCP service account key. + +If you wish to run tests both against a Public and a Hybrid instance: + +1. First configure the credentials to the public org as described above. +2. Add the `APIGEE_EDGE_ACCOUNT_JSON_KEY` environment variable. +3. Add a`APIGEE_EDGE_HYBRID_ORGANIZATION` environment variable, which specifies the Hybrid organization to use for tests. + + +## Running tests + After you have these environment variables set you can execute tests of this module with the following command (note the location of the `phpunit` executable may vary): diff --git a/README.md b/README.md index 1afbf207..1bfcd469 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,12 @@ some submodules are marked as "Experimental". They are provided for evaluation a considered to be in development. Experimental modules are included in the "Apigee (Experimental)" package on the "Extend" page of a Drupal site (/admin/modules). +## Support for Apigee Hybrid Cloud: Alpha Release + +Support for [Apigee hybrid API](https://docs.apigee.com/hybrid/reference-overview) has been added but is considered to +be an alpha. If you run into any problems, add an issue to our [GitHub issue queue](https://github.com/apigee/apigee-edge-drupal/issues). +Please note that Team APIs and Monetization APIs are not currently supported on Apigee hybrid. + ## Requirements * The Apigee Edge module requires **Drupal 8.7.x** or higher and PHP 7.1 or higher. diff --git a/apigee_edge.install b/apigee_edge.install index d38f4a7c..891e2398 100644 --- a/apigee_edge.install +++ b/apigee_edge.install @@ -23,11 +23,9 @@ * Install, update and uninstall functions for Apigee Edge. */ -use Drupal\apigee_edge\Plugin\KeyType\ApigeeAuthKeyType; +use Apigee\Edge\Utility\OrganizationFeatures; use Drupal\apigee_edge\OauthTokenFileStorage; -use Drupal\Component\Serialization\Json; use Drupal\Core\Url; -use Drupal\key\Plugin\KeyProviderSettableValueInterface; use Drupal\user\RoleInterface; /** @@ -35,6 +33,7 @@ use Drupal\user\RoleInterface; */ function apigee_edge_requirements($phase) { $requirements = []; + $hybrid_support_message = t('Support for Apigee hybrid in the Apigee modules is in Alpha. Connecting to a hybrid organization is appropriate for evaluation and testing purposes during this pre-production stage.'); if ($phase === 'install') { // This should be checked only if Drupal is installed. @@ -51,12 +50,26 @@ function apigee_edge_requirements($phase) { ]; } } + + \Drupal::messenger()->addWarning($hybrid_support_message); } elseif ($phase === 'runtime') { /** @var \Drupal\apigee_edge\SDKConnectorInterface $sdk_connector */ $sdk_connector = \Drupal::service('apigee_edge.sdk_connector'); try { $sdk_connector->testConnection(); + + // Hybrid support warning. + $org_controller = \Drupal::service('apigee_edge.controller.organization'); + /* @var \Apigee\Edge\Api\Management\Entity\Organization $organization */ + $organization = $org_controller->load($sdk_connector->getOrganization()); + if ($organization && OrganizationFeatures::isHybridEnabled($organization)) { + $requirements['apigee_edge_hybrid_support'] = [ + 'title' => t('Apigee Edge'), + 'description' => $hybrid_support_message, + 'severity' => REQUIREMENT_WARNING, + ]; + } } catch (\Exception $exception) { $requirements['apigee_edge_connection_error'] = [ @@ -166,15 +179,6 @@ function apigee_edge_update_8001() { * Update defaults on Apigee Auth Keys. */ function apigee_edge_update_8002() { - $keys = \Drupal::service('key.repository')->getKeys(); - foreach ($keys as $key) { - /* @var \Drupal\key\Entity\Key $key */ - if ($key->getKeyType() instanceof ApigeeAuthKeyType && $key->getKeyProvider() instanceof KeyProviderSettableValueInterface) { - $values = $key->getKeyValues(); - $values['endpoint_type'] = $values['endpoint'] ? 'custom' : 'default'; - $values['authorization_server_type'] = $values['authorization_server'] ? 'custom' : 'default'; - $key->setKeyValue(Json::encode($values)); - $key->save(); - } - } + // Empty. + // Removed because Hybrid support makes this update hook unneeded. } diff --git a/composer.json b/composer.json index e0e3ec25..423ad320 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "description": "Apigee Edge for Drupal.", "require": { "php": ">=7.1", - "apigee/apigee-client-php": "^2.0.1", + "apigee/apigee-client-php": "^2.0.4", "cweagans/composer-patches": "^1.6.5", "drupal/core": "~8.7.0", "drupal/entity": "^1.0", diff --git a/modules/apigee_edge_teams/apigee_edge_teams.install b/modules/apigee_edge_teams/apigee_edge_teams.install new file mode 100644 index 00000000..2310e0ea --- /dev/null +++ b/modules/apigee_edge_teams/apigee_edge_teams.install @@ -0,0 +1,61 @@ +load($sdk_connector->getOrganization()); + if ($organization && !OrganizationFeatures::isCompaniesFeatureAvailable($organization)) { + $url = [ + ':url' => 'https://docs.apigee.com/hybrid/compare-hybrid-edge#unsupported-apis', + ]; + $message = ($phase == 'runtime') ? + t("The Apigee Edge Teams module functionality is not available for your org and should be uninstalled, because Edge company APIs are not supported in Apigee hybrid orgs.", $url) : + t("The Apigee Edge Teams module functionality is not available for your org because Edge company APIs are not supported in Apigee hybrid orgs.", $url); + $requirements['apigee_edge_teams_not_supported'] = [ + 'title' => t('Apigee Edge Teams'), + 'description' => $message, + 'severity' => REQUIREMENT_ERROR, + ]; + } + } + catch (\Exception $exception) { + // Do nothing if connection to Edge is not available. + } + } + + return $requirements; +} diff --git a/src/Connector/HybridAuthentication.php b/src/Connector/HybridAuthentication.php new file mode 100644 index 00000000..9ef7bbfd --- /dev/null +++ b/src/Connector/HybridAuthentication.php @@ -0,0 +1,41 @@ +buildClient(new NullAuthentication(), $this->getAuthServer()); + } + +} diff --git a/src/Connector/HybridCredentials.php b/src/Connector/HybridCredentials.php new file mode 100644 index 00000000..40193227 --- /dev/null +++ b/src/Connector/HybridCredentials.php @@ -0,0 +1,54 @@ +getKeyType() instanceof EdgeKeyTypeInterface + && ($auth_type = $key->getKeyType()->getAuthenticationType($key)) + && $auth_type === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT + ) { + parent::__construct($key); + } + else { + throw new InvalidArgumentException("The `{$key->id()}` key is not configured for Hybrid Authentication."); + } + } + +} diff --git a/src/Credentials.php b/src/Credentials.php index 910966c5..ee21acaa 100644 --- a/src/Credentials.php +++ b/src/Credentials.php @@ -25,6 +25,8 @@ /** * The API credentials. + * + * @todo: move to \Drupal\apigee_edge\Connector namespace. */ class Credentials implements CredentialsInterface { diff --git a/src/CredentialsInterface.php b/src/CredentialsInterface.php index dd77c256..160b586b 100644 --- a/src/CredentialsInterface.php +++ b/src/CredentialsInterface.php @@ -25,6 +25,8 @@ /** * Defines an interface for credentials classes. + * + * @todo: move to \Drupal\apigee_edge\Connector namespace. */ interface CredentialsInterface { diff --git a/src/KeyEntityFormEnhancer.php b/src/KeyEntityFormEnhancer.php index b4d4ff97..2ba0ee95 100644 --- a/src/KeyEntityFormEnhancer.php +++ b/src/KeyEntityFormEnhancer.php @@ -21,8 +21,11 @@ namespace Drupal\apigee_edge; use Apigee\Edge\Exception\ApiRequestException; +use Apigee\Edge\Exception\HybridOauth2AuthenticationException; use Apigee\Edge\Exception\OauthAuthenticationException; use Apigee\Edge\HttpClient\Plugin\Authentication\Oauth; +use DomainException; +use Drupal\apigee_edge\Exception\AuthenticationKeyException; use Drupal\apigee_edge\Exception\InvalidArgumentException; use Drupal\apigee_edge\Exception\KeyProviderRequirementsException; use Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface; @@ -246,9 +249,16 @@ public function alterForm(array &$form, FormStateInterface $form_state): void { ], '#states' => [ 'enabled' => [ - ':input[name="key_input_settings[password]"]' => ['filled' => TRUE], - ':input[name="key_input_settings[organization]"]' => ['filled' => TRUE], - ':input[name="key_input_settings[username]"]' => ['filled' => TRUE], + [ + ':input[name="key_input_settings[organization]"]' => ['empty' => FALSE], + ':input[name="key_input_settings[password]"]' => ['empty' => FALSE], + ':input[name="key_input_settings[username]"]' => ['empty' => FALSE], + ], + [ + ':input[name="key_input_settings[instance_type]"]' => ['value' => EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID], + ':input[name="key_input_settings[organization]"]' => ['empty' => FALSE], + ':input[name="key_input_settings[account_json_key]"]' => ['empty' => FALSE], + ], ], ], ]; @@ -326,7 +336,7 @@ public function validateForm(array &$form, FormStateInterface $form_state): void $test_key_type = $test_key->getKeyType(); $test_auth_type = $test_key_type->getAuthenticationType($test_key); try { - if ($test_auth_type === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { + if (in_array($test_auth_type, [EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH, EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT])) { // Check the requirements first. $this->oauthTokenStorage->checkRequirements(); // Clear existing OAuth token data. @@ -352,7 +362,9 @@ public function validateForm(array &$form, FormStateInterface $form_state): void // still not clear the submitted value. // \Drupal\apigee_edge\Plugin\KeyInput\ApigeeAuthKeyInput::buildConfigurationForm() // does not get called in this case. - $form['settings']['input_section']['key_input_settings']['password']['#attributes']['value'] = $test_key_type->getPassword($test_key); + if ($test_key_type->getInstanceType($test_key) != EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $form['settings']['input_section']['key_input_settings']['password']['#attributes']['value'] = $test_key_type->getPassword($test_key); + } } finally { // Clear Oauth token data that may have been saved during testing @@ -452,8 +464,29 @@ private function createSuggestion(\Exception $exception, KeyInterface $key): Mar /** @var \Drupal\apigee_edge\Plugin\KeyType\ApigeeAuthKeyType $key_type */ $key_type = $key->getKeyType(); + if ($exception instanceof AuthenticationKeyException) { + $suggestion = $this->t('@fail_text Verify the Apigee Edge connection settings.', [ + '@fail_text' => $fail_text, + ]); + } + + elseif ($exception instanceof HybridOauth2AuthenticationException) { + $fail_text = $this->t('Failed to connect to the authorization server.'); + // General error message. + $suggestion = $this->t('@fail_text Check the debug information below for more details.', [ + '@fail_text' => $fail_text, + ]); + + // Invalid key / OpenSSL unable to sign data. + if ($exception->getPrevious() && $exception->getPrevious() instanceof DomainException) { + $suggestion = $this->t('@fail_text The private key in the GCP service account key JSON is invalid.', [ + '@fail_text' => $fail_text, + ]); + } + } + // Failed to connect to the Oauth authorization server. - if ($exception instanceof OauthAuthenticationException) { + elseif ($exception instanceof OauthAuthenticationException) { $fail_text = $this->t('Failed to connect to the OAuth authorization server.'); // General error message. $suggestion = $this->t('@fail_text Check the debug information below for more details.', [ @@ -510,9 +543,8 @@ private function createSuggestion(\Exception $exception, KeyInterface $key): Mar // the MGMT server returns HTTP 500 with an error instead of HTTP 401. if ($exception->getCode() === 401 || ($exception->getCode() === 500 && $exception->getEdgeErrorCode() === 'usersandroles.SsoInternalServerError')) { - // If on public cloud (using the default endpoint), the username should - // be an email. - if ($key_type->getEndpointType($key) === EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_DEFAULT && !$this->emailValidator->isValid($key_type->getUsername($key))) { + // If on public cloud, the username should be an email. + if ($key_type->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC && !$this->emailValidator->isValid($key_type->getUsername($key))) { $suggestion = $this->t('@fail_text The organization username should be a valid email.', [ '@fail_text' => $fail_text, ]); @@ -580,22 +612,27 @@ private function createSuggestion(\Exception $exception, KeyInterface $key): Mar */ private function createDebugText(\Exception $exception, KeyInterface $key): string { $key_type = $key->getKeyType(); - - $credentials = !($key_type instanceof EdgeKeyTypeInterface) ? [] : [ - 'endpoint' => $key_type->getEndpoint($key), - 'organization' => $key_type->getOrganization($key), - 'username' => $key_type->getUsername($key), - ]; - + $credentials = []; $keys = [ 'auth_type' => ($key_type instanceof EdgeKeyTypeInterface) ? $key_type->getAuthenticationType($key) : 'invalid credentials', 'key_provider' => get_class($key->getKeyProvider()), ]; - if (!empty($credentials) && $keys['auth_type'] === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { - $credentials['authorization_server'] = $key_type->getAuthorizationServer($key); - $credentials['client_id'] = $key_type->getClientId($key); - $credentials['client_secret'] = $key_type->getClientSecret($key) === Oauth::DEFAULT_CLIENT_SECRET ? Oauth::DEFAULT_CLIENT_SECRET : '***client-secret***'; + if ($key_type instanceof EdgeKeyTypeInterface) { + $credentials = [ + 'endpoint' => $key_type->getEndpoint($key), + 'organization' => $key_type->getOrganization($key), + ]; + + if ($key_type->getInstanceType($key) != EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $credentials['username'] = $key_type->getUsername($key); + } + + if ($key_type->getAuthenticationType($key) === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { + $credentials['authorization_server'] = $key_type->getAuthorizationServer($key); + $credentials['client_id'] = $key_type->getClientId($key); + $credentials['client_secret'] = $key_type->getClientSecret($key) === Oauth::DEFAULT_CLIENT_SECRET ? Oauth::DEFAULT_CLIENT_SECRET : '***client-secret***'; + } } // Sanitize exception text. diff --git a/src/OauthAuthentication.php b/src/OauthAuthentication.php index 17edb239..48a833c7 100644 --- a/src/OauthAuthentication.php +++ b/src/OauthAuthentication.php @@ -26,6 +26,8 @@ /** * Decorator for OAuth authentication plugin. + * + * @todo: move to \Drupal\apigee_edge\Connector namespace. */ class OauthAuthentication extends Oauth { @@ -35,7 +37,7 @@ class OauthAuthentication extends Oauth { protected function authClient(): ClientInterface { /** @var \Drupal\apigee_edge\SDKConnectorInterface $sdk_connector */ $sdk_connector = \Drupal::service('apigee_edge.sdk_connector'); - return $sdk_connector->buildClient(new BasicAuth($this->clientId, $this->clientSecret), $this->auth_server); + return $sdk_connector->buildClient(new BasicAuth($this->clientId, $this->clientSecret), $this->getAuthServer()); } } diff --git a/src/OauthCredentials.php b/src/OauthCredentials.php index c00e79e5..5d1b8ada 100644 --- a/src/OauthCredentials.php +++ b/src/OauthCredentials.php @@ -26,6 +26,8 @@ /** * The API credentials for OAuth. + * + * @todo: move to \Drupal\apigee_edge\Connector namespace. */ class OauthCredentials extends Credentials { diff --git a/src/OauthTokenFileStorage.php b/src/OauthTokenFileStorage.php index a38c82fa..7fb6b4c0 100644 --- a/src/OauthTokenFileStorage.php +++ b/src/OauthTokenFileStorage.php @@ -26,6 +26,8 @@ /** * Stores OAuth token data in a file. + * + * @todo: move to \Drupal\apigee_edge\Connector namespace. */ final class OauthTokenFileStorage implements OauthTokenStorageInterface { diff --git a/src/OauthTokenStorageInterface.php b/src/OauthTokenStorageInterface.php index 0519d460..682adeea 100644 --- a/src/OauthTokenStorageInterface.php +++ b/src/OauthTokenStorageInterface.php @@ -24,6 +24,8 @@ /** * Base definition of the OAuth token storage service implementations. + * + * @todo: move to \Drupal\apigee_edge\Connector namespace. */ interface OauthTokenStorageInterface extends EdgeOauthTokenStorageInterface { diff --git a/src/Plugin/EdgeKeyTypeBase.php b/src/Plugin/EdgeKeyTypeBase.php index f55158d5..acbf789a 100644 --- a/src/Plugin/EdgeKeyTypeBase.php +++ b/src/Plugin/EdgeKeyTypeBase.php @@ -20,6 +20,7 @@ namespace Drupal\apigee_edge\Plugin; use Apigee\Edge\Client; +use Apigee\Edge\ClientInterface; use Apigee\Edge\HttpClient\Plugin\Authentication\Oauth; use Drupal\apigee_edge\Exception\AuthenticationKeyValueMalformedException; use Drupal\Component\Serialization\Json; @@ -49,6 +50,10 @@ public function unserialize($value) { * {@inheritdoc} */ public function getAuthenticationType(KeyInterface $key): string { + if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + return EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT; + } + if (!isset($key->getKeyValues()['auth_type'])) { throw new AuthenticationKeyValueMalformedException('auth_type'); } @@ -59,24 +64,43 @@ public function getAuthenticationType(KeyInterface $key): string { * {@inheritdoc} */ public function getEndpoint(KeyInterface $key): string { - return $key->getKeyValues()['endpoint'] ?? Client::DEFAULT_ENDPOINT; + if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + return ClientInterface::HYBRID_ENDPOINT; + } + elseif ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC) { + return Client::DEFAULT_ENDPOINT; + } + return $key->getKeyValues()['endpoint']; } /** * {@inheritdoc} */ public function getEndpointType(KeyInterface $key): string { - if (isset($key->getKeyValues()['endpoint_type'])) { - return $key->getKeyValues()['endpoint_type']; - } - - if (empty($key->getKeyValues()['endpoint']) || $key->getKeyValues()['endpoint'] === Client::DEFAULT_ENDPOINT) { + if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC) { return EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_DEFAULT; } return EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_CUSTOM; } + /** + * {@inheritdoc} + */ + public function getInstanceType(KeyInterface $key): string { + $key_values = $key->getKeyValues(); + if (isset($key_values['instance_type'])) { + return $key_values['instance_type']; + } + + // Backwards compatibility, before Hybrid support. + if (empty($key_values['endpoint']) || $key_values['endpoint'] === ClientInterface::DEFAULT_ENDPOINT) { + return EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC; + } + + return EdgeKeyTypeInterface::INSTANCE_TYPE_PRIVATE; + } + /** * {@inheritdoc} */ @@ -128,4 +152,16 @@ public function getClientSecret(KeyInterface $key): string { return $key->getKeyValues()['client_secret'] ?? Oauth::DEFAULT_CLIENT_SECRET; } + /** + * {@inheritdoc} + */ + public function getAccountKey(KeyInterface $key): array { + $value = $key->getKeyValues()['account_json_key'] ?? ''; + $json = json_decode($value, TRUE); + if (empty($json['private_key']) || empty($json['client_email'])) { + throw new AuthenticationKeyValueMalformedException('account_json_key'); + } + return $json; + } + } diff --git a/src/Plugin/EdgeKeyTypeInterface.php b/src/Plugin/EdgeKeyTypeInterface.php index 8e83ded2..aa5c2697 100644 --- a/src/Plugin/EdgeKeyTypeInterface.php +++ b/src/Plugin/EdgeKeyTypeInterface.php @@ -27,6 +27,26 @@ */ interface EdgeKeyTypeInterface extends KeyTypeMultivalueInterface, KeyTypeAuthenticationMethodInterface { + /** + * Apigee instance on public cloud. + * + * @var string + */ + public const INSTANCE_TYPE_PUBLIC = 'public'; + + /** + * Apigee instance on private cloud. + * + * @var string + */ + public const INSTANCE_TYPE_PRIVATE = 'private'; + + /** + * Apigee instance on hybrid cloud. + * + * @var string + */ + public const INSTANCE_TYPE_HYBRID = 'hybrid'; /** * ID of the basic authentication method. * @@ -41,10 +61,22 @@ interface EdgeKeyTypeInterface extends KeyTypeMultivalueInterface, KeyTypeAuthen */ const EDGE_AUTH_TYPE_OAUTH = 'oauth'; + /** + * ID of the JWT authentication method. + * + * @var string + */ + const EDGE_AUTH_TYPE_JWT = 'jwt'; + /** * The endpoint type for default. * * @var string + * + * @deprecated Deprecated in apigee_edge:8.x-1.2 and is removed from + * apigee_edge:8.x-2.0. Check for endpoint type instead. + * + * @see EdgeKeyTypeInterface::getEndpointType(). */ const EDGE_ENDPOINT_TYPE_DEFAULT = 'default'; @@ -52,6 +84,11 @@ interface EdgeKeyTypeInterface extends KeyTypeMultivalueInterface, KeyTypeAuthen * The endpoint type for custom. * * @var string + * + * @deprecated Deprecated in apigee_edge:8.x-1.2 and is removed from + * apigee_edge:8.x-2.0. Check for endpoint type instead. + * + * @see EdgeKeyTypeInterface::getEndpointType(). */ const EDGE_ENDPOINT_TYPE_CUSTOM = 'custom'; @@ -77,17 +114,32 @@ public function getAuthenticationType(KeyInterface $key): string; */ public function getEndpoint(KeyInterface $key): string; + /** + * Gets the instance type (public, private or hybrid). + * + * @param \Drupal\key\KeyInterface $key + * The key entity. + * + * @return string + * The instance type, either `public`, `private` or `hybrid`. + */ + public function getInstanceType(KeyInterface $key): string; + /** * Gets the API endpoint type (default or custom). * - * If the "endpoint_type" property is empty, it returns "default" if the - * "endpoint" is empty or the same as the default endpoint. + * It returns "default" on a public cloud instance, otherwise "custom". * * @param \Drupal\key\KeyInterface $key * The key entity. * * @return string * The API endpoint type. + * + * @deprecated Deprecated in apigee_edge:8.x-1.2 and is removed from + * apigee_edge:8.x-2.0. Use getInstanceType() instead. + * + * @see https://github.com/apigee/apigee-edge-drupal/issues/268 */ public function getEndpointType(KeyInterface $key): string; @@ -157,4 +209,15 @@ public function getClientId(KeyInterface $key): string; */ public function getClientSecret(KeyInterface $key): string; + /** + * Return the JSON account key decoded as an array. + * + * @param \Drupal\key\KeyInterface $key + * The key entity. + * + * @return array + * The account key as an array. + */ + public function getAccountKey(KeyInterface $key): array; + } diff --git a/src/Plugin/KeyInput/ApigeeAuthKeyInput.php b/src/Plugin/KeyInput/ApigeeAuthKeyInput.php index 2d430ca2..2f75dc93 100644 --- a/src/Plugin/KeyInput/ApigeeAuthKeyInput.php +++ b/src/Plugin/KeyInput/ApigeeAuthKeyInput.php @@ -20,7 +20,6 @@ namespace Drupal\apigee_edge\Plugin\KeyInput; use Apigee\Edge\HttpClient\Plugin\Authentication\Oauth; -use Apigee\Edge\ClientInterface; use Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface; use Drupal\Component\Serialization\Json; use Drupal\Core\Form\FormStateInterface; @@ -63,9 +62,41 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta // Could be an empty array. $values = Json::decode($key_value); - $values['endpoint_type'] = empty($values['endpoint']) ? EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_DEFAULT : EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_CUSTOM; $values['authorization_server_type'] = empty($values['authorization_server']) ? 'default' : 'custom'; + $state_for_public = [ + ':input[name="key_input_settings[instance_type]"]' => ['value' => EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC], + ]; + $state_for_private = [ + ':input[name="key_input_settings[instance_type]"]' => ['value' => EdgeKeyTypeInterface::INSTANCE_TYPE_PRIVATE], + ]; + $state_for_hybrid = [ + ':input[name="key_input_settings[instance_type]"]' => ['value' => EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID], + ]; + + $form['instance_type'] = [ + '#type' => 'radios', + '#title' => $this->t('Apigee instance type'), + '#description' => $this->t('Select the Apigee instance type you are connecting to. More information can be found in the Apigee documentation.', [ + '@link' => 'https://www.drupal.org/docs/8/modules/apigee-edge/configure-the-connection-to-apigee-edge', + ]), + '#required' => TRUE, + '#options' => [ + EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC => $this->t('Public Cloud'), + EdgeKeyTypeInterface::INSTANCE_TYPE_PRIVATE => $this->t('Private Cloud'), + EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID => $this->t('Hybrid Cloud'), + ], + '#default_value' => $values['instance_type'] ?? 'public', + ]; + $form['hybrid_support_info'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Support for Apigee hybrid'), + '#description' => $this->t('Support for Apigee hybrid in the Apigee modules is in Alpha. Connecting to a hybrid organization is appropriate for evaluation and testing purposes during this pre-production stage.'), + + '#states' => [ + 'visible' => $state_for_hybrid, + ], + ]; $form['auth_type'] = [ '#type' => 'select', '#title' => $this->t('Authentication type'), @@ -76,8 +107,11 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta EdgeKeyTypeInterface::EDGE_AUTH_TYPE_BASIC => $this->t('HTTP basic'), ], '#default_value' => $values['auth_type'] ?? EdgeKeyTypeInterface::EDGE_AUTH_TYPE_BASIC, + '#states' => [ + 'visible' => [$state_for_public, $state_for_private], + 'required' => [$state_for_public, $state_for_private], + ], ]; - $form['organization'] = [ '#type' => 'textfield', '#title' => $this->t('Organization'), @@ -90,51 +124,50 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#type' => 'textfield', '#title' => $this->t('Username'), '#description' => $this->t("Apigee user's email address or identity provider username that is used for authenticating with the endpoint."), - '#required' => TRUE, '#default_value' => $values['username'] ?? '', '#attributes' => ['autocomplete' => 'off'], + '#states' => [ + 'visible' => [$state_for_public, $state_for_private], + 'required' => [$state_for_public, $state_for_private], + ], ]; $form['password'] = [ '#type' => 'password', '#title' => $this->t('Password'), '#description' => $this->t("Organization user's password that is used for authenticating with the endpoint."), - '#required' => TRUE, '#attributes' => [ 'autocomplete' => 'off', // Password field should not forget the submitted value. 'value' => $values['password'] ?? '', ], + '#states' => [ + 'visible' => [$state_for_public, $state_for_private], + 'required' => [$state_for_public, $state_for_private], + ], ]; - $form['endpoint_type'] = [ - '#title' => $this->t('Apigee Edge endpoint'), - '#type' => 'radios', - '#required' => TRUE, - '#default_value' => $values['endpoint_type'], - '#options' => [ - EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_DEFAULT => $this->t('Default'), - EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_CUSTOM => $this->t('Custom'), + $form['account_json_key'] = [ + '#type' => 'textarea', + '#title' => $this->t('GCP service account key'), + '#description' => $this->t("Paste the contents of the GCP service account key JSON file."), + '#default_value' => $values['account_json_key'] ?? '', + '#rows' => '8', + '#states' => [ + 'visible' => $state_for_hybrid, + 'required' => $state_for_hybrid, ], - '#description' => $this->t('Apigee Edge endpoint where the API calls are being sent. Use the default (%endpoint) when pointing to an organization on Public Cloud, or custom when using Private Cloud.', [ - '%endpoint' => ClientInterface::DEFAULT_ENDPOINT, - '@link' => 'https://docs.apigee.com/api-platform/get-started/what-apigee-edge#cloudvonprem', - ]), ]; $form['endpoint'] = [ '#type' => 'textfield', - '#title' => $this->t('Custom Apigee Edge endpoint'), - '#description' => $this->t('For a Private Cloud installation, it is in the form: %form_a or %form_b.', [ + '#title' => $this->t('Apigee Edge endpoint'), + '#description' => $this->t('Apigee Edge endpoint where the API calls are being sent. For a Private Cloud installation it is in the form: %form_a or %form_b.', [ '%form_a' => 'http://ms_IP_or_DNS:8080/v1', '%form_b' => 'https://ms_IP_or_DNS:TLSport/v1', ]), '#default_value' => $values['endpoint'] ?? '', '#attributes' => ['autocomplete' => 'off'], '#states' => [ - 'visible' => [ - ':input[name="key_input_settings[endpoint_type]"]' => ['value' => 'custom'], - ], - 'required' => [ - ':input[name="key_input_settings[endpoint_type]"]' => ['value' => 'custom'], - ], + 'visible' => $state_for_private, + 'required' => $state_for_private, ], ]; $form['authorization_server_type'] = [ @@ -151,6 +184,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta ]), '#states' => [ 'visible' => [ + [$state_for_public, $state_for_private], ':input[name="key_input_settings[auth_type]"]' => ['value' => EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH], ], ], @@ -165,10 +199,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#attributes' => ['autocomplete' => 'off'], '#states' => [ 'visible' => [ + [$state_for_public, $state_for_private], ':input[name="key_input_settings[auth_type]"]' => ['value' => EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH], ':input[name="key_input_settings[authorization_server_type]"]' => ['value' => 'custom'], ], 'required' => [ + [$state_for_public, $state_for_private], + ':input[name="key_input_settings[auth_type]"]' => ['value' => EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH], ':input[name="key_input_settings[authorization_server_type]"]' => ['value' => 'custom'], ], ], @@ -183,6 +220,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#attributes' => ['autocomplete' => 'off'], '#states' => [ 'visible' => [ + [$state_for_public, $state_for_private], ':input[name="key_input_settings[auth_type]"]' => ['value' => EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH], ], ], @@ -197,6 +235,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#attributes' => ['autocomplete' => 'off'], '#states' => [ 'visible' => [ + [$state_for_public, $state_for_private], ':input[name="key_input_settings[auth_type]"]' => ['value' => EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH], ], ], @@ -210,6 +249,21 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta return $form; } + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + $input_values = $form_state->getUserInput()['key_input_settings']; + + if ($input_values['instance_type'] == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $account_key = $input_values['account_json_key'] ?? ''; + $json = json_decode($account_key, TRUE); + if (empty($json['private_key']) || empty($json['client_email'])) { + $form_state->setErrorByName('key_input_settings][account_json_key', $this->t('GCP service account key JSON file is invalid.')); + } + } + } + /** * {@inheritdoc} */ @@ -217,16 +271,37 @@ public function processSubmittedKeyValue(FormStateInterface $form_state) { // Get input values. $input_values = $form_state->getValues(); - // Make sure the endpoint defaults are not overridden by other values. - if (empty($input_values['endpoint_type']) || $input_values['endpoint_type'] == EdgeKeyTypeInterface::EDGE_ENDPOINT_TYPE_DEFAULT) { - $input_values['endpoint'] = ''; - } - if (empty($input_values['authorization_server_type']) || $input_values['authorization_server_type'] == 'default') { - $input_values['authorization_server'] = ''; + if (!empty($input_values)) { + $instance_type = $input_values['instance_type'] ?? NULL; + + // Make sure the endpoint defaults are not overridden by other values. + if ($instance_type == EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC) { + $input_values['endpoint'] = ''; + } + if (empty($input_values['authorization_server_type']) || $input_values['authorization_server_type'] == 'default') { + $input_values['authorization_server'] = ''; + } + + // Remove unneeded values if on a Hybrid instance. + if ($instance_type == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $input_values['auth_type'] = ''; + $input_values['username'] = ''; + $input_values['password'] = ''; + $input_values['endpoint'] = ''; + $input_values['authorization_server_type'] = ''; + $input_values['authorization_server'] = ''; + $input_values['client_id'] = ''; + $input_values['client_secret'] = ''; + } + // Remove unneeded values if on a Public or Private instance. + else { + $input_values['account_json_key'] = ''; + } + + // Remove `key_value` so it doesn't get double encoded. + unset($input_values['key_value']); } - // Remove `key_value` so it doesn't get double encoded. - unset($input_values['key_value']); // Reset values to just `key_value`. $form_state->setValues(['key_value' => Json::encode(array_filter($input_values))]); return parent::processSubmittedKeyValue($form_state); diff --git a/src/Plugin/KeyType/ApigeeAuthKeyType.php b/src/Plugin/KeyType/ApigeeAuthKeyType.php index 4495201e..f7dd5d35 100644 --- a/src/Plugin/KeyType/ApigeeAuthKeyType.php +++ b/src/Plugin/KeyType/ApigeeAuthKeyType.php @@ -19,6 +19,7 @@ namespace Drupal\apigee_edge\Plugin\KeyType; +use Drupal\apigee_edge\Connector\HybridAuthentication; use Drupal\apigee_edge\OauthAuthentication; use Drupal\apigee_edge\Plugin\EdgeKeyTypeBase; use Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface; @@ -41,9 +42,13 @@ * multivalue = { * "enabled" = true, * "fields" = { + * "instance_type" = { + * "label" = @Translation("Instance type"), + * "required" = false + * }, * "auth_type" = { * "label" = @Translation("Authentication type"), - * "required" = true + * "required" = false * }, * "organization" = { * "label" = @Translation("Organization"), @@ -51,11 +56,11 @@ * }, * "username" = { * "label" = @Translation("Username"), - * "required" = true + * "required" = false * }, * "password" = { * "label" = @Translation("Password"), - * "required" = true + * "required" = false * }, * "endpoint" = { * "label" = @Translation("Apigee Edge endpoint"), @@ -72,6 +77,10 @@ * "client_secret" = { * "label" = @Translation("Client secret"), * "required" = false + * }, + * "account_json_key" = { + * "label" = @Translation("Account JSON key"), + * "required" = false * } * } * } @@ -121,7 +130,13 @@ public function validateKeyValue(array $form, FormStateInterface $form_state, $k */ public function getAuthenticationMethod(KeyInterface $key): Authentication { $values = $key->getKeyValues(); - if ($values['auth_type'] === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { + + if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $account_key = $this->getAccountKey($key); + return new HybridAuthentication($account_key['client_email'], $account_key['private_key'], \Drupal::service('apigee_edge.authentication.oauth_token_storage')); + } + + elseif ($values['auth_type'] === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { // Use Oauth authentication. return new OauthAuthentication($this->getUsername($key), $this->getPassword($key), \Drupal::service('apigee_edge.authentication.oauth_token_storage'), NULL, $this->getClientId($key), $this->getClientSecret($key), NULL, $this->getAuthorizationServer($key)); } diff --git a/src/SDKConnector.php b/src/SDKConnector.php index c383ff00..ce0985ae 100644 --- a/src/SDKConnector.php +++ b/src/SDKConnector.php @@ -23,6 +23,7 @@ use Apigee\Edge\Client; use Apigee\Edge\ClientInterface; use Apigee\Edge\HttpClient\Utility\Builder; +use Drupal\apigee_edge\Connector\HybridCredentials; use Drupal\apigee_edge\Exception\AuthenticationKeyException; use Drupal\apigee_edge\Exception\AuthenticationKeyNotFoundException; use Drupal\apigee_edge\Exception\InvalidArgumentException; @@ -231,7 +232,10 @@ private function setCredentials(CredentialsInterface $credentials) { private function buildCredentials(KeyInterface $key): CredentialsInterface { /** @var \Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface $key */ if ($key->getKeyType() instanceof EdgeKeyTypeInterface) { - if ($key->getKeyType()->getAuthenticationType($key) === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { + if ($key->getKeyType()->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + return new HybridCredentials($key); + } + elseif ($key->getKeyType()->getAuthenticationType($key) === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) { return new OauthCredentials($key); } return new Credentials($key); diff --git a/tests/src/FunctionalJavascript/Form/AuthenticationFormJsTest.php b/tests/src/FunctionalJavascript/Form/AuthenticationFormJsTest.php index 86dc8dc0..15dc6a27 100644 --- a/tests/src/FunctionalJavascript/Form/AuthenticationFormJsTest.php +++ b/tests/src/FunctionalJavascript/Form/AuthenticationFormJsTest.php @@ -21,6 +21,7 @@ use Drupal\apigee_edge\Form\AuthenticationForm; use Drupal\apigee_edge\OauthTokenFileStorage; +use Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface; use Drupal\Core\Url; use Drupal\key\Entity\Key; use Drupal\Tests\apigee_edge\FunctionalJavascript\ApigeeEdgeFunctionalJavascriptTestBase; @@ -61,6 +62,20 @@ class AuthenticationFormJsTest extends ApigeeEdgeFunctionalJavascriptTestBase { */ private $endpoint; + /** + * The Apigee instance type. + * + * @var string + */ + private $instanceType; + + /** + * The account JSON key. + * + * @var string + */ + private $account_key; + /** * {@inheritdoc} */ @@ -70,10 +85,17 @@ protected function setUp() { /** @var \Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface $test_key_type */ $test_key = Key::load($this->config(AuthenticationForm::CONFIG_NAME)->get('active_key')); $test_key_type = $test_key->getKeyType(); - $this->username = $test_key_type->getUsername($test_key); - $this->password = $test_key_type->getPassword($test_key); + $this->instanceType = $test_key_type->getInstanceType($test_key); + $this->organization = $test_key_type->getOrganization($test_key); - $this->endpoint = $test_key_type->getEndpoint($test_key); + if ($this->instanceType != EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $this->username = $test_key_type->getUsername($test_key); + $this->password = $test_key_type->getPassword($test_key); + $this->endpoint = $test_key_type->getEndpoint($test_key); + } + else { + $this->account_key = $test_key_type->getAccountKey($test_key); + } // Restore the default HTTP timeout set by the testing module because // we would like to run a test that tries to connect to an invalid // endpoint and we should not wait 6 minutes for the result. @@ -84,6 +106,10 @@ protected function setUp() { * Tests the Authentication form. */ public function testAuthenticationForm() { + if ($this->instanceType == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $this->markTestSkipped('Skipping "testAuthenticationForm": can only be tested with public/private cloud credentials.'); + } + $web_assert = $this->assertSession(); // Test the authentication form using the default key stored by environment @@ -102,7 +128,6 @@ public function testAuthenticationForm() { $web_assert->fieldValueEquals('Organization', $this->organization); $web_assert->fieldValueEquals('Username', $this->username); $web_assert->fieldValueEquals('Password', $this->password); - $web_assert->fieldValueEquals('Custom Apigee Edge endpoint', $this->endpoint); } /** @@ -113,6 +138,10 @@ public function testAuthenticationForm() { * form is a customized Key edit form. */ public function testKeyAddForm() { + if ($this->instanceType == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) { + $this->markTestSkipped('Skipping "testKeyAddForm": can only be tested with public/private cloud credentials.'); + } + $web_assert = $this->assertSession(); // Test the authentication form using the default key stored by environment @@ -141,6 +170,42 @@ public function testKeyAddForm() { $this->validateForm([$this, 'visitKeyAddForm']); } + /** + * Tests the Authentication form using Hybrid auth. + * + * @group hybrid + */ + public function testUsingHybridForm() { + // We have to structure the key variables so that this test class can + // be run both against a Public and Hybrid cloud orgs. Because of this, + // if the APIGEE_EDGE_HYBRID_ORGANIZATION environment var is set, it will + // use it as the Hybrid org for this test. + // Similarly, if the configured key credentials are for a Public/Private + // cloud org, then retrieve the account key directly from the environment. + $organization = getenv('APIGEE_EDGE_HYBRID_ORGANIZATION') ?: $this->organization; + $account_key = $this->account_key ? json_encode($this->account_key) : getenv('APIGEE_EDGE_ACCOUNT_JSON_KEY'); + + if (!$organization || !$account_key) { + $this->markTestSkipped('Skipping "testUsingHybridForm": missing test environment variables APIGEE_EDGE_HYBRID_ORGANIZATION and/or APIGEE_EDGE_ACCOUNT_JSON_KEY.'); + } + + $web_assert = $this->assertSession(); + + // Test the authentication form. + $this->drupalLogin($this->rootUser); + $this->drupalGet(Url::fromRoute('entity.key.add_form')); + $this->visitKeyAddForm(); + + $page = $this->getSession()->getPage(); + + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID); + $page->fillField('Organization', $organization); + $page->fillField('GCP service account key', $account_key); + + $this->assertSendRequestMessage('.messages--status', 'Connection successful.'); + $web_assert->elementNotExists('css', 'details[data-drupal-selector="edit-debug"]'); + } + /** * Visits the Authentication form for testing. */ @@ -219,11 +284,10 @@ protected function validateForm(callable $visitFormAsAdmin): void { $this->assertFalse($this->cssSelect('input[name="key_input_settings[client_secret]"]')[0]->isVisible()); // Test the connection with basic auth. + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC); $page->fillField('Username', $this->username); $page->fillField('Password', $this->password); $page->fillField('Organization', $this->organization); - $page->selectFieldOption('key_input_settings[endpoint_type]', 'custom'); - $page->fillField('Custom Apigee Edge endpoint', $this->endpoint); $this->assertSession()->pageTextContains('Send request using the given API credentials.'); $this->assertSendRequestMessage('.messages--status', 'Connection successful.'); $web_assert->elementNotExists('css', 'details[data-drupal-selector="edit-debug"]'); @@ -263,11 +327,10 @@ protected function validateForm(callable $visitFormAsAdmin): void { // page on success therefore we have to re-visit the form again. $visitFormAsAdmin(); // Setup valid credentials again. + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC); $page->fillField('Username', $this->username); $page->fillField('Password', $this->password); $page->fillField('Organization', $this->organization); - $page->selectFieldOption('key_input_settings[endpoint_type]', 'custom'); - $page->fillField('Custom Apigee Edge endpoint', $this->endpoint); // Test invalid password. $random_pass = $this->randomString(); @@ -282,8 +345,8 @@ protected function validateForm(callable $visitFormAsAdmin): void { $web_assert->elementNotContains('css', 'textarea[data-drupal-selector="edit-debug-text"]', $random_pass); $page->fillField('Password', $this->password); - // Test invalid username when using default endpoint. - $page->selectFieldOption('key_input_settings[endpoint_type]', 'default'); + // Test invalid username when using public cloud endpoint. + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC); $page->fillField('Username', $this->randomMachineName()); $this->assertSendRequestMessage('.messages--error', "Failed to connect to Apigee Edge. The organization username should be a valid email. Error message: "); $page->fillField('Username', $this->username); @@ -297,27 +360,27 @@ protected function validateForm(callable $visitFormAsAdmin): void { $page->fillField('Organization', $this->organization); // Test invalid endpoint. - $page->selectFieldOption('key_input_settings[endpoint_type]', 'custom'); + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PRIVATE); $invalid_domain = "{$this->randomGenerator->word(16)}.example.com"; - $page->fillField('Custom Apigee Edge endpoint', "http://{$invalid_domain}/"); + $page->fillField('Apigee Edge endpoint', "http://{$invalid_domain}/"); $this->assertSendRequestMessage('.messages--error', "Failed to connect to Apigee Edge. The given endpoint (http://{$invalid_domain}/) is incorrect or something is wrong with the connection. Error message: "); $web_assert->elementContains('css', 'textarea[data-drupal-selector="edit-debug-text"]', "\"endpoint\": \"http:\/\/{$invalid_domain}\/\""); - $web_assert->fieldValueEquals('Custom Apigee Edge endpoint', "http://{$invalid_domain}/"); - $page->fillField('Custom Apigee Edge endpoint', ''); - $page->selectFieldOption('key_input_settings[endpoint_type]', 'default'); + $web_assert->fieldValueEquals('Apigee Edge endpoint', "http://{$invalid_domain}/"); + $page->fillField('Apigee Edge endpoint', ''); + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC); // Test another invalid endpoint scenario: // This endpoint is not a Management API endpoint, but still returns // HTTP 200 with a JSON response. $invalid_endpoint = 'enterprise.apigee.com/platform/orgname'; - $page->selectFieldOption('key_input_settings[endpoint_type]', 'custom'); + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PRIVATE); $page->fillField('Apigee Edge endpoint', "https://{$invalid_endpoint}/"); $this->assertSendRequestMessage('.messages--error', "Failed to connect to Apigee Edge. The given endpoint (https://{$invalid_endpoint}/) is incorrect or something is wrong with the connection. Error message: "); $invalid_endpoint_escaped = str_replace('/', '\/', $invalid_endpoint); $web_assert->elementContains('css', 'textarea[data-drupal-selector="edit-debug-text"]', "\"endpoint\": \"https:\/\/{$invalid_endpoint_escaped}\/\""); - $web_assert->fieldValueEquals('Custom Apigee Edge endpoint', "https://{$invalid_endpoint}/"); - $page->fillField('Custom Apigee Edge endpoint', ''); - $page->selectFieldOption('key_input_settings[endpoint_type]', 'default'); + $web_assert->fieldValueEquals('Apigee Edge endpoint', "https://{$invalid_endpoint}/"); + $page->fillField('Apigee Edge endpoint', ''); + $page->selectFieldOption('key_input_settings[instance_type]', EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC); // Test invalid authorization server. $this->cssSelect('select[data-drupal-selector="edit-key-input-settings-auth-type"]')[0]->setValue('oauth');