LDAP authentication support (#815)
This commit is contained in:
@@ -21,11 +21,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
*/
|
||||
abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
{
|
||||
/**
|
||||
* @param string $role
|
||||
* @return Client
|
||||
*/
|
||||
protected function getClientForAuthenticatedUser(string $role = User::ROLE_USER)
|
||||
protected function getClientForAuthenticatedUser(string $role = User::ROLE_USER): Client
|
||||
{
|
||||
switch ($role) {
|
||||
case User::ROLE_SUPER_ADMIN:
|
||||
@@ -57,7 +53,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
break;
|
||||
|
||||
default:
|
||||
$client = null;
|
||||
throw new \Exception(sprintf('Unknown role "%s"', $role));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -74,11 +70,6 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
return '/' . ltrim($url, '/') . ($json ? '.json' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertRequestIsSecured(Client $client, string $url, $method = 'GET')
|
||||
{
|
||||
$this->request($client, $url, $method);
|
||||
@@ -134,26 +125,13 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @param string $content
|
||||
* @return Crawler
|
||||
*/
|
||||
protected function request(Client $client, string $url, $method = 'GET', array $parameters = [], string $content = null)
|
||||
protected function request(Client $client, string $url, $method = 'GET', array $parameters = [], string $content = null): Crawler
|
||||
{
|
||||
$server = ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'];
|
||||
|
||||
return $client->request($method, $this->createUrl($url), $parameters, [], $server, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertEntityNotFound(string $role, string $url, string $method = 'GET')
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
@@ -172,11 +150,6 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
*/
|
||||
protected function assertEntityNotFoundForPatch(string $role, string $url, array $data)
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
@@ -198,11 +171,6 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
*/
|
||||
protected function assertEntityNotFoundForDelete(string $role, string $url, array $data)
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
@@ -224,10 +192,6 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
* @param string $message
|
||||
*/
|
||||
protected function assertApiException(Response $response, string $message)
|
||||
{
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
@@ -235,22 +199,12 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
$this->assertEquals(['code' => 500, 'message' => $message], json_decode($response->getContent(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
* @param string $message
|
||||
*/
|
||||
protected function assertApiAccessDenied(Client $client, string $url, string $message)
|
||||
{
|
||||
$this->request($client, $url);
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
* @param string $message
|
||||
*/
|
||||
protected function assertApiResponseAccessDenied(Response $response, string $message)
|
||||
{
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
namespace App\Tests\API\Model;
|
||||
|
||||
use App\API\Model\I18n;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Command\InstallCommand;
|
||||
use App\Utils\File;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,7 @@ class InstallCommandTest extends KernelTestCase
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
protected function getCommand($permission = 0777): InstallCommand
|
||||
protected function getCommand($permission = 0777): Command
|
||||
{
|
||||
$fileMock = $this->getMockBuilder(File::class)->setMethods(['getPermissions'])->getMock();
|
||||
$fileMock->expects($this->exactly(5))->method('getPermissions')->willReturn($permission);
|
||||
|
||||
52
tests/Configuration/LdapConfigurationTest.php
Normal file
52
tests/Configuration/LdapConfigurationTest.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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\Configuration;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\LdapConfiguration
|
||||
*/
|
||||
class LdapConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings)
|
||||
{
|
||||
return new LdapConfiguration($settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'host' => '1.2.3.4',
|
||||
],
|
||||
'user' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
'role' => [
|
||||
'bar' => 'foo',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testMapping()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertTrue($sut->isActivated());
|
||||
$this->assertEquals(['foo' => 'bar'], $sut->getUserParameters());
|
||||
$this->assertEquals(['bar' => 'foo'], $sut->getRoleParameters());
|
||||
$this->assertEquals(['host' => '1.2.3.4'], $sut->getConnectionParameters());
|
||||
|
||||
$sut = $this->getSut(['active' => false]);
|
||||
$this->assertFalse($sut->isActivated());
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
$this->assertAccessIsGranted($client, '/admin/activity/create');
|
||||
$form = $client->getCrawler()->filter('form[name=activity_edit_form]')->form();
|
||||
$this->assertTrue($form->has('activity_edit_form[create_more]'));
|
||||
$this->assertNull($form->get('activity_edit_form[create_more]')->getValue());
|
||||
$this->assertFalse($form->get('activity_edit_form[create_more]')->hasValue());
|
||||
$client->submit($form, [
|
||||
'activity_edit_form' => [
|
||||
'name' => 'An AcTiVitY Name',
|
||||
@@ -87,6 +87,7 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$form = $client->getCrawler()->filter('form[name=activity_edit_form]')->form();
|
||||
$this->assertTrue($form->has('activity_edit_form[create_more]'));
|
||||
$this->assertTrue($form->get('activity_edit_form[create_more]')->hasValue());
|
||||
$this->assertEquals(1, $form->get('activity_edit_form[create_more]')->getValue());
|
||||
$this->assertEquals($selectedProject, $form->get('activity_edit_form[project]')->getValue());
|
||||
}
|
||||
|
||||
@@ -95,12 +95,13 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertRequestIsSecured(Client $client, string $url, $method = 'GET')
|
||||
protected function assertRequestIsSecured(Client $client, string $url, ?string $method = 'GET')
|
||||
{
|
||||
$this->request($client, $url, $method);
|
||||
|
||||
/* @var RedirectResponse $response */
|
||||
/** @var RedirectResponse $response */
|
||||
$response = $client->getResponse();
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
|
||||
$this->assertTrue(
|
||||
$response->isRedirect(),
|
||||
@@ -153,32 +154,19 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param $url
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
*/
|
||||
protected function assertAccessIsGranted(Client $client, $url, $method = 'GET', array $parameters = [])
|
||||
protected function assertAccessIsGranted(Client $client, string $url, string $method = 'GET', array $parameters = [])
|
||||
{
|
||||
$this->request($client, $url, $method, $parameters);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
*/
|
||||
protected function assertRouteNotFound(Client $client)
|
||||
{
|
||||
$this->assertFalse($client->getResponse()->isSuccessful());
|
||||
$this->assertEquals(404, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $classname
|
||||
*/
|
||||
protected function assertMainContentClass(Client $client, $classname)
|
||||
protected function assertMainContentClass(Client $client, string $classname)
|
||||
{
|
||||
$this->assertContains('<section class="content ' . $classname . '">', $client->getResponse()->getContent());
|
||||
}
|
||||
@@ -279,19 +267,11 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
$this->assertContains($message, $node->text());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string|null $message
|
||||
*/
|
||||
protected function assertHasFlashDeleteSuccess(Client $client)
|
||||
{
|
||||
$this->assertHasFlashSuccess($client, 'Entry was deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string|null $message
|
||||
*/
|
||||
protected function assertHasFlashSaveSuccess(Client $client)
|
||||
{
|
||||
$this->assertHasFlashSuccess($client, 'Saved changes');
|
||||
|
||||
@@ -39,7 +39,7 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->assertAccessIsGranted($client, '/admin/project/create');
|
||||
$form = $client->getCrawler()->filter('form[name=project_edit_form]')->form();
|
||||
$this->assertTrue($form->has('project_edit_form[create_more]'));
|
||||
$this->assertNull($form->get('project_edit_form[create_more]')->getValue());
|
||||
$this->assertFalse($form->get('project_edit_form[create_more]')->hasValue());
|
||||
$client->submit($form, [
|
||||
'project_edit_form' => [
|
||||
'name' => 'Test 2',
|
||||
@@ -80,6 +80,7 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$form = $client->getCrawler()->filter('form[name=project_edit_form]')->form();
|
||||
$this->assertTrue($form->has('project_edit_form[create_more]'));
|
||||
$this->assertTrue($form->get('project_edit_form[create_more]')->hasValue());
|
||||
$this->assertEquals(1, $form->get('project_edit_form[create_more]')->getValue());
|
||||
$this->assertEquals($selectedCustomer, $form->get('project_edit_form[customer]')->getValue());
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
|
||||
foreach ($result as $item) {
|
||||
$this->assertContains('btn btn-default', $item->getAttribute('class'));
|
||||
$this->assertEquals('i', $item->firstChild->tagName);
|
||||
/** @var \DOMElement $domElement */
|
||||
$domElement = $item->firstChild;
|
||||
$this->assertEquals('i', $domElement->tagName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
|
||||
foreach ($result as $item) {
|
||||
$this->assertContains('btn btn-default', $item->getAttribute('class'));
|
||||
$this->assertEquals('i', $item->firstChild->tagName);
|
||||
/** @var \DOMElement $domElement */
|
||||
$domElement = $item->firstChild;
|
||||
$this->assertEquals('i', $domElement->tagName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ class UserControllerTest extends ControllerBaseTest
|
||||
$form = $client->getCrawler()->filter('form[name=user_create]')->form();
|
||||
$this->assertTrue($form->has('user_create[create_more]'));
|
||||
$this->assertFalse($form->get('user_create[create_more]')->hasValue());
|
||||
$this->assertNull($form->get('user_create[create_more]')->getValue());
|
||||
$client->submit($form, [
|
||||
'user_create' => [
|
||||
'username' => $username,
|
||||
@@ -86,6 +85,7 @@ class UserControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$form = $client->getCrawler()->filter('form[name=user_create]')->form();
|
||||
$this->assertTrue($form->has('user_create[create_more]'));
|
||||
$this->assertTrue($form->get('user_create[create_more]')->hasValue());
|
||||
$this->assertEquals(1, $form->get('user_create[create_more]')->getValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +54,7 @@ class TagFixtures extends Fixture
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tagName
|
||||
* @return Tag
|
||||
*/
|
||||
protected function createTagEntry($tagName)
|
||||
protected function createTagEntry(string $tagName): Tag
|
||||
{
|
||||
$tagObject = new Tag();
|
||||
$tagObject->setName($tagName);
|
||||
|
||||
@@ -58,7 +58,7 @@ class TimesheetFixtures extends Fixture
|
||||
*/
|
||||
protected $allowEmptyDescriptions = true;
|
||||
/**
|
||||
* @var int
|
||||
* @var bool
|
||||
*/
|
||||
protected $exported = false;
|
||||
/**
|
||||
@@ -265,11 +265,7 @@ class TimesheetFixtures extends Fixture
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cnt
|
||||
* @return array
|
||||
*/
|
||||
protected function getTagObjectList($cnt)
|
||||
protected function getTagObjectList(int $cnt): array
|
||||
{
|
||||
if (true === $this->useTags) {
|
||||
$tagObject = new Tag();
|
||||
@@ -281,11 +277,7 @@ class TimesheetFixtures extends Fixture
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $i
|
||||
* @return bool|\DateTime
|
||||
*/
|
||||
protected function getDateTime($i)
|
||||
protected function getDateTime(int $i): \DateTime
|
||||
{
|
||||
$start = \DateTime::createFromFormat('Y-m-d', $this->startDate);
|
||||
$start->modify("+ $i days");
|
||||
|
||||
316
tests/DependencyInjection/AppExtensionTest.php
Normal file
316
tests/DependencyInjection/AppExtensionTest.php
Normal file
@@ -0,0 +1,316 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use App\DependencyInjection\AppExtension;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @covers \App\DependencyInjection\AppExtension
|
||||
*/
|
||||
class AppExtensionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var AppExtension
|
||||
*/
|
||||
private $extension;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->extension = new AppExtension();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContainerBuilder
|
||||
*/
|
||||
private function getContainer()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
protected function getMinConfig()
|
||||
{
|
||||
return [
|
||||
'kimai' => [
|
||||
'data_dir' => '/tmp/',
|
||||
'plugin_dir' => '/tmp/',
|
||||
'timesheet' => [],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function testDefaultValues()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$expected = [
|
||||
'kimai.data_dir' => '/tmp/',
|
||||
'kimai.plugin_dir' => '/tmp/',
|
||||
'kimai.languages' => [],
|
||||
'kimai.calendar' => [
|
||||
'week_numbers' => true,
|
||||
'day_limit' => 4,
|
||||
'businessHours' => [
|
||||
'days' => [1, 2, 3, 4, 5],
|
||||
'begin' => '08:00',
|
||||
'end' => '20:00',
|
||||
],
|
||||
'visibleHours' => [
|
||||
'begin' => '00:00',
|
||||
'end' => '24:00',
|
||||
],
|
||||
'google' => [
|
||||
'api_key' => null,
|
||||
'sources' => [],
|
||||
],
|
||||
'weekends' => true
|
||||
],
|
||||
'kimai.dashboard' => [],
|
||||
'kimai.widgets' => [],
|
||||
'kimai.invoice.documents' => [
|
||||
'var/invoices/',
|
||||
'templates/invoice/renderer/',
|
||||
],
|
||||
'kimai.defaults' => [
|
||||
'customer' => [
|
||||
'timezone' => 'Europe/Berlin',
|
||||
'country' => 'DE',
|
||||
'currency' => 'EUR',
|
||||
]
|
||||
],
|
||||
|
||||
'kimai.theme' => [
|
||||
'active_warning' => 3,
|
||||
'box_color' => 'green',
|
||||
'select_type' => null,
|
||||
'show_about' => true,
|
||||
],
|
||||
'kimai.theme.select_type' => null,
|
||||
'kimai.theme.show_about' => true,
|
||||
|
||||
'kimai.fosuser' => [
|
||||
'registration' => true,
|
||||
'password_reset' => true,
|
||||
],
|
||||
|
||||
'kimai.timesheet' => [
|
||||
'mode' => 'default',
|
||||
'markdown_content' => false,
|
||||
'rounding' => [],
|
||||
'rates' => [],
|
||||
'active_entries' => [
|
||||
'soft_limit' => 1,
|
||||
'hard_limit' => 1,
|
||||
],
|
||||
'rules' => [
|
||||
'allow_future_times' => true,
|
||||
],
|
||||
],
|
||||
'kimai.timesheet.rates' => [],
|
||||
'kimai.timesheet.rounding' => [],
|
||||
'kimai.ldap' => [
|
||||
'active' => false,
|
||||
'user' => [
|
||||
'baseDn' => null,
|
||||
'filter' => '',
|
||||
'usernameAttribute' => 'uid',
|
||||
'attributes' => [],
|
||||
],
|
||||
'role' => [
|
||||
'baseDn' => null,
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [],
|
||||
'usernameAttribute' => 'dn',
|
||||
],
|
||||
'connection' => [
|
||||
'baseDn' => null,
|
||||
'host' => null,
|
||||
'port' => 389,
|
||||
'useStartTls' => false,
|
||||
'useSsl' => false,
|
||||
'bindRequiresDn' => true,
|
||||
'accountFilterFormat' => '(&(uid=%s))',
|
||||
],
|
||||
],
|
||||
'kimai.permissions' => [
|
||||
'ROLE_USER' => [],
|
||||
'ROLE_TEAMLEAD' => [],
|
||||
'ROLE_ADMIN' => [],
|
||||
'ROLE_SUPER_ADMIN' => [],
|
||||
],
|
||||
];
|
||||
|
||||
// nasty parameter, should be removed!!!
|
||||
$this->assertTrue($container->hasParameter('kimai.config'));
|
||||
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertTrue($container->hasParameter($key), 'Could not find config: ' . $key);
|
||||
$this->assertEquals($value, $container->getParameter($key), 'Invalid config: ' . $key);
|
||||
}
|
||||
}
|
||||
|
||||
public function testAdditionalAuthenticationRoutes()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$adminLte = [
|
||||
'adminlte_registration' => 'foo',
|
||||
'adminlte_password_reset' => 'bar',
|
||||
];
|
||||
|
||||
$container = $this->getContainer();
|
||||
$container->setParameter('admin_lte_theme.routes', $adminLte);
|
||||
|
||||
$this->extension->load($minConfig, $container);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'adminlte_registration' => 'foo',
|
||||
'adminlte_password_reset' => 'bar',
|
||||
],
|
||||
$container->getParameter('admin_lte_theme.routes')
|
||||
);
|
||||
}
|
||||
|
||||
public function testDeactivateAdditionalAuthenticationRoutes()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['user'] = [
|
||||
'registration' => false,
|
||||
'password_reset' => false,
|
||||
];
|
||||
$adminLte = [
|
||||
'adminlte_registration' => 'foo',
|
||||
'adminlte_password_reset' => 'bar',
|
||||
];
|
||||
|
||||
$container = $this->getContainer();
|
||||
$container->setParameter('admin_lte_theme.routes', $adminLte);
|
||||
|
||||
$this->extension->load($minConfig, $container);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'adminlte_registration' => null,
|
||||
'adminlte_password_reset' => null,
|
||||
],
|
||||
$container->getParameter('admin_lte_theme.routes')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PHPUnit\Framework\Error\Notice
|
||||
* @expectedExceptionMessage Found ambiguous configuration. Please remove "kimai.timesheet.duration_only" and set "kimai.timesheet.mode" instead.
|
||||
* @expectedDeprecation Configuration "kimai.timesheet.duration_only" is deprecated, please remove it
|
||||
* @group legacy
|
||||
*/
|
||||
public function testDurationOnlyDeprecationIsTriggered()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['timesheet']['duration_only'] = true;
|
||||
$minConfig['kimai']['timesheet']['mode'] = 'punch';
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
}
|
||||
|
||||
public function testLdapDefaultValues()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'host' => '9.9.9.9',
|
||||
'baseDn' => 'lkhiuzhkj',
|
||||
'accountFilterFormat' => '(uid=%s)'
|
||||
],
|
||||
'user' => [
|
||||
'baseDn' => '123123123',
|
||||
'usernameAttribute' => 'xxx',
|
||||
'filter' => '(..........)'
|
||||
],
|
||||
];
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$ldapConfig = $container->getParameter('kimai.ldap');
|
||||
$this->assertEquals('123123123', $ldapConfig['user']['baseDn']);
|
||||
$this->assertEquals('(..........)', $ldapConfig['user']['filter']);
|
||||
$this->assertEquals('xxx', $ldapConfig['user']['usernameAttribute']);
|
||||
$this->assertEquals('lkhiuzhkj', $ldapConfig['connection']['baseDn']);
|
||||
$this->assertEquals('(uid=%s)', $ldapConfig['connection']['accountFilterFormat']);
|
||||
}
|
||||
|
||||
public function testLdapFallbackValue()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'host' => '9.9.9.9',
|
||||
],
|
||||
'user' => [
|
||||
'baseDn' => '123123123',
|
||||
'usernameAttribute' => 'xxx',
|
||||
],
|
||||
];
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$ldapConfig = $container->getParameter('kimai.ldap');
|
||||
$this->assertEquals('123123123', $ldapConfig['user']['baseDn']);
|
||||
$this->assertEquals('xxx', $ldapConfig['user']['usernameAttribute']);
|
||||
$this->assertEquals('123123123', $ldapConfig['connection']['baseDn']);
|
||||
$this->assertEquals('(&(xxx=%s))', $ldapConfig['connection']['accountFilterFormat']);
|
||||
$this->assertEquals('', $ldapConfig['user']['filter']);
|
||||
}
|
||||
|
||||
public function testLdapMoreFallbackValue()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'host' => '9.9.9.9',
|
||||
'baseDn' => '7658765',
|
||||
],
|
||||
'user' => [
|
||||
'baseDn' => '123123123',
|
||||
'usernameAttribute' => 'zzzz',
|
||||
'filter' => '(&(objectClass=inetOrgPerson))',
|
||||
],
|
||||
];
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$ldapConfig = $container->getParameter('kimai.ldap');
|
||||
$this->assertEquals('123123123', $ldapConfig['user']['baseDn']);
|
||||
$this->assertEquals('zzzz', $ldapConfig['user']['usernameAttribute']);
|
||||
$this->assertEquals('7658765', $ldapConfig['connection']['baseDn']);
|
||||
$this->assertEquals('(&(&(objectClass=inetOrgPerson))(zzzz=%s))', $ldapConfig['connection']['accountFilterFormat']);
|
||||
$this->assertEquals('(&(objectClass=inetOrgPerson))', $ldapConfig['user']['filter']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PHPUnit\Framework\Error\Notice
|
||||
* @expectedExceptionMessage Found invalid "kimai" configuration: The child node "data_dir" at path "kimai" must be configured.
|
||||
*/
|
||||
public function testInvalidConfiguration()
|
||||
{
|
||||
$this->extension->load([], $container = $this->getContainer());
|
||||
}
|
||||
|
||||
// TODO test permissions
|
||||
}
|
||||
246
tests/DependencyInjection/ConfigurationTest.php
Normal file
246
tests/DependencyInjection/ConfigurationTest.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use App\DependencyInjection\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\DependencyInjection\Configuration
|
||||
*/
|
||||
class ConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getMinConfig($dataDir = '/tmp/', $pluginDir = '/tmp/')
|
||||
{
|
||||
return [
|
||||
'data_dir' => $dataDir,
|
||||
'plugin_dir' => $pluginDir,
|
||||
'timesheet' => [],
|
||||
];
|
||||
}
|
||||
|
||||
protected function assertConfig($inputConfig, $expectedConfig)
|
||||
{
|
||||
$finalizedConfig = $this->getCompiledConfig($inputConfig);
|
||||
|
||||
self::assertEquals($expectedConfig, $finalizedConfig);
|
||||
}
|
||||
|
||||
protected function getCompiledConfig($inputConfig)
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
|
||||
$node = $configuration->getConfigTreeBuilder()->buildTree();
|
||||
$normalizedConfig = $node->normalize($inputConfig);
|
||||
|
||||
return $node->finalize($normalizedConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.data_dir": Data directory does not exist
|
||||
*/
|
||||
public function testValidateDataDir()
|
||||
{
|
||||
$this->assertConfig($this->getMinConfig('sdfsdfsdfds'), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.plugin_dir": Plugin directory does not exist
|
||||
*/
|
||||
public function testValidatePluginDir()
|
||||
{
|
||||
$this->assertConfig($this->getMinConfig('/tmp/', 'sdfsdfs'), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap": The "ldap.user.baseDn" config must be set if LDAP is activated.
|
||||
*/
|
||||
public function testValidateLdapConfigUserBaseDn()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'host' => 'foo'
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap": The "ldap.connection.host" config must be set if LDAP is activated.
|
||||
*/
|
||||
public function testValidateLdapConfigConnectionHost()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.connection": The ldap.connection.useSsl and ldap.connection.useStartTls options are mutually exclusive.
|
||||
*/
|
||||
public function testValidateLdapConfig()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => false,
|
||||
'connection' => [
|
||||
'useSsl' => true,
|
||||
'useStartTls' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.user.filter": The ldap.user.filter must be enclosed by a matching number of parentheses "()" and must NOT contain a "%s" replacer
|
||||
*/
|
||||
public function testValidateLdapFilterIncludingReplacer()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'user' => [
|
||||
'filter' => '(sdfsdfsdf)(uid=%s)',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.user.filter": The ldap.user.filter must be enclosed by a matching number of parentheses "()" and must NOT contain a "%s" replacer
|
||||
*/
|
||||
public function testValidateLdapFilterMissingStartingParenthesis()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'user' => [
|
||||
'filter' => 's(dfsdfsdf)',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.user.filter": The ldap.user.filter must be enclosed by a matching number of parentheses "()" and must NOT contain a "%s" replacer
|
||||
*/
|
||||
public function testValidateLdapFilterInvalidParenthesisCounter()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'user' => [
|
||||
'filter' => '(dfsdfsdf))',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.connection.accountFilterFormat": The accountFilterFormat must be enclosed by a matching number of parentheses "()" and contain one "%s" replacer for the username
|
||||
*/
|
||||
public function testValidateLdapAccountFilterFormatMissingUserAttributeReplacer()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => false,
|
||||
'connection' => [
|
||||
'accountFilterFormat' => '(sdfsdfsdf)(uid=xx)',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.connection.accountFilterFormat": The accountFilterFormat must be enclosed by a matching number of parentheses "()" and contain one "%s" replacer for the username
|
||||
*/
|
||||
public function testValidateLdapAccountFilterFormatMissingStartingParenthesis()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'accountFilterFormat' => 's(dfsdfsdf)',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid configuration for path "kimai.ldap.connection.accountFilterFormat": The accountFilterFormat must be enclosed by a matching number of parentheses "()" and contain one "%s" replacer for the username
|
||||
*/
|
||||
public function testValidateLdapAccountFilterFormatInvalidParenthesisCounter()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['ldap'] = [
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'accountFilterFormat' => '(dfsdfsdf))',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($config, []);
|
||||
}
|
||||
|
||||
public function testDefaultLdapSettings()
|
||||
{
|
||||
$finalizedConfig = $this->getCompiledConfig($this->getMinConfig());
|
||||
$expected = [
|
||||
'active' => false,
|
||||
'user' => [
|
||||
'baseDn' => '',
|
||||
'filter' => '',
|
||||
'usernameAttribute' => 'uid',
|
||||
'attributes' => [],
|
||||
],
|
||||
'role' => [
|
||||
'baseDn' => null,
|
||||
'usernameAttribute' => 'dn',
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [],
|
||||
],
|
||||
'connection' => [
|
||||
'host' => null,
|
||||
'port' => 389,
|
||||
'useStartTls' => false,
|
||||
'useSsl' => false,
|
||||
'bindRequiresDn' => true,
|
||||
'accountFilterFormat' => '',
|
||||
]
|
||||
];
|
||||
self::assertEquals($expected, $finalizedConfig['ldap']);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\EventSubscriber;
|
||||
namespace App\Tests\Doctrine;
|
||||
|
||||
use App\Doctrine\SqliteSessionInitSubscriber;
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
@@ -18,10 +18,10 @@ use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
abstract class AbstractEntityTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @param $entity
|
||||
* @param object $entity
|
||||
* @param array|string $fieldNames
|
||||
*/
|
||||
protected function assertHasViolationForField($entity, $fieldNames)
|
||||
protected function assertHasViolationForField(object $entity, $fieldNames)
|
||||
{
|
||||
self::bootKernel();
|
||||
$validator = static::$kernel->getContainer()->get('validator');
|
||||
|
||||
@@ -24,7 +24,7 @@ class ActivityTest extends AbstractEntityTest
|
||||
$this->assertNull($sut->getName());
|
||||
$this->assertNull($sut->getComment());
|
||||
$this->assertTrue($sut->getVisible());
|
||||
// timesheets
|
||||
self::assertIsIterable($sut->getTimesheets());
|
||||
$this->assertNull($sut->getFixedRate());
|
||||
$this->assertNull($sut->getHourlyRate());
|
||||
$this->assertNull($sut->getColor());
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Tests\Entity;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
|
||||
/**
|
||||
* @covers \App\Entity\Customer
|
||||
@@ -24,7 +23,8 @@ class CustomerTest extends AbstractEntityTest
|
||||
$this->assertNull($sut->getName());
|
||||
$this->assertNull($sut->getNumber());
|
||||
$this->assertNull($sut->getComment());
|
||||
// projects
|
||||
self::assertIsIterable($sut->getProjects());
|
||||
self::assertEmpty($sut->getProjects());
|
||||
$this->assertTrue($sut->getVisible());
|
||||
|
||||
$this->assertNull($sut->getCompany());
|
||||
@@ -61,10 +61,6 @@ class CustomerTest extends AbstractEntityTest
|
||||
$this->assertInstanceOf(Customer::class, $sut->setColor('#fffccc'));
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
|
||||
$projects = [(new Project())->setName('Test')];
|
||||
$this->assertInstanceOf(Customer::class, $sut->setProjects($projects));
|
||||
$this->assertSame($projects, $sut->getProjects());
|
||||
|
||||
$this->assertInstanceOf(Customer::class, $sut->setCompany('test company'));
|
||||
$this->assertEquals('test company', $sut->getCompany());
|
||||
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
|
||||
namespace App\Tests\Entity;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Timesheet;
|
||||
|
||||
/**
|
||||
* @covers \App\Entity\Project
|
||||
@@ -29,10 +27,12 @@ class ProjectTest extends AbstractEntityTest
|
||||
$this->assertNull($sut->getComment());
|
||||
$this->assertTrue($sut->getVisible());
|
||||
$this->assertEquals(0.0, $sut->getBudget());
|
||||
// activities
|
||||
$this->assertNull($sut->getFixedRate());
|
||||
$this->assertNull($sut->getHourlyRate());
|
||||
$this->assertNull($sut->getTimesheets());
|
||||
self::assertIsIterable($sut->getTimesheets());
|
||||
self::assertEmpty($sut->getTimesheets());
|
||||
self::assertIsIterable($sut->getActivities());
|
||||
self::assertEmpty($sut->getActivities());
|
||||
$this->assertNull($sut->getColor());
|
||||
}
|
||||
|
||||
@@ -62,17 +62,9 @@ class ProjectTest extends AbstractEntityTest
|
||||
$this->assertInstanceOf(Project::class, $sut->setBudget(12345.67));
|
||||
$this->assertEquals(12345.67, $sut->getBudget());
|
||||
|
||||
$activities = [(new Activity())->setName('foo')];
|
||||
$this->assertInstanceOf(Project::class, $sut->setActivities($activities));
|
||||
$this->assertSame($activities, $sut->getActivities());
|
||||
|
||||
$this->assertInstanceOf(Project::class, $sut->setFixedRate(13.47));
|
||||
$this->assertEquals(13.47, $sut->getFixedRate());
|
||||
$this->assertInstanceOf(Project::class, $sut->setHourlyRate(99));
|
||||
$this->assertEquals(99, $sut->getHourlyRate());
|
||||
|
||||
$timesheets = [(new Timesheet())->setDescription('foo'), (new Timesheet())->setDescription('bar')];
|
||||
$this->assertInstanceOf(Project::class, $sut->setTimesheets($timesheets));
|
||||
$this->assertSame($timesheets, $sut->getTimesheets());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,13 @@ class UserTest extends AbstractEntityTest
|
||||
$user->addPreference($preference);
|
||||
$this->assertEquals('foobar', $user->getPreferenceValue('test', 'foo'));
|
||||
$this->assertEquals($preference, $user->getPreference('test'));
|
||||
|
||||
$user->setPreferenceValue('test', 'Hello World');
|
||||
$this->assertEquals('Hello World', $user->getPreferenceValue('test', 'foo'));
|
||||
|
||||
$this->assertNull($user->getPreferenceValue('test2'));
|
||||
$user->setPreferenceValue('test2', 'I like rain');
|
||||
$this->assertEquals('I like rain', $user->getPreferenceValue('test2'));
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\EventSubscriber;
|
||||
namespace App\Tests\Event;
|
||||
|
||||
use App\Event\ConfigureAdminMenuEvent;
|
||||
use KevinPapst\AdminLTEBundle\Model\MenuItemModel;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\EventSubscriber;
|
||||
namespace App\Tests\Event;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Event\DashboardEvent;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\EventSubscriber;
|
||||
namespace App\Tests\Event;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Event\ThemeEvent;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\EventSubscriber;
|
||||
namespace App\Tests\Event;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserPreference;
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Export\Renderer;
|
||||
namespace App\Tests\Form\DataTransformer;
|
||||
|
||||
use App\Entity\Tag;
|
||||
use App\Form\DataTransformer\TagArrayToStringTransformer;
|
||||
use App\Repository\TagRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Form\DataTransformer\TagArrayToStringTransformer
|
||||
*/
|
||||
class TagArrayToStringTransformerTest extends AbstractRendererTest
|
||||
class TagArrayToStringTransformerTest extends TestCase
|
||||
{
|
||||
public function testTransform()
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Invoice\Calculator;
|
||||
namespace App\Tests\Invoice\NumberGenerator;
|
||||
|
||||
use App\Invoice\NumberGenerator\DateNumberGenerator;
|
||||
use App\Model\InvoiceModel;
|
||||
|
||||
@@ -190,11 +190,7 @@ abstract class AbstractRendererTest extends KernelTestCase
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Timesheet[] $timesheets
|
||||
* @return InvoiceModel
|
||||
*/
|
||||
protected function getInvoiceModelOneEntry()
|
||||
protected function getInvoiceModelOneEntry(): InvoiceModel
|
||||
{
|
||||
$customer = new Customer();
|
||||
$customer->setCurrency('USD');
|
||||
|
||||
@@ -54,7 +54,7 @@ class DebugRenderer implements RendererInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param mixed $amount
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedMoney($amount)
|
||||
@@ -72,7 +72,7 @@ class DebugRenderer implements RendererInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $seconds
|
||||
* @param mixed $seconds
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedDuration($seconds)
|
||||
|
||||
50
tests/Ldap/FormLoginLdapFactoryTest.php
Normal file
50
tests/Ldap/FormLoginLdapFactoryTest.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Ldap\FormLoginLdapFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\FormLoginLdapFactory
|
||||
*/
|
||||
class FormLoginLdapFactoryTest extends TestCase
|
||||
{
|
||||
public function testStaticValues()
|
||||
{
|
||||
$sut = new FormLoginLdapFactory();
|
||||
self::assertEquals('kimai_ldap', $sut->getKey());
|
||||
self::assertEquals('pre_auth', $sut->getPosition());
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$sut = new FormLoginLdapFactory();
|
||||
$result = $sut->create($container, 'test', ['foo' => 'bar'], 'fosuserbundle', 'secured_area');
|
||||
|
||||
self::assertEquals([
|
||||
'kimai_ldap.security.authentication.provider.test',
|
||||
'security.authentication.listener.form.test',
|
||||
'secured_area'
|
||||
], $result);
|
||||
|
||||
$definition = $container->getDefinition('kimai_ldap.security.authentication.provider.test');
|
||||
self::assertInstanceOf(ChildDefinition::class, $definition);
|
||||
self::assertEquals('test', $definition->getArguments()['index_1']);
|
||||
|
||||
$definition = $container->getDefinition('security.authentication.listener.form.test');
|
||||
self::assertInstanceOf(ChildDefinition::class, $definition);
|
||||
self::assertEquals('test', $definition->getArguments()['index_4']);
|
||||
self::assertEquals(['foo' => 'bar'], $definition->getArguments()['index_5']);
|
||||
}
|
||||
}
|
||||
238
tests/Ldap/LdapAuthenticationProviderTest.php
Normal file
238
tests/Ldap/LdapAuthenticationProviderTest.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Ldap\LdapAuthenticationProvider;
|
||||
use App\Ldap\LdapManager;
|
||||
use App\Ldap\LdapUserProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserChecker;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\LdapAuthenticationProvider
|
||||
*/
|
||||
class LdapAuthenticationProviderTest extends TestCase
|
||||
{
|
||||
public function testDeactivatedSupports()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => false]);
|
||||
$userProvider = new LdapUserProvider($manager, $config);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'bar', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$result = $sut->supports($token);
|
||||
self::assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||
* @expectedExceptionMessage The token is not supported by this authentication provider.
|
||||
*/
|
||||
public function testDeactivatedAuthenticate()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => false]);
|
||||
$userProvider = new LdapUserProvider($manager, $config);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'bar', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$sut->authenticate($token);
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = new LdapUserProvider($manager, $config);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'bar', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$result = $sut->supports($token);
|
||||
self::assertTrue($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
|
||||
* @expectedExceptionMessage The password in the token is empty. Check `erase_credentials` in your `security.yaml`
|
||||
*/
|
||||
public function testAuthenticateWithTokenUserButEnptyPasswordThrowsException()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = new LdapUserProvider($manager, $config);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$user = (new User())->setUsername('foo')->setEnabled(true);
|
||||
$token = new UsernamePasswordToken($user, '', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$actual = $sut->authenticate($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
|
||||
* @expectedExceptionMessage The presented password cannot be empty.
|
||||
*/
|
||||
public function testAuthenticateWithUsernameReturnsUser()
|
||||
{
|
||||
$user = (new User())->setUsername('foo')->setEnabled(true);
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->once())->method('loadUserByUsername')->willReturn($user);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', '', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$actual = $sut->authenticate($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
|
||||
* @expectedExceptionMessage The presented password is invalid.
|
||||
*/
|
||||
public function testAuthenticateWithUsernameThrowsExceptionOnFailedBind()
|
||||
{
|
||||
$user = (new User())->setUsername('foo')->setEnabled(true);
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['bind'])->getMock();
|
||||
$manager->expects($this->once())->method('bind')->willReturn(false);
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->once())->method('loadUserByUsername')->willReturn($user);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'sdfsdf', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$actual = $sut->authenticate($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
|
||||
* @expectedExceptionMessage The credentials were changed from another session.
|
||||
*/
|
||||
public function testAuthenticateWithUserThrowsExceptionOnFailedBind()
|
||||
{
|
||||
$user = (new User())->setUsername('foo')->setEnabled(true);
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['bind'])->getMock();
|
||||
$manager->expects($this->once())->method('bind')->willReturn(false);
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->never())->method('loadUserByUsername');
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken($user, 'sdfsdf', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$actual = $sut->authenticate($token);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithUsernameReturnsUserAndBinds()
|
||||
{
|
||||
$user = (new User())->setUsername('foo')->setEnabled(true);
|
||||
$user->setPreferenceValue('ldap.dn', 'blub');
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['bind', 'updateUser'])->getMock();
|
||||
$manager->expects($this->once())->method('bind')->willReturn(true);
|
||||
$manager->expects($this->once())->method('updateUser')->willReturnCallback(function ($updateUser) use ($user) {
|
||||
self::assertSame($updateUser, $user);
|
||||
});
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->once())->method('loadUserByUsername')->willReturn($user);
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'test', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$token = $sut->authenticate($token);
|
||||
self::assertSame($token->getUser(), $user);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithUserReturnsUserAndBinds()
|
||||
{
|
||||
$user = (new User())->setUsername('foo')->setEnabled(true);
|
||||
$user->setPreferenceValue('ldap.dn', 'blub');
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['bind', 'updateUser'])->getMock();
|
||||
$manager->expects($this->once())->method('bind')->willReturn(true);
|
||||
$manager->expects($this->once())->method('updateUser')->willReturnCallback(function ($updateUser) use ($user) {
|
||||
self::assertSame($updateUser, $user);
|
||||
});
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->never())->method('loadUserByUsername');
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken($user, 'test', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$token = $sut->authenticate($token);
|
||||
self::assertSame($token->getUser(), $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
|
||||
* @expectedExceptionMessage blub foo bar
|
||||
*/
|
||||
public function testAuthenticateThrowsExceptionOnLdapNotFound()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->once())->method('loadUserByUsername')->willThrowException(new UsernameNotFoundException('blub foo bar'));
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'test', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$sut->authenticate($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationServiceException
|
||||
* @expectedExceptionMessage server away
|
||||
* @expectedExceptionCode 1234
|
||||
*/
|
||||
public function testAuthenticateThrowsExceptionOnLdapDown()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
$userProvider = $this->getMockBuilder(LdapUserProvider::class)->disableOriginalConstructor()->setMethods(['loadUserByUsername'])->getMock();
|
||||
$userProvider->expects($this->once())->method('loadUserByUsername')->willThrowException(new \Exception('server away', 1234));
|
||||
$providerKey = 'secured_area';
|
||||
$userChecker = new UserChecker();
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'test', $providerKey);
|
||||
|
||||
$sut = new LdapAuthenticationProvider($userChecker, $providerKey, $userProvider, $manager, $config, false);
|
||||
$sut->authenticate($token);
|
||||
}
|
||||
}
|
||||
28
tests/Ldap/LdapDriverExceptionTest.php
Normal file
28
tests/Ldap/LdapDriverExceptionTest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Ldap\LdapDriverException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\LdapDriverException
|
||||
*/
|
||||
class LdapDriverExceptionTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$sut = new LdapDriverException('Whooops');
|
||||
|
||||
self::assertInstanceOf(\Exception::class, $sut);
|
||||
|
||||
self::assertEquals('Whooops', $sut->getMessage());
|
||||
}
|
||||
}
|
||||
71
tests/Ldap/LdapDriverTest.php
Normal file
71
tests/Ldap/LdapDriverTest.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Ldap\LdapDriver;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Zend\Ldap\Exception\LdapException;
|
||||
use Zend\Ldap\Ldap;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\LdapDriver
|
||||
*/
|
||||
class LdapDriverTest extends TestCase
|
||||
{
|
||||
public function testBindSuccess()
|
||||
{
|
||||
$zendLdap = $this->getMockBuilder(Ldap::class)->disableOriginalConstructor()->setMethods(['bind'])->getMock();
|
||||
$zendLdap->expects($this->once())->method('bind')->willReturnSelf();
|
||||
|
||||
$user = new User();
|
||||
$sut = new LdapDriver($zendLdap);
|
||||
$result = $sut->bind($user, 'test123');
|
||||
self::assertTrue($result);
|
||||
}
|
||||
|
||||
public function testBindException()
|
||||
{
|
||||
$zendLdap = $this->getMockBuilder(Ldap::class)->disableOriginalConstructor()->setMethods(['bind'])->getMock();
|
||||
$zendLdap->expects($this->once())->method('bind')->willThrowException(new LdapException());
|
||||
|
||||
$user = new User();
|
||||
$sut = new LdapDriver($zendLdap);
|
||||
$result = $sut->bind($user, 'test123');
|
||||
self::assertFalse($result);
|
||||
}
|
||||
|
||||
public function testSearchSuccess()
|
||||
{
|
||||
$zendLdap = $this->getMockBuilder(Ldap::class)->disableOriginalConstructor()->setMethods(['bind', 'searchEntries'])->getMock();
|
||||
$zendLdap->expects($this->once())->method('bind');
|
||||
$zendLdap->expects($this->once())->method('searchEntries')->willReturn([1, 2, 3]);
|
||||
|
||||
$sut = new LdapDriver($zendLdap);
|
||||
$result = $sut->search('', '', []);
|
||||
self::assertEquals(['count' => 3, 1, 2, 3], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \App\Ldap\LdapDriverException
|
||||
* @expectedExceptionMessage An error occurred with the search operation.
|
||||
*/
|
||||
public function testSearchException()
|
||||
{
|
||||
$zendLdap = $this->getMockBuilder(Ldap::class)->disableOriginalConstructor()->setMethods(['bind', 'searchEntries'])->getMock();
|
||||
$zendLdap->expects($this->once())->method('bind');
|
||||
$zendLdap->expects($this->once())->method('searchEntries')->willThrowException(
|
||||
new LdapException($zendLdap, '', LdapException::LDAP_SERVER_DOWN)
|
||||
);
|
||||
|
||||
$sut = new LdapDriver($zendLdap);
|
||||
$sut->search('', '', []);
|
||||
}
|
||||
}
|
||||
454
tests/Ldap/LdapManagerTest.php
Normal file
454
tests/Ldap/LdapManagerTest.php
Normal file
@@ -0,0 +1,454 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Ldap\LdapDriver;
|
||||
use App\Ldap\LdapManager;
|
||||
use App\Ldap\LdapUserHydrator;
|
||||
use App\Security\RoleService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\LdapManager
|
||||
*/
|
||||
class LdapManagerTest extends TestCase
|
||||
{
|
||||
protected function getLdapManager(LdapDriver $driver, $roleConfig = null)
|
||||
{
|
||||
if (null === $roleConfig) {
|
||||
$roleConfig = [
|
||||
'baseDn' => 'ou=groups, dc=kimai, dc=org',
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [
|
||||
['ldap_value' => 'group1', 'role' => 'ROLE_TEAMLEAD'],
|
||||
['ldap_value' => 'group2', 'role' => 'ROLE_ADMIN'],
|
||||
['ldap_value' => 'group3', 'role' => 'ROLE_CUSTOMER'], // not existing!
|
||||
['ldap_value' => 'group4', 'role' => 'ROLE_SUPER_ADMIN'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$config = new LdapConfiguration([
|
||||
'user' => [
|
||||
'attributes' => [],
|
||||
'filter' => '(&(objectClass=inetOrgPerson))',
|
||||
'usernameAttribute' => 'uid',
|
||||
'baseDn' => 'ou=users, dc=kimai, dc=org',
|
||||
],
|
||||
'role' => $roleConfig,
|
||||
]);
|
||||
|
||||
$hydrator = new LdapUserHydrator($config, new RoleService([
|
||||
'ROLE_TEAMLEAD' => ['ROLE_USER'],
|
||||
'ROLE_ADMIN' => ['ROLE_TEAMLEAD'],
|
||||
'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN']
|
||||
]));
|
||||
|
||||
return new LdapManager($driver, $hydrator, $config);
|
||||
}
|
||||
|
||||
public function testFindUserByUsernameOnZeroResults()
|
||||
{
|
||||
$expected = [
|
||||
'count' => 0
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->once())->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
self::assertEquals('ou=users, dc=kimai, dc=org', $baseDn);
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foo))', $filter);
|
||||
|
||||
return $expected;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$actual = $sut->findUserByUsername('foo');
|
||||
self::assertNull($actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \App\Ldap\LdapDriverException
|
||||
* @expectedExceptionMessage This search must only return a single user
|
||||
*/
|
||||
public function testFindUserByUsernameOnMultiResults()
|
||||
{
|
||||
$expected = [
|
||||
'count' => 3
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->once())->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
self::assertEquals('ou=users, dc=kimai, dc=org', $baseDn);
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foo))', $filter);
|
||||
|
||||
return $expected;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$sut->findUserByUsername('foo');
|
||||
}
|
||||
|
||||
public function testFindUserByUsernameOnValidResult()
|
||||
{
|
||||
$expected = [
|
||||
0 => ['dn' => 'foo'],
|
||||
'count' => 1,
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->once())->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
self::assertEquals('ou=users, dc=kimai, dc=org', $baseDn);
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foo))', $filter);
|
||||
|
||||
return $expected;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$actual = $sut->findUserByUsername('foo');
|
||||
self::assertInstanceOf(User::class, $actual);
|
||||
}
|
||||
|
||||
public function testFindUserByOnZeroResults()
|
||||
{
|
||||
$expected = [
|
||||
'count' => 0
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->once())->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
self::assertEquals('ou=users, dc=kimai, dc=org', $baseDn);
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foo))', $filter);
|
||||
|
||||
return $expected;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$actual = $sut->findUserBy(['uid' => 'foo']);
|
||||
self::assertNull($actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \App\Ldap\LdapDriverException
|
||||
* @expectedExceptionMessage This search must only return a single user
|
||||
*/
|
||||
public function testFindUserByOnMultiResults()
|
||||
{
|
||||
$expected = [
|
||||
'count' => 3
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->once())->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
self::assertEquals('ou=users, dc=kimai, dc=org', $baseDn);
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foo))', $filter);
|
||||
|
||||
return $expected;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$sut->findUserBy(['uid' => 'foo']);
|
||||
}
|
||||
|
||||
public function testFindUserByOnValidResult()
|
||||
{
|
||||
$expected = [
|
||||
0 => ['dn' => 'foo'],
|
||||
'count' => 1,
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->once())->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
self::assertEquals('ou=users, dc=kimai, dc=org', $baseDn);
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(träl=alß#\\\aa=XY\5cZ0)(test=fu=n))', $filter);
|
||||
|
||||
return $expected;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$actual = $sut->findUserBy(['träl=alß#\\\aa' => 'XY\Z0', 'test' => 'fu=n']);
|
||||
self::assertInstanceOf(User::class, $actual);
|
||||
}
|
||||
|
||||
public function testBind()
|
||||
{
|
||||
$user = (new User())->setUsername('foobar');
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['bind'])->getMock();
|
||||
$driver->expects($this->once())->method('bind')->willReturnCallback(function ($bindUser, $password) use ($user) {
|
||||
self::assertSame($user, $bindUser);
|
||||
self::assertEquals('a-very-secret-secret', $password);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$actual = $sut->bind($user, 'a-very-secret-secret');
|
||||
self::assertTrue($actual);
|
||||
}
|
||||
|
||||
public function testUpdateUserOnZeroResults()
|
||||
{
|
||||
$user = (new User())->setUsername('foobar');
|
||||
$user->setPreferenceValue('ldap.dn', 'fooooooooooo');
|
||||
$expected = [
|
||||
[
|
||||
0 => ['dn' => 'blub'],
|
||||
'count' => 1,
|
||||
],
|
||||
[
|
||||
'count' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->exactly(2))->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
if ($baseDn === 'ou=users, dc=kimai, dc=org') {
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foobar))', $filter);
|
||||
|
||||
return $expected[0];
|
||||
} elseif ($baseDn === 'blub') {
|
||||
self::assertEquals('(objectClass=*)', $filter);
|
||||
|
||||
return $expected[1];
|
||||
}
|
||||
$this->fail(sprintf('Unexpected search with baseDn %s', $baseDn));
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
|
||||
$userOrig = clone $user;
|
||||
$sut->updateUser($user);
|
||||
self::assertEquals($userOrig, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \App\Ldap\LdapDriverException
|
||||
* @expectedExceptionMessage This search must only return a single user
|
||||
*/
|
||||
public function testUpdateUserOnMultiResults()
|
||||
{
|
||||
$user = (new User())->setUsername('foobar');
|
||||
$user->setPreferenceValue('ldap.dn', 'xxxxxxx');
|
||||
|
||||
$expected = [
|
||||
[
|
||||
0 => ['dn' => 'blub'],
|
||||
'count' => 1,
|
||||
],
|
||||
[
|
||||
'count' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->exactly(2))->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
if ($baseDn === 'ou=users, dc=kimai, dc=org') {
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foobar))', $filter);
|
||||
|
||||
return $expected[0];
|
||||
} elseif ($baseDn === 'blub') {
|
||||
self::assertEquals('(objectClass=*)', $filter);
|
||||
|
||||
return $expected[1];
|
||||
}
|
||||
|
||||
$this->fail(sprintf('Unexpected search with baseDn %s', $baseDn));
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver);
|
||||
$sut->updateUser($user);
|
||||
}
|
||||
|
||||
public function testUpdateUserOnValidResultWithEmptyRoleBaseDn()
|
||||
{
|
||||
$user = (new User())->setUsername('foobar');
|
||||
$user->setPreferenceValue('ldap.dn', 'sssssss');
|
||||
|
||||
$expected = [
|
||||
[
|
||||
0 => ['dn' => 'blub'],
|
||||
'count' => 1,
|
||||
],
|
||||
[
|
||||
0 => ['dn' => 'blub-updated'],
|
||||
'count' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->exactly(2))->method('search')->willReturnCallback(function ($baseDn, $filter) use ($expected) {
|
||||
if ($baseDn === 'ou=users, dc=kimai, dc=org') {
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=foobar))', $filter);
|
||||
|
||||
return $expected[0];
|
||||
} elseif ($baseDn === 'blub') {
|
||||
self::assertEquals('(objectClass=*)', $filter);
|
||||
|
||||
return $expected[1];
|
||||
}
|
||||
|
||||
$this->fail(sprintf('Unexpected search with baseDn %s', $baseDn));
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver, [
|
||||
'baseDn' => null,
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [
|
||||
['ldap_value' => 'group1', 'role' => 'ROLE_TEAMLEAD'],
|
||||
['ldap_value' => 'group2', 'role' => 'ROLE_ADMIN'],
|
||||
['ldap_value' => 'group3', 'role' => 'ROLE_CUSTOMER'], // not existing!
|
||||
['ldap_value' => 'group4', 'role' => 'ROLE_SUPER_ADMIN'],
|
||||
],
|
||||
]);
|
||||
|
||||
$userOrig = clone $user;
|
||||
$sut->updateUser($user);
|
||||
self::assertEquals($userOrig->setEmail('foobar'), $user);
|
||||
self::assertEquals($user->getPreferenceValue('ldap.dn'), 'blub-updated');
|
||||
}
|
||||
|
||||
public function getValidConfigsTestData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
[
|
||||
0 => [
|
||||
'dn' => 'blub',
|
||||
'uid' => ['Karl-Heinz'],
|
||||
// just some rubbish data
|
||||
'blub' => ['dfsdfsdf'],
|
||||
'foo' => ['count' => 1, 'bar'],
|
||||
'bar' => ['count' => 1, 'foo', 'xxx'],
|
||||
'xxxxxxxx' => ['https://www.example.com'],
|
||||
'blub1' => ['dfsdfsdf'],
|
||||
],
|
||||
'count' => 1,
|
||||
],
|
||||
[
|
||||
'baseDn' => 'ou=groups, dc=kimai, dc=org',
|
||||
'nameAttribute' => 'cn',
|
||||
'usernameAttribute' => 'cn', // test that "cn" is not set and fallback to "dn" happens
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [
|
||||
['ldap_value' => 'group1', 'role' => 'ROLE_TEAMLEAD'],
|
||||
['ldap_value' => 'group2', 'role' => 'ROLE_ADMIN'],
|
||||
['ldap_value' => 'group3', 'role' => 'ROLE_CUSTOMER'], // not existing!
|
||||
['ldap_value' => 'group4', 'role' => 'ROLE_SUPER_ADMIN'],
|
||||
],
|
||||
],
|
||||
'(&(member=blub))'
|
||||
],
|
||||
[
|
||||
[
|
||||
0 => [
|
||||
'dn' => 'blub',
|
||||
'uid' => ['Karl-Heinz'],
|
||||
// just some rubbish data
|
||||
'blub' => ['dfsdfsdf'],
|
||||
'foo' => ['count' => 1, 'bar'],
|
||||
'bar' => ['count' => 1, 'foo', 'xxx'],
|
||||
'xxxxxxxx' => ['https://www.example.com'],
|
||||
'blub1' => ['dfsdfsdf'],
|
||||
],
|
||||
'count' => 1,
|
||||
],
|
||||
[
|
||||
'baseDn' => 'ou=groups, dc=kimai, dc=org',
|
||||
'nameAttribute' => 'cn',
|
||||
'usernameAttribute' => 'blub1',
|
||||
'userDnAttribute' => 'memberuid',
|
||||
'groups' => [
|
||||
['ldap_value' => 'group1', 'role' => 'ROLE_TEAMLEAD'],
|
||||
['ldap_value' => 'group2', 'role' => 'ROLE_ADMIN'],
|
||||
['ldap_value' => 'group3', 'role' => 'ROLE_CUSTOMER'], // not existing!
|
||||
['ldap_value' => 'group4', 'role' => 'ROLE_SUPER_ADMIN'],
|
||||
],
|
||||
],
|
||||
'(&(memberuid=dfsdfsdf))'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidConfigsTestData
|
||||
*/
|
||||
public function testUpdateUserOnValidResultWithRolesResult(array $expectedUsers, array $groupConfig, string $expectedGroupQuery)
|
||||
{
|
||||
$expected = [
|
||||
0 => ['dn' => 'blub'],
|
||||
'count' => 1,
|
||||
];
|
||||
|
||||
$expectedGroups = [
|
||||
// ROLE_TEAMLEAD
|
||||
0 => [
|
||||
'cn' => [0 => 'group1'],
|
||||
'member' => [0 => 'uid=user1,ou=users,dc=kimai,dc=org', 1 => 'uid=user2,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// ROLE_ADMIN
|
||||
1 => [
|
||||
'cn' => [0 => 'admin'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// will be ignored: unknown group
|
||||
2 => [
|
||||
'cn' => [0 => 'kimai_admin'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// will be ignored: unknown group
|
||||
3 => [
|
||||
'cn' => [0 => 'group3'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// will be ignored: the counter below does not announce this group!
|
||||
4 => [
|
||||
'cn' => [0 => 'group4'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
'count' => 4
|
||||
];
|
||||
|
||||
$driver = $this->getMockBuilder(LdapDriver::class)->disableOriginalConstructor()->setMethods(['search'])->getMock();
|
||||
$driver->expects($this->exactly(3))->method('search')->willReturnCallback(function ($baseDn, $filter, $attributes) use ($expectedUsers, $expectedGroups, $expectedGroupQuery, $expected) {
|
||||
if ($baseDn === 'ou=users, dc=kimai, dc=org') {
|
||||
self::assertEquals('(&(&(objectClass=inetOrgPerson))(uid=Karl-Heinz))', $filter);
|
||||
|
||||
return $expected;
|
||||
} elseif ($baseDn === 'blub') {
|
||||
// user attributes search
|
||||
self::assertEquals('(objectClass=*)', $filter);
|
||||
|
||||
return $expectedUsers;
|
||||
} elseif ($baseDn === 'ou=groups, dc=kimai, dc=org') {
|
||||
// roles search
|
||||
self::assertEquals($expectedGroupQuery, $filter);
|
||||
self::assertEquals([0 => 'cn'], $attributes);
|
||||
|
||||
return $expectedGroups;
|
||||
}
|
||||
$this->fail(sprintf('Unexpected search with baseDn %s', $baseDn));
|
||||
});
|
||||
|
||||
$sut = $this->getLdapManager($driver, $groupConfig);
|
||||
|
||||
$user = (new User())->setUsername('Karl-Heinz');
|
||||
$user->setPreferenceValue('ldap.dn', 'blub');
|
||||
$userOrig = clone $user;
|
||||
$userOrig->setEmail('Karl-Heinz')->setRoles(['ROLE_TEAMLEAD', 'ROLE_ADMIN']);
|
||||
|
||||
$sut->updateUser($user);
|
||||
self::assertEquals($userOrig, $user);
|
||||
self::assertEquals(['ROLE_TEAMLEAD', 'ROLE_ADMIN', 'ROLE_USER'], $user->getRoles());
|
||||
}
|
||||
}
|
||||
189
tests/Ldap/LdapUserHydratorTest.php
Normal file
189
tests/Ldap/LdapUserHydratorTest.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Ldap\LdapUserHydrator;
|
||||
use App\Security\RoleService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\LdapUserHydrator
|
||||
*/
|
||||
class LdapUserHydratorTest extends TestCase
|
||||
{
|
||||
public function testEmptyHydrate()
|
||||
{
|
||||
$config = new LdapConfiguration([
|
||||
'active' => false,
|
||||
'connection' => [
|
||||
'host' => '1.1.1.1'
|
||||
],
|
||||
'user' => [
|
||||
'usernameAttribute' => 'foo',
|
||||
'attributes' => []
|
||||
],
|
||||
'role' => [],
|
||||
]);
|
||||
|
||||
$sut = new LdapUserHydrator($config, new RoleService([]));
|
||||
$user = $sut->hydrate(['dn' => 'blub']);
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertEmpty($user->getUsername());
|
||||
self::assertEmpty($user->getEmail());
|
||||
}
|
||||
|
||||
public function testHydrate()
|
||||
{
|
||||
$config = new LdapConfiguration([
|
||||
'active' => false,
|
||||
'connection' => [
|
||||
'host' => '1.1.1.1'
|
||||
],
|
||||
'user' => [
|
||||
'usernameAttribute' => 'foo',
|
||||
'attributes' => [
|
||||
['ldap_attr' => 'uid', 'user_method' => 'setUsername'],
|
||||
['ldap_attr' => 'foo', 'user_method' => 'setAlias'],
|
||||
['ldap_attr' => 'bar', 'user_method' => 'setTitle'],
|
||||
['ldap_attr' => 'roles', 'user_method' => 'setRoles'],
|
||||
['ldap_attr' => 'xxxxxxxx', 'user_method' => 'setAvatar'],
|
||||
['ldap_attr' => 'blubXX', 'user_method' => 'setAvatar'],
|
||||
]
|
||||
],
|
||||
'role' => [],
|
||||
]);
|
||||
|
||||
$ldapEntry = [
|
||||
'uid' => ['Karl-Heinz'],
|
||||
'blub' => ['dfsdfsdf'],
|
||||
'foo' => ['count' => 1, 0 => 'bar'],
|
||||
'bar' => ['foo'],
|
||||
'roles' => ['count' => 2, 0 => 'ROLE_TEAMLEAD', 1 => 'ROLE_ADMIN'],
|
||||
'xxxxxxxx' => ['https://www.example.com'],
|
||||
'blub1' => ['dfsdfsdf'],
|
||||
'dn' => 'blub',
|
||||
];
|
||||
|
||||
$sut = new LdapUserHydrator($config, new RoleService([]));
|
||||
$user = $sut->hydrate($ldapEntry);
|
||||
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertEquals('Karl-Heinz', $user->getUsername());
|
||||
self::assertEquals('bar', $user->getAlias());
|
||||
self::assertEquals('foo', $user->getTitle());
|
||||
self::assertEquals(['ROLE_TEAMLEAD', 'ROLE_ADMIN', 'ROLE_USER'], $user->getRoles());
|
||||
self::assertEquals('https://www.example.com', $user->getAvatar());
|
||||
self::assertEquals('Karl-Heinz', $user->getEmail());
|
||||
}
|
||||
|
||||
public function testHydrateUser()
|
||||
{
|
||||
$config = new LdapConfiguration([
|
||||
'active' => false,
|
||||
'connection' => [
|
||||
'host' => '1.1.1.1'
|
||||
],
|
||||
'user' => [
|
||||
'usernameAttribute' => 'foo',
|
||||
'attributes' => [
|
||||
['ldap_attr' => 'uid', 'user_method' => 'setUsername'],
|
||||
['ldap_attr' => 'foo', 'user_method' => 'setAlias'],
|
||||
['ldap_attr' => 'bar', 'user_method' => 'setTitle'],
|
||||
['ldap_attr' => 'xxxxxxxx', 'user_method' => 'setAvatar'],
|
||||
]
|
||||
],
|
||||
'role' => [],
|
||||
]);
|
||||
|
||||
$ldapEntry = [
|
||||
'uid' => ['Karl-Heinz'],
|
||||
'blub' => ['dfsdfsdf'],
|
||||
'foo' => ['bar'],
|
||||
'bar' => ['foo'],
|
||||
'xxxxxxxx' => ['https://www.example.com'],
|
||||
'blub1' => ['dfsdfsdf'],
|
||||
'dn' => 'blub',
|
||||
];
|
||||
|
||||
$sut = new LdapUserHydrator($config, new RoleService([]));
|
||||
$user = new User();
|
||||
$user->setPassword('foobar');
|
||||
$sut->hydrateUser($user, $ldapEntry);
|
||||
self::assertEquals('Karl-Heinz', $user->getUsername());
|
||||
self::assertEquals('bar', $user->getAlias());
|
||||
self::assertEquals('foo', $user->getTitle());
|
||||
self::assertEquals('https://www.example.com', $user->getAvatar());
|
||||
self::assertEquals('Karl-Heinz', $user->getEmail());
|
||||
|
||||
// make sure that the password was resetted in hydrate
|
||||
$pwdCheck = clone $user;
|
||||
$pwdCheck->setPassword('');
|
||||
self::assertEquals($pwdCheck, $user);
|
||||
}
|
||||
|
||||
public function testHydrateRoles()
|
||||
{
|
||||
$config = new LdapConfiguration([
|
||||
'user' => [
|
||||
'attributes' => []
|
||||
],
|
||||
'role' => [
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [
|
||||
['ldap_value' => 'group1', 'role' => 'ROLE_TEAMLEAD'],
|
||||
['ldap_value' => 'group2', 'role' => 'ROLE_ADMIN'],
|
||||
['ldap_value' => 'group3', 'role' => 'ROLE_CUSTOMER'], // not existing!
|
||||
['ldap_value' => 'group4', 'role' => 'ROLE_SUPER_ADMIN'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$ldapGroups = [
|
||||
// ROLE_TEAMLEAD
|
||||
0 => [
|
||||
'cn' => [0 => 'group1'],
|
||||
'member' => [0 => 'uid=user1,ou=users,dc=kimai,dc=org', 1 => 'uid=user2,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// ROLE_ADMIN
|
||||
1 => [
|
||||
'cn' => [0 => 'admin'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// will be ignored: unknown group
|
||||
2 => [
|
||||
'cn' => [0 => 'kimai_admin'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// will be ignored: unknown group
|
||||
3 => [
|
||||
'cn' => [0 => 'group3'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
// will be ignored: the counter below does not announce this group!
|
||||
4 => [
|
||||
'cn' => [0 => 'group4'],
|
||||
'member' => [0 => 'uid=user2,ou=users,dc=kimai,dc=org', 1 => 'uid=user3,ou=users,dc=kimai,dc=org'],
|
||||
],
|
||||
'count' => 4
|
||||
];
|
||||
|
||||
$sut = new LdapUserHydrator($config, new RoleService([
|
||||
'ROLE_TEAMLEAD' => ['ROLE_USER'],
|
||||
'ROLE_ADMIN' => ['ROLE_TEAMLEAD'],
|
||||
'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN']
|
||||
]));
|
||||
$user = new User();
|
||||
$sut->hydrateRoles($user, $ldapGroups);
|
||||
self::assertEquals(['ROLE_TEAMLEAD', 'ROLE_ADMIN', 'ROLE_USER'], $user->getRoles());
|
||||
}
|
||||
}
|
||||
102
tests/Ldap/LdapUserProviderTest.php
Normal file
102
tests/Ldap/LdapUserProviderTest.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Ldap\LdapManager;
|
||||
use App\Ldap\LdapUserProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\LdapUserProvider
|
||||
*/
|
||||
class LdapUserProviderTest extends TestCase
|
||||
{
|
||||
public function testDeactivatedSupportsClass()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => false]);
|
||||
|
||||
$sut = new LdapUserProvider($manager, $config);
|
||||
self::assertFalse($sut->supportsClass(User::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
|
||||
* @expectedExceptionMessage LDAP is deactivated, user "test" not searched
|
||||
*/
|
||||
public function testDeactivatedLoadUserByUsername()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => false]);
|
||||
|
||||
$sut = new LdapUserProvider($manager, $config);
|
||||
$sut->loadUserByUsername('test');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\UnsupportedUserException
|
||||
* @expectedExceptionMessage Instances of "App\Entity\User" are not supported.
|
||||
*/
|
||||
public function testDeactivatedRefreshUser()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->getMock();
|
||||
$config = new LdapConfiguration(['active' => false]);
|
||||
|
||||
$sut = new LdapUserProvider($manager, $config);
|
||||
$sut->refreshUser(new User());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
|
||||
* @expectedExceptionMessage User "test" not found
|
||||
*/
|
||||
public function testLoadUserByUsernameReturnsNull()
|
||||
{
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['findUserByUsername'])->getMock();
|
||||
$manager->expects($this->once())->method('findUserByUsername')->willReturn(null);
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
|
||||
$sut = new LdapUserProvider($manager, $config);
|
||||
$sut->loadUserByUsername('test');
|
||||
}
|
||||
|
||||
public function testLoadUserByUsernameReturnsUser()
|
||||
{
|
||||
$user = new User();
|
||||
$user->setUsername('foobar');
|
||||
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['findUserByUsername'])->getMock();
|
||||
$manager->expects($this->once())->method('findUserByUsername')->willReturn($user);
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
|
||||
$sut = new LdapUserProvider($manager, $config);
|
||||
$actual = $sut->loadUserByUsername('test');
|
||||
self::assertInstanceOf(User::class, $actual);
|
||||
self::assertSame($user, $actual);
|
||||
}
|
||||
|
||||
public function testRefreshUserReturnsUser()
|
||||
{
|
||||
$user = new User();
|
||||
$user->setUsername('foobar');
|
||||
$user->setPreferenceValue('ldap.dn', 'sdfdsf');
|
||||
|
||||
$manager = $this->getMockBuilder(LdapManager::class)->disableOriginalConstructor()->setMethods(['updateUser'])->getMock();
|
||||
$config = new LdapConfiguration(['active' => true]);
|
||||
|
||||
$sut = new LdapUserProvider($manager, $config);
|
||||
$actual = $sut->refreshUser($user);
|
||||
|
||||
self::assertInstanceOf(User::class, $actual);
|
||||
self::assertSame($user, $actual);
|
||||
}
|
||||
}
|
||||
31
tests/Ldap/SanitizingExceptionTest.php
Normal file
31
tests/Ldap/SanitizingExceptionTest.php
Normal 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\Ldap;
|
||||
|
||||
use App\Ldap\SanitizingException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\SanitizingException
|
||||
*/
|
||||
class SanitizingExceptionTest extends TestCase
|
||||
{
|
||||
public function testMessagesAreSanitized()
|
||||
{
|
||||
$ex = new \Exception('Could not find user foo with password bar in your LDAP');
|
||||
$sut = new SanitizingException($ex, 'bar');
|
||||
|
||||
self::assertInstanceOf(\Exception::class, $sut);
|
||||
|
||||
self::assertNotContains('bar', $sut->getMessage());
|
||||
self::assertNotContains('bar', (string) $sut);
|
||||
self::assertEquals('Could not find user foo with password **** in your LDAP', $sut->getMessage());
|
||||
}
|
||||
}
|
||||
48
tests/Ldap/ZendLdapTest.php
Normal file
48
tests/Ldap/ZendLdapTest.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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\Ldap;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use App\Ldap\ZendLdap;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Ldap\ZendLdap
|
||||
*/
|
||||
class ZendLdapTest extends TestCase
|
||||
{
|
||||
public function testConstructDeactivated()
|
||||
{
|
||||
$config = new LdapConfiguration([
|
||||
'active' => false,
|
||||
'connection' => [
|
||||
'host' => '1.1.1.1'
|
||||
]
|
||||
]);
|
||||
|
||||
$sut = new ZendLdap($config);
|
||||
$options = $sut->getOptions();
|
||||
self::assertNull($options['host']);
|
||||
}
|
||||
|
||||
public function testConstructActivatedPassesOptions()
|
||||
{
|
||||
$config = new LdapConfiguration([
|
||||
'active' => true,
|
||||
'connection' => [
|
||||
'host' => '1.1.1.1'
|
||||
]
|
||||
]);
|
||||
|
||||
$sut = new ZendLdap($config);
|
||||
$options = $sut->getOptions();
|
||||
self::assertEquals('1.1.1.1', $options['host']);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ abstract class AbstractRepositoryTest extends KernelTestCase
|
||||
use KernelTestTrait;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
* @var EntityManager|null
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Tests\Repository;
|
||||
|
||||
use App\Entity\Tag;
|
||||
use App\Repository\TagRepository;
|
||||
use App\Tests\DataFixtures\TagFixtures;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,7 @@ class TagRepositoryTest extends AbstractRepositoryTest
|
||||
public function testFindIds()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var TagRepository $repository */
|
||||
$repository = $em->getRepository(Tag::class);
|
||||
|
||||
$result = $repository->findIdsByTagNameList('2018,Test');
|
||||
@@ -47,6 +49,7 @@ class TagRepositoryTest extends AbstractRepositoryTest
|
||||
public function testFindNoIds()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var TagRepository $repository */
|
||||
$repository = $em->getRepository(Tag::class);
|
||||
|
||||
$result = $repository->findIdsByTagNameList('Simply');
|
||||
@@ -58,6 +61,7 @@ class TagRepositoryTest extends AbstractRepositoryTest
|
||||
public function testFindAllTagNames()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var TagRepository $repository */
|
||||
$repository = $em->getRepository(Tag::class);
|
||||
|
||||
$result = $repository->findAllTagNames('2018');
|
||||
@@ -75,6 +79,7 @@ class TagRepositoryTest extends AbstractRepositoryTest
|
||||
public function testFindNoTagNames()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var TagRepository $repository */
|
||||
$repository = $em->getRepository(Tag::class);
|
||||
|
||||
$result = $repository->findAllTagNames('Nothing');
|
||||
|
||||
@@ -14,9 +14,12 @@ use App\Entity\Project;
|
||||
use App\Entity\Tag;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Repository\RepositoryException;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
@@ -29,6 +32,7 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
public function testResultTypeForQueryState()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var TimesheetRepository $repository */
|
||||
$repository = $em->getRepository(Timesheet::class);
|
||||
|
||||
$query = new TimesheetQuery();
|
||||
@@ -53,6 +57,7 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole($em, User::ROLE_USER);
|
||||
/** @var TimesheetRepository $repository */
|
||||
$repository = $em->getRepository(Timesheet::class);
|
||||
|
||||
$fixtures = new TimesheetFixtures();
|
||||
@@ -66,6 +71,7 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
$query->setUser($user);
|
||||
$query->setState(TimesheetQuery::STATE_STOPPED);
|
||||
|
||||
/** @var array $entities */
|
||||
$entities = $repository->findByQuery($query);
|
||||
|
||||
$this->assertCount(1, $entities);
|
||||
@@ -81,6 +87,7 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole($em, User::ROLE_USER);
|
||||
/** @var TimesheetRepository $repository */
|
||||
$repository = $em->getRepository(Timesheet::class);
|
||||
|
||||
$fixtures = new TimesheetFixtures();
|
||||
@@ -100,12 +107,15 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
public function testSave()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var ActivityRepository $activityRepository */
|
||||
$activityRepository = $em->getRepository(Activity::class);
|
||||
$activity = $activityRepository->find(1);
|
||||
/** @var ProjectRepository $projectRepository */
|
||||
$projectRepository = $em->getRepository(Project::class);
|
||||
$project = $projectRepository->find(1);
|
||||
|
||||
$user = $this->getUserByRole($em, User::ROLE_USER);
|
||||
/** @var TimesheetRepository $repository */
|
||||
$repository = $em->getRepository(Timesheet::class);
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet
|
||||
@@ -124,12 +134,15 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
public function testSaveWithTags()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
/** @var ActivityRepository $activityRepository */
|
||||
$activityRepository = $em->getRepository(Activity::class);
|
||||
$activity = $activityRepository->find(1);
|
||||
/** @var ProjectRepository $projectRepository */
|
||||
$projectRepository = $em->getRepository(Project::class);
|
||||
$project = $projectRepository->find(1);
|
||||
|
||||
$user = $this->getUserByRole($em, User::ROLE_USER);
|
||||
/** @var TimesheetRepository $repository */
|
||||
$repository = $em->getRepository(Timesheet::class);
|
||||
$tagOne = new Tag();
|
||||
$tagOne->setName('Travel');
|
||||
|
||||
34
tests/Security/RoleServiceTest.php
Normal file
34
tests/Security/RoleServiceTest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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\Security;
|
||||
|
||||
use App\Security\RoleService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Security\RoleService
|
||||
*/
|
||||
class RoleServiceTest extends TestCase
|
||||
{
|
||||
public function testGetAvailableNames()
|
||||
{
|
||||
$real = [
|
||||
'ROLE_TEAMLEAD' => [0 => 'ROLE_USER'],
|
||||
'ROLE_ADMIN' => [0 => 'ROLE_TEAMLEAD'],
|
||||
'ROLE_SUPER_ADMIN' => [0 => 'ROLE_ADMIN']
|
||||
];
|
||||
|
||||
$sut = new RoleService($real);
|
||||
|
||||
$expected = ['ROLE_TEAMLEAD', 'ROLE_USER', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN'];
|
||||
|
||||
self::assertEquals($expected, $sut->getAvailableNames());
|
||||
}
|
||||
}
|
||||
@@ -7,51 +7,57 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Model;
|
||||
namespace App\Tests\Security;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Security\UserChecker;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\User as SymfonyUser;
|
||||
|
||||
/**
|
||||
* @covers \App\Security\UserChecker
|
||||
*/
|
||||
class UserCheckerTest extends TestCase
|
||||
{
|
||||
public function testCheckPreAuth()
|
||||
public function testCheckPreAuthReturnsOnUnknownUserClass()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
$user = new User();
|
||||
|
||||
try {
|
||||
$sut->checkPreAuth($user);
|
||||
$sut->checkPreAuth(new SymfonyUser('sdf', null));
|
||||
} catch (\Exception $ex) {
|
||||
$this->fail('UserChecker should not throw exception in checkPreAuth()');
|
||||
$this->fail('UserChecker should not throw exception in checkPreAuth(), ' . $ex->getMessage());
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCheckPostAuthReturnsOnUnknownUserClass()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
|
||||
try {
|
||||
$sut->checkPostAuth(new SymfonyUser('sdf', null));
|
||||
} catch (\Exception $ex) {
|
||||
$this->fail('UserChecker should not throw exception in checkPostAuth(), ' . $ex->getMessage());
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\LockedException
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\DisabledException
|
||||
* @expectedExceptionMessage User account is disabled.
|
||||
*/
|
||||
public function testDisabledCannotLogin()
|
||||
public function testDisabledCannotLoginInCheckPreAuth()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
$user = new User();
|
||||
$user->setEnabled(false);
|
||||
|
||||
$sut->checkPostAuth($user);
|
||||
(new UserChecker())->checkPreAuth((new User())->setEnabled(false));
|
||||
}
|
||||
|
||||
public function testCheckPostAuth()
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\DisabledException
|
||||
* @expectedExceptionMessage User account is disabled.
|
||||
*/
|
||||
public function testDisabledCannotLoginInCheckPostAuth()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
|
||||
$mock = $this->getMockBuilder(UserInterface::class)->setMethods(['isEnabled'])->getMockForAbstractClass();
|
||||
$mock->expects($this->never())->method('isEnabled')->willReturn(false);
|
||||
|
||||
$sut->checkPostAuth($mock);
|
||||
$this->assertTrue(true);
|
||||
(new UserChecker())->checkPostAuth((new User())->setEnabled(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Timesheet\Calculator;
|
||||
namespace App\Tests\Timesheet\Rounding;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Timesheet\Rounding\CeilRounding;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Timesheet\Calculator;
|
||||
namespace App\Tests\Timesheet\Rounding;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Timesheet\Rounding\ClosestRounding;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Timesheet\Calculator;
|
||||
namespace App\Tests\Timesheet\Rounding;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Timesheet\Rounding\DefaultRounding;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Timesheet\Calculator;
|
||||
namespace App\Tests\Timesheet\Rounding;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Timesheet\Rounding\FloorRounding;
|
||||
|
||||
@@ -242,7 +242,6 @@ class ExtensionsTest extends TestCase
|
||||
'timesheet.html#duration-format' => 'https://www.kimai.org/documentation/timesheet.html#duration-format',
|
||||
'invoice.html' => 'https://www.kimai.org/documentation/invoice.html',
|
||||
'' => 'https://www.kimai.org/documentation/',
|
||||
null => 'https://www.kimai.org/documentation/',
|
||||
];
|
||||
|
||||
$sut = $this->getSut($this->localeEn);
|
||||
|
||||
@@ -28,18 +28,18 @@ abstract class AbstractVoterTest extends TestCase
|
||||
$isAuthenticated = empty($user->getRoles());
|
||||
$accessManager = $this->getMockBuilder(AclDecisionManager::class)->disableOriginalConstructor()->getMock();
|
||||
$accessManager->method('isFullyAuthenticated')->willReturn($isAuthenticated);
|
||||
$accessManager->method('hasRole')->willReturnCallback(function ($role) use ($user) {
|
||||
return in_array($role, $user->getRoles());
|
||||
});
|
||||
|
||||
$class = new \ReflectionClass($voterClass);
|
||||
/** @var AbstractVoter $voter */
|
||||
$voter = $class->newInstance($accessManager, $this->getRolePermissionManager());
|
||||
self::assertInstanceOf(AbstractVoter::class, $voter);
|
||||
|
||||
return $class->newInstance($accessManager, $this->getRolePermissionManager());
|
||||
return $voter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $role
|
||||
* @param int $id
|
||||
* @param string $role
|
||||
* @return User
|
||||
*/
|
||||
protected function getUser($id, $role)
|
||||
|
||||
@@ -148,8 +148,8 @@ class TimesheetVoterTest extends AbstractVoterTest
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $role
|
||||
* @param int $id
|
||||
* @param string $role
|
||||
* @return User
|
||||
*/
|
||||
protected function getUser($id, $role)
|
||||
|
||||
10
tests/phpstan.neon
Normal file
10
tests/phpstan.neon
Normal file
@@ -0,0 +1,10 @@
|
||||
includes:
|
||||
- %rootDir%/../phpstan-symfony/extension.neon
|
||||
- %rootDir%/../phpstan-doctrine/extension.neon
|
||||
- %rootDir%/../phpstan-phpunit/extension.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
- '#Access to an undefined property Faker\\Generator::\$stateAbbr.#'
|
||||
- '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'
|
||||
- '#Access to an undefined property Faker\\Generator::\$bs.#'
|
||||
Reference in New Issue
Block a user