View source
<?php
namespace Drupal\Tests\apigee_edge\FunctionalJavascript;
use Apigee\Edge\Api\Management\Entity\App;
use Drupal\apigee_edge\Entity\ApiProduct;
use Drupal\apigee_edge\Entity\ApiProductInterface;
use Drupal\apigee_edge\Entity\DeveloperApp;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
class ApiProductAccessTest extends ApigeeEdgeFunctionalJavascriptTestBase {
protected const USER_WITH_BYPASS_PERM = 'user_with_bypass_perm';
protected const INTERNAL_ROLE = 'internal';
protected const PUBLIC_VISIBILITY = 'public';
protected const PRIVATE_VISIBILITY = 'private';
protected const INTERNAL_VISIBILITY = 'internal';
protected const VISIBILITIES = [
self::PUBLIC_VISIBILITY,
self::PRIVATE_VISIBILITY,
self::INTERNAL_VISIBILITY,
];
protected const SUPPORTED_OPERATIONS = [
'view',
'view label',
'assign',
];
protected $apiProducts = [];
protected $developerApps = [];
protected $users = [];
protected $roleStorage;
protected $accessControlHandler;
protected $ridCombinations;
protected function setUp() {
parent::setUp();
$this->roleStorage = $this->container
->get('entity_type.manager')
->getStorage('user_role');
$this->accessControlHandler = $this->container
->get('entity_type.manager')
->getAccessControlHandler('api_product');
$this->users[AccountInterface::ANONYMOUS_ROLE] = User::getAnonymousUser();
$this->users[AccountInterface::AUTHENTICATED_ROLE] = $this
->createAccount();
$this->users[self::USER_WITH_BYPASS_PERM] = $this
->createAccount([
'bypass api product access control',
]);
$this
->createRole([], self::INTERNAL_ROLE, self::INTERNAL_ROLE);
$this->users[self::INTERNAL_ROLE] = $this
->createAccount([]);
$this->users[self::INTERNAL_ROLE]
->addRole(self::INTERNAL_ROLE);
$this->users[self::INTERNAL_ROLE]
->save();
foreach (self::VISIBILITIES as $visibility) {
$api_product = ApiProduct::create([
'name' => $this
->randomMachineName(),
'displayName' => $this
->randomMachineName() . " ({$visibility})",
'approvalType' => ApiProduct::APPROVAL_TYPE_AUTO,
]);
$api_product
->setAttribute('access', $visibility);
$api_product
->save();
$this->apiProducts[$visibility] = $api_product;
}
$this->ridCombinations = $this
->calculateRidCombinations(array_keys($this->roleStorage
->loadMultiple()));
}
protected function tearDown() {
$entities = array_merge($this->users, $this->apiProducts);
foreach ($entities as $entity) {
try {
if ($entity !== NULL) {
$entity
->delete();
}
} catch (\Exception $exception) {
$this
->logException($exception);
}
}
parent::tearDown();
}
public function testApiProductAccess() {
$this
->entityAccessTest();
$this
->developerAppEditFormTest();
}
protected function entityAccessTest() {
$authenticatedRoles = array_filter(array_keys($this->roleStorage
->loadMultiple()), function ($rid) {
return $rid !== AccountInterface::ANONYMOUS_ROLE;
});
$visibilityCombinations = $this
->calculateTestCombinations();
$testScenarios = [];
foreach ($visibilityCombinations as $visibilityCombination) {
foreach ($this->ridCombinations as $ridCombination) {
$settings = array_combine($visibilityCombination, array_fill(0, count($visibilityCombination), $ridCombination));
$settings += [
self::PUBLIC_VISIBILITY => [],
self::PRIVATE_VISIBILITY => [],
self::INTERNAL_VISIBILITY => [],
];
$this
->saveAccessSettings($settings);
$this->accessControlHandler
->resetCache();
foreach ($this->users as $userRole => $user) {
foreach ($this->apiProducts as $product) {
$rolesWithAccess = $this
->getRolesWithAccess($product);
if (in_array(AccountInterface::AUTHENTICATED_ROLE, $rolesWithAccess)) {
$rolesWithAccess = array_merge($rolesWithAccess, $authenticatedRoles);
}
sort($rolesWithAccess);
$testId = md5(sprintf('test-%s-%s-%s', $product
->id(), $user
->id(), implode('-', $rolesWithAccess) ?? 'empty'));
if (array_key_exists($testId, $testScenarios)) {
continue;
}
$testScenarios[$testId] = $rolesWithAccess;
foreach (self::SUPPORTED_OPERATIONS as $operation) {
$accessGranted = $product
->access($operation, $user);
if (in_array($userRole, $rolesWithAccess)) {
$this
->assertTrue($accessGranted, $this
->messageIfUserShouldHaveAccessByRole($operation, $user, $userRole, $rolesWithAccess, $product));
}
elseif ($this->users[self::USER_WITH_BYPASS_PERM]
->id() === $user
->id()) {
$this
->assertTrue($accessGranted, $this
->messageIfUserShouldHaveAccessWithBypassPerm($operation, $user));
}
else {
$this
->assertFalse($accessGranted, $this
->messageIfUserShouldNotHaveAccess($operation, $user, $userRole, $rolesWithAccess, $product));
}
}
}
}
}
}
}
protected function developerAppEditFormTest() {
$onlyPublicProductVisible = function () {
$this
->checkProductVisibility([
self::PUBLIC_VISIBILITY,
], [
self::PRIVATE_VISIBILITY,
self::INTERNAL_VISIBILITY,
]);
};
$allProductsVisible = function () {
$this
->checkProductVisibility([
self::PUBLIC_VISIBILITY,
self::PRIVATE_VISIBILITY,
self::INTERNAL_VISIBILITY,
]);
};
$justPublicAndPrivateVisible = function () {
$this
->checkProductVisibility([
self::PUBLIC_VISIBILITY,
self::PRIVATE_VISIBILITY,
], [
self::INTERNAL_VISIBILITY,
]);
};
$this
->saveAccessSettings([
self::PUBLIC_VISIBILITY => [
AccountInterface::AUTHENTICATED_ROLE,
],
self::PRIVATE_VISIBILITY => [],
self::INTERNAL_VISIBILITY => [],
]);
$auth_user_app = DeveloperApp::create([
'name' => $this
->randomMachineName(),
'status' => App::STATUS_APPROVED,
'developerId' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->get('apigee_edge_developer_id')->value,
]);
$auth_user_app
->setOwner($this->users[AccountInterface::AUTHENTICATED_ROLE]);
$auth_user_app
->save();
$bypass_user_app = DeveloperApp::create([
'name' => $this
->randomMachineName(),
'status' => App::STATUS_APPROVED,
'developerId' => $this->users[self::USER_WITH_BYPASS_PERM]
->get('apigee_edge_developer_id')->value,
]);
$bypass_user_app
->setOwner($this->users[self::USER_WITH_BYPASS_PERM]);
$bypass_user_app
->save();
$this
->drupalLogin($this->users[AccountInterface::AUTHENTICATED_ROLE]);
$this
->drupalGet(Url::fromRoute('entity.developer_app.add_form_for_developer', [
'user' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->id(),
]));
$onlyPublicProductVisible();
$this
->drupalGet(Url::fromRoute('entity.developer_app.edit_form_for_developer', [
'user' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->id(),
'app' => $auth_user_app
->getName(),
]));
$onlyPublicProductVisible();
$this
->drupalLogout();
$role = $this
->createRole([
'administer developer_app',
]);
$this->users[self::USER_WITH_BYPASS_PERM]
->addRole($role);
$this->users[self::USER_WITH_BYPASS_PERM]
->save();
$this
->drupalLogin($this->users[self::USER_WITH_BYPASS_PERM]);
$this
->drupalGet(Url::fromRoute('entity.developer_app.add_form_for_developer', [
'user' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->id(),
]));
$onlyPublicProductVisible();
$this
->drupalGet(Url::fromRoute('entity.developer_app.edit_form_for_developer', [
'user' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->id(),
'app' => $auth_user_app
->getName(),
]));
$onlyPublicProductVisible();
$this
->drupalGet(Url::fromRoute('entity.developer_app.add_form_for_developer', [
'user' => $this->users[self::USER_WITH_BYPASS_PERM]
->id(),
]));
$allProductsVisible();
$this
->drupalGet(Url::fromRoute('entity.developer_app.edit_form_for_developer', [
'user' => $this->users[self::USER_WITH_BYPASS_PERM]
->id(),
'app' => $bypass_user_app
->getName(),
]));
$allProductsVisible();
$this
->drupalLogout();
$this->users[self::USER_WITH_BYPASS_PERM]
->removeRole($role);
$this->users[self::USER_WITH_BYPASS_PERM]
->save();
$dacc = $this->container
->get('apigee_edge.controller.developer_app_credential_factory')
->developerAppCredentialController($this->users[AccountInterface::AUTHENTICATED_ROLE]
->get('apigee_edge_developer_id')->value, $auth_user_app
->getName());
$credentials = $auth_user_app
->getCredentials();
$credential = reset($credentials);
$dacc
->addProducts($credential
->getConsumerKey(), [
$this->apiProducts[self::PRIVATE_VISIBILITY]
->id(),
]);
$this
->drupalLogin($this->users[AccountInterface::AUTHENTICATED_ROLE]);
$this
->drupalGet(Url::fromRoute('entity.developer_app.add_form_for_developer', [
'user' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->id(),
]));
$onlyPublicProductVisible();
$this
->drupalGet(Url::fromRoute('entity.developer_app.edit_form_for_developer', [
'user' => $this->users[AccountInterface::AUTHENTICATED_ROLE]
->id(),
'app' => $auth_user_app
->getName(),
]));
$justPublicAndPrivateVisible();
$this
->drupalLogout();
}
protected function calculateRidCombinations(array $rids) : array {
$ridCombinations = [
[],
];
foreach ($rids as $rid) {
foreach ($ridCombinations as $ridCombination) {
array_push($ridCombinations, array_merge([
$rid,
], $ridCombination));
}
}
return $ridCombinations;
}
private function calculateTestCombinations() : array {
$ridCombinations = $this->ridCombinations;
$visibilityCombinations = [
[],
];
foreach (self::VISIBILITIES as $visibility) {
foreach ($visibilityCombinations as $visibilityCombination) {
array_push($visibilityCombinations, array_merge([
$visibility,
], $visibilityCombination));
}
}
array_shift($ridCombinations);
array_shift($visibilityCombinations);
$visibilityCombinations[] = [];
return $visibilityCombinations;
}
protected function saveAccessSettings(array $settings) {
$this
->config('apigee_edge.api_product_settings')
->set('access', $settings)
->save();
}
protected function getRolesWithAccess(ApiProductInterface $product) : array {
$prodVisibility = $product
->getAttributeValue('access');
return $this
->config('apigee_edge.api_product_settings')
->get('access')[$prodVisibility] ?? [];
}
protected function messageIfUserShouldHaveAccessByRole(string $operation, UserInterface $user, string $user_rid, array $rids_with_access, ApiProductInterface $product) : string {
return sprintf('User with "%s" role should have "%s" access to an API Product with "%s" visibility. Roles with access granted: %s.', $user_rid, $operation, $product
->getAttributeValue('access') ?? 'public', empty($rids_with_access) ? 'none' : implode(', ', $rids_with_access));
}
protected function messageIfUserShouldHaveAccessWithBypassPerm(string $operation, UserInterface $user) : string {
return "User with \"Bypass API Product access control\" permission should have \"{$operation}\" access to the API product.";
}
protected function messageIfUserShouldNotHaveAccess(string $operation, UserInterface $user, string $user_rid, array $rids_with_access, ApiProductInterface $product) : string {
return sprintf('"%s" user without "Bypass API Product access control" permission should not have "%s" access to an API Product with "%s" visibility. Roles with access granted: %s.', $user_rid, $operation, $product
->getAttributeValue('access') ?? 'public', empty($rids_with_access) ? 'none' : implode(', ', $rids_with_access));
}
protected function checkProductVisibility(array $visible = [], array $hidden = []) {
foreach ($visible as $visibility) {
$this
->assertSession()
->pageTextContains($this->apiProducts[$visibility]
->label());
}
foreach ($hidden as $visibility) {
$this
->assertSession()
->pageTextNotContains($this->apiProducts[$visibility]
->label());
}
}
}