farm-2.x-dev/modules/core/api/tests/src/Functional/ConsumerConfigTest.php
modules/core/api/tests/src/Functional/ConsumerConfigTest.php
<?php
namespace Drupal\Tests\farm_api\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
use Drupal\user\Entity\Role;
/**
* Tests using the consumer.client_id field.
*
* @group farm
*/
class ConsumerConfigTest extends OauthTestBase {
/**
* The URL for debugging tokens.
*
* @var \Drupal\Core\Url
*/
protected $tokenDebugUrl;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->tokenDebugUrl = Url::fromRoute('oauth2_token.user_debug');
// Override the additional roles created by parent.
$this->additionalRoles = [];
for ($i = 0; $i < 4; $i++) {
$role = Role::create([
'id' => 'scope_' . $i,
'label' => 'Scope: ' . $i,
'is_admin' => FALSE,
]);
$role->save();
$this->additionalRoles[] = $role;
}
}
/**
* Test consumer.grant_user_access config.
*/
public function testGrantUserAccess() {
// Set up the client.
$this->client->set('grant_user_access', FALSE);
$this->client->set('limit_requested_access', FALSE);
$this->client->set('limit_user_access', FALSE);
$this->client->save();
// Grant the user more roles than the consumer.
$this->user->addRole('scope_1');
$this->user->addRole('scope_2');
$this->user->save();
// 1. Test that only the consumers roles are granted.
// Prepare expected roles. Include all roles the consumer has.
$expected_roles = array_merge($this->getClientRoleIds(), ['authenticated']);
// Check the token.
$access_token = $this->getAccessToken();
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
// 2. Test that the user's roles are granted as well.
// Update the client.
$this->client->set('grant_user_access', TRUE);
$this->client->save();
// Include the consumer + user roles.
$expected_roles = array_merge($expected_roles, ['scope_1', 'scope_2']);
// Check the token.
$access_token = $this->getAccessToken();
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
// 3. Test that additional roles are not granted.
// Request "scope_3" even though it is not given to the user or consumer.
// Check the token.
$access_token = $this->getAccessToken(['scope_3']);
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
}
/**
* Test consumer.limit_requested_access.
*/
public function testLimitRequestedAccess() {
// Set up the client.
$this->client->set('grant_user_access', FALSE);
$this->client->set('limit_requested_access', FALSE);
$this->client->set('limit_user_access', FALSE);
$this->client->save();
// Grant the user additional roles.
$this->user->addRole('scope_1');
$this->user->addRole('scope_2');
$this->user->save();
// Grant the client additional roles.
$client_roles = array_merge(
$this->getClientRoleIds(),
['scope_3']
);
$this->grantClientRoles($client_roles);
// Array of expected roles. Includes all roles the consumer has.
$expected_roles = array_merge($client_roles, ['authenticated']);
// 1. Test that all roles on the consumer are granted.
$access_token = $this->getAccessToken();
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
// 2. Test that only the requested scopes (roles) are granted.
// Update the client.
$this->client->set('limit_requested_access', TRUE);
$this->client->save();
$requested_roles = ['scope_3'];
$expected_roles = array_merge($requested_roles, ['authenticated']);
// Check the token.
$access_token = $this->getAccessToken($requested_roles);
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
// 3. Test only the requested roles are granted,
// even if user roles are granted.
$this->client->set('limit_requested_access', TRUE);
$this->client->set('grant_user_access', TRUE);
$this->client->save();
$requested_roles = ['scope_1', 'scope_3'];
$expected_roles = array_merge($requested_roles, ['authenticated']);
// Check the token.
$access_token = $this->getAccessToken($requested_roles);
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
}
/**
* Test consumer.limit_user_access.
*/
public function testLimitUserAccess() {
// Set up the client.
$this->client->set('grant_user_access', FALSE);
$this->client->set('limit_requested_access', FALSE);
$this->client->set('limit_user_access', FALSE);
$this->client->save();
// Grant the user one additional role.
$this->user->addRole('scope_1');
$this->user->save();
// Grant the client all roles.
$client_roles = array_merge(
$this->getClientRoleIds(),
['scope_1', 'scope_2', 'scope_3']
);
$this->grantClientRoles($client_roles);
// Array of expected roles. Includes all roles the consumer has.
$expected_roles = array_merge($client_roles, ['authenticated']);
// 1. Test that all roles on the consumer are granted.
$access_token = $this->getAccessToken();
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
// 2. Test that only the roles the user has are granted.
// Update the client.
$this->client->set('limit_user_access', TRUE);
$this->client->save();
$requested_roles = ['scope_1', 'scope_3'];
$expected_roles = ['scope_1', 'authenticated'];
// Check the token.
$access_token = $this->getAccessToken($requested_roles);
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
// 3. Test that limit_user_access and grant_user_access work together.
$this->client->set('grant_user_access', TRUE);
$this->client->set('limit_user_access', TRUE);
$this->client->save();
$requested_roles = [];
$expected_roles = ['scope_1', 'authenticated'];
// Check the token.
$access_token = $this->getAccessToken($requested_roles);
$token_info = $this->getTokenInfo($access_token);
$this->assertEquals($this->user->id(), $token_info['id']);
$this->assertEqualsCanonicalizing($expected_roles, $token_info['roles']);
}
/**
* Return the response from oauth/debug.
*
* @param string $access_token
* The access_token to use for authentication.
*
* @return mixed
* The JSON parsed response.
*/
protected function getTokenInfo($access_token) {
$response = $this->get(
$this->tokenDebugUrl,
[
'query' => ['_format' => 'json'],
'headers' => [
'Authorization' => 'Bearer ' . $access_token,
],
]
);
return Json::decode((string) $response->getBody());
}
/**
* Helper function to get role IDs the client has.
*
* @return array
* Array of role IDs.
*/
protected function getClientRoleIds() {
return array_map(function ($role) {
return $role['target_id'];
}, $this->client->get('roles')->getValue());
}
/**
* Helper function to grant roles to the client.
*
* @param array $role_ids
* Role IDs to add.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function grantClientRoles(array $role_ids) {
$roles = [];
foreach ($role_ids as $id) {
$roles[] = ['target_id' => $id];
}
$this->client->set('roles', $roles);
$this->client->save();
}
/**
* Return an access token.
*
* @param array $scopes
* The scopes.
*
* @return string
* The access token.
*/
protected function getAccessToken(array $scopes = []) {
$valid_payload = [
'grant_type' => 'password',
'client_id' => $this->client->get('client_id')->value,
'client_secret' => $this->clientSecret,
'username' => $this->user->getAccountName(),
'password' => $this->user->pass_raw,
];
if (!empty($scopes)) {
$valid_payload['scope'] = implode(' ', $scopes);
}
$response = $this->post($this->url, $valid_payload);
$parsed_response = Json::decode((string) $response->getBody());
return $parsed_response['access_token'] ?? NULL;
}
}
