Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\Authenticator;
|
||||
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\PluginBundle\Integration\AbstractSsoServiceIntegration;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Event\AuthenticationEvent;
|
||||
use Mautic\UserBundle\Security\Authentication\AuthenticationHandler;
|
||||
use Mautic\UserBundle\Security\Authentication\Token\Permissions\TokenPermissions;
|
||||
use Mautic\UserBundle\Security\Authentication\Token\PluginToken;
|
||||
use Mautic\UserBundle\Security\Authenticator\Passport\Badge\PluginBadge;
|
||||
use Mautic\UserBundle\Security\Authenticator\PluginAuthenticator;
|
||||
use Mautic\UserBundle\UserEvents;
|
||||
use OAuth2\OAuth2;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||
use Symfony\Component\Security\Http\SecurityEvents;
|
||||
use Symfony\Component\Security\Http\SecurityRequestAttributes;
|
||||
|
||||
class PluginAuthenticatorTest extends TestCase
|
||||
{
|
||||
public function testAuthenticateByPreAuthenticationReplacesToken(): void
|
||||
{
|
||||
$firewallName = 'main';
|
||||
$integration = 'the integration';
|
||||
$authenticatedIntegration = 'Auth integration';
|
||||
$userIdentifier = 'some identifier';
|
||||
$request = new Request(['integration' => $integration]);
|
||||
|
||||
$pluginToken = new PluginToken($firewallName, $integration);
|
||||
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
|
||||
$integrationService = $this->createMock(AbstractSsoServiceIntegration::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$integrationHelper->expects($this->once())
|
||||
->method('getIntegrationObjects')
|
||||
->with($integration, ['sso_service'], false, null, true)
|
||||
->willReturn([$integrationService]);
|
||||
|
||||
$authEvent = new AuthenticationEvent(
|
||||
null,
|
||||
$pluginToken,
|
||||
$userProvider,
|
||||
$request,
|
||||
false, // because there is no request attributes
|
||||
$integration,
|
||||
[$integrationService]
|
||||
);
|
||||
|
||||
// If there will be an issue with this, then please replace with proper class name.
|
||||
// I'm not 100% sure the SSO will return a User instance.
|
||||
$authenticatedUser = $this->createMock(User::class);
|
||||
$authenticatedUser->method('getUserIdentifier')->willReturn($userIdentifier);
|
||||
$returnedPluginToken = new PluginToken($firewallName, $authenticatedIntegration);
|
||||
$returnedPluginToken->setUser($authenticatedUser);
|
||||
$returnedAuthEvent = clone $authEvent;
|
||||
// Change token. Note this also changes authenticated integration and sets user.
|
||||
$returnedAuthEvent->setToken($authenticatedIntegration, $returnedPluginToken);
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->expects($this->once())
|
||||
->method('hasListeners')
|
||||
->with(UserEvents::USER_PRE_AUTHENTICATION)
|
||||
->willReturn(true);
|
||||
$dispatcher->expects($this->once())
|
||||
->method('dispatch')
|
||||
->with($authEvent)
|
||||
->willReturn($returnedAuthEvent);
|
||||
|
||||
$authenticateResult = new PluginAuthenticator(
|
||||
$this->createMock(TokenPermissions::class),
|
||||
$dispatcher,
|
||||
$integrationHelper,
|
||||
$userProvider,
|
||||
$this->createMock(AuthenticationHandler::class),
|
||||
$this->createMock(OAuth2::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$firewallName
|
||||
);
|
||||
|
||||
$authenticateResult = $authenticateResult->authenticate($request);
|
||||
\assert($authenticateResult instanceof SelfValidatingPassport);
|
||||
self::assertCount(2, $authenticateResult->getBadges());
|
||||
|
||||
$userBadge = $authenticateResult->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($userIdentifier, $userBadge->getUserIdentifier());
|
||||
self::assertSame($authenticatedUser, $userBadge->getUser());
|
||||
|
||||
$pluginBadge = $authenticateResult->getBadge(PluginBadge::class);
|
||||
\assert($pluginBadge instanceof PluginBadge);
|
||||
self::assertSame($returnedPluginToken, $pluginBadge->getPreAuthenticatedToken());
|
||||
self::assertSame($authenticatedIntegration, $pluginBadge->getAuthenticatingService());
|
||||
}
|
||||
|
||||
public function testAuthenticateByPreAuthenticationSameToken(): void
|
||||
{
|
||||
$firewallName = 'main';
|
||||
$integration = 'the integration';
|
||||
$authenticatedIntegration = 'Auth integration';
|
||||
$userIdentifier = 'some identifier';
|
||||
$request = new Request(['integration' => $integration]);
|
||||
|
||||
$pluginToken = new PluginToken($firewallName, $integration);
|
||||
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
|
||||
$integrationService = $this->createMock(AbstractSsoServiceIntegration::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$integrationHelper->expects($this->once())
|
||||
->method('getIntegrationObjects')
|
||||
->with($integration, ['sso_service'], false, null, true)
|
||||
->willReturn([$integrationService]);
|
||||
|
||||
$authEvent = new AuthenticationEvent(
|
||||
null,
|
||||
$pluginToken,
|
||||
$userProvider,
|
||||
$request,
|
||||
false, // because there is no request attributes
|
||||
$integration,
|
||||
[$integrationService]
|
||||
);
|
||||
|
||||
// If there will be an issue with this, then please replace with proper class name.
|
||||
// I'm not 100% sure the SSO will return a User instance.
|
||||
$authenticatedUser = $this->createMock(User::class);
|
||||
$authenticatedUser->method('getUserIdentifier')->willReturn($userIdentifier);
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->expects($this->once())
|
||||
->method('hasListeners')
|
||||
->with(UserEvents::USER_PRE_AUTHENTICATION)
|
||||
->willReturn(true);
|
||||
$dispatcher->expects($this->once())
|
||||
->method('dispatch')
|
||||
->with($authEvent)
|
||||
->willReturnCallback(static function (AuthenticationEvent $event) use ($authenticatedIntegration, $authenticatedUser): AuthenticationEvent {
|
||||
$event->setIsAuthenticated($authenticatedIntegration, $authenticatedUser, false);
|
||||
$event->getToken()->setUser($authenticatedUser);
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$pluginAuthenticator = new PluginAuthenticator(
|
||||
$this->createMock(TokenPermissions::class),
|
||||
$dispatcher,
|
||||
$integrationHelper,
|
||||
$userProvider,
|
||||
$this->createMock(AuthenticationHandler::class),
|
||||
$this->createMock(OAuth2::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$firewallName
|
||||
);
|
||||
|
||||
$authenticateResult = $pluginAuthenticator->authenticate($request);
|
||||
\assert($authenticateResult instanceof SelfValidatingPassport);
|
||||
self::assertCount(2, $authenticateResult->getBadges());
|
||||
|
||||
$userBadge = $authenticateResult->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($userIdentifier, $userBadge->getUserIdentifier());
|
||||
self::assertSame($authenticatedUser, $userBadge->getUser());
|
||||
|
||||
$pluginBadge = $authenticateResult->getBadge(PluginBadge::class);
|
||||
\assert($pluginBadge instanceof PluginBadge);
|
||||
self::assertEquals(new PluginToken($firewallName, $integration, $authenticatedUser), $pluginBadge->getPreAuthenticatedToken());
|
||||
self::assertSame($authenticatedIntegration, $pluginBadge->getAuthenticatingService());
|
||||
}
|
||||
|
||||
public function testCreateTokenHasToken(): void
|
||||
{
|
||||
$firewallName = 'test';
|
||||
$authenticatingService = 'Auth service';
|
||||
$encodedPassword = 'En pass.';
|
||||
$roles = ['role', 'the', 'roly'];
|
||||
$pluginResponse = new Response();
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->expects(self::never())->method('hasListeners');
|
||||
$dispatcher->expects(self::never())->method('dispatch');
|
||||
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$integrationHelper->expects(self::never())->method('getIntegrationObjects');
|
||||
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
|
||||
$passportUser = $this->createMock(User::class);
|
||||
$passportUser->method('getPassword')->willReturn($encodedPassword);
|
||||
$passportUser->method('getRoles')->willReturn($roles);
|
||||
|
||||
$userBadge = new UserBadge('', function () use ($passportUser): UserInterface {
|
||||
return $passportUser;
|
||||
});
|
||||
|
||||
$pluginBadge = new PluginBadge(null, $pluginResponse, $authenticatingService);
|
||||
|
||||
$passport = new SelfValidatingPassport(
|
||||
$userBadge,
|
||||
[$pluginBadge],
|
||||
);
|
||||
|
||||
$pluginToken = new PluginToken(
|
||||
$firewallName,
|
||||
$authenticatingService,
|
||||
$passportUser,
|
||||
$encodedPassword,
|
||||
$roles,
|
||||
$pluginBadge->getPluginResponse()
|
||||
);
|
||||
|
||||
$tokenPermissions = $this->createMock(TokenPermissions::class);
|
||||
$tokenPermissions->expects(self::once())
|
||||
->method('setActivePermissionsOnAuthToken')
|
||||
->with()
|
||||
->willReturn($passportUser);
|
||||
|
||||
$pluginAuthenticator = new PluginAuthenticator(
|
||||
$tokenPermissions,
|
||||
$dispatcher,
|
||||
$integrationHelper,
|
||||
$userProvider,
|
||||
$this->createMock(AuthenticationHandler::class),
|
||||
$this->createMock(OAuth2::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$firewallName
|
||||
);
|
||||
|
||||
self::assertEquals($pluginToken, $pluginAuthenticator->createToken($passport, $firewallName));
|
||||
}
|
||||
|
||||
public function testHappyPathAuthenticationSuccess(): void
|
||||
{
|
||||
$firewallName = 'test';
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
$token = new PluginToken(null);
|
||||
|
||||
$authenticationHandler = $this->createMock(AuthenticationHandler::class);
|
||||
$authenticationHandler->expects(self::once())
|
||||
->method('onAuthenticationSuccess')
|
||||
->with($request, $token)
|
||||
->willReturn($response);
|
||||
|
||||
$session = $this->createMock(SessionInterface::class);
|
||||
$session->expects(self::once())
|
||||
->method('remove')
|
||||
->with(SecurityRequestAttributes::AUTHENTICATION_ERROR);
|
||||
$request->setSession($session);
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->expects(self::once())
|
||||
->method('dispatch')
|
||||
->with(
|
||||
new InteractiveLoginEvent($request, $token),
|
||||
SecurityEvents::INTERACTIVE_LOGIN
|
||||
)
|
||||
->willReturnArgument(0);
|
||||
|
||||
$pluginAuthenticator = new PluginAuthenticator(
|
||||
$this->createMock(TokenPermissions::class),
|
||||
$dispatcher,
|
||||
$this->createMock(IntegrationHelper::class),
|
||||
$this->createMock(UserProviderInterface::class),
|
||||
$authenticationHandler,
|
||||
$this->createMock(OAuth2::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$firewallName
|
||||
);
|
||||
|
||||
self::assertSame($response, $pluginAuthenticator->onAuthenticationSuccess($request, $token, $firewallName));
|
||||
}
|
||||
|
||||
public function testHappyPathAuthenticationFailure(): void
|
||||
{
|
||||
$firewallName = 'test';
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
$exception = $this->createMock(AuthenticationException::class);
|
||||
|
||||
$authenticationHandler = $this->createMock(AuthenticationHandler::class);
|
||||
$authenticationHandler->expects(self::once())
|
||||
->method('onAuthenticationFailure')
|
||||
->with($request, $exception)
|
||||
->willReturn($response);
|
||||
|
||||
$pluginAuthenticator = new PluginAuthenticator(
|
||||
$this->createMock(TokenPermissions::class),
|
||||
$this->createMock(EventDispatcherInterface::class),
|
||||
$this->createMock(IntegrationHelper::class),
|
||||
$this->createMock(UserProviderInterface::class),
|
||||
$authenticationHandler,
|
||||
$this->createMock(OAuth2::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$firewallName
|
||||
);
|
||||
|
||||
self::assertSame($response, $pluginAuthenticator->onAuthenticationFailure($request, $exception));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\Authenticator;
|
||||
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\PluginBundle\Integration\AbstractSsoServiceIntegration;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Event\AuthenticationEvent;
|
||||
use Mautic\UserBundle\Security\Authentication\Token\PluginToken;
|
||||
use Mautic\UserBundle\Security\Authenticator\Passport\Badge\PasswordStrengthBadge;
|
||||
use Mautic\UserBundle\Security\Authenticator\SsoAuthenticator;
|
||||
use Mautic\UserBundle\Security\Provider\UserProvider;
|
||||
use Mautic\UserBundle\UserEvents;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||
use Symfony\Component\Security\Http\HttpUtils;
|
||||
use Symfony\Component\Security\Http\SecurityRequestAttributes;
|
||||
|
||||
class SsoAuthenticatorTest extends TestCase
|
||||
{
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('provideIsPost')]
|
||||
public function testIsPost(string $method, bool $isPost, bool $expected): void
|
||||
{
|
||||
$path = '/path';
|
||||
$options = ['post_only' => $isPost, 'check_path' => $path, 'form_only' => false];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->server->set('REQUEST_METHOD', $method);
|
||||
|
||||
if (true === $expected) {
|
||||
$httpUtils->method('checkRequestPath')
|
||||
->with($request, $path)
|
||||
->willReturn(true);
|
||||
|
||||
if ($isPost) {
|
||||
$request->request->set('integration', 'integration');
|
||||
} else {
|
||||
$request->query->set('integration', 'integration');
|
||||
}
|
||||
}
|
||||
|
||||
self::assertSame($expected, $authenticator->supports($request));
|
||||
}
|
||||
|
||||
public static function provideIsPost(): \Generator
|
||||
{
|
||||
yield 'is not POST and POST only' => [Request::METHOD_GET, true, false];
|
||||
yield 'is POST and POST only' => [Request::METHOD_POST, true, true];
|
||||
yield 'is not POST and not POST only' => [Request::METHOD_GET, false, true];
|
||||
yield 'is POST and not POST only' => [Request::METHOD_POST, false, true];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('provideCheckPath')]
|
||||
public function testCheckPath(bool $expected): void
|
||||
{
|
||||
$path = '/path';
|
||||
$options = ['post_only' => true, 'check_path' => $path, 'form_only' => false];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->server->set('REQUEST_METHOD', Request::METHOD_POST);
|
||||
$request->request->set('integration', 'integration');
|
||||
|
||||
$httpUtils->expects(self::once())
|
||||
->method('checkRequestPath')
|
||||
->with($request, $path)
|
||||
->willReturn($expected);
|
||||
|
||||
self::assertSame($expected, $authenticator->supports($request));
|
||||
}
|
||||
|
||||
public static function provideCheckPath(): \Generator
|
||||
{
|
||||
yield 'Is correct path' => [true];
|
||||
yield 'Is not correct path' => [false];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('provideFormOnly')]
|
||||
public function testFormOnly(string $mimeType, bool $isForm, bool $expected): void
|
||||
{
|
||||
$path = '/path';
|
||||
$options = ['post_only' => true, 'check_path' => $path, 'form_only' => $isForm];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->server->set('REQUEST_METHOD', Request::METHOD_POST);
|
||||
$request->request->set('integration', 'integration');
|
||||
|
||||
$request->headers->set('CONTENT_TYPE', $mimeType);
|
||||
|
||||
$httpUtils->expects(self::once())
|
||||
->method('checkRequestPath')
|
||||
->with($request, $path)
|
||||
->willReturn(true);
|
||||
|
||||
self::assertSame($expected, $authenticator->supports($request));
|
||||
}
|
||||
|
||||
public static function provideFormOnly(): \Generator
|
||||
{
|
||||
yield 'is not form and form only' => ['application/json', true, false];
|
||||
yield 'is form and form only' => ['application/x-www-form-urlencoded', true, true];
|
||||
yield 'is not form and not form only' => ['application/json', false, true];
|
||||
yield 'is form and not form only' => ['application/x-www-form-urlencoded', false, true];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('provideRequestIntegrationParameter')]
|
||||
public function testHasRequestIntegrationParameter(?bool $addToPost, bool $isPost, bool $expected): void
|
||||
{
|
||||
$path = '/path';
|
||||
$options = ['post_only' => $isPost, 'check_path' => $path, 'form_only' => false];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->server->set('REQUEST_METHOD', Request::METHOD_POST);
|
||||
|
||||
if (null !== $addToPost) {
|
||||
if ($addToPost) {
|
||||
$request->request->set('integration', 'integration');
|
||||
} else {
|
||||
$request->query->set('integration', 'integration');
|
||||
}
|
||||
}
|
||||
|
||||
$httpUtils->expects(self::once())
|
||||
->method('checkRequestPath')
|
||||
->with($request, $path)
|
||||
->willReturn(true);
|
||||
|
||||
self::assertSame($expected, $authenticator->supports($request));
|
||||
}
|
||||
|
||||
public static function provideRequestIntegrationParameter(): \Generator
|
||||
{
|
||||
yield 'has POST parameter and is POST only' => [true, true, true];
|
||||
yield 'has no POST parameter and is POST only' => [false, true, false];
|
||||
yield 'has GET parameter and is not POST only' => [false, false, true];
|
||||
yield 'has POST parameter and is not POST only' => [true, false, true];
|
||||
yield 'has no POST or GET parameter and is not POST only' => [null, false, false];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('provideEnableCsrf')]
|
||||
public function testBadges(bool $enableCsrf): void
|
||||
{
|
||||
$username = 'mautic';
|
||||
$password = 'pw';
|
||||
$integration = 'integration';
|
||||
$csrfToken = 'token';
|
||||
$options = ['post_only' => true, 'enable_csrf' => $enableCsrf];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$session = $this->createMock(SessionInterface::class);
|
||||
$session->expects(self::once())
|
||||
->method('set')
|
||||
->with(SecurityRequestAttributes::LAST_USERNAME, $username);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
$request->request->set('_username', $username);
|
||||
$request->request->set('_password', $password);
|
||||
$request->request->set('integration', $integration);
|
||||
$request->request->set('_csrf_token', $csrfToken);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
$badges = $passport->getBadges();
|
||||
self::assertCount($enableCsrf ? 4 : 3, $badges);
|
||||
|
||||
$userBadge = $passport->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($username, $userBadge->getUserIdentifier());
|
||||
|
||||
$passwordBadge = $passport->getBadge(PasswordCredentials::class);
|
||||
\assert($passwordBadge instanceof PasswordCredentials);
|
||||
self::assertSame($password, $passwordBadge->getPassword());
|
||||
|
||||
self::assertTrue($passport->hasBadge(RememberMeBadge::class));
|
||||
|
||||
// Badge will be added later by PasswordStrengthSubscriber
|
||||
$passwordStrengthBadge = $passport->getBadge(PasswordStrengthBadge::class);
|
||||
self::assertNull($passwordStrengthBadge);
|
||||
|
||||
if (!$enableCsrf) {
|
||||
self::assertFalse($passport->hasBadge(CsrfTokenBadge::class));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$csrfTokenBadge = $passport->getBadge(CsrfTokenBadge::class);
|
||||
\assert($csrfTokenBadge instanceof CsrfTokenBadge);
|
||||
self::assertSame($csrfToken, $csrfTokenBadge->getCsrfToken());
|
||||
self::assertSame('authenticate', $csrfTokenBadge->getCsrfTokenId());
|
||||
}
|
||||
|
||||
public static function provideEnableCsrf(): \Generator
|
||||
{
|
||||
yield 'enable csrf' => [true];
|
||||
yield 'not enable csrf' => [false];
|
||||
}
|
||||
|
||||
public function testAuthenticateDoesNotLoadFromProviderAndNoListenersReturnsNoUser(): void
|
||||
{
|
||||
$username = 'mautic';
|
||||
$password = 'pw';
|
||||
$integration = 'integration';
|
||||
$csrfToken = 'token';
|
||||
$options = ['post_only' => true];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProvider::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$session = $this->createMock(SessionInterface::class);
|
||||
$session->expects(self::once())
|
||||
->method('set')
|
||||
->with(SecurityRequestAttributes::LAST_USERNAME, $username);
|
||||
|
||||
$integrations = [$this->createMock(AbstractSsoServiceIntegration::class)];
|
||||
$integrationHelper->expects(self::once())
|
||||
->method('getIntegrationObjects')
|
||||
->with($integration, ['sso_form'], false, null, true)
|
||||
->willReturn($integrations);
|
||||
|
||||
$userProvider->expects(self::once())
|
||||
->method('loadUserByIdentifier')
|
||||
->with($username)
|
||||
->willThrowException(new UserNotFoundException());
|
||||
|
||||
$dispatcher->expects(self::once())
|
||||
->method('hasListeners')
|
||||
->with(UserEvents::USER_FORM_AUTHENTICATION)
|
||||
->willReturn(false);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
$request->request->set('_username', $username);
|
||||
$request->request->set('_password', $password);
|
||||
$request->request->set('integration', $integration);
|
||||
$request->request->set('_csrf_token', $csrfToken);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
|
||||
$userBadge = $passport->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($username, $userBadge->getUserIdentifier());
|
||||
|
||||
$this->expectException(UserNotFoundException::class);
|
||||
|
||||
$userBadge->getUser();
|
||||
}
|
||||
|
||||
public function testAuthenticateLoadsFromProviderAndNoListenersReturnsUser(): void
|
||||
{
|
||||
$username = 'mautic';
|
||||
$password = 'pw';
|
||||
$integration = 'integration';
|
||||
$csrfToken = 'token';
|
||||
$options = ['post_only' => true];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProvider::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$session = $this->createMock(SessionInterface::class);
|
||||
$session->expects(self::once())
|
||||
->method('set')
|
||||
->with(SecurityRequestAttributes::LAST_USERNAME, $username);
|
||||
|
||||
$integrations = [$this->createMock(AbstractSsoServiceIntegration::class)];
|
||||
$integrationHelper->expects(self::once())
|
||||
->method('getIntegrationObjects')
|
||||
->with($integration, ['sso_form'], false, null, true)
|
||||
->willReturn($integrations);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
$user->expects(self::once())
|
||||
->method('getRoles')
|
||||
->willReturn([]);
|
||||
$userProvider->expects(self::once())
|
||||
->method('loadUserByIdentifier')
|
||||
->with($username)
|
||||
->willReturn($user);
|
||||
|
||||
$dispatcher->expects(self::once())
|
||||
->method('hasListeners')
|
||||
->with(UserEvents::USER_FORM_AUTHENTICATION)
|
||||
->willReturn(false);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
$request->request->set('_username', $username);
|
||||
$request->request->set('_password', $password);
|
||||
$request->request->set('integration', $integration);
|
||||
$request->request->set('_csrf_token', $csrfToken);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
|
||||
$userBadge = $passport->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($username, $userBadge->getUserIdentifier());
|
||||
self::assertSame($user, $userBadge->getUser());
|
||||
}
|
||||
|
||||
public function testAuthenticateListenerForcesFailure(): void
|
||||
{
|
||||
$username = 'mautic';
|
||||
$password = 'pw';
|
||||
$integration = 'integration';
|
||||
$csrfToken = 'token';
|
||||
$userRoles = ['ROLE'];
|
||||
$options = ['post_only' => true];
|
||||
$failedMessage = 'Failure';
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProvider::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$session = $this->createMock(SessionInterface::class);
|
||||
$session->expects(self::once())
|
||||
->method('set')
|
||||
->with(SecurityRequestAttributes::LAST_USERNAME, $username);
|
||||
|
||||
$integrations = [$this->createMock(AbstractSsoServiceIntegration::class)];
|
||||
$integrationHelper->expects(self::once())
|
||||
->method('getIntegrationObjects')
|
||||
->with($integration, ['sso_form'], false, null, true)
|
||||
->willReturn($integrations);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
$user->expects(self::once())
|
||||
->method('getRoles')
|
||||
->willReturn($userRoles);
|
||||
$userProvider->expects(self::once())
|
||||
->method('loadUserByIdentifier')
|
||||
->with($username)
|
||||
->willReturn($user);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
$request->request->set('_username', $username);
|
||||
$request->request->set('_password', $password);
|
||||
$request->request->set('integration', $integration);
|
||||
$request->request->set('_csrf_token', $csrfToken);
|
||||
|
||||
$token = new PluginToken(
|
||||
null,
|
||||
$integration,
|
||||
$username,
|
||||
$password,
|
||||
$userRoles,
|
||||
);
|
||||
|
||||
$callEvent = new AuthenticationEvent(
|
||||
$user,
|
||||
$token,
|
||||
$userProvider,
|
||||
$request,
|
||||
false,
|
||||
$integration,
|
||||
$integrations
|
||||
);
|
||||
|
||||
$returnEvent = clone $callEvent;
|
||||
$returnEvent->setFailedAuthenticationMessage($failedMessage);
|
||||
$returnEvent->setIsFailedAuthentication();
|
||||
|
||||
$dispatcher->expects(self::once())
|
||||
->method('hasListeners')
|
||||
->with(UserEvents::USER_FORM_AUTHENTICATION)
|
||||
->willReturn(true);
|
||||
$dispatcher->expects(self::once())
|
||||
->method('dispatch')
|
||||
->with($callEvent, UserEvents::USER_FORM_AUTHENTICATION)
|
||||
->willReturn($returnEvent);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
|
||||
$userBadge = $passport->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($username, $userBadge->getUserIdentifier());
|
||||
|
||||
$this->expectException(AuthenticationException::class);
|
||||
$this->expectExceptionMessage($failedMessage);
|
||||
|
||||
$userBadge->getUser();
|
||||
}
|
||||
|
||||
public function testAuthenticateListenerLoadsUser(): void
|
||||
{
|
||||
$username = 'mautic';
|
||||
$password = 'pw';
|
||||
$integration = 'integration';
|
||||
$csrfToken = 'token';
|
||||
$options = ['post_only' => true];
|
||||
$httpUtils = $this->createMock(HttpUtils::class);
|
||||
$userProvider = $this->createMock(UserProvider::class);
|
||||
$successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
|
||||
$failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$session = $this->createMock(SessionInterface::class);
|
||||
$session->expects(self::once())
|
||||
->method('set')
|
||||
->with(SecurityRequestAttributes::LAST_USERNAME, $username);
|
||||
|
||||
$integrations = [$this->createMock(AbstractSsoServiceIntegration::class)];
|
||||
$integrationHelper->expects(self::once())
|
||||
->method('getIntegrationObjects')
|
||||
->with($integration, ['sso_form'], false, null, true)
|
||||
->willReturn($integrations);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
$userProvider->expects(self::once())
|
||||
->method('loadUserByIdentifier')
|
||||
->with($username)
|
||||
->willThrowException(new UserNotFoundException());
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
$request->request->set('_username', $username);
|
||||
$request->request->set('_password', $password);
|
||||
$request->request->set('integration', $integration);
|
||||
$request->request->set('_csrf_token', $csrfToken);
|
||||
|
||||
$token = new PluginToken(
|
||||
null,
|
||||
$integration,
|
||||
$username,
|
||||
'',
|
||||
[],
|
||||
);
|
||||
|
||||
$callEvent = new AuthenticationEvent(
|
||||
$username,
|
||||
$token,
|
||||
$userProvider,
|
||||
$request,
|
||||
false,
|
||||
$integration,
|
||||
$integrations
|
||||
);
|
||||
|
||||
$returnEvent = clone $callEvent;
|
||||
$returnEvent->setIsAuthenticated($integration, $user, false);
|
||||
|
||||
$dispatcher->expects(self::once())
|
||||
->method('hasListeners')
|
||||
->with(UserEvents::USER_FORM_AUTHENTICATION)
|
||||
->willReturn(true);
|
||||
$dispatcher->expects(self::once())
|
||||
->method('dispatch')
|
||||
->with($callEvent, UserEvents::USER_FORM_AUTHENTICATION)
|
||||
->willReturn($returnEvent);
|
||||
|
||||
$authenticator = new SsoAuthenticator(
|
||||
$options,
|
||||
$httpUtils,
|
||||
$userProvider,
|
||||
$successHandler,
|
||||
$failureHandler,
|
||||
$integrationHelper,
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
|
||||
$userBadge = $passport->getBadge(UserBadge::class);
|
||||
\assert($userBadge instanceof UserBadge);
|
||||
self::assertSame($username, $userBadge->getUserIdentifier());
|
||||
|
||||
self::assertSame($user, $userBadge->getUser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\Authenticator;
|
||||
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Security\TimingSafeFormLoginAuthenticator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
||||
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator;
|
||||
|
||||
class TimingSafeFormLoginAuthenticatorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private function getCredentials(TimingSafeFormLoginAuthenticator $authenticator, Request $request): array
|
||||
{
|
||||
$method = new \ReflectionMethod(TimingSafeFormLoginAuthenticator::class, 'getCredentials');
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invoke($authenticator, $request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithExistingUser(): void
|
||||
{
|
||||
$request = new Request([], ['username' => 'testuser', 'password' => 'password']);
|
||||
$request->setSession(new Session(new MockArraySessionStorage()));
|
||||
$user = new User();
|
||||
$user->setUsername('testuser');
|
||||
|
||||
/** @var UserProviderInterface|\PHPUnit\Framework\MockObject\MockObject $userProvider */
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$userProvider->expects($this->once())
|
||||
->method('loadUserByIdentifier')
|
||||
->with('testuser')
|
||||
->willReturn($user);
|
||||
|
||||
$passwordHasher = $this->createMock(PasswordHasherInterface::class);
|
||||
/** @var PasswordHasherFactoryInterface|\PHPUnit\Framework\MockObject\MockObject $passwordHasherFactory */
|
||||
$passwordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
|
||||
$passwordHasherFactory->expects($this->never())
|
||||
->method('getPasswordHasher');
|
||||
|
||||
/** @var FormLoginAuthenticator|\PHPUnit\Framework\MockObject\MockObject $formLoginAuthenticator */
|
||||
$formLoginAuthenticator = $this->createMock(FormLoginAuthenticator::class);
|
||||
|
||||
$authenticator = new TimingSafeFormLoginAuthenticator(
|
||||
$formLoginAuthenticator,
|
||||
$userProvider,
|
||||
$passwordHasherFactory,
|
||||
[
|
||||
'enable_csrf' => false,
|
||||
'username_parameter' => 'username',
|
||||
'password_parameter' => 'password',
|
||||
'csrf_parameter' => '_csrf_token',
|
||||
'post_only' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$credentials = $this->getCredentials($authenticator, $request);
|
||||
$this->assertEquals('testuser', $credentials['username']);
|
||||
$this->assertEquals('password', $credentials['password']);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
$passport->getUser();
|
||||
}
|
||||
|
||||
public function testAuthenticateWithNonExistingUser(): void
|
||||
{
|
||||
$this->expectException(UserNotFoundException::class);
|
||||
|
||||
$request = new Request([], ['username' => 'testuser', 'password' => 'password']);
|
||||
$request->setSession(new Session(new MockArraySessionStorage()));
|
||||
|
||||
/** @var UserProviderInterface|\PHPUnit\Framework\MockObject\MockObject $userProvider */
|
||||
$userProvider = $this->createMock(UserProviderInterface::class);
|
||||
$userProvider->expects($this->once())
|
||||
->method('loadUserByIdentifier')
|
||||
->with('testuser')
|
||||
->willThrowException(new UserNotFoundException());
|
||||
|
||||
/** @var PasswordHasherInterface|\PHPUnit\Framework\MockObject\MockObject $passwordHasher */
|
||||
$passwordHasher = $this->createMock(PasswordHasherInterface::class);
|
||||
$passwordHasher->expects($this->once())
|
||||
->method('verify')
|
||||
->with('$2y$13$aAwXNyqA87lcXQQuk8Cp6eo2amRywLct29oG2uWZ8lYBeamFZ8UhK', 'password');
|
||||
|
||||
/** @var PasswordHasherFactoryInterface|\PHPUnit\Framework\MockObject\MockObject $passwordHasherFactory */
|
||||
$passwordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
|
||||
$passwordHasherFactory->expects($this->once())
|
||||
->method('getPasswordHasher')
|
||||
->willReturn($passwordHasher);
|
||||
|
||||
/** @var FormLoginAuthenticator|\PHPUnit\Framework\MockObject\MockObject $formLoginAuthenticator */
|
||||
$formLoginAuthenticator = $this->createMock(FormLoginAuthenticator::class);
|
||||
|
||||
$authenticator = new TimingSafeFormLoginAuthenticator(
|
||||
$formLoginAuthenticator,
|
||||
$userProvider,
|
||||
$passwordHasherFactory,
|
||||
[
|
||||
'enable_csrf' => false,
|
||||
'username_parameter' => 'username',
|
||||
'password_parameter' => 'password',
|
||||
'csrf_parameter' => '_csrf_token',
|
||||
'post_only' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$passport = $authenticator->authenticate($request);
|
||||
$passport->getUser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML;
|
||||
|
||||
use LightSaml\Credential\X509Certificate;
|
||||
use LightSaml\Credential\X509Credential;
|
||||
use LightSaml\Store\Credential\CredentialStoreInterface;
|
||||
use Mautic\UserBundle\Security\SAML\EntityDescriptorProviderFactory;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
class EntityDescriptorProviderFactoryTest extends TestCase
|
||||
{
|
||||
public function testBuild(): void
|
||||
{
|
||||
$router = $this->createMock(RouterInterface::class);
|
||||
$credentialStore = $this->createMock(CredentialStoreInterface::class);
|
||||
$entityId = 'https://example.com';
|
||||
$samlRoute = '/saml/login';
|
||||
|
||||
$router->expects($this->once())
|
||||
->method('generate')
|
||||
->with($samlRoute)
|
||||
->willReturn($samlRoute);
|
||||
|
||||
$credentialStore->expects($this->once())
|
||||
->method('getByEntityId')
|
||||
->with($entityId)
|
||||
->willReturn([$credential = $this->createMock(X509Credential::class)]);
|
||||
|
||||
$credential->expects($this->once())
|
||||
->method('getCertificate')
|
||||
->willReturn(new X509Certificate());
|
||||
|
||||
$builder = EntityDescriptorProviderFactory::build(
|
||||
$entityId,
|
||||
$router,
|
||||
$samlRoute,
|
||||
$credentialStore
|
||||
);
|
||||
|
||||
$entityDescriptor = $builder->get();
|
||||
|
||||
Assert::assertCount(
|
||||
1,
|
||||
$entityDescriptor->getFirstSpSsoDescriptor()->getAllAssertionConsumerServicesByUrl('https://example.com/saml/login'),
|
||||
'When building the SpSsoDescriptor, it should add a single AssertionConsumerService with the correct url. '
|
||||
);
|
||||
|
||||
Assert::assertEquals(
|
||||
$entityId,
|
||||
$entityDescriptor->getEntityID(),
|
||||
'The entity ID should be set to the passed entity ID'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML\Store;
|
||||
|
||||
use LightSaml\Credential\X509Credential;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\UserBundle\Security\SAML\Store\CredentialsStore;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CredentialsStoreTest extends TestCase
|
||||
{
|
||||
private string $cacheDir;
|
||||
|
||||
/**
|
||||
* @var CoreParametersHelper|MockObject
|
||||
*/
|
||||
private MockObject $coreParametersHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$this->cacheDir = dirname((new \ReflectionClass(\Composer\Autoload\ClassLoader::class))->getFileName(), 3);
|
||||
}
|
||||
|
||||
public function testEmptyArrayReturnedIfEntityIdsDoNotMatch(): void
|
||||
{
|
||||
$store = new CredentialsStore($this->coreParametersHelper, 'foobar');
|
||||
|
||||
$this->assertEquals([], $store->getByEntityId('barfoo'));
|
||||
}
|
||||
|
||||
public function testDefaultCredentialsAreUsedIfSamlIsDisabled(): void
|
||||
{
|
||||
$matcher = $this->exactly(2);
|
||||
$this->coreParametersHelper->expects($matcher)->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_metadata', $parameters[0]);
|
||||
|
||||
return '';
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('cache_path', $parameters[0]);
|
||||
|
||||
return $this->cacheDir;
|
||||
}
|
||||
});
|
||||
|
||||
$store = new CredentialsStore($this->coreParametersHelper, 'foobar');
|
||||
|
||||
$credentials = $store->getByEntityId('foobar');
|
||||
$this->assertCount(1, $credentials);
|
||||
|
||||
$this->assertInstanceOf(X509Credential::class, $credentials[0]);
|
||||
}
|
||||
|
||||
public function testDefaultCredentialsAreUsedIfCustomCertificateIsNotProvided(): void
|
||||
{
|
||||
$matcher = $this->exactly(3);
|
||||
$this->coreParametersHelper->expects($matcher)->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_metadata', $parameters[0]);
|
||||
|
||||
return '1';
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_own_certificate', $parameters[0]);
|
||||
|
||||
return '';
|
||||
}
|
||||
if (3 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('cache_path', $parameters[0]);
|
||||
|
||||
return $this->cacheDir;
|
||||
}
|
||||
});
|
||||
|
||||
$store = new CredentialsStore($this->coreParametersHelper, 'foobar');
|
||||
|
||||
$credentials = $store->getByEntityId('foobar');
|
||||
$this->assertCount(1, $credentials);
|
||||
|
||||
$this->assertInstanceOf(X509Credential::class, $credentials[0]);
|
||||
}
|
||||
|
||||
public function testOwnCredentialsAreUsedIfProvided(): void
|
||||
{
|
||||
$matcher = $this->exactly(5);
|
||||
$this->coreParametersHelper->expects($matcher)->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_metadata', $parameters[0]);
|
||||
|
||||
return '1';
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_own_certificate', $parameters[0]);
|
||||
|
||||
return 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNOakNDQVorZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREE0TVFzd0NRWURWUVFHRXdKMWN6RUwKTUFrR0ExVUVDQXdDVkZneERUQUxCZ05WQkFvTUJGUmxjM1F4RFRBTEJnTlZCQU1NQkZSbGMzUXdIaGNOTVRreApNakk1TVRjME56RTBXaGNOTWpBeE1qSTRNVGMwTnpFMFdqQTRNUXN3Q1FZRFZRUUdFd0oxY3pFTE1Ba0dBMVVFCkNBd0NWRmd4RFRBTEJnTlZCQW9NQkZSbGMzUXhEVEFMQmdOVkJBTU1CRlJsYzNRd2daOHdEUVlKS29aSWh2Y04KQVFFQkJRQURnWTBBTUlHSkFvR0JBTDQ4eCtJY29BQVVjOVEvL2QxRkhxZFQ1WjNWejRCSVIzNFJqNUUvQkpkegpmODN0dGx0NnBKNFdCbEFYcFlHWW5PSDh4YXpjdGJEUzd2QVVhbmtQMUxBV2haUnBDeFVkdHg2VlV3MXZlNS8xCnRjV1VBcnBZdFVIMXJHdEdoaDlncFJMVkxEMktxaWQzengyMjlXeHJmaHV0NjVBbEJKRzlSeVV6T2E4cWlVS2IKQWdNQkFBR2pVREJPTUIwR0ExVWREZ1FXQkJUZWtkN0RvWUI4dFc0K2N3TGYzR0FKNTl5VFVEQWZCZ05WSFNNRQpHREFXZ0JUZWtkN0RvWUI4dFc0K2N3TGYzR0FKNTl5VFVEQU1CZ05WSFJNRUJUQURBUUgvTUEwR0NTcUdTSWIzCkRRRUJEUVVBQTRHQkFGd05Uc3lHNVZ5dG5EdWF5ZjBmbi9zOGtPcG1mcG1FcDBTRDFBajdvRGhNTytHdG5SWGEKUGZsWVozWlFJWCt4Wkl2K1FSOTNZNUZDM1h2V1JWbk9abWtybzh3YmZoZkFOa2ZGWnFiNFg3SlFqY2YrOVNOTwoxenpyVVVKK1BSVGpBSnR3REdrRVB6Q2d3UDk5QVIrUm5UQ1RaUS9OM2xoQXl3Zm1qRTNQNUpoNwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t';
|
||||
}
|
||||
if (3 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_own_certificate', $parameters[0]);
|
||||
|
||||
return 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNOakNDQVorZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREE0TVFzd0NRWURWUVFHRXdKMWN6RUwKTUFrR0ExVUVDQXdDVkZneERUQUxCZ05WQkFvTUJGUmxjM1F4RFRBTEJnTlZCQU1NQkZSbGMzUXdIaGNOTVRreApNakk1TVRjME56RTBXaGNOTWpBeE1qSTRNVGMwTnpFMFdqQTRNUXN3Q1FZRFZRUUdFd0oxY3pFTE1Ba0dBMVVFCkNBd0NWRmd4RFRBTEJnTlZCQW9NQkZSbGMzUXhEVEFMQmdOVkJBTU1CRlJsYzNRd2daOHdEUVlKS29aSWh2Y04KQVFFQkJRQURnWTBBTUlHSkFvR0JBTDQ4eCtJY29BQVVjOVEvL2QxRkhxZFQ1WjNWejRCSVIzNFJqNUUvQkpkegpmODN0dGx0NnBKNFdCbEFYcFlHWW5PSDh4YXpjdGJEUzd2QVVhbmtQMUxBV2haUnBDeFVkdHg2VlV3MXZlNS8xCnRjV1VBcnBZdFVIMXJHdEdoaDlncFJMVkxEMktxaWQzengyMjlXeHJmaHV0NjVBbEJKRzlSeVV6T2E4cWlVS2IKQWdNQkFBR2pVREJPTUIwR0ExVWREZ1FXQkJUZWtkN0RvWUI4dFc0K2N3TGYzR0FKNTl5VFVEQWZCZ05WSFNNRQpHREFXZ0JUZWtkN0RvWUI4dFc0K2N3TGYzR0FKNTl5VFVEQU1CZ05WSFJNRUJUQURBUUgvTUEwR0NTcUdTSWIzCkRRRUJEUVVBQTRHQkFGd05Uc3lHNVZ5dG5EdWF5ZjBmbi9zOGtPcG1mcG1FcDBTRDFBajdvRGhNTytHdG5SWGEKUGZsWVozWlFJWCt4Wkl2K1FSOTNZNUZDM1h2V1JWbk9abWtybzh3YmZoZkFOa2ZGWnFiNFg3SlFqY2YrOVNOTwoxenpyVVVKK1BSVGpBSnR3REdrRVB6Q2d3UDk5QVIrUm5UQ1RaUS9OM2xoQXl3Zm1qRTNQNUpoNwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t';
|
||||
}
|
||||
if (4 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_own_private_key', $parameters[0]);
|
||||
|
||||
return 'LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLQpNSUlDeGpCQUJna3Foa2lHOXcwQkJRMHdNekFiQmdrcWhraUc5dzBCQlF3d0RnUUkrdGdUM1FGaGpFZ0NBZ2dBCk1CUUdDQ3FHU0liM0RRTUhCQWcxSEdyU0hiN3pWd1NDQW9CRzg5enFBeEF4K3ZQdmhRVlc2ZGZKRkJUU3BBR3EKUlJsZml5Z3IwaXdMQ3hwbm9UNVpZLzBPZDI4dXZCL0hrQWIwY0NnNnNZdk5WdkRERGl0c3BDYjN2SVUvZ1BtTgpoOFZFdGd4RGxYRUUxWXdpbkRuMEJmTzV0Um9DNFNmT3JKUnNyUmRYNHFjN3hrODBLazc0Y2J6TEp5NlZFU1E1CnUvWlk4WXcyRTlleElINHJsWitkQmZzNEpla1FTL2ZMYmlYR2R0U2RWRjZSTzBlOUloUnNiM2RVaFNxcUphZmwKNitWa3B0aGVYR0Z3VHlTZjhjNXlMbU1VQy96Mk5DR3hPNUc5MXVVeHNoRVF2ZFEyTk0wa3Z0OUFHM1Rjd1o2ZwpvYnFqSFdmVkVtVDlqOVJhcWJrbis5ZGVzYWxLUk9OYmUxbEkwSUwxdmNJQlhXZHVRVU5RSzFkVDlnaGJ3R1QrCnZlZ3c4NnhsTXRMTlBydkZ4YzNHNlpWdGlkL1Qxd0RYWkVLS0NxcDlVZWkzZmgwU0xLeTJ3aXRqdDV5dmFiYmMKUWhYT1MyaFZBOXpTUTBJR2N5Y1d6eGVhZitOYjgyNnh2dkFWOVRmMUdocmVUNnZRV0M4QnhEQ1ZyOXczMWg2eQpMbkM1UjJkeHpvbTFkL2tpQnFsY0doMXU5ZCsyT3lDRnBmWXZ5bVdsS2NYWVlPOEUyTnUyb0s0SWx6QjBZZEpwCjgveTdQTEd6YWlwZjU3ZThzckdNR3ZMTUt3UVRMQ3ZhRHUvZ3hsNGQrYXd3VFo5VXNHMnFNT0taU2tJVTRJTXUKdVAwQ0RTYlJ4YkhCcTlnWTRaaUVDdUF4bW1vYWRaWG1OT3U4aUdRV2E5cm1vQ3FXNVhtQWd3dmNuZnhEV2F3SQpaVEpVZ0hkWjZUZmUyalBGSmpTWlZvVS9lbjBXNUJRWGd5MXUzQkRYNjhDOG5BZlo0eG1leUVMY011YjloVFliCkhURG1ZSUJvelpOSWNZSEI2T0dablVSdUdlb2ZNVkpxTWtOZm5FdVN1b0NKc1hHSWhMem5ES3A4RzAwRjllUjIKMUwrQjBaVVp2L084MnFFR3pDL0lYNytDRm1TRFN0VjlSNDAwY0R2aSs4QnNkTU1CK1dWNlNNbksKLS0tLS1FTkQgRU5DUllQVEVEIFBSSVZBVEUgS0VZLS0tLS0=';
|
||||
}
|
||||
if (5 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('saml_idp_own_password', $parameters[0]);
|
||||
|
||||
return 'abc123';
|
||||
}
|
||||
});
|
||||
|
||||
$store = new CredentialsStore($this->coreParametersHelper, 'foobar');
|
||||
|
||||
$credentials = $store->getByEntityId('foobar');
|
||||
$this->assertCount(1, $credentials);
|
||||
|
||||
$cert = $credentials[0];
|
||||
$this->assertInstanceOf(X509Credential::class, $cert);
|
||||
|
||||
$issuer = $cert->getCertificate()->getIssuer();
|
||||
$this->assertEquals('TX', $issuer['ST']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML\Store;
|
||||
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\UserBundle\Security\SAML\Store\EntityDescriptorStore;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class EntityDescriptorStoreTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CoreParametersHelper|MockObject
|
||||
*/
|
||||
private MockObject $coreParametersHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
}
|
||||
|
||||
public function testNullIsReturnedIfEntityIdDoesNotMatch(): void
|
||||
{
|
||||
$store = new EntityDescriptorStore($this->coreParametersHelper);
|
||||
|
||||
$this->coreParametersHelper->method('get')
|
||||
->with('saml_idp_metadata')
|
||||
->willReturn(
|
||||
'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48bWQ6RW50aXR5RGVzY3JpcHRvciB4bWxuczptZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm1ldGFkYXRhIiBlbnRpdHlJRD0iaHR0cHM6Ly9tYXV0aWMtZGV2LWVkLm15LnNhbGVzZm9yY2UuY29tIiB2YWxpZFVudGlsPSIyMDI5LTEyLTI4VDE0OjUyOjA2LjIyMFoiIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICAgPG1kOklEUFNTT0Rlc2NyaXB0b3IgcHJvdG9jb2xTdXBwb3J0RW51bWVyYXRpb249InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCI+CiAgICAgIDxtZDpLZXlEZXNjcmlwdG9yIHVzZT0ic2lnbmluZyI+CiAgICAgICAgIDxkczpLZXlJbmZvPgogICAgICAgICAgICA8ZHM6WDUwOURhdGE+CiAgICAgICAgICAgICAgIDxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRVpEQ0NBMHlnQXdJQkFnSU9BVzlNNS9Nb0FBQUFBRUpEc2Vjd0RRWUpLb1pJaHZjTkFRRUxCUUF3ZWpFU01CQUdBMVVFQXd3SlUwRk5URjkwWlhOME1SZ3dGZ1lEVlFRTERBOHdNRVJxTURBd01EQXdNRXd3U1RreEZ6QVZCZ05WQkFvTURsTmhiR1Z6Wm05eVkyVXVZMjl0TVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcGMyTnZNUXN3Q1FZRFZRUUlEQUpEUVRFTU1Bb0dBMVVFQmhNRFZWTkJNQjRYRFRFNU1USXlPREUwTWpjME4xb1hEVEl3TVRJeU9ERXlNREF3TUZvd2VqRVNNQkFHQTFVRUF3d0pVMEZOVEY5MFpYTjBNUmd3RmdZRFZRUUxEQTh3TUVScU1EQXdNREF3TUV3d1NUa3hGekFWQmdOVkJBb01EbE5oYkdWelptOXlZMlV1WTI5dE1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaGJtTnBjMk52TVFzd0NRWURWUVFJREFKRFFURU1NQW9HQTFVRUJoTURWVk5CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFoVVBxVEoyQ3YreVhPYzcwaW13d05IWE44OTBzQzliU1FsU05MbnJ6cHN5MFB4R0paQmRuL3hIWVlVS2FUZWxvMytHOXRGL1BIQkdHQlMrMGZPN0Rjd254KzVKRnhUQW1MR0ptdnBTN2UrdWc0T2F1SDNidWQ0ck9kbnVzNTczUjd5SjNPZi9IT25DTEpNN3R4TGxaMUorZmUxT2FkOVhHK1dWZGIvL1U0UzBqU09Lb1c5QVlxQjlPd0pLak1aNm9GWXFnQnltZzBiRS9YRFZyTHZZcktNMEkwaEpUQzQ2R1pVc1ZJZUZGM1lDVWtxcDhTZkYzWlFUZzF5SHltbjZiOHJvQjZYVy9yd3dUWVR5MFkwOFlYR0ltWEVseTVoTXFRQ25zc3BjNnJwa3VuUHlqSUY5TlV2NHBCeEU3SXhQcFFld0NrbjBGdVNIRVJQQUM5MVA5eHdJREFRQUJvNEhuTUlIa01CMEdBMVVkRGdRV0JCUlVFVnlKUSs2czdGUzhsM210R3V1ZmpMQXpnVEFQQmdOVkhSTUJBZjhFQlRBREFRSC9NSUd4QmdOVkhTTUVnYWt3Z2FhQUZGUVJYSWxEN3F6c1ZMeVhlYTBhNjUrTXNET0JvWDZrZkRCNk1SSXdFQVlEVlFRRERBbFRRVTFNWDNSbGMzUXhHREFXQmdOVkJBc01EekF3Ukdvd01EQXdNREF3VERCSk9URVhNQlVHQTFVRUNnd09VMkZzWlhObWIzSmpaUzVqYjIweEZqQVVCZ05WQkFjTURWTmhiaUJHY21GdVkybHpZMjh4Q3pBSkJnTlZCQWdNQWtOQk1Rd3dDZ1lEVlFRR0V3TlZVMEdDRGdGdlRPZnpLQUFBQUFCQ1E3SG5NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUE4U3NDS3lMVXE5L25RYXpxK1B0N1RRWWpMaVBWMldOeVcxeEFGQWQxekFEcW5vR1ovZFRZNkQrdTVrZExuK3paUEptaXFuVGdad01Rc3AxdXJ3SmlaK3JncXg0R3hkRlhPakZRTTZnV2RjN0xuSTJxcTI1M2F4SHRaZFNuVTE5NDFWaEc5RXVSdDNIa2tLR3VOVGUwK05GTGJKYXR6Tk04bW80dGZ4Vkxub3NxWUFSTFEvaHVKUURYUUVhcE90ZUhxYkVJbE1OTjJGUi9hYk9lNTRlaWpSRmFncXJqWEtwMlVJTFh2NEFIcE5YVjI2ek43WVpOKzhJc1pPam9RYUtLYlB4MStwRWk1NzZvQlFSSUZ1N01sRkNsc3h0QW9DNmpPb1dCV01QbXR5UGxTNEdKWlRrY056UHJNbGxRem9uZWRGWDlvTk9ZRExiRnRlak1jOWlmWjwvZHM6WDUwOUNlcnRpZmljYXRlPgogICAgICAgICAgICA8L2RzOlg1MDlEYXRhPgogICAgICAgICA8L2RzOktleUluZm8+CiAgICAgIDwvbWQ6S2V5RGVzY3JpcHRvcj4KICAgICAgPG1kOlNpbmdsZUxvZ291dFNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCIgTG9jYXRpb249Imh0dHBzOi8vbWF1dGljLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbS9zZXJ2aWNlcy9hdXRoL2lkcC9zYW1sMi9sb2dvdXQiLz4KICAgICAgPG1kOlNpbmdsZUxvZ291dFNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUmVkaXJlY3QiIExvY2F0aW9uPSJodHRwczovL21hdXRpYy1kZXYtZWQubXkuc2FsZXNmb3JjZS5jb20vc2VydmljZXMvYXV0aC9pZHAvc2FtbDIvbG9nb3V0Ii8+CiAgICAgIDxtZDpOYW1lSURGb3JtYXQ+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQ8L21kOk5hbWVJREZvcm1hdD4KICAgICAgPG1kOlNpbmdsZVNpZ25PblNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCIgTG9jYXRpb249Imh0dHBzOi8vbWF1dGljLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbS9pZHAvZW5kcG9pbnQvSHR0cFBvc3QiLz4KICAgICAgPG1kOlNpbmdsZVNpZ25PblNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUmVkaXJlY3QiIExvY2F0aW9uPSJodHRwczovL21hdXRpYy1kZXYtZWQubXkuc2FsZXNmb3JjZS5jb20vaWRwL2VuZHBvaW50L0h0dHBSZWRpcmVjdCIvPgogICA8L21kOklEUFNTT0Rlc2NyaXB0b3I+CjwvbWQ6RW50aXR5RGVzY3JpcHRvcj4='
|
||||
);
|
||||
|
||||
$descriptor = $store->get('foobar');
|
||||
|
||||
$this->assertNull($descriptor);
|
||||
}
|
||||
|
||||
public function testHasReturnsFalseIfSamlIsDisabled(): void
|
||||
{
|
||||
$store = new EntityDescriptorStore($this->coreParametersHelper);
|
||||
|
||||
$this->coreParametersHelper->method('get')
|
||||
->with('saml_idp_metadata')
|
||||
->willReturn('');
|
||||
|
||||
$this->assertFalse($store->has('foobar'));
|
||||
}
|
||||
|
||||
public function testHasReturnsFalseIfEntityIdDoesNotMatch(): void
|
||||
{
|
||||
$store = new EntityDescriptorStore($this->coreParametersHelper);
|
||||
|
||||
$this->coreParametersHelper->method('get')
|
||||
->with('saml_idp_metadata')
|
||||
->willReturn(
|
||||
'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48bWQ6RW50aXR5RGVzY3JpcHRvciB4bWxuczptZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm1ldGFkYXRhIiBlbnRpdHlJRD0iaHR0cHM6Ly9tYXV0aWMtZGV2LWVkLm15LnNhbGVzZm9yY2UuY29tIiB2YWxpZFVudGlsPSIyMDI5LTEyLTI4VDE0OjUyOjA2LjIyMFoiIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICAgPG1kOklEUFNTT0Rlc2NyaXB0b3IgcHJvdG9jb2xTdXBwb3J0RW51bWVyYXRpb249InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCI+CiAgICAgIDxtZDpLZXlEZXNjcmlwdG9yIHVzZT0ic2lnbmluZyI+CiAgICAgICAgIDxkczpLZXlJbmZvPgogICAgICAgICAgICA8ZHM6WDUwOURhdGE+CiAgICAgICAgICAgICAgIDxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRVpEQ0NBMHlnQXdJQkFnSU9BVzlNNS9Nb0FBQUFBRUpEc2Vjd0RRWUpLb1pJaHZjTkFRRUxCUUF3ZWpFU01CQUdBMVVFQXd3SlUwRk5URjkwWlhOME1SZ3dGZ1lEVlFRTERBOHdNRVJxTURBd01EQXdNRXd3U1RreEZ6QVZCZ05WQkFvTURsTmhiR1Z6Wm05eVkyVXVZMjl0TVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcGMyTnZNUXN3Q1FZRFZRUUlEQUpEUVRFTU1Bb0dBMVVFQmhNRFZWTkJNQjRYRFRFNU1USXlPREUwTWpjME4xb1hEVEl3TVRJeU9ERXlNREF3TUZvd2VqRVNNQkFHQTFVRUF3d0pVMEZOVEY5MFpYTjBNUmd3RmdZRFZRUUxEQTh3TUVScU1EQXdNREF3TUV3d1NUa3hGekFWQmdOVkJBb01EbE5oYkdWelptOXlZMlV1WTI5dE1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaGJtTnBjMk52TVFzd0NRWURWUVFJREFKRFFURU1NQW9HQTFVRUJoTURWVk5CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFoVVBxVEoyQ3YreVhPYzcwaW13d05IWE44OTBzQzliU1FsU05MbnJ6cHN5MFB4R0paQmRuL3hIWVlVS2FUZWxvMytHOXRGL1BIQkdHQlMrMGZPN0Rjd254KzVKRnhUQW1MR0ptdnBTN2UrdWc0T2F1SDNidWQ0ck9kbnVzNTczUjd5SjNPZi9IT25DTEpNN3R4TGxaMUorZmUxT2FkOVhHK1dWZGIvL1U0UzBqU09Lb1c5QVlxQjlPd0pLak1aNm9GWXFnQnltZzBiRS9YRFZyTHZZcktNMEkwaEpUQzQ2R1pVc1ZJZUZGM1lDVWtxcDhTZkYzWlFUZzF5SHltbjZiOHJvQjZYVy9yd3dUWVR5MFkwOFlYR0ltWEVseTVoTXFRQ25zc3BjNnJwa3VuUHlqSUY5TlV2NHBCeEU3SXhQcFFld0NrbjBGdVNIRVJQQUM5MVA5eHdJREFRQUJvNEhuTUlIa01CMEdBMVVkRGdRV0JCUlVFVnlKUSs2czdGUzhsM210R3V1ZmpMQXpnVEFQQmdOVkhSTUJBZjhFQlRBREFRSC9NSUd4QmdOVkhTTUVnYWt3Z2FhQUZGUVJYSWxEN3F6c1ZMeVhlYTBhNjUrTXNET0JvWDZrZkRCNk1SSXdFQVlEVlFRRERBbFRRVTFNWDNSbGMzUXhHREFXQmdOVkJBc01EekF3Ukdvd01EQXdNREF3VERCSk9URVhNQlVHQTFVRUNnd09VMkZzWlhObWIzSmpaUzVqYjIweEZqQVVCZ05WQkFjTURWTmhiaUJHY21GdVkybHpZMjh4Q3pBSkJnTlZCQWdNQWtOQk1Rd3dDZ1lEVlFRR0V3TlZVMEdDRGdGdlRPZnpLQUFBQUFCQ1E3SG5NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUE4U3NDS3lMVXE5L25RYXpxK1B0N1RRWWpMaVBWMldOeVcxeEFGQWQxekFEcW5vR1ovZFRZNkQrdTVrZExuK3paUEptaXFuVGdad01Rc3AxdXJ3SmlaK3JncXg0R3hkRlhPakZRTTZnV2RjN0xuSTJxcTI1M2F4SHRaZFNuVTE5NDFWaEc5RXVSdDNIa2tLR3VOVGUwK05GTGJKYXR6Tk04bW80dGZ4Vkxub3NxWUFSTFEvaHVKUURYUUVhcE90ZUhxYkVJbE1OTjJGUi9hYk9lNTRlaWpSRmFncXJqWEtwMlVJTFh2NEFIcE5YVjI2ek43WVpOKzhJc1pPam9RYUtLYlB4MStwRWk1NzZvQlFSSUZ1N01sRkNsc3h0QW9DNmpPb1dCV01QbXR5UGxTNEdKWlRrY056UHJNbGxRem9uZWRGWDlvTk9ZRExiRnRlak1jOWlmWjwvZHM6WDUwOUNlcnRpZmljYXRlPgogICAgICAgICAgICA8L2RzOlg1MDlEYXRhPgogICAgICAgICA8L2RzOktleUluZm8+CiAgICAgIDwvbWQ6S2V5RGVzY3JpcHRvcj4KICAgICAgPG1kOlNpbmdsZUxvZ291dFNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCIgTG9jYXRpb249Imh0dHBzOi8vbWF1dGljLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbS9zZXJ2aWNlcy9hdXRoL2lkcC9zYW1sMi9sb2dvdXQiLz4KICAgICAgPG1kOlNpbmdsZUxvZ291dFNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUmVkaXJlY3QiIExvY2F0aW9uPSJodHRwczovL21hdXRpYy1kZXYtZWQubXkuc2FsZXNmb3JjZS5jb20vc2VydmljZXMvYXV0aC9pZHAvc2FtbDIvbG9nb3V0Ii8+CiAgICAgIDxtZDpOYW1lSURGb3JtYXQ+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQ8L21kOk5hbWVJREZvcm1hdD4KICAgICAgPG1kOlNpbmdsZVNpZ25PblNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCIgTG9jYXRpb249Imh0dHBzOi8vbWF1dGljLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbS9pZHAvZW5kcG9pbnQvSHR0cFBvc3QiLz4KICAgICAgPG1kOlNpbmdsZVNpZ25PblNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUmVkaXJlY3QiIExvY2F0aW9uPSJodHRwczovL21hdXRpYy1kZXYtZWQubXkuc2FsZXNmb3JjZS5jb20vaWRwL2VuZHBvaW50L0h0dHBSZWRpcmVjdCIvPgogICA8L21kOklEUFNTT0Rlc2NyaXB0b3I+CjwvbWQ6RW50aXR5RGVzY3JpcHRvcj4='
|
||||
);
|
||||
|
||||
$this->assertFalse($store->has('foobar'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML\Store;
|
||||
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use LightSaml\Provider\TimeProvider\TimeProviderInterface;
|
||||
use Mautic\UserBundle\Entity\IdEntry;
|
||||
use Mautic\UserBundle\Security\SAML\Store\IdStore;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class IdStoreTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ObjectManager|MockObject
|
||||
*/
|
||||
private MockObject $manager;
|
||||
|
||||
/**
|
||||
* @var TimeProviderInterface|MockObject
|
||||
*/
|
||||
private MockObject $timeProvider;
|
||||
|
||||
private IdStore $store;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->manager = $this->createMock(ObjectManager::class);
|
||||
$this->timeProvider = $this->createMock(TimeProviderInterface::class);
|
||||
$this->store = new IdStore($this->manager, $this->timeProvider);
|
||||
}
|
||||
|
||||
public function testNewIdEntryCreatedIfEntityIdNotFound(): void
|
||||
{
|
||||
$expiry = new \DateTime('+5 minutes');
|
||||
$this->manager->expects($this->once())
|
||||
->method('persist')
|
||||
->willReturnCallback(function (IdEntry $idEntry) use ($expiry): void {
|
||||
$this->assertEquals('foobar', $idEntry->getEntityId());
|
||||
$this->assertEquals('abc', $idEntry->getId());
|
||||
$this->assertEquals($expiry->getTimestamp(), $idEntry->getExpiryTime()->getTimestamp());
|
||||
});
|
||||
|
||||
$this->store->set('foobar', 'abc', $expiry);
|
||||
}
|
||||
|
||||
public function testIdEntryUpdatedIfEntityIdFound(): void
|
||||
{
|
||||
$expiry = new \DateTime('+5 minutes');
|
||||
$idEntry = new IdEntry();
|
||||
$idEntry->setEntityId('foobar');
|
||||
$idEntry->setId('abc');
|
||||
$idEntry->setExpiryTime($expiry);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn($idEntry);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('persist')
|
||||
->with($idEntry);
|
||||
|
||||
$this->store->set('foobar', 'abc', $expiry);
|
||||
}
|
||||
|
||||
public function testIdEntryIsFoundAndNotExpired(): void
|
||||
{
|
||||
$expiry = new \DateTime('+5 minutes');
|
||||
$idEntry = new IdEntry();
|
||||
$idEntry->setEntityId('foobar');
|
||||
$idEntry->setId('abc');
|
||||
$idEntry->setExpiryTime($expiry);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn($idEntry);
|
||||
|
||||
$this->assertTrue($this->store->has('foobar', 'abc'));
|
||||
}
|
||||
|
||||
public function testIdEntryIsFoundButIsExpired(): void
|
||||
{
|
||||
$this->timeProvider->expects($this->once())
|
||||
->method('getTimestamp')
|
||||
->willReturn(time());
|
||||
|
||||
$expiry = new \DateTime('-5 minutes');
|
||||
$idEntry = new IdEntry();
|
||||
$idEntry->setEntityId('foobar');
|
||||
$idEntry->setId('abc');
|
||||
$idEntry->setExpiryTime($expiry);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn($idEntry);
|
||||
|
||||
$this->assertFalse($this->store->has('foobar', 'abc'));
|
||||
}
|
||||
|
||||
public function testIdEntryIsNotFound(): void
|
||||
{
|
||||
$this->timeProvider->expects($this->never())
|
||||
->method('getTimestamp');
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn(null);
|
||||
|
||||
$this->assertFalse($this->store->has('foobar', 'abc'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML\Store\Request;
|
||||
|
||||
use LightSaml\State\Request\RequestState;
|
||||
use Mautic\CacheBundle\Cache\CacheProviderInterface;
|
||||
use Mautic\UserBundle\Security\SAML\Store\Request\RequestStateStore;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
|
||||
class RequestStateStoreTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CacheProviderInterface&MockObject
|
||||
*/
|
||||
private MockObject $cacheProvider;
|
||||
|
||||
private CacheItem $cacheItem;
|
||||
|
||||
private RequestStateStore $requestStateStore;
|
||||
private string $cachePrefix = 'prefix_suffix';
|
||||
private string $stateId = 'state_id';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->cacheItem = new CacheItem();
|
||||
|
||||
$this->cacheProvider = $this->createMock(CacheProviderInterface::class);
|
||||
$this->cacheProvider->method('getItem')
|
||||
->with($this->cachePrefix.$this->stateId)
|
||||
->willReturn($this->cacheItem);
|
||||
|
||||
$this->requestStateStore = new RequestStateStore($this->cacheProvider, 'prefix', '_suffix');
|
||||
}
|
||||
|
||||
public function testSet(): void
|
||||
{
|
||||
$state = $this->createMock(RequestState::class);
|
||||
$state->expects(self::once())
|
||||
->method('getId')
|
||||
->willReturn($this->stateId);
|
||||
|
||||
$this->cacheProvider->expects(self::once())
|
||||
->method('save')
|
||||
->with($this->cacheItem);
|
||||
|
||||
$this->requestStateStore->set($state);
|
||||
|
||||
$check = \Closure::bind(
|
||||
static function (CacheItem $actual, TestCase $case) use ($state): void {
|
||||
$case::assertEqualsWithDelta(
|
||||
(2 * 60) + microtime(true),
|
||||
$actual->expiry,
|
||||
1
|
||||
);
|
||||
$case::assertSame($state, $actual->value);
|
||||
},
|
||||
null,
|
||||
CacheItem::class
|
||||
);
|
||||
($check)($this->cacheItem, $this);
|
||||
}
|
||||
|
||||
public function testGetNotHit(): void
|
||||
{
|
||||
self::assertNull($this->requestStateStore->get($this->stateId));
|
||||
}
|
||||
|
||||
public function testGetIsHitButNotRequestState(): void
|
||||
{
|
||||
$setUp = \Closure::bind(
|
||||
static function (CacheItem $item): void {
|
||||
$item->isHit = true;
|
||||
$item->value = 'string';
|
||||
},
|
||||
null,
|
||||
CacheItem::class
|
||||
);
|
||||
($setUp)($this->cacheItem);
|
||||
|
||||
self::assertNull($this->requestStateStore->get($this->stateId));
|
||||
}
|
||||
|
||||
public function testGetIsHitRequestState(): void
|
||||
{
|
||||
$state = $this->createMock(RequestState::class);
|
||||
|
||||
$setUp = \Closure::bind(
|
||||
static function (CacheItem $item, RequestState $state): void {
|
||||
$item->isHit = true;
|
||||
$item->value = $state;
|
||||
},
|
||||
null,
|
||||
CacheItem::class
|
||||
);
|
||||
($setUp)($this->cacheItem, $state);
|
||||
|
||||
self::assertSame($state, $this->requestStateStore->get($this->stateId));
|
||||
}
|
||||
|
||||
public function testRemove(): void
|
||||
{
|
||||
$id = 'whatever';
|
||||
$this->cacheProvider->expects(self::once())
|
||||
->method('deleteItem')
|
||||
->with($this->cachePrefix.$id)
|
||||
->willReturn(true);
|
||||
|
||||
self::assertTrue($this->requestStateStore->remove($id));
|
||||
}
|
||||
|
||||
public function testClear(): void
|
||||
{
|
||||
$this->cacheProvider->expects(self::once())
|
||||
->method('clear')
|
||||
->with($this->cachePrefix);
|
||||
|
||||
$this->requestStateStore->clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML\Store;
|
||||
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\UserBundle\Security\SAML\Store\TrustOptionsStore;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TrustOptionsStoreTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var CoreParametersHelper|MockObject
|
||||
*/
|
||||
private MockObject $coreParametersHelper;
|
||||
|
||||
private TrustOptionsStore $store;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$this->store = new TrustOptionsStore($this->coreParametersHelper, 'foobar');
|
||||
}
|
||||
|
||||
public function testHasTrustOptionsIfSamlConfiguredAndEntityIdMatches(): void
|
||||
{
|
||||
$this->coreParametersHelper->expects($this->once())
|
||||
->method('get')
|
||||
->with('saml_idp_metadata')
|
||||
->willReturn('1');
|
||||
|
||||
$this->assertTrue($this->store->has('foobar'));
|
||||
}
|
||||
|
||||
public function testNotHaveTrustOptionsIfSamlDisabled(): void
|
||||
{
|
||||
$this->coreParametersHelper->expects($this->once())
|
||||
->method('get')
|
||||
->with('saml_idp_metadata')
|
||||
->willReturn('');
|
||||
|
||||
$this->assertFalse($this->store->has('foobar'));
|
||||
}
|
||||
|
||||
public function testNotHaveTrustOptionsIfEntityIdDoesNotMatch(): void
|
||||
{
|
||||
$this->coreParametersHelper->expects($this->once())
|
||||
->method('get')
|
||||
->with('saml_idp_metadata')
|
||||
->willReturn('1');
|
||||
|
||||
$this->assertFalse($this->store->has('barfoo'));
|
||||
}
|
||||
|
||||
public function testTrustOptionsDoNotSignRequestForDefault(): void
|
||||
{
|
||||
$this->coreParametersHelper->expects($this->once())
|
||||
->method('get')
|
||||
->with('saml_idp_own_certificate')
|
||||
->willReturn('');
|
||||
|
||||
$store = $this->store->get('foobar');
|
||||
$this->assertFalse($store->getSignAuthnRequest());
|
||||
}
|
||||
|
||||
public function testTrustOptionsSignRequestForCustom(): void
|
||||
{
|
||||
$this->coreParametersHelper->expects($this->once())
|
||||
->method('get')
|
||||
->with('saml_idp_own_certificate')
|
||||
->willReturn('abc');
|
||||
|
||||
$store = $this->store->get('foobar');
|
||||
$this->assertTrue($store->getSignAuthnRequest());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security\SAML\User;
|
||||
|
||||
use LightSaml\Model\Assertion\Assertion;
|
||||
use LightSaml\Model\Assertion\Attribute;
|
||||
use LightSaml\Model\Assertion\AttributeStatement;
|
||||
use LightSaml\Model\Protocol\Response;
|
||||
use Mautic\UserBundle\Security\SAML\User\UserMapper;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserMapperTest extends TestCase
|
||||
{
|
||||
private UserMapper $mapper;
|
||||
|
||||
/**
|
||||
* @var Response|MockObject
|
||||
*/
|
||||
private MockObject $response;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->mapper = new UserMapper(
|
||||
[
|
||||
'email' => 'EmailAddress',
|
||||
'firstname' => 'FirstName',
|
||||
'lastname' => 'LastName',
|
||||
'username' => null,
|
||||
]
|
||||
);
|
||||
|
||||
$emailAttribute = $this->createMock(Attribute::class);
|
||||
$emailAttribute->method('getFirstAttributeValue')
|
||||
->willReturn('hello@there.com');
|
||||
|
||||
$firstnameAttribute = $this->createMock(Attribute::class);
|
||||
$firstnameAttribute->method('getFirstAttributeValue')
|
||||
->willReturn('Joe');
|
||||
|
||||
$lastnameAttribute = $this->createMock(Attribute::class);
|
||||
$lastnameAttribute->method('getFirstAttributeValue')
|
||||
->willReturn('Smith');
|
||||
|
||||
$defaultAttribute = $this->createMock(Attribute::class);
|
||||
$defaultAttribute->method('getFirstAttributeValue')
|
||||
->willReturn('default');
|
||||
|
||||
$statement = $this->createMock(AttributeStatement::class);
|
||||
$statement->method('getFirstAttributeByName')
|
||||
->willReturnCallback(
|
||||
fn ($attributeName) => match ($attributeName) {
|
||||
'EmailAddress' => $emailAttribute,
|
||||
'FirstName' => $firstnameAttribute,
|
||||
'LastName' => $lastnameAttribute,
|
||||
default => $defaultAttribute,
|
||||
}
|
||||
);
|
||||
|
||||
$assertion = $this->createMock(Assertion::class);
|
||||
$assertion->method('getAllAttributeStatements')
|
||||
->willReturn([$statement]);
|
||||
|
||||
$this->response = $this->createMock(Response::class);
|
||||
$this->response->method('getAllAssertions')
|
||||
->willReturn([$assertion]);
|
||||
}
|
||||
|
||||
public function testUserEntityIsPopulatedFromAssertions(): void
|
||||
{
|
||||
$user = $this->mapper->getUser($this->response);
|
||||
$this->assertEquals('hello@there.com', $user->getEmail());
|
||||
$this->assertEquals('hello@there.com', $user->getUserIdentifier());
|
||||
$this->assertEquals('Joe', $user->getFirstName());
|
||||
$this->assertEquals('Smith', $user->getLastName());
|
||||
}
|
||||
|
||||
public function testUsernameIsReturned(): void
|
||||
{
|
||||
$username = $this->mapper->getUsername($this->response);
|
||||
$this->assertEquals('hello@there.com', $username);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\UserBundle\Tests\Traits\CreateEntityTrait;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class UserLoginTest extends MauticMysqlTestCase
|
||||
{
|
||||
use CreateEntityTrait;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
if (strpos($this->name(), 'WithSaml') > 0) {
|
||||
$this->configParams['saml_idp_metadata'] = 'any_string';
|
||||
}
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* User can login with acquia email.
|
||||
*/
|
||||
public function testSuccessfulLoginWithAcquiaUserWithSaml(): void
|
||||
{
|
||||
$this->logoutUser();
|
||||
$password = Uuid::uuid4()->toString();
|
||||
$this->createUser($this->createRole(), 'test@acquia.com', $password);
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/login');
|
||||
|
||||
// Get the form
|
||||
$form = $crawler->filter('form')->form();
|
||||
$form->setValues([
|
||||
'_username' => 'test@acquia.com',
|
||||
'_password' => $password,
|
||||
]);
|
||||
$crawler = $this->client->submit($form);
|
||||
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$this->assertEquals(200, $clientResponse->getStatusCode());
|
||||
|
||||
// user has logged in
|
||||
$title = $crawler->filterXPath('//head/title')->text();
|
||||
$this->assertStringContainsString('Dashboard |', $title);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Tests\Security;
|
||||
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Test\AbstractMauticTestCase;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Mautic\UserBundle\Security\UserTokenSetter;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class UserTokenSetterTest extends AbstractMauticTestCase
|
||||
{
|
||||
public function testSetUserMakesTheUserAvailableToUserHelper(): void
|
||||
{
|
||||
/** @var MockObject&UserModel $userModel */
|
||||
$userModel = $this->createMock(UserModel::class);
|
||||
$user = new User();
|
||||
|
||||
$userModel->method('getEntity')
|
||||
->with(1)
|
||||
->willReturn($user);
|
||||
|
||||
$userTokenSetter = new UserTokenSetter($userModel, $this->getContainer()->get('security.token_storage'));
|
||||
|
||||
$userTokenSetter->setUser(1);
|
||||
|
||||
/** @var UserHelper $userHelper */
|
||||
$userHelper = $this->getContainer()->get('mautic.helper.user');
|
||||
|
||||
$this->assertSame($user, $userHelper->getUser());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user