2020-11-15 18:47:30 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Engelsystem\Test\Unit\Controllers;
|
|
|
|
|
|
|
|
use Engelsystem\Config\Config;
|
|
|
|
use Engelsystem\Controllers\AuthController;
|
|
|
|
use Engelsystem\Controllers\OAuthController;
|
2021-11-23 11:59:53 +01:00
|
|
|
use Engelsystem\Events\EventDispatcher;
|
2020-11-15 18:47:30 +01:00
|
|
|
use Engelsystem\Helpers\Authenticator;
|
|
|
|
use Engelsystem\Http\Exceptions\HttpNotFound;
|
|
|
|
use Engelsystem\Http\Redirector;
|
|
|
|
use Engelsystem\Http\Request;
|
|
|
|
use Engelsystem\Http\Response;
|
|
|
|
use Engelsystem\Http\UrlGenerator;
|
|
|
|
use Engelsystem\Models\OAuth;
|
|
|
|
use Engelsystem\Models\User\User;
|
|
|
|
use Engelsystem\Test\Unit\HasDatabase;
|
|
|
|
use Engelsystem\Test\Unit\TestCase;
|
|
|
|
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
|
|
|
use League\OAuth2\Client\Provider\GenericProvider;
|
|
|
|
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
|
|
|
|
use League\OAuth2\Client\Token\AccessToken;
|
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
|
|
use Psr\Log\Test\TestLogger;
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Session as Session;
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
|
|
|
|
|
|
|
class OAuthControllerTest extends TestCase
|
|
|
|
{
|
|
|
|
use HasDatabase;
|
|
|
|
|
|
|
|
protected Authenticator|MockObject $auth;
|
|
|
|
|
|
|
|
protected AuthController|MockObject $authController;
|
|
|
|
|
|
|
|
protected User $authenticatedUser;
|
|
|
|
|
|
|
|
protected User $otherAuthenticatedUser;
|
|
|
|
|
|
|
|
protected User $otherUser;
|
|
|
|
|
|
|
|
protected Config $config;
|
|
|
|
|
|
|
|
protected TestLogger $log;
|
|
|
|
|
|
|
|
protected OAuth $oauth;
|
|
|
|
|
|
|
|
protected Redirector|MockObject $redirect;
|
|
|
|
|
|
|
|
protected Session $session;
|
|
|
|
|
|
|
|
protected UrlGenerator|MockObject $url;
|
|
|
|
|
|
|
|
/** @var string[][] */
|
|
|
|
protected array $oauthConfig = [
|
|
|
|
'testprovider' => [
|
|
|
|
'client_id' => 'testsystem',
|
|
|
|
'client_secret' => 'foo-bar-baz',
|
|
|
|
'url_auth' => 'http://localhost/auth',
|
|
|
|
'url_token' => 'http://localhost/token',
|
|
|
|
'url_info' => 'http://localhost/info',
|
|
|
|
'id' => 'uid',
|
|
|
|
'username' => 'user',
|
|
|
|
'email' => 'email',
|
|
|
|
'first_name' => 'given-name',
|
|
|
|
'last_name' => 'last-name',
|
|
|
|
'url' => 'http://localhost/',
|
2022-05-01 00:01:14 +02:00
|
|
|
'scope' => ['foo', 'bar'],
|
2020-11-15 18:47:30 +01:00
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::__construct
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::handleArrive
|
2021-12-11 23:28:03 +01:00
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::getId
|
2020-11-15 18:47:30 +01:00
|
|
|
*/
|
|
|
|
public function testIndexArrive(): void
|
|
|
|
{
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['code' => 'lorem-ipsum-code', 'state' => 'some-internal-state']);
|
|
|
|
|
|
|
|
$this->session->set('oauth2_state', 'some-internal-state');
|
|
|
|
$this->session->set('oauth2_connect_provider', 'testprovider');
|
|
|
|
|
|
|
|
$accessToken = $this->createMock(AccessToken::class);
|
2020-12-26 18:24:20 +01:00
|
|
|
$this->setExpects($accessToken, 'getToken', null, 'test-token', $this->atLeastOnce());
|
|
|
|
$this->setExpects($accessToken, 'getRefreshToken', null, 'test-refresh-token', $this->atLeastOnce());
|
|
|
|
$this->setExpects($accessToken, 'getExpires', null, 4242424242, $this->atLeastOnce());
|
2020-11-15 18:47:30 +01:00
|
|
|
|
|
|
|
/** @var ResourceOwnerInterface|MockObject $resourceOwner */
|
|
|
|
$resourceOwner = $this->createMock(ResourceOwnerInterface::class);
|
|
|
|
$this->setExpects($resourceOwner, 'toArray', null, [], $this->atLeastOnce());
|
2021-12-11 23:28:03 +01:00
|
|
|
$resourceOwner->expects($this->exactly(5))
|
2020-11-15 18:47:30 +01:00
|
|
|
->method('getId')
|
|
|
|
->willReturnOnConsecutiveCalls(
|
|
|
|
'other-provider-user-identifier',
|
|
|
|
'other-provider-user-identifier',
|
|
|
|
'provider-user-identifier',
|
|
|
|
'provider-user-identifier',
|
|
|
|
'provider-user-identifier'
|
|
|
|
);
|
|
|
|
|
|
|
|
/** @var GenericProvider|MockObject $provider */
|
|
|
|
$provider = $this->createMock(GenericProvider::class);
|
|
|
|
$this->setExpects(
|
|
|
|
$provider,
|
|
|
|
'getAccessToken',
|
|
|
|
['authorization_code', ['code' => 'lorem-ipsum-code']],
|
|
|
|
$accessToken,
|
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
$this->setExpects($provider, 'getResourceOwner', [$accessToken], $resourceOwner, $this->atLeastOnce());
|
|
|
|
|
2021-11-23 11:59:53 +01:00
|
|
|
/** @var EventDispatcher|MockObject $event */
|
|
|
|
$dispatcher = $this->createMock(EventDispatcher::class);
|
|
|
|
$this->app->instance('events.dispatcher', $dispatcher);
|
|
|
|
$this->setExpects($dispatcher, 'dispatch', ['oauth2.login'], $dispatcher, 4);
|
|
|
|
|
2020-11-15 18:47:30 +01:00
|
|
|
$this->authController->expects($this->atLeastOnce())
|
|
|
|
->method('loginUser')
|
|
|
|
->willReturnCallback(function (User $user) {
|
|
|
|
$this->assertTrue(in_array(
|
|
|
|
$user->id,
|
|
|
|
[$this->authenticatedUser->id, $this->otherUser->id]
|
|
|
|
));
|
|
|
|
|
|
|
|
return new Response();
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->auth->expects($this->exactly(4))
|
|
|
|
->method('user')
|
|
|
|
->willReturnOnConsecutiveCalls(
|
|
|
|
$this->otherUser,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null
|
|
|
|
);
|
|
|
|
|
|
|
|
$controller = $this->getMock(['getProvider', 'addNotification']);
|
|
|
|
$this->setExpects($controller, 'getProvider', ['testprovider'], $provider, $this->atLeastOnce());
|
|
|
|
$this->setExpects($controller, 'addNotification', ['oauth.connected']);
|
|
|
|
|
|
|
|
// Connect to provider
|
|
|
|
$controller->index($request);
|
|
|
|
$this->assertTrue($this->log->hasInfoThatContains('Connected OAuth'));
|
|
|
|
$this->assertCount(1, $this->otherUser->oauth);
|
|
|
|
|
|
|
|
// Login using provider
|
|
|
|
$controller->index($request);
|
|
|
|
$this->assertFalse($this->session->has('oauth2_connect_provider'));
|
2020-12-26 18:24:20 +01:00
|
|
|
$this->assertFalse((bool)$this->otherUser->state->arrived);
|
|
|
|
|
|
|
|
// Tokens updated
|
|
|
|
$oauth = $this->otherUser->oauth[0];
|
|
|
|
$this->assertEquals('test-token', $oauth->access_token);
|
|
|
|
$this->assertEquals('test-refresh-token', $oauth->refresh_token);
|
|
|
|
$this->assertEquals(4242424242, $oauth->expires_at->unix());
|
2020-11-15 18:47:30 +01:00
|
|
|
|
|
|
|
// Mark as arrived
|
|
|
|
$oauthConfig = $this->config->get('oauth');
|
|
|
|
$oauthConfig['testprovider']['mark_arrived'] = true;
|
|
|
|
$this->config->set('oauth', $oauthConfig);
|
|
|
|
$controller->index($request);
|
|
|
|
|
|
|
|
$this->assertTrue((bool)User::find(1)->state->arrived);
|
|
|
|
$this->assertTrue($this->log->hasInfoThatContains('as arrived'));
|
|
|
|
$this->log->reset();
|
|
|
|
|
|
|
|
// Don't set arrived if already done
|
|
|
|
$controller->index($request);
|
|
|
|
$this->assertTrue((bool)User::find(1)->state->arrived);
|
|
|
|
$this->assertFalse($this->log->hasInfoThatContains('as arrived'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::getProvider
|
|
|
|
*/
|
|
|
|
public function testIndexRedirectToProvider(): void
|
|
|
|
{
|
|
|
|
$this->redirect->expects($this->once())
|
|
|
|
->method('to')
|
|
|
|
->willReturnCallback(function ($url) {
|
|
|
|
$this->assertStringStartsWith('http://localhost/auth', $url);
|
|
|
|
$this->assertStringContainsString('testsystem', $url);
|
|
|
|
$this->assertStringContainsString('code', $url);
|
2022-05-01 00:01:14 +02:00
|
|
|
$this->assertStringContainsString('scope=foo%20bar', $url);
|
2020-11-15 18:47:30 +01:00
|
|
|
return new Response();
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->setExpects($this->url, 'to', ['oauth/testprovider'], 'http://localhost/oauth/testprovider');
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider');
|
|
|
|
|
|
|
|
$controller = $this->getMock();
|
|
|
|
|
|
|
|
$controller->index($request);
|
|
|
|
|
|
|
|
$this->assertNotEmpty($this->session->get('oauth2_state'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
|
|
|
*/
|
|
|
|
public function testIndexInvalidState(): void
|
|
|
|
{
|
|
|
|
/** @var GenericProvider|MockObject $provider */
|
|
|
|
$provider = $this->createMock(GenericProvider::class);
|
|
|
|
|
|
|
|
$this->session->set('oauth2_state', 'some-internal-state');
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['code' => 'lorem-ipsum-code', 'state' => 'some-wrong-state']);
|
|
|
|
|
|
|
|
$controller = $this->getMock(['getProvider']);
|
|
|
|
$this->setExpects($controller, 'getProvider', ['testprovider'], $provider);
|
|
|
|
|
|
|
|
$exception = null;
|
|
|
|
try {
|
|
|
|
$controller->index($request);
|
|
|
|
} catch (HttpNotFound $e) {
|
|
|
|
$exception = $e;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->assertFalse($this->session->has('oauth2_state'));
|
|
|
|
$this->log->hasWarningThatContains('Invalid');
|
|
|
|
$this->assertNotNull($exception, 'Exception not thrown');
|
|
|
|
$this->assertEquals('oauth.invalid-state', $exception->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
2021-11-23 11:59:53 +01:00
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::handleOAuthError
|
2020-11-15 18:47:30 +01:00
|
|
|
*/
|
|
|
|
public function testIndexProviderError(): void
|
|
|
|
{
|
2021-11-23 11:59:53 +01:00
|
|
|
/** @var AccessToken|MockObject $accessToken */
|
|
|
|
$accessToken = $this->createMock(AccessToken::class);
|
|
|
|
|
|
|
|
$thrown = false;
|
2020-11-15 18:47:30 +01:00
|
|
|
/** @var GenericProvider|MockObject $provider */
|
|
|
|
$provider = $this->createMock(GenericProvider::class);
|
2021-11-23 11:59:53 +01:00
|
|
|
$provider->expects($this->exactly(2))
|
2020-11-15 18:47:30 +01:00
|
|
|
->method('getAccessToken')
|
|
|
|
->with('authorization_code', ['code' => 'lorem-ipsum-code'])
|
2021-11-23 11:59:53 +01:00
|
|
|
->willReturnCallback(function () use (&$thrown, $accessToken) {
|
|
|
|
if (!$thrown) {
|
|
|
|
$thrown = true;
|
|
|
|
throw new IdentityProviderException(
|
|
|
|
'Oops',
|
|
|
|
42,
|
|
|
|
['error' => 'some_error', 'error_description' => 'Some kind of error']
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $accessToken;
|
|
|
|
});
|
|
|
|
$provider->expects($this->once())
|
|
|
|
->method('getResourceOwner')
|
|
|
|
->with($accessToken)
|
2020-11-15 18:47:30 +01:00
|
|
|
->willThrowException(new IdentityProviderException(
|
2021-11-23 11:59:53 +01:00
|
|
|
'Something\'s wrong!',
|
|
|
|
1337,
|
|
|
|
'500 Internal server error'
|
2020-11-15 18:47:30 +01:00
|
|
|
));
|
|
|
|
|
|
|
|
$this->session->set('oauth2_state', 'some-internal-state');
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['code' => 'lorem-ipsum-code', 'state' => 'some-internal-state']);
|
|
|
|
|
|
|
|
$controller = $this->getMock(['getProvider']);
|
2021-11-23 11:59:53 +01:00
|
|
|
$this->setExpects($controller, 'getProvider', ['testprovider'], $provider, 2);
|
2020-11-15 18:47:30 +01:00
|
|
|
|
2021-11-23 11:59:53 +01:00
|
|
|
// Invalid state
|
2020-11-15 18:47:30 +01:00
|
|
|
$exception = null;
|
|
|
|
try {
|
|
|
|
$controller->index($request);
|
|
|
|
} catch (HttpNotFound $e) {
|
|
|
|
$exception = $e;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->log->hasErrorThatContains('Some kind of error');
|
|
|
|
$this->log->hasErrorThatContains('some_error');
|
|
|
|
$this->assertNotNull($exception, 'Exception not thrown');
|
|
|
|
$this->assertEquals('oauth.provider-error', $exception->getMessage());
|
2021-11-23 11:59:53 +01:00
|
|
|
|
|
|
|
// Error while getting data
|
|
|
|
$exception = null;
|
|
|
|
try {
|
|
|
|
$controller->index($request);
|
|
|
|
} catch (HttpNotFound $e) {
|
|
|
|
$exception = $e;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->log->hasErrorThatContains('500');
|
|
|
|
$this->assertNotNull($exception, 'Exception not thrown');
|
|
|
|
$this->assertEquals('oauth.provider-error', $exception->getMessage());
|
2020-11-15 18:47:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
|
|
|
*/
|
|
|
|
public function testIndexAlreadyConnectedToAUser(): void
|
|
|
|
{
|
|
|
|
$accessToken = $this->createMock(AccessToken::class);
|
|
|
|
|
|
|
|
/** @var ResourceOwnerInterface|MockObject $resourceOwner */
|
|
|
|
$resourceOwner = $this->createMock(ResourceOwnerInterface::class);
|
|
|
|
$this->setExpects($resourceOwner, 'getId', null, 'provider-user-identifier', $this->atLeastOnce());
|
|
|
|
|
|
|
|
/** @var GenericProvider|MockObject $provider */
|
|
|
|
$provider = $this->createMock(GenericProvider::class);
|
|
|
|
$this->setExpects(
|
|
|
|
$provider,
|
|
|
|
'getAccessToken',
|
|
|
|
['authorization_code', ['code' => 'lorem-ipsum-code']],
|
|
|
|
$accessToken,
|
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
$this->setExpects($provider, 'getResourceOwner', [$accessToken], $resourceOwner);
|
|
|
|
|
|
|
|
$this->session->set('oauth2_state', 'some-internal-state');
|
|
|
|
|
|
|
|
$this->setExpects($this->auth, 'user', null, $this->otherAuthenticatedUser);
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['code' => 'lorem-ipsum-code', 'state' => 'some-internal-state']);
|
|
|
|
|
|
|
|
$controller = $this->getMock(['getProvider']);
|
|
|
|
$this->setExpects($controller, 'getProvider', ['testprovider'], $provider);
|
|
|
|
|
|
|
|
$exception = null;
|
|
|
|
try {
|
|
|
|
$controller->index($request);
|
|
|
|
} catch (HttpNotFound $e) {
|
|
|
|
$exception = $e;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->assertNotNull($exception, 'Exception not thrown');
|
|
|
|
$this->assertEquals('oauth.already-connected', $exception->getMessage());
|
|
|
|
}
|
|
|
|
|
2022-04-30 23:56:45 +02:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
|
|
|
* @dataProvider oAuthErrorCodeProvider
|
|
|
|
*/
|
|
|
|
public function testIndexOAuthErrorResponse(string $oauth_error_code): void
|
|
|
|
{
|
|
|
|
$controller = $this->getMock(['getProvider']);
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['error' => $oauth_error_code]);
|
|
|
|
|
|
|
|
$exception = null;
|
|
|
|
try {
|
|
|
|
$controller->index($request);
|
|
|
|
} catch (HttpNotFound $e) {
|
|
|
|
$exception = $e;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->assertNotNull($exception, 'Exception not thrown');
|
|
|
|
$this->assertEquals('oauth.' . $oauth_error_code, $exception->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function oAuthErrorCodeProvider(): array
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
['invalid_request'],
|
|
|
|
['unauthorized_client'],
|
|
|
|
['access_denied'],
|
|
|
|
['unsupported_response_type'],
|
|
|
|
['invalid_scope'],
|
|
|
|
['server_error'],
|
|
|
|
['temporarily_unavailable']
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2020-11-15 18:47:30 +01:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
2021-11-23 11:59:53 +01:00
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::redirectRegister
|
2020-11-15 18:47:30 +01:00
|
|
|
*/
|
|
|
|
public function testIndexRedirectRegister(): void
|
|
|
|
{
|
|
|
|
$accessToken = $this->createMock(AccessToken::class);
|
2020-12-26 18:24:20 +01:00
|
|
|
$this->setExpects($accessToken, 'getToken', null, 'test-token', $this->atLeastOnce());
|
|
|
|
$this->setExpects($accessToken, 'getRefreshToken', null, 'test-refresh-token', $this->atLeastOnce());
|
|
|
|
$this->setExpects($accessToken, 'getExpires', null, 4242424242, $this->atLeastOnce());
|
2020-11-15 18:47:30 +01:00
|
|
|
|
|
|
|
/** @var ResourceOwnerInterface|MockObject $resourceOwner */
|
|
|
|
$resourceOwner = $this->createMock(ResourceOwnerInterface::class);
|
|
|
|
$this->setExpects(
|
|
|
|
$resourceOwner,
|
|
|
|
'getId',
|
|
|
|
null,
|
2021-01-01 20:50:20 +01:00
|
|
|
'ProVIdeR-User-IdenTifIer', // Case sensitive variation of existing entry
|
2020-11-15 18:47:30 +01:00
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
$this->setExpects(
|
|
|
|
$resourceOwner,
|
|
|
|
'toArray',
|
|
|
|
null,
|
|
|
|
[
|
2021-01-01 20:50:20 +01:00
|
|
|
'uid' => 'ProVIdeR-User-IdenTifIer',
|
2020-11-15 18:47:30 +01:00
|
|
|
'user' => 'username',
|
|
|
|
'email' => 'foo.bar@localhost',
|
|
|
|
'given-name' => 'Foo',
|
|
|
|
'last-name' => 'Bar',
|
|
|
|
],
|
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
|
|
|
|
/** @var GenericProvider|MockObject $provider */
|
|
|
|
$provider = $this->createMock(GenericProvider::class);
|
|
|
|
$this->setExpects(
|
|
|
|
$provider,
|
|
|
|
'getAccessToken',
|
|
|
|
['authorization_code', ['code' => 'lorem-ipsum-code']],
|
|
|
|
$accessToken,
|
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
$this->setExpects($provider, 'getResourceOwner', [$accessToken], $resourceOwner, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$this->session->set('oauth2_state', 'some-internal-state');
|
|
|
|
|
|
|
|
$this->setExpects($this->auth, 'user', null, null, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$this->setExpects($this->redirect, 'to', ['/register']);
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['code' => 'lorem-ipsum-code', 'state' => 'some-internal-state']);
|
|
|
|
|
|
|
|
$controller = $this->getMock(['getProvider']);
|
|
|
|
$this->setExpects($controller, 'getProvider', ['testprovider'], $provider, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$this->config->set('registration_enabled', true);
|
|
|
|
$controller->index($request);
|
|
|
|
$this->assertEquals('testprovider', $this->session->get('oauth2_connect_provider'));
|
2021-01-01 20:50:20 +01:00
|
|
|
$this->assertEquals('ProVIdeR-User-IdenTifIer', $this->session->get('oauth2_user_id'));
|
2020-12-26 18:24:20 +01:00
|
|
|
$this->assertEquals('test-token', $this->session->get('oauth2_access_token'));
|
|
|
|
$this->assertEquals('test-refresh-token', $this->session->get('oauth2_refresh_token'));
|
|
|
|
$this->assertEquals(4242424242, $this->session->get('oauth2_expires_at')->unix());
|
2021-12-10 01:22:02 +01:00
|
|
|
$this->assertFalse($this->session->get('oauth2_enable_password'));
|
2021-12-03 23:00:37 +01:00
|
|
|
$this->assertEquals(null, $this->session->get('oauth2_allow_registration'));
|
2020-11-15 18:47:30 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
[
|
|
|
|
'name' => 'username',
|
|
|
|
'email' => 'foo.bar@localhost',
|
|
|
|
'first_name' => 'Foo',
|
|
|
|
'last_name' => 'Bar',
|
|
|
|
],
|
|
|
|
$this->session->get('form_data')
|
|
|
|
);
|
|
|
|
|
|
|
|
$this->config->set('registration_enabled', false);
|
|
|
|
$this->expectException(HttpNotFound::class);
|
|
|
|
$controller->index($request);
|
|
|
|
}
|
|
|
|
|
2021-12-11 23:28:03 +01:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::index
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::getId
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::redirectRegister
|
|
|
|
*/
|
|
|
|
public function testIndexRedirectRegisterNestedInfo(): void
|
|
|
|
{
|
|
|
|
$accessToken = $this->createMock(AccessToken::class);
|
|
|
|
$this->setExpects($accessToken, 'getToken', null, 'test-token', $this->atLeastOnce());
|
|
|
|
$this->setExpects($accessToken, 'getRefreshToken', null, 'test-refresh-token', $this->atLeastOnce());
|
|
|
|
$this->setExpects($accessToken, 'getExpires', null, 4242424242, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$config = $this->config->get('oauth');
|
|
|
|
$config['testprovider'] = array_merge($config['testprovider'], [
|
|
|
|
'nested_info' => true,
|
|
|
|
'id' => 'nested.id',
|
|
|
|
'email' => 'nested.email',
|
|
|
|
'username' => 'nested.name',
|
|
|
|
'first_name' => 'nested.first',
|
|
|
|
'last_name' => 'nested.last',
|
|
|
|
]);
|
|
|
|
$this->config->set('oauth', $config);
|
|
|
|
|
|
|
|
$this->config->set('registration_enabled', true);
|
|
|
|
|
|
|
|
/** @var ResourceOwnerInterface|MockObject $resourceOwner */
|
|
|
|
$resourceOwner = $this->createMock(ResourceOwnerInterface::class);
|
|
|
|
$this->setExpects($resourceOwner, 'getId', null, null, $this->never());
|
|
|
|
$this->setExpects(
|
|
|
|
$resourceOwner,
|
|
|
|
'toArray',
|
|
|
|
null,
|
|
|
|
[
|
|
|
|
'nested' => [
|
|
|
|
'id' => 'new-provider-user-identifier',
|
|
|
|
'name' => 'testuser',
|
|
|
|
'email' => 'foo.bar@localhost',
|
|
|
|
'first' => 'Test',
|
|
|
|
'last' => 'Tester',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
|
|
|
|
/** @var GenericProvider|MockObject $provider */
|
|
|
|
$provider = $this->createMock(GenericProvider::class);
|
|
|
|
$this->setExpects(
|
|
|
|
$provider,
|
|
|
|
'getAccessToken',
|
|
|
|
['authorization_code', ['code' => 'lorem-ipsum-code']],
|
|
|
|
$accessToken,
|
|
|
|
$this->atLeastOnce()
|
|
|
|
);
|
|
|
|
$this->setExpects($provider, 'getResourceOwner', [$accessToken], $resourceOwner, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$this->session->set('oauth2_state', 'some-internal-state');
|
|
|
|
|
|
|
|
$this->setExpects($this->auth, 'user', null, null, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$this->setExpects($this->redirect, 'to', ['/register']);
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$request = $request
|
|
|
|
->withAttribute('provider', 'testprovider')
|
|
|
|
->withQueryParams(['code' => 'lorem-ipsum-code', 'state' => 'some-internal-state']);
|
|
|
|
|
|
|
|
$controller = $this->getMock(['getProvider']);
|
|
|
|
$this->setExpects($controller, 'getProvider', ['testprovider'], $provider, $this->atLeastOnce());
|
|
|
|
|
|
|
|
$controller->index($request);
|
|
|
|
$this->assertEquals([
|
|
|
|
'email' => 'foo.bar@localhost',
|
|
|
|
'name' => 'testuser',
|
|
|
|
'first_name' => 'Test',
|
|
|
|
'last_name' => 'Tester',
|
|
|
|
], $this->session->get('form_data'));
|
|
|
|
}
|
|
|
|
|
2020-11-15 18:47:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::connect
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::requireProvider
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::isValidProvider
|
|
|
|
*/
|
|
|
|
public function testConnect(): void
|
|
|
|
{
|
|
|
|
$controller = $this->getMock(['index']);
|
|
|
|
$this->setExpects($controller, 'index', null, new Response());
|
|
|
|
|
|
|
|
$request = (new Request())
|
|
|
|
->withAttribute('provider', 'testprovider');
|
|
|
|
|
|
|
|
$controller->connect($request);
|
|
|
|
|
|
|
|
$this->assertEquals('testprovider', $this->session->get('oauth2_connect_provider'));
|
|
|
|
|
|
|
|
// Provider not found
|
|
|
|
$request = $request->withAttribute('provider', 'notExistingProvider');
|
|
|
|
$this->expectException(HttpNotFound::class);
|
|
|
|
|
|
|
|
$controller->connect($request);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Controllers\OAuthController::disconnect
|
|
|
|
*/
|
|
|
|
public function testDisconnect(): void
|
|
|
|
{
|
|
|
|
$controller = $this->getMock(['addNotification']);
|
|
|
|
$this->setExpects($controller, 'addNotification', ['oauth.disconnected']);
|
|
|
|
|
|
|
|
$request = (new Request())
|
|
|
|
->withAttribute('provider', 'testprovider');
|
|
|
|
|
|
|
|
$this->setExpects($this->auth, 'user', null, $this->authenticatedUser);
|
|
|
|
$this->setExpects($this->redirect, 'back', null, new Response());
|
|
|
|
|
|
|
|
$controller->disconnect($request);
|
|
|
|
$this->assertCount(1, OAuth::all());
|
|
|
|
$this->log->hasInfoThatContains('Disconnected');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return OAuthController|MockObject
|
|
|
|
*/
|
|
|
|
protected function getMock(array $mockMethods = []): OAuthController
|
|
|
|
{
|
|
|
|
/** @var OAuthController|MockObject $controller */
|
|
|
|
$controller = $this->getMockBuilder(OAuthController::class)
|
|
|
|
->setConstructorArgs([
|
|
|
|
$this->auth,
|
|
|
|
$this->authController,
|
|
|
|
$this->config,
|
|
|
|
$this->log,
|
|
|
|
$this->oauth,
|
|
|
|
$this->redirect,
|
|
|
|
$this->session,
|
|
|
|
$this->url
|
|
|
|
])
|
|
|
|
->onlyMethods($mockMethods)
|
|
|
|
->getMock();
|
|
|
|
|
|
|
|
return $controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Setup the DB
|
|
|
|
*/
|
|
|
|
public function setUp(): void
|
|
|
|
{
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
$this->auth = $this->createMock(Authenticator::class);
|
|
|
|
$this->authController = $this->createMock(AuthController::class);
|
|
|
|
$this->config = new Config(['oauth' => $this->oauthConfig]);
|
|
|
|
$this->log = new TestLogger();
|
|
|
|
$this->oauth = new OAuth();
|
|
|
|
$this->redirect = $this->createMock(Redirector::class);
|
|
|
|
$this->session = new Session(new MockArraySessionStorage());
|
|
|
|
$this->url = $this->createMock(UrlGenerator::class);
|
|
|
|
|
|
|
|
$this->app->instance('session', $this->session);
|
|
|
|
|
2021-06-29 00:27:57 +02:00
|
|
|
$this->authenticatedUser = User::factory()->create();
|
2020-11-15 18:47:30 +01:00
|
|
|
(new OAuth(['provider' => 'testprovider', 'identifier' => 'provider-user-identifier']))
|
|
|
|
->user()
|
|
|
|
->associate($this->authenticatedUser)
|
|
|
|
->save();
|
|
|
|
|
2021-06-29 00:27:57 +02:00
|
|
|
$this->otherUser = User::factory()->create();
|
|
|
|
|
|
|
|
$this->otherAuthenticatedUser = User::factory()->create();
|
2020-11-15 18:47:30 +01:00
|
|
|
(new OAuth(['provider' => 'testprovider', 'identifier' => 'provider-baz-identifier']))
|
|
|
|
->user()
|
|
|
|
->associate($this->otherAuthenticatedUser)
|
|
|
|
->save();
|
|
|
|
}
|
|
|
|
}
|