Add Symfony Deprecation Contracts package

- Created CHANGELOG.md to maintain version history.
- Added README.md with usage instructions for the trigger_deprecation() function.
- Initialized composer.json for the Symfony Deprecation Contracts library, specifying dependencies and autoloading.
This commit is contained in:
2025-12-15 08:43:18 +01:00
parent 0eedc919db
commit 778ee26fe4
643 changed files with 113899 additions and 109267 deletions

View File

@@ -0,0 +1,564 @@
<?php
namespace
{
$mockFileGetContents = null;
}
namespace Stevenmaguire\OAuth2\Client\Provider
{
function file_get_contents()
{
global $mockFileGetContents;
if (isset($mockFileGetContents) && ! is_null($mockFileGetContents)) {
if (is_a($mockFileGetContents, 'Exception')) {
throw $mockFileGetContents;
}
return $mockFileGetContents;
} else {
return call_user_func_array('\file_get_contents', func_get_args());
}
}
}
namespace Stevenmaguire\OAuth2\Client\Test\Provider
{
use DateInterval;
use DateTimeImmutable;
use Firebase\JWT\JWT;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Token\AccessToken;
use League\OAuth2\Client\Tool\QueryBuilderTrait;
use Mockery as m;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamInterface;
use Stevenmaguire\OAuth2\Client\Provider\Exception\EncryptionConfigurationException;
use Stevenmaguire\OAuth2\Client\Provider\Keycloak;
class KeycloakTest extends TestCase
{
use QueryBuilderTrait;
public const ENCRYPTION_KEY = <<<EOD
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
ehde/zUxo6UvS7UrBQIDAQAB
-----END PUBLIC KEY-----
EOD;
public const ENCRYPTION_ALGORITHM = 'HS256';
private $jwtTemplate = <<<EOF
{
"exp": "%s",
"iat": "%s",
"jti": "e11a85c8-aa91-4f75-9088-57db4586f8b9",
"iss": "https://example.org/auth/realms/test-realm",
"aud": "account",
"nbf": "%s",
"sub": "4332085e-b944-4acc-9eb1-27d8f5405f3e",
"typ": "Bearer",
"azp": "test-app",
"session_state": "c90c8e0d-aabb-4c71-b8a8-e88792cacd96",
"acr": "1",
"realm_access": {
"roles": [
"default-roles-test-realm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid email profile",
"sid": "c90c8e0d-aabb-4c71-b8a8-e88792cacd96",
"address": {},
"email_verified": true,
"name": "Test User",
"preferred_username": "test-user",
"given_name": "Test",
"family_name": "User",
"email": "test-user@example.org"
}
EOF;
protected $provider;
protected function setUp(): void
{
$this->provider = new Keycloak([
'authServerUrl' => 'http://mock.url/auth',
'realm' => 'mock_realm',
'clientId' => 'mock_client_id',
'clientSecret' => 'mock_secret',
'redirectUri' => 'none',
]);
}
public function tearDown(): void
{
m::close();
parent::tearDown();
}
public function testAuthorizationUrl()
{
$url = $this->provider->getAuthorizationUrl();
$uri = parse_url($url);
parse_str($uri['query'], $query);
$this->assertArrayHasKey('client_id', $query);
$this->assertArrayHasKey('redirect_uri', $query);
$this->assertArrayHasKey('state', $query);
$this->assertArrayHasKey('scope', $query);
$this->assertArrayHasKey('response_type', $query);
$this->assertArrayHasKey('approval_prompt', $query);
$this->assertNotNull($this->provider->getState());
}
public function testEncryptionAlgorithm()
{
$algorithm = uniqid();
$provider = new Keycloak([
'encryptionAlgorithm' => $algorithm,
]);
$this->assertEquals($algorithm, $provider->encryptionAlgorithm);
$algorithm = uniqid();
$provider->setEncryptionAlgorithm($algorithm);
$this->assertEquals($algorithm, $provider->encryptionAlgorithm);
}
public function testEncryptionKey()
{
$key = uniqid();
$provider = new Keycloak([
'encryptionKey' => $key,
]);
$this->assertEquals($key, $provider->encryptionKey);
$key = uniqid();
$provider->setEncryptionKey($key);
$this->assertEquals($key, $provider->encryptionKey);
}
public function testEncryptionKeyPath()
{
global $mockFileGetContents;
$path = uniqid();
$key = uniqid();
$mockFileGetContents = $key;
$provider = new Keycloak([
'encryptionKeyPath' => $path,
]);
$this->assertEquals($key, $provider->encryptionKey);
$path = uniqid();
$key = uniqid();
$mockFileGetContents = $key;
$provider->setEncryptionKeyPath($path);
$this->assertEquals($key, $provider->encryptionKey);
}
public function testEncryptionKeyPathFails()
{
$this->markTestIncomplete('Need to assess the test to see what is required to be checked.');
global $mockFileGetContents;
$path = uniqid();
$key = uniqid();
$mockFileGetContents = new \Exception();
$provider = new Keycloak([
'encryptionKeyPath' => $path,
]);
$provider->setEncryptionKeyPath($path);
}
public function testScopes()
{
$scopeSeparator = ' ';
$options = ['scope' => [uniqid(), uniqid()]];
$query = ['scope' => implode($scopeSeparator, $options['scope'])];
$url = $this->provider->getAuthorizationUrl($options);
$encodedScope = $this->buildQueryString($query);
$this->assertStringContainsString($encodedScope, $url);
}
public function testGetAuthorizationUrl()
{
$url = $this->provider->getAuthorizationUrl();
$uri = parse_url($url);
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/auth', $uri['path']);
}
public function testGetLogoutUrl()
{
$url = $this->provider->getLogoutUrl();
$uri = parse_url($url);
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/logout', $uri['path']);
}
public function testGetLogoutUrlWithIdTokenHint()
{
$this->provider->setVersion('18.0.0');
$options = [
'access_token' => new AccessToken(
[
'id_token' => 'the_id_token',
'access_token' => 'the_access_token',
]
),
];
$url = $this->provider->getLogoutUrl($options);
$uri = parse_url($url);
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/logout', $uri['path']);
$this->assertStringContainsString('id_token_hint=the_id_token', $uri['query']);
}
public function testGetBaseAccessTokenUrl()
{
$params = [];
$url = $this->provider->getBaseAccessTokenUrl($params);
$uri = parse_url($url);
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/token', $uri['path']);
}
public function testGetAccessToken()
{
$stream = $this->createMock(StreamInterface::class);
$stream
->method('__toString')
->willReturn('{"access_token":"mock_access_token","scope":"email","token_type":"bearer"}');
$response = m::mock('Psr\Http\Message\ResponseInterface');
$response
->shouldReceive('getBody')
->andReturn($stream);
$response
->shouldReceive('getHeader')
->andReturn(['content-type' => 'json']);
$client = m::mock('GuzzleHttp\ClientInterface');
$client->shouldReceive('send')
->times(1)
->andReturn($response);
$this->provider->setHttpClient($client);
$token = $this
->provider
->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
$this->assertEquals('mock_access_token', $token->getToken());
$this->assertNull($token->getExpires());
$this->assertNull($token->getRefreshToken());
$this->assertNull($token->getResourceOwnerId());
}
public function testUserData()
{
$userId = rand(1000, 9999);
$name = uniqid();
$email = uniqid();
$username = uniqid();
$firstName = uniqid();
$lastName = uniqid();
$getAccessTokenResponseStream = $this->createMock(StreamInterface::class);
$getAccessTokenResponseStream
->method('__toString')
->willReturn(
'{"access_token":"mock_access_token","expires":"3600","refresh_token":"mock_refresh_token","otherKey":[1234]}'
);
$postResponse = m::mock('Psr\Http\Message\ResponseInterface');
$postResponse
->shouldReceive('getBody')
->andReturn($getAccessTokenResponseStream);
$postResponse
->shouldReceive('getHeader')
->andReturn(['content-type' => 'json']);
$getResourceOwnerResponseStream = $this->createMock(StreamInterface::class);
$getResourceOwnerResponseStream
->method('__toString')
->willReturn(
sprintf(
'{"sub": "%s", "name": "%s", "email": "%s", "preferred_username": "%s", "given_name": "%s", "family_name": "%s"}',
$userId,
$name,
$email,
$username,
$firstName,
$lastName
)
);
$userResponse = m::mock('Psr\Http\Message\ResponseInterface');
$userResponse
->shouldReceive('getBody')
->andReturn($getResourceOwnerResponseStream);
$userResponse
->shouldReceive('getHeader')
->andReturn(['content-type' => 'json']);
$client = m::mock('GuzzleHttp\ClientInterface');
$client
->shouldReceive('send')
->andReturn($postResponse, $userResponse);
$this->provider->setHttpClient($client);
$token = $this->provider->getAccessToken(
'authorization_code',
[
'code' => 'mock_authorization_code',
'access_token' => 'mock_access_token',
]
);
$user = $this->provider->getResourceOwner($token);
$this->assertEquals($userId, $user->getId());
$this->assertEquals($userId, $user->toArray()['sub']);
$this->assertEquals($name, $user->getName());
$this->assertEquals($name, $user->toArray()['name']);
$this->assertEquals($email, $user->getEmail());
$this->assertEquals($email, $user->toArray()['email']);
$this->assertEquals($username, $user->getUsername());
$this->assertEquals($username, $user->toArray()['preferred_username']);
$this->assertEquals($firstName, $user->getFirstName());
$this->assertEquals($firstName, $user->toArray()['given_name']);
$this->assertEquals($lastName, $user->getLastName());
$this->assertEquals($lastName, $user->toArray()['family_name']);
}
public function testUserDataWithEncryption()
{
$jwt = JWT::encode(
json_decode(
sprintf(
$this->jwtTemplate,
(new DateTimeImmutable())->add(new DateInterval('PT1H'))->getTimestamp(),
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp(),
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp()
),
true
),
self::ENCRYPTION_KEY,
self::ENCRYPTION_ALGORITHM
);
$getAccessTokenResponseStream = $this->createMock(StreamInterface::class);
$getAccessTokenResponseStream
->method('__toString')
->willReturn(
sprintf(
'{"access_token":"%s","expires":"3600","refresh_token":"mock_refresh_token","otherKey":[1234]}',
$jwt
)
);
$accessTokenResponse = m::mock('Psr\Http\Message\ResponseInterface');
$accessTokenResponse
->shouldReceive('getBody')
->andReturn($getAccessTokenResponseStream);
$accessTokenResponse
->shouldReceive('getHeader')
->andReturn(['content-type' => 'json']);
$accessTokenResponse
->shouldReceive('getStatusCode')
->andReturn(200);
$getResourceOwnerResponseStream = $this->createMock(StreamInterface::class);
$getResourceOwnerResponseStream
->method('__toString')
->willReturn($jwt);
$resourceOwnerResponse = m::mock('Psr\Http\Message\ResponseInterface');
$resourceOwnerResponse
->shouldReceive('getBody')
->andReturn($getResourceOwnerResponseStream);
$resourceOwnerResponse
->shouldReceive('getHeader')
->andReturn(['content-type' => 'application/jwt']);
$resourceOwnerResponse
->shouldReceive('getStatusCode')
->andReturn(200);
$client = m::mock('GuzzleHttp\ClientInterface');
$client
->shouldReceive('send')
->times(2)
->andReturn($accessTokenResponse, $resourceOwnerResponse);
$this->provider->setHttpClient($client);
$token = $this
->provider
->setEncryptionAlgorithm(self::ENCRYPTION_ALGORITHM)
->setEncryptionKey(self::ENCRYPTION_KEY)
->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
$user = $this->provider->getResourceOwner($token);
$email = "test-user@example.org";
$name = "Test User";
$userId = "4332085e-b944-4acc-9eb1-27d8f5405f3e";
$username = "test-user";
$firstName = "Test";
$lastName = "User";
$this->assertEquals($userId, $user->getId());
$this->assertEquals($userId, $user->toArray()['sub']);
$this->assertEquals($name, $user->getName());
$this->assertEquals($name, $user->toArray()['name']);
$this->assertEquals($email, $user->getEmail());
$this->assertEquals($email, $user->toArray()['email']);
$this->assertEquals($username, $user->getUsername());
$this->assertEquals($username, $user->toArray()['preferred_username']);
$this->assertEquals($firstName, $user->getFirstName());
$this->assertEquals($firstName, $user->toArray()['given_name']);
$this->assertEquals($lastName, $user->getLastName());
$this->assertEquals($lastName, $user->toArray()['family_name']);
}
public function testUserDataFailsWhenEncryptionEncounteredAndNotConfigured()
{
$this->expectException(EncryptionConfigurationException::class);
$accessTokenResponseStream = $this->createMock(StreamInterface::class);
$accessTokenResponseStream
->method('__toString')
->willReturn(
'{"access_token":"mock_access_token","expires":"3600","refresh_token":"mock_refresh_token","otherKey":[1234]}'
);
$getAccessTokenResponse = m::mock('Psr\Http\Message\ResponseInterface');
$getAccessTokenResponse
->shouldReceive('getBody')
->andReturn($accessTokenResponseStream);
$getAccessTokenResponse
->shouldReceive('getHeader')
->andReturn(['content-type' => 'json']);
$getAccessTokenResponse
->shouldReceive('getStatusCode')
->andReturn(200);
$resourceOwnerResponseStream = $this->createMock(StreamInterface::class);
$resourceOwnerResponseStream
->method('__toString')
->willReturn(uniqid());
$getResourceOwnerResponse = m::mock('Psr\Http\Message\ResponseInterface');
$getResourceOwnerResponse
->shouldReceive('getBody')
->andReturn($resourceOwnerResponseStream);
$getResourceOwnerResponse
->shouldReceive('getHeader')
->andReturn(['content-type' => 'application/jwt']);
$getResourceOwnerResponse
->shouldReceive('getStatusCode')
->andReturn(200);
$client = m::mock('GuzzleHttp\ClientInterface');
$client
->shouldReceive('send')
->times(2)
->andReturn($getAccessTokenResponse, $getResourceOwnerResponse);
$this->provider->setHttpClient($client);
$token = $this->provider->getAccessToken(
'authorization_code', #
['code' => 'mock_authorization_code']
);
$user = $this->provider->getResourceOwner($token);
}
public function testErrorResponse()
{
$this->expectException(IdentityProviderException::class);
$accessTokenResponseStream = $this->createMock(StreamInterface::class);
$accessTokenResponseStream
->method('__toString')
->willReturn(
'{"error": "invalid_grant", "error_description": "Code not found"}'
);
$response = m::mock('Psr\Http\Message\ResponseInterface');
$response
->shouldReceive('getBody')
->andReturn($accessTokenResponseStream);
$response
->shouldReceive('getHeader')
$response
->shouldReceive('getStatusCode')
->andReturn(401);
$client = m::mock('GuzzleHttp\ClientInterface');
$client
->shouldReceive('send')
->times(1)
->andReturn($response);
$this->provider->setHttpClient($client);
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
}
public function testCanDecryptResponseThrowsExceptionIfResponseIsNotAStringAndEncryptionIsNotUsed()
{
$this->expectException(EncryptionConfigurationException::class);
$this->provider->decryptResponse('');
$this->assertFalse($this->provider->usesEncryption());
}
public function testCanDecryptResponseReturnsResponseWhenEncryptionIsUsed()
{
$jwtPayload = json_decode(
sprintf(
$this->jwtTemplate,
(new DateTimeImmutable())->add(new DateInterval('PT1H'))->getTimestamp(),
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp(),
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp()
),
true
);
$jwt = JWT::encode(
$jwtPayload,
self::ENCRYPTION_KEY,
self::ENCRYPTION_ALGORITHM
);
$this->provider
->setEncryptionAlgorithm(self::ENCRYPTION_ALGORITHM)
->setEncryptionKey(self::ENCRYPTION_KEY);
$response = $this->provider->decryptResponse($jwt);
$this->assertSame($jwtPayload, $response);
}
}
}