Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#432] Implement credentials deletion and generation #446

Merged
merged 45 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0a8efa0
[#432] Implement credential deletion and generation
shadcn Jul 16, 2020
66430e2
[#432] Add operations links and expiration date
shadcn Jul 17, 2020
bd56b7a
Merge branch '8.x-1.x' into 432-api-keys
shadcn Jul 17, 2020
5a49e13
[#432] Add tests
shadcn Jul 20, 2020
bad7da9
[#432] Implement routes for Team App
shadcn Jul 20, 2020
8a50973
[#430] Refactor to a trait
shadcn Jul 20, 2020
b0c8d1e
[#432] Lock rules version
shadcn Jul 20, 2020
ab1e898
[#432] Fix rules version
shadcn Jul 20, 2020
ab52c12
[#430] Fix remove product event test
shadcn Jul 21, 2020
b2c358c
[#432] Remove approve operation
shadcn Aug 11, 2020
ae8498a
[#432] Update credential form to use most recently active one
shadcn Aug 11, 2020
f476cca
[#432] Update tests
shadcn Aug 11, 2020
37a1e46
[#432] Fix phpcs notices
shadcn Aug 11, 2020
60dde9a
[#432] Make add key form use modal
shadcn Aug 11, 2020
95471ff
[#432] Rename credential to api keys
shadcn Aug 11, 2020
2dcd1bc
Merge branch '8.x-1.x' into 432-api-keys
shadcn Aug 11, 2020
099de41
[#432] Fix conflicts
shadcn Aug 11, 2020
366737e
[#432] Fix merge conflicts
shadcn Aug 11, 2020
48f568b
[#432] Update selector for dropbutton
shadcn Aug 11, 2020
37edf3e
[#432] Revert php change
shadcn Aug 11, 2020
a76120c
[#432] Do not show warning for revoked credentials
shadcn Aug 11, 2020
ea25747
[#432] Skip api key routes for permission tests
shadcn Aug 11, 2020
8df46d2
[#432] Implement api keys permissions
shadcn Aug 11, 2020
29d780c
[#432] Add tests for expired credentials warning
shadcn Aug 11, 2020
de02ba3
[#432] Remove update access check
shadcn Aug 11, 2020
54031af
[#432] Hide revoke credentials in collapsible section
shadcn Aug 11, 2020
f5489fc
[#432] Fix tests
shadcn Aug 12, 2020
22707fd
[#432] Add api key routes permission tests
shadcn Aug 12, 2020
58ea3d8
[#432] Add add_api_key to permission matrix
shadcn Aug 12, 2020
1b997b3
Merge branch '8.x-1.x' into 432-api-keys
shadcn Aug 12, 2020
c128681
[#432] Update route for team test
shadcn Aug 12, 2020
2797f18
[#432] Prevent revoking/deleting the only active key
shadcn Aug 12, 2020
b502012
[#432] Fix tests for new permission
shadcn Aug 13, 2020
b69a8a6
[#432] Use machine name
shadcn Aug 13, 2020
3da059e
[#432] Clean up developer and developerApp in tear down
shadcn Aug 13, 2020
317409a
[#432] Add permission for editing API products
shadcn Aug 13, 2020
61d9bdd
[#432] Assign default permissions
shadcn Aug 18, 2020
c643df0
[#432] Update test permissions
shadcn Aug 18, 2020
e5d3dea
Fix credentials show/hide functionality issue when it gets revoked.
minnur Aug 18, 2020
d7fdd89
Merge branch '432-api-keys' of github.com:arshad/apigee-edge-drupal i…
minnur Aug 18, 2020
32a9563
Add revoke_api_key permission to authenticated users
shadcn Aug 24, 2020
f5452d4
Remove add_keys from under content
shadcn Aug 24, 2020
92ea96e
[#432] Add "edit_api_products" to authenticated user default permissi…
arlina-espinoza Aug 28, 2020
6c518d7
[#432] Fix DeveloperAppApiKeysPermissionTest for integration and mock…
arlina-espinoza Sep 1, 2020
bdcc139
[#432] Fix DeveloperAppApiKeyTest for integration and mock tests.
arlina-espinoza Sep 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions apigee_edge.install
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ function apigee_edge_install() {
'update own developer_app',
'delete own developer_app',
'analytics own developer_app',
'add_api_key own developer_app',
'revoke_api_key own developer_app',
'edit_api_products developer_app',
];
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, $authenticated_user_permissions);
}
Expand Down Expand Up @@ -258,3 +261,16 @@ function apigee_edge_update_8102() {
$last_installed_schema_repository->setLastInstalledFieldStorageDefinitions($entity_type_id, $original_storage_definitions);
}

/**
* Assign the "add_api_key own developer_app" permission to authenticated users.
*/
function apigee_edge_update_8103() {
if (\Drupal::moduleHandler()->moduleExists('user')) {
$authenticated_user_permissions = [
'add_api_key own developer_app',
'revoke_api_key own developer_app',
'edit_api_products developer_app',
];
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, $authenticated_user_permissions);
}
}
6 changes: 6 additions & 0 deletions apigee_edge.libraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ apigee_edge.app_view:
- core/drupal
- core/drupalSettings

apigee_edge.app_credential:
version: 1.0
css:
theme:
css/apigee_edge.app_credential.css: {}

apiproduct_access_admin:
version: 1.0
js:
Expand Down
90 changes: 84 additions & 6 deletions apigee_edge.module
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,53 @@ function apigee_edge_entity_view(array &$build, EntityInterface $entity, EntityV
$build['credentials'] = [
'#type' => 'container',
];
$index = 0;
foreach ($entity->getCredentials() as $credential) {
$build['credentials'][] = [
$build['credentials'][$credential->getStatus()][] = [
'#type' => 'app_credential',
'#credential' => $credential,
'#app_name' => $entity->getName(),
'#team_app_name' => isset($team_app_name) ? $team_app_name : '',
'#app' => $entity,
'#attributes' => [
'class' => 'items--inline',
'data-app' => $entity->getName(),
'data-team' => isset($team_app_name) ? $team_app_name : '',
'data-app-container-index' => $index,
],
] + $defaults;
$index++;
}

// Hide revoked credentials in a collapsible section.
if (!empty($build['credentials'][AppCredentialInterface::STATUS_REVOKED])) {
$revoked_credentials = $build['credentials'][AppCredentialInterface::STATUS_REVOKED];
$build['credentials'][AppCredentialInterface::STATUS_REVOKED] = [
'#type' => 'details',
'#title' => t('Revoked keys (@count)', ['@count' => count($revoked_credentials)]),
'#weight' => 100,
'credentials' => $revoked_credentials,
];
}
}

// Add link to add keys.
if($entity->access('add_api_key') && $entity->hasLinkTemplate('add-api-key-form')) {
$build['add_keys'] = Link::fromTextAndUrl(t('Add key'), $entity->toUrl('add-api-key-form', [
'attributes' => [
'data-dialog-type' => 'modal',
'data-dialog-options' => json_encode([
'width' => 500,
'height' => 250,
'draggable' => FALSE,
'autoResize' => FALSE,
]),
'class' => [
'use-ajax',
'button',
],
],
]))->toRenderable();
}
}
}
Expand Down Expand Up @@ -457,6 +493,27 @@ function apigee_edge_api_product_access(EntityInterface $entity, $operation, Acc
return $result->cachePerUser()->addCacheTags(['config:' . $config_name]);
}

/**
* Implements hook_entity_access().
*/
function apigee_edge_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
if (!$entity->getEntityType()->entityClassImplements(AppInterface::class) || !in_array($operation, ['revoke_api_key', 'delete_api_key'])) {
return AccessResult::neutral();
}

/** @var \Drupal\apigee_edge\Entity\AppInterface $entity **/

$approved_credentials = array_filter($entity->getCredentials(), function (AppCredentialInterface $credential) {
return $credential->getStatus() === AppCredentialInterface::STATUS_APPROVED;
});

// Prevent revoking/deleting the only active key.
if (count($approved_credentials) <= 1) {
$action = $operation === "revoke_api_key" ? "revoke" : "delete";
return AccessResult::forbidden("You cannot $action the only active key.");
}
}

/**
* Implements hook_form_FORM_ID_alter().
*/
Expand Down Expand Up @@ -1031,6 +1088,8 @@ function template_preprocess_app_credential_product_list(array &$variables) {
function template_preprocess_app_credential(array &$variables) {
/** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */
$credential = $variables['elements']['#credential'];
/** @var \Drupal\apigee_edge\Entity\AppInterface $app */
$app = $variables['elements']['#app'];
/** @var \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter */
$dateFormatter = Drupal::service('date.formatter');
$serializer = new AppCredentialSerializer();
Expand Down Expand Up @@ -1069,14 +1128,10 @@ function template_preprocess_app_credential(array &$variables) {
'#type' => 'container',
'#attributes' => [
'class' => 'wrapper--primary app-details-wrapper',
'data-app' => $variables['elements']['#app_name'],
],
];

if (!empty($variables['elements']['#team_app_name'])) {
$variables['primary_wrapper']['#attributes']['data-team'] = $variables['elements']['#team_app_name'];
}

$index = 0;
foreach ($properties_in_primary as $property => $def) {
$variables['primary_wrapper'][$property] = [
'#type' => 'container',
Expand Down Expand Up @@ -1120,6 +1175,7 @@ function template_preprocess_app_credential(array &$variables) {
'#attributes' => [
'class' => 'secret',
'data-secret-type' => $property,
'data-app-index' => $index,
],
'#value' => '',
];
Expand All @@ -1140,6 +1196,7 @@ function template_preprocess_app_credential(array &$variables) {
'#markup' => Xss::filter($value),
];
}
$index++;
}

$variables['secondary_wrapper'] = [
Expand All @@ -1160,6 +1217,27 @@ function template_preprocess_app_credential(array &$variables) {

// Helpful $content variable for templates.
$variables['content'] = $normalized;

// Add operations.
$variables['operations'] = [
'#type' => 'operations',
];

if ($credential->getStatus() === AppCredentialInterface::STATUS_APPROVED && $app->access('revoke_api_key') && $app->hasLinkTemplate('revoke-api-key-form')) {
$variables['operations']['#links']['revoke'] = [
'title' => t('Revoke'),
'url' => $app->toUrl('revoke-api-key-form')
->setRouteParameter('consumer_key', $credential->getConsumerKey()),
];
}

if ($app->access('delete_api_key') && $app->hasLinkTemplate('delete-api-key-form')) {
$variables['operations']['#links']['delete'] = [
'title' => t('Delete'),
'url' => $app->toUrl('delete-api-key-form')
->setRouteParameter('consumer_key', $credential->getConsumerKey()),
];
}
}

/**
Expand Down
26 changes: 26 additions & 0 deletions css/apigee_edge.app_credential.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2020 Google Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
*
* 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 General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

.app-credential {
position: relative;
}

.app-credential .dropbutton-wrapper {
position: absolute;
top: 0;
right: 20px;
}
5 changes: 5 additions & 0 deletions css/apigee_edge.components.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
vertical-align: super;
}

/* This fixes the ui datepicker position when displayed in a modal. */
.ui-datepicker {
shadcn marked this conversation as resolved.
Show resolved Hide resolved
z-index: 1500 !important;
}

@media screen and (min-width: 768px) {
.apigee-edge--form .form-checkboxes .form-item {
width: 49%;
Expand Down
8 changes: 4 additions & 4 deletions js/apigee_edge.app_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
Drupal.apigeeEdgeDetails = {
editActions: function (context, settings) {
var secrets = $('.secret', context);
var appElWrapper = '.app-details-wrapper';
var appElWrapper = '.app-credential';
var showHideEl = 'a.secret-show-hide';
var pClass = 'processing';
var loader = '<img src="' + drupalSettings.path.baseUrl + 'core/misc/throbber-active.gif" border="0" />';
Expand All @@ -54,8 +54,8 @@
$(this).parent().parent(),
$wrapper.data('team'),
$wrapper.data('app'),
$wrapper.closest('fieldset').parent().find('fieldset').index($(this).closest('fieldset')),
$wrapper.find(showHideEl).index(this)
$wrapper.data('app-container-index'),
$(this).closest('.secret').data('app-index')
);
}
});
Expand All @@ -81,7 +81,7 @@
*/
function callEndpoint(teamApp, app, callback) {
var endpoint = drupalSettings.path.baseUrl + 'user/' + drupalSettings.currentUser + '/apps/' + app + '/api-keys';
if (teamApp !== undefined) {
if (teamApp !== undefined && teamApp !== 0 && teamApp !== '') {
endpoint = drupalSettings.path.baseUrl + 'teams/' + teamApp + '/apps/' + app + '/api-keys';
}
$.get(endpoint, function(data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,6 @@ public function testEvent() {
'product' => $api_product,
],
]);
$this->stack->queueMockResponse([
'get_developer_apps' => [
'apps' => [$developer_app]
],
]);
$app_credential_controller->deleteApiProduct($consumer_key, $api_product->id());

$this->assertLogsContains("Event apigee_edge_actions_entity_remove_product:developer_app was dispatched.");
Expand Down
17 changes: 17 additions & 0 deletions modules/apigee_edge_teams/apigee_edge_teams.install
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,20 @@ function apigee_edge_teams_requirements($phase) {

return $requirements;
}

/**
* Assign "add_api_key", "revoke_api_key" and "edit_api_products" permissions to team administrators.
*/
function apigee_edge_teams_update_8701() {
$role = 'admin';
$api_key_permissions = [
'team_app_add_api_key',
'team_app_edit_api_products',
'team_app_revoke_api_key',
];
/** @var \Drupal\apigee_edge_teams\Entity\Storage\TeamRoleStorageInterface $storage */
$storage = Drupal::entityTypeManager()->getStorage('team_role');
/** @var \Drupal\apigee_edge_teams\Entity\TeamRoleInterface $admin_role */
$admin_role = $storage->load($role);
$storage->changePermissions($role, $admin_role->getPermissions() + array_combine($api_key_permissions, $api_key_permissions));
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ permissions:
- team_manage_members
- team_app_delete
- team_app_update
- team_app_add_api_key
- team_app_edit_api_products
- team_app_revoke_api_key
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public function permissions(): array {
'update' => $this->t('Edit any Team Apps'),
'delete' => $this->t('Delete any Team Apps'),
'analytics' => $this->t('View analytics of any Team Apps'),
'add_api_key' => $this->t('Add API key to Team Apps'),
'revoke_api_key' => $this->t('Revoke API key from Team Apps'),
'delete_api_key' => $this->t('Delete API key from Team Apps'),
'edit_api_products' => $this->t('Edit API products for Team Apps'),
],
],
'api_product' => [
Expand Down
30 changes: 28 additions & 2 deletions modules/apigee_edge_teams/src/Entity/Form/TeamAppEditForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Drupal\apigee_edge\Entity\Controller\AppCredentialControllerInterface;
use Drupal\apigee_edge\Entity\Form\AppEditForm;
use Drupal\apigee_edge_teams\Entity\Controller\TeamAppCredentialControllerFactoryInterface;
use Drupal\apigee_edge_teams\TeamPermissionHandlerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
Expand All @@ -44,6 +45,13 @@ class TeamAppEditForm extends AppEditForm {
*/
protected $appCredentialControllerFactory;

/**
* The team permission handler.
*
* @var \Drupal\apigee_edge_teams\TeamPermissionHandlerInterface
*/
protected $teamPermissionHandler;

/**
* Constructs TeamAppEditForm.
*
Expand All @@ -53,10 +61,16 @@ class TeamAppEditForm extends AppEditForm {
* The renderer service.
* @param \Drupal\apigee_edge_teams\Entity\Controller\TeamAppCredentialControllerFactoryInterface $app_credential_controller_factory
* The team app credential controller factory.
* @param \Drupal\apigee_edge_teams\TeamPermissionHandlerInterface $team_permission_handler
* The team permission handler.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, TeamAppCredentialControllerFactoryInterface $app_credential_controller_factory) {
public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, TeamAppCredentialControllerFactoryInterface $app_credential_controller_factory, TeamPermissionHandlerInterface $team_permission_handler = NULL) {
if (!$team_permission_handler) {
$team_permission_handler = \Drupal::service('apigee_edge_teams.team_permissions');
}
parent::__construct($entity_type_manager, $renderer);
$this->appCredentialControllerFactory = $app_credential_controller_factory;
$this->teamPermissionHandler = $team_permission_handler;
}

/**
Expand All @@ -66,7 +80,8 @@ public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('renderer'),
$container->get('apigee_edge_teams.controller.team_app_credential_controller_factory')
$container->get('apigee_edge_teams.controller.team_app_credential_controller_factory'),
$container->get('apigee_edge_teams.team_permissions')
);
}

Expand Down Expand Up @@ -99,4 +114,15 @@ protected function getRedirectUrl(): Url {
return parent::getRedirectUrl();
}

/**
* {@inheritdoc}
*/
protected function canEditApiProducts(): bool {
if (($team_name = $this->entity->getAppOwner()) && $team = $this->entityTypeManager->getStorage('team')->load($team_name)) {
return in_array('team_app_edit_api_products', $this->teamPermissionHandler->getDeveloperPermissionsByTeam($team, $this->currentUser()));
}

return parent::canEditApiProducts();
}

}
6 changes: 6 additions & 0 deletions modules/apigee_edge_teams/src/Entity/TeamApp.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
* "edit" = "Drupal\apigee_edge_teams\Entity\Form\TeamAppEditForm",
* "delete" = "Drupal\apigee_edge_teams\Entity\Form\TeamAppDeleteForm",
* "analytics" = "Drupal\apigee_edge_teams\Form\TeamAppAnalyticsForm",
* "add_api_key" = "Drupal\apigee_edge_teams\Form\TeamAppApiKeyAddForm",
* "delete_api_key" = "Drupal\apigee_edge_teams\Form\TeamAppApiKeyDeleteForm",
* "revoke_api_key" = "Drupal\apigee_edge_teams\Form\TeamAppApiKeyRevokeForm",
* },
* "list_builder" = "Drupal\apigee_edge_teams\Entity\ListBuilder\TeamAppListBuilder",
* "view_builder" = "Drupal\apigee_edge\Entity\AppViewBuilder",
Expand All @@ -67,6 +70,9 @@
* "delete-form" = "/teams/{team}/apps/{app}/delete",
* "analytics" = "/teams/{team}/apps/{app}/analytics",
* "api-keys" = "/teams/{team}/apps/{app}/api-keys",
* "add-api-key-form" = "/teams/{team}/apps/{app}/api-keys/add",
* "delete-api-key-form" = "/teams/{team}/apps/{app}/api-keys/{consumer_key}/delete",
* "revoke-api-key-form" = "/teams/{team}/apps/{app}/api-keys/{consumer_key}/revoke",
* },
* entity_keys = {
* "id" = "appId",
Expand Down
Loading