Release 2.26 (#5189)

* bring back deprecated methods
* bump packages
* fix SAML redirect
* config flag for break times
* use class constant instead of string in attributes
* throw if all tags were not found - fixes #4792
This commit is contained in:
Kevin Papst
2024-12-05 10:42:07 +01:00
committed by GitHub
parent 70741eebfd
commit 82a3b99a31
39 changed files with 437 additions and 397 deletions

View File

@@ -188,6 +188,14 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
]);
}
protected function assertBadRequestResponse(Response $response): void
{
$this->assertApiException($response, [
'code' => Response::HTTP_BAD_REQUEST,
'message' => 'Bad Request'
]);
}
protected function assertApiAccessDenied(HttpKernelBrowser $client, string $url, string $message = 'Forbidden'): void
{
$this->request($client, $url);

View File

@@ -1173,18 +1173,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertIsArray($result[0]);
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
$query = ['tags' => ['Nothing-2-see', 'here']];
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$content = $client->getResponse()->getContent();
self::assertIsString($content);
$result = json_decode($content, true);
self::assertIsArray($result);
self::assertNotEmpty($result);
self::assertEquals(20, \count($result));
$this->assertIsArray($result[0]);
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
$query = ['tags' => ['Nothing-2-see', 'not-existing-here']];
$this->request($client, '/api/timesheets', 'GET', $query);
$this->assertBadRequestResponse($client->getResponse());
}
public function testRestartAction(): void

View File

@@ -10,11 +10,13 @@
namespace App\Tests\Controller\Auth;
use App\Configuration\SamlConfiguration;
use App\Configuration\SamlConfigurationInterface;
use App\Configuration\SystemConfiguration;
use App\Controller\Auth\SamlController;
use App\Saml\SamlAuthFactory;
use App\Tests\Configuration\TestConfigLoader;
use App\Tests\Mocks\Saml\SamlAuthFactoryFactory;
use App\Tests\Mocks\SecurityFactory;
use App\Tests\Mocks\SystemConfigurationFactory;
use OneLogin\Saml2\Auth;
use PHPUnit\Framework\TestCase;
@@ -28,19 +30,14 @@ use Symfony\Component\Security\Http\SecurityRequestAttributes;
*/
class SamlControllerTest extends TestCase
{
/**
* @param array $settings
* @param array $loaderSettings
* @return SystemConfiguration
*/
protected function getSystemConfigurationMock(array $settings, array $loaderSettings = [])
protected function getSystemConfigurationMock(array $settings, array $loaderSettings = []): SystemConfiguration
{
$loader = new TestConfigLoader($loaderSettings);
return SystemConfigurationFactory::create($loader, $settings);
}
protected function getDefaultSettings(bool $activated = true)
protected function getDefaultSettings(bool $activated = true): array
{
return [
'saml' => [
@@ -66,10 +63,15 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$sut = new SamlController($factory, $this->getSamlConfiguration());
$sut = $this->getSut($factory, $this->getSamlConfiguration());
$sut->assertionConsumerServiceAction();
}
public function getSut(SamlAuthFactory $authFactory, SamlConfigurationInterface $samlConfiguration): SamlController
{
return new SamlController($authFactory, $samlConfiguration, (new SecurityFactory($this))->create());
}
public function testMetadataAction(): void
{
$expectedXmlString = <<<EOD
@@ -101,7 +103,7 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$factory->expects($this->once())->method('create')->willReturn($oauth);
$sut = new SamlController($factory, $this->getSamlConfiguration());
$sut = $this->getSut($factory, $this->getSamlConfiguration());
$result = $sut->metadataAction();
self::assertEquals('xml', $result->headers->get('Content-Type'));
@@ -129,7 +131,7 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$sut = new SamlController($factory, $this->getSamlConfiguration());
$sut = $this->getSut($factory, $this->getSamlConfiguration());
$sut->loginAction($request);
}
@@ -140,7 +142,7 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$sut = new SamlController($factory, $this->getSamlConfiguration(false));
$sut = $this->getSut($factory, $this->getSamlConfiguration(false));
$sut->loginAction(new Request());
}
@@ -151,7 +153,7 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$sut = new SamlController($factory, $this->getSamlConfiguration(false));
$sut = $this->getSut($factory, $this->getSamlConfiguration(false));
$sut->metadataAction();
}
@@ -162,7 +164,7 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$sut = new SamlController($factory, $this->getSamlConfiguration(false));
$sut = $this->getSut($factory, $this->getSamlConfiguration(false));
$sut->logoutAction();
}
@@ -173,7 +175,7 @@ class SamlControllerTest extends TestCase
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
$sut = new SamlController($factory, $this->getSamlConfiguration(false));
$sut = $this->getSut($factory, $this->getSamlConfiguration(false));
$sut->assertionConsumerServiceAction();
}
}

View File

@@ -290,6 +290,7 @@ class ConfigurationTest extends TestCase
'break_warning_duration' => 0,
'long_running_duration' => 0,
'require_activity' => true,
'break_time_active' => false,
],
'duration_increment' => 15,
'time_increment' => 15,

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Mocks;
use App\Entity\User;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class SecurityFactory extends AbstractMockFactory
{
public function create(): Security
{
$interface = $this->createMock(TokenInterface::class);
$interface->method('getUser')->willReturn(new User());
$storage = $this->createMock(TokenStorageInterface::class);
$storage->method('getToken')->willReturn($interface);
$container = $this->createMock(ContainerInterface::class);
$container->method('get')->willReturn($storage);
return new Security($container);
}
}

View File

@@ -15,6 +15,7 @@ use App\Saml\Security\SamlAuthenticationSuccessHandler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\HttpUtils;
/**
@@ -22,19 +23,9 @@ use Symfony\Component\Security\Http\HttpUtils;
*/
class SamlAuthenticationSuccessHandlerTest extends TestCase
{
public function testWithAlwaysUseDefaultTargetPath(): void
{
$httpUtils = new HttpUtils($this->getUrlGenerator());
$handler = new SamlAuthenticationSuccessHandler($httpUtils, ['always_use_default_target_path' => true]);
$defaultTargetPath = $httpUtils->generateUri($this->getRequest('/sso/login'), $this->getOption($handler, 'default_target_path', '/'));
$response = $handler->onAuthenticationSuccess($this->getRequest('/login', 'http://localhost/relayed'), $this->getSamlToken());
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertTrue($response->isRedirect($defaultTargetPath));
}
public function testRelayState(): void
{
$handler = new SamlAuthenticationSuccessHandler(new HttpUtils($this->getUrlGenerator()), ['always_use_default_target_path' => false]);
$handler = new SamlAuthenticationSuccessHandler(new HttpUtils($this->getUrlGenerator()));
$response = $handler->onAuthenticationSuccess($this->getRequest('/sso/login', 'http://localhost/relayed'), $this->getSamlToken());
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertTrue($response->isRedirect('http://localhost/relayed'));
@@ -43,8 +34,8 @@ class SamlAuthenticationSuccessHandlerTest extends TestCase
public function testWithoutRelayState(): void
{
$httpUtils = new HttpUtils($this->getUrlGenerator());
$handler = new SamlAuthenticationSuccessHandler($httpUtils, ['always_use_default_target_path' => false]);
$defaultTargetPath = $httpUtils->generateUri($this->getRequest('/sso/login'), $this->getOption($handler, 'default_target_path', '/'));
$handler = new SamlAuthenticationSuccessHandler($httpUtils);
$defaultTargetPath = $httpUtils->generateUri($this->getRequest('/sso/login'), '/');
$response = $handler->onAuthenticationSuccess($this->getRequest(), $this->getSamlToken());
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertTrue($response->isRedirect($defaultTargetPath));
@@ -53,16 +44,16 @@ class SamlAuthenticationSuccessHandlerTest extends TestCase
public function testRelayStateLoop(): void
{
$httpUtils = new HttpUtils($this->getUrlGenerator());
$handler = new SamlAuthenticationSuccessHandler($httpUtils, ['always_use_default_target_path' => false]);
$loginPath = $httpUtils->generateUri($this->getRequest('/sso/login'), $this->getOption($handler, 'login_path', '/login'));
$handler = new SamlAuthenticationSuccessHandler($httpUtils);
$loginPath = $httpUtils->generateUri($this->getRequest('/sso/login'), '/login');
$response = $handler->onAuthenticationSuccess($this->getRequest($loginPath), $this->getSamlToken());
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertTrue(!$response->isRedirect($loginPath));
}
private function getUrlGenerator()
private function getUrlGenerator(): UrlGeneratorInterface
{
$urlGenerator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock();
$urlGenerator = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock();
$urlGenerator
->expects($this->any())
->method('generate')
@@ -74,7 +65,7 @@ class SamlAuthenticationSuccessHandlerTest extends TestCase
return $urlGenerator;
}
private function getRequest($path = '/', $relayState = null)
private function getRequest(string $path = '/', ?string $relayState = null): Request
{
$params = [];
if (null !== $relayState) {
@@ -84,7 +75,7 @@ class SamlAuthenticationSuccessHandlerTest extends TestCase
return Request::create($path, 'get', $params);
}
private function getSamlToken()
private function getSamlToken(): SamlToken
{
$user = new User();
$user->setUserIdentifier('admin');
@@ -94,17 +85,4 @@ class SamlAuthenticationSuccessHandlerTest extends TestCase
return $token;
}
private function getOption($handler, $name, $default = null)
{
$reflection = new \ReflectionObject($handler);
$options = $reflection->getProperty('options');
$options->setAccessible(true);
$arr = $options->getValue($handler);
if (!\is_array($arr) || !isset($arr[$name])) {
return $default;
}
return $arr[$name];
}
}

View File

@@ -599,11 +599,6 @@ parameters:
count: 2
path: Controller/Auth/SamlControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Auth\\\\SamlControllerTest\\:\\:getDefaultSettings\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Auth/SamlControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Auth\\\\SamlControllerTest\\:\\:getSystemConfigurationMock\\(\\) has parameter \\$loaderSettings with no value type specified in iterable type array\\.$#"
count: 1
@@ -2364,51 +2359,6 @@ parameters:
count: 1
path: Saml/SamlProviderTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getOption\\(\\) has no return type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getOption\\(\\) has parameter \\$default with no type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getOption\\(\\) has parameter \\$handler with no type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getOption\\(\\) has parameter \\$name with no type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getRequest\\(\\) has no return type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getRequest\\(\\) has parameter \\$path with no type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getRequest\\(\\) has parameter \\$relayState with no type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getSamlToken\\(\\) has no return type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Saml\\\\Security\\\\SamlAuthenticationSuccessHandlerTest\\:\\:getUrlGenerator\\(\\) has no return type specified\\.$#"
count: 1
path: Saml/Security/SamlAuthenticationSuccessHandlerTest.php
-
message: "#^Method App\\\\Tests\\\\Timesheet\\\\Calculator\\\\BillableCalculatorTest\\:\\:getTestData\\(\\) has no return type specified\\.$#"
count: 1