Skip to content

Commit cce7175

Browse files
Add the ability to use Google Compute Engine Service Account for Authentication (#505)
* GCE Authentication test * Added support for GCE Service accounts to Apigee Hybrid Update composer.json Fixed issues Fixed logic errors Fix form structure Provide endpoint to ngsaas Version change to add new Service Account Key Type object to array Added a checkbox for allowing GCP default service accounts to be used on the GCP platform removed changes to composer.json removed changes to composer.json fixed Imports if GCE service account is available use that by default Fix Code Sniffer Errors Fix Code Sniffer Errors Fix Code Sniffer Errors Fix Code Sniffer Errors Fix Code Sniffer Errors Fix the headers key Changes after the code review. * Moved the GCE Service Account class to the Apigee PHP SDK * Require apigee/apigee-client-php 2.0.6, minor cleanup. Co-authored-by: Arlina Espinoza <[email protected]>
1 parent 99238e8 commit cce7175

6 files changed

+115
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2020 Google Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU General Public License
8+
* version 2 as published by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18+
* MA 02110-1301, USA.
19+
*/
20+
21+
namespace Drupal\apigee_edge\Connector;
22+
23+
use Apigee\Edge\ClientInterface;
24+
use Apigee\Edge\HttpClient\Plugin\Authentication\GceServiceAccount;
25+
26+
/**
27+
* Decorator for Hybrid authentication plugin.
28+
*/
29+
class GceServiceAccountAuthentication extends GceServiceAccount {
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
protected function authClient(): ClientInterface {
35+
/** @var \Drupal\apigee_edge\SDKConnectorInterface $sdk_connector */
36+
$sdk_connector = \Drupal::service('apigee_edge.sdk_connector');
37+
return $sdk_connector->buildClient($this->getAuthHeader(), $this->getAuthServer());
38+
}
39+
40+
}

src/Connector/HybridCredentials.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ class HybridCredentials extends Credentials {
4242
public function __construct(KeyInterface $key) {
4343
if ($key->getKeyType() instanceof EdgeKeyTypeInterface
4444
&& ($auth_type = $key->getKeyType()->getAuthenticationType($key))
45-
&& $auth_type === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT
45+
&& (
46+
$auth_type === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT ||
47+
$auth_type === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_DEFAULT_GCE_SERVICE_ACCOUNT
48+
)
4649
) {
4750
parent::__construct($key);
4851
}

src/Plugin/EdgeKeyTypeBase.php

+13-2
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,13 @@ public function unserialize($value) {
5151
*/
5252
public function getAuthenticationType(KeyInterface $key): string {
5353
if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) {
54-
return EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT;
54+
if ($this->useGcpDefaultServiceAccount($key)) {
55+
return EdgeKeyTypeInterface::EDGE_AUTH_TYPE_DEFAULT_GCE_SERVICE_ACCOUNT;
56+
}
57+
else {
58+
return EdgeKeyTypeInterface::EDGE_AUTH_TYPE_JWT;
59+
}
5560
}
56-
5761
if (!isset($key->getKeyValues()['auth_type'])) {
5862
throw new AuthenticationKeyValueMalformedException('auth_type');
5963
}
@@ -166,4 +170,11 @@ public function getAccountKey(KeyInterface $key): array {
166170
return $json;
167171
}
168172

173+
/**
174+
* {@inheritdoc}
175+
*/
176+
public function useGcpDefaultServiceAccount(KeyInterface $key): bool {
177+
return !empty($key->getKeyValues()['gcp_hosted']);
178+
}
179+
169180
}

src/Plugin/EdgeKeyTypeInterface.php

+14
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ interface EdgeKeyTypeInterface extends KeyTypeMultivalueInterface, KeyTypeAuthen
6868
*/
6969
const EDGE_AUTH_TYPE_JWT = 'jwt';
7070

71+
const EDGE_AUTH_TYPE_DEFAULT_GCE_SERVICE_ACCOUNT = 'gce-service-account';
7172
/**
7273
* The endpoint type for default.
7374
*
@@ -220,4 +221,17 @@ public function getClientSecret(KeyInterface $key): string;
220221
*/
221222
public function getAccountKey(KeyInterface $key): array;
222223

224+
/**
225+
* Return if you should use the Default Service account.
226+
*
227+
* This applies to portals hosted on Google Compute Engine.
228+
*
229+
* @param \Drupal\key\KeyInterface $key
230+
* The key entity.
231+
*
232+
* @return bool
233+
* The account key as an array.
234+
*/
235+
public function useGcpDefaultServiceAccount(KeyInterface $key): bool;
236+
223237
}

src/Plugin/KeyInput/ApigeeAuthKeyInput.php

+32-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace Drupal\apigee_edge\Plugin\KeyInput;
2121

2222
use Apigee\Edge\HttpClient\Plugin\Authentication\Oauth;
23+
use Drupal\apigee_edge\Connector\GceServiceAccountAuthentication;
2324
use Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface;
2425
use Drupal\Component\Serialization\Json;
2526
use Drupal\Core\Form\FormStateInterface;
@@ -92,9 +93,11 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
9293
'#type' => 'textfield',
9394
'#title' => $this->t('Organization'),
9495
'#description' => $this->t('Name of the organization on Apigee Edge. Changing this value could make your site stop working.'),
95-
'#required' => TRUE,
9696
'#default_value' => $values['organization'] ?? '',
97+
'#required' => TRUE,
9798
'#attributes' => ['autocomplete' => 'off'],
99+
'#prefix' => '<div id="edit-organization-field">',
100+
'#suffix' => '</div>',
98101
];
99102
$form['username'] = [
100103
'#type' => 'textfield',
@@ -123,15 +126,33 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
123126
if (empty($values['organization'])) {
124127
$form['password']['#states']['required'] = [$state_for_public, $state_for_private];
125128
}
129+
130+
$state_for_not_gcp_hosted = [];
131+
$gceServiceAccountAuth = new GceServiceAccountAuthentication(\Drupal::service('apigee_edge.authentication.oauth_token_storage'));
132+
if ($gceServiceAccountAuth->isAvailable()) {
133+
$form['gcp_hosted'] = [
134+
'#type' => 'checkbox',
135+
'#title' => $this->t('Use the default service account if this portal is hosted on GCP'),
136+
'#description' => $this->t("Please ensure you have added 'Apigee Developer Administrator' role to the default compute engine service account hosting this portal."),
137+
'#default_value' => $values['gcp_hosted'] ?? TRUE,
138+
'#states' => [
139+
'visible' => $state_for_hybrid,
140+
],
141+
];
142+
$state_for_not_gcp_hosted = [
143+
':input[name="key_input_settings[gcp_hosted]"]' => ['checked' => FALSE],
144+
];
145+
}
146+
126147
$form['account_json_key'] = [
127148
'#type' => 'textarea',
128149
'#title' => $this->t('GCP service account key'),
129150
'#description' => $this->t("Paste the contents of the GCP service account key JSON file."),
130151
'#default_value' => $values['account_json_key'] ?? '',
131152
'#rows' => '8',
132153
'#states' => [
133-
'visible' => $state_for_hybrid,
134-
'required' => $state_for_hybrid,
154+
'visible' => $state_for_hybrid + $state_for_not_gcp_hosted,
155+
'required' => $state_for_hybrid + $state_for_not_gcp_hosted,
135156
],
136157
];
137158
$form['endpoint'] = [
@@ -232,8 +253,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
232253
*/
233254
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
234255
$input_values = $form_state->getUserInput()['key_input_settings'];
235-
236-
if ($input_values['instance_type'] == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) {
256+
if ($input_values['instance_type'] == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID &&
257+
empty($input_values['gcp_hosted'])) {
237258
$account_key = $input_values['account_json_key'] ?? '';
238259
$json = json_decode($account_key, TRUE);
239260
if (empty($json['private_key']) || empty($json['client_email'])) {
@@ -270,11 +291,16 @@ public function processSubmittedKeyValue(FormStateInterface $form_state) {
270291
$input_values['authorization_server'] = '';
271292
$input_values['client_id'] = '';
272293
$input_values['client_secret'] = '';
294+
if (!empty($input_values['gcp_hosted'])) {
295+
$input_values['account_json_key'] = '';
296+
}
273297
}
274298
else {
275299
// Remove unneeded values if on a Public or Private instance.
276300
$input_values['account_json_key'] = '';
277-
301+
if (!empty($input_values['gcp_hosted'])) {
302+
unset($input_values['gcp_hosted']);
303+
}
278304
// If password field is empty we just skip it and preserve the initial
279305
// password if there is one already.
280306
if (empty($input_values['password']) && !empty($form_state->get('key_value')['current'])) {

src/Plugin/KeyType/ApigeeAuthKeyType.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
namespace Drupal\apigee_edge\Plugin\KeyType;
2121

22+
use Drupal\apigee_edge\Connector\GceServiceAccountAuthentication;
2223
use Drupal\apigee_edge\Connector\HybridAuthentication;
2324
use Drupal\apigee_edge\OauthAuthentication;
2425
use Drupal\apigee_edge\Plugin\EdgeKeyTypeBase;
@@ -81,6 +82,10 @@
8182
* "account_json_key" = {
8283
* "label" = @Translation("Account JSON key"),
8384
* "required" = false
85+
* },
86+
* "gcp_hosted" = {
87+
* "label" = @Translation("Use default service account if hosted on GCP"),
88+
* "required" = false
8489
* }
8590
* }
8691
* }
@@ -130,12 +135,15 @@ public function validateKeyValue(array $form, FormStateInterface $form_state, $k
130135
*/
131136
public function getAuthenticationMethod(KeyInterface $key): Authentication {
132137
$values = $key->getKeyValues();
133-
134138
if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) {
135-
$account_key = $this->getAccountKey($key);
136-
return new HybridAuthentication($account_key['client_email'], $account_key['private_key'], \Drupal::service('apigee_edge.authentication.oauth_token_storage'));
139+
if ($this->useGcpDefaultServiceAccount($key)) {
140+
return new GceServiceAccountAuthentication(\Drupal::service('apigee_edge.authentication.oauth_token_storage'));
141+
}
142+
else {
143+
$account_key = $this->getAccountKey($key);
144+
return new HybridAuthentication($account_key['client_email'], $account_key['private_key'], \Drupal::service('apigee_edge.authentication.oauth_token_storage'));
145+
}
137146
}
138-
139147
elseif ($values['auth_type'] === EdgeKeyTypeInterface::EDGE_AUTH_TYPE_OAUTH) {
140148
// Use Oauth authentication.
141149
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));

0 commit comments

Comments
 (0)