From b733ca169e240636b479c1e388d81aa7a8913338 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 6 Apr 2023 09:35:54 +0200 Subject: [PATCH 01/11] fix: update target php version --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2c763a..ad13cc0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - php: ["7.4"] + php: ["8.2"] steps: - uses: actions/checkout@v2 From fe124bb658286c8606a0d3fe8968b6864731d4c6 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 6 Apr 2023 09:40:01 +0200 Subject: [PATCH 02/11] fix: nodejs deprecation notice --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad13cc0..a6b30b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-20.04] php: ["8.2"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create config files run: | From d1cd1da67fdd512c401561497a4e4c96ce623582 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 6 Apr 2023 09:43:40 +0200 Subject: [PATCH 03/11] chg: [temp] run actions in this branch --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a6b30b8..1d39de6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: test on: push: - branches: [main, develop] + branches: [main, develop, fix-test-action] pull_request: branches: [main, develop] From 9b7c693bb9c037fd2b663e06d451847a1136d286 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 6 Apr 2023 17:59:22 +0200 Subject: [PATCH 04/11] fix: fix some deprecation notices in 8.2 --- config/app.php | 1 - plugins/Tags/src/View/Helper/TagHelper.php | 2 +- src/Command/ImporterCommand.php | 2 +- src/Controller/Component/CRUDComponent.php | 6 +++--- src/Controller/Component/RestResponseComponent.php | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/config/app.php b/config/app.php index 8faea7d..480c342 100644 --- a/config/app.php +++ b/config/app.php @@ -178,7 +178,6 @@ return [ */ 'Error' => [ 'errorLevel' => E_ALL, - 'exceptionRenderer' => ExceptionRenderer::class, 'skipLog' => [], 'log' => true, 'trace' => true, diff --git a/plugins/Tags/src/View/Helper/TagHelper.php b/plugins/Tags/src/View/Helper/TagHelper.php index 405c513..7cb1411 100644 --- a/plugins/Tags/src/View/Helper/TagHelper.php +++ b/plugins/Tags/src/View/Helper/TagHelper.php @@ -106,7 +106,7 @@ class TagHelper extends Helper $deleteButton = $this->Bootstrap->button([ 'size' => 'sm', 'icon' => 'times', - 'class' => ['ms-1', 'border-0', "text-${textColour}"], + 'class' => ['ms-1', 'border-0', "text-$textColour"], 'variant' => 'text', 'title' => __('Delete tag'), 'onclick' => sprintf('deleteTag(\'%s\', \'%s\', this)', diff --git a/src/Command/ImporterCommand.php b/src/Command/ImporterCommand.php index dee4e24..02c4164 100644 --- a/src/Command/ImporterCommand.php +++ b/src/Command/ImporterCommand.php @@ -136,7 +136,7 @@ class ImporterCommand extends Command $entity = null; if (isset($item[$primary_key])) { $query = $table->find('all') - ->where(["${primary_key}" => $item[$primary_key]]); + ->where(["$primary_key" => $item[$primary_key]]); $entity = $query->first(); } if (is_null($entity)) { diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index d56609c..96855da 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -1465,7 +1465,7 @@ class CRUDComponent extends Component { $prefixedConditions = []; foreach ($conditions as $condField => $condValue) { - $prefixedConditions["${prefix}.${condField}"] = $condValue; + $prefixedConditions["$prefix.$condField"] = $condValue; } return $prefixedConditions; } @@ -1589,13 +1589,13 @@ class CRUDComponent extends Component [sprintf('%s.id = %s.%s', $this->Table->getAlias(), $associatedTable->getAlias(), $association->getForeignKey())] ) ->where([ - ["${field} IS NOT" => NULL] + ["$field IS NOT" => NULL] ]); } else if ($associationType == 'manyToOne') { $fieldToExtract = sprintf('%s.%s', Inflector::singularize(strtolower($model)), $subField); $query = $this->Table->find()->contain($model); } else { - throw new Exception("Association ${associationType} not supported in CRUD Component"); + throw new Exception("Association $associationType not supported in CRUD Component"); } } else { $fieldToExtract = $field; diff --git a/src/Controller/Component/RestResponseComponent.php b/src/Controller/Component/RestResponseComponent.php index 8da9128..19a522b 100644 --- a/src/Controller/Component/RestResponseComponent.php +++ b/src/Controller/Component/RestResponseComponent.php @@ -390,7 +390,7 @@ class RestResponseComponent extends Component return '[]'; } - public function saveFailResponse($controller, $action, $id = false, $validationErrors, $format = false) + public function saveFailResponse($controller, $action, $id, $validationErrors, $format = false) { $this->autoRender = false; $response = array(); From 4ebdbf8ba3f20094f90fe11bcd58b0d913c96e45 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 6 Apr 2023 18:00:46 +0200 Subject: [PATCH 05/11] fix: fix most of the tests --- tests/Fixture/BroodsFixture.php | 2 +- .../Fixture/MetaTemplateDirectoryFixture.php | 30 +++++++++++++++++++ tests/Fixture/MetaTemplateFieldsFixture.php | 1 + tests/Fixture/MetaTemplatesFixture.php | 2 ++ tests/Helper/WireMockTestTrait.php | 14 +++++---- .../LocalTools/MispInterConnectionTest.php | 18 +++++------ .../TestCase/Api/Users/DeleteUserApiTest.php | 2 ++ tests/TestCase/ApplicationTest.php | 3 +- 8 files changed, 54 insertions(+), 18 deletions(-) create mode 100644 tests/Fixture/MetaTemplateDirectoryFixture.php diff --git a/tests/Fixture/BroodsFixture.php b/tests/Fixture/BroodsFixture.php index c329e6a..7cc26a1 100644 --- a/tests/Fixture/BroodsFixture.php +++ b/tests/Fixture/BroodsFixture.php @@ -56,7 +56,7 @@ class BroodsFixture extends TestFixture 'id' => self::BROOD_WIREMOCK_ID, 'uuid' => $faker->uuid(), 'name' => 'wiremock', - 'url' => 'http://localhost:8080', + 'url' => sprintf('http://%s:%s', $_ENV['WIREMOCK_HOST'] ?? 'localhost', $_ENV['WIREMOCK_PORT'] ?? '8080'), 'description' => $faker->text, 'organisation_id' => OrganisationsFixture::ORGANISATION_B_ID, 'trusted' => true, diff --git a/tests/Fixture/MetaTemplateDirectoryFixture.php b/tests/Fixture/MetaTemplateDirectoryFixture.php new file mode 100644 index 0000000..04c6242 --- /dev/null +++ b/tests/Fixture/MetaTemplateDirectoryFixture.php @@ -0,0 +1,30 @@ +records = [ + [ + 'id' => self::META_TEMPLATE_DIRECTORY_ID, + 'uuid' => self::META_TEMPLATE_DIRECTORY_UUID, + 'name' => 'Test Meta Template Directory', + 'namespace' => 'cerebrate', + 'version' => '1' + ] + ]; + + parent::init(); + } +} diff --git a/tests/Fixture/MetaTemplateFieldsFixture.php b/tests/Fixture/MetaTemplateFieldsFixture.php index 141a7aa..ff38317 100644 --- a/tests/Fixture/MetaTemplateFieldsFixture.php +++ b/tests/Fixture/MetaTemplateFieldsFixture.php @@ -17,6 +17,7 @@ class MetaTemplateFieldsFixture extends TestFixture 'field' => 'test_field_1', 'type' => 'text', 'meta_template_id' => MetaTemplatesFixture::ENABLED_TEST_ORG_META_TEMPLATE_ID, + 'meta_template_directory_id' => MetaTemplateDirectoryFixture::META_TEMPLATE_DIRECTORY_ID, 'regex' => null, 'multiple' => 1, 'enabled' => 1, diff --git a/tests/Fixture/MetaTemplatesFixture.php b/tests/Fixture/MetaTemplatesFixture.php index cbe96a2..6eed2bb 100644 --- a/tests/Fixture/MetaTemplatesFixture.php +++ b/tests/Fixture/MetaTemplatesFixture.php @@ -44,6 +44,7 @@ class MetaTemplatesFixture extends TestFixture $this->records = [ [ 'id' => self::ENABLED_TEST_ORG_META_TEMPLATE_ID, + 'meta_template_directory_id' => MetaTemplateDirectoryFixture::META_TEMPLATE_DIRECTORY_ID, 'scope' => 'organisation', 'name' => 'Test Meta Template (enabled)', 'namespace' => 'cerebrate', @@ -58,6 +59,7 @@ class MetaTemplatesFixture extends TestFixture ], [ 'id' => self::DISABLED_TEST_ORG_META_TEMPLATE_ID, + 'meta_template_directory_id' => MetaTemplateDirectoryFixture::META_TEMPLATE_DIRECTORY_ID, 'scope' => 'organisation', 'name' => 'Test Meta Template (disabled)', 'namespace' => 'cerebrate', diff --git a/tests/Helper/WireMockTestTrait.php b/tests/Helper/WireMockTestTrait.php index 41aea61..69d33c5 100644 --- a/tests/Helper/WireMockTestTrait.php +++ b/tests/Helper/WireMockTestTrait.php @@ -15,16 +15,18 @@ trait WireMockTestTrait private $wiremock; /** @var array */ - private $config = [ - 'hostname' => 'localhost', - 'port' => 8080 - ]; + private $config; public function initializeWireMock(): void { + $this->config = [ + 'hostname' => $_ENV['WIREMOCK_HOST'] ?? 'localhost', + 'port' => $_ENV['WIREMOCK_PORT'] ?? 8080 + ]; + $this->wiremock = WireMock::create( - $_ENV['WIREMOCK_HOST'] ?? $this->config['hostname'], - $_ENV['WIREMOCK_PORT'] ?? $this->config['port'] + $this->config['hostname'], + $this->config['port'] ); if (!$this->wiremock->isAlive()) { diff --git a/tests/TestCase/Api/LocalTools/MispInterConnectionTest.php b/tests/TestCase/Api/LocalTools/MispInterConnectionTest.php index 7f643d3..e81c4ec 100644 --- a/tests/TestCase/Api/LocalTools/MispInterConnectionTest.php +++ b/tests/TestCase/Api/LocalTools/MispInterConnectionTest.php @@ -31,20 +31,20 @@ class MispInterConnectionTest extends TestCase ]; /** constants related to the local Cerebrate instance */ - private const LOCAL_CEREBRATE_URL = 'http://127.0.0.1'; + private const LOCAL_CEREBRATE_URL = 'http://localhost'; /** constants related to the local MISP instance */ - private const LOCAL_MISP_INSTANCE_URL = 'http://localhost:8080/MISP_LOCAL'; + private const LOCAL_MISP_INSTANCE_URL = '/MISP_LOCAL'; private const LOCAL_MISP_ADMIN_USER_AUTHKEY = 'b17ce79ac0f05916f382ab06ea4790665dbc174c'; /** constants related to the remote Cerebrate instance */ - private const REMOTE_CEREBRATE_URL = 'http://127.0.0.1:8080/CEREBRATE_REMOTE'; + private const REMOTE_CEREBRATE_URL = '/CEREBRATE_REMOTE'; private const REMOTE_CEREBRATE_AUTHKEY = 'a192ba3c749b545f9cec6b6bba0643736f6c3022'; /** constants related to the remote MISP instance */ private const REMOTE_MISP_SYNC_USER_ID = 333; private const REMOTE_MISP_SYNC_USER_EMAIL = 'sync@misp.remote'; - private const REMOTE_MISP_INSTANCE_URL = 'http://localhost:8080/MISP_REMOTE'; + private const REMOTE_MISP_INSTANCE_URL = '/MISP_REMOTE'; private const REMOTE_MISP_AUTHKEY = '19ca57ecebd2fe34c1c17d729980678eb648d541'; @@ -64,7 +64,7 @@ class MispInterConnectionTest extends TestCase 'name' => 'MISP_LOCAL', 'connector' => 'MispConnector', 'settings' => json_encode([ - 'url' => self::LOCAL_MISP_INSTANCE_URL, + 'url' => $this->getWireMockBaseUrl() . self::LOCAL_MISP_INSTANCE_URL, 'authkey' => self::LOCAL_MISP_ADMIN_USER_AUTHKEY, 'skip_ssl' => true, ]), @@ -90,7 +90,7 @@ class MispInterConnectionTest extends TestCase [ 'uuid' => $LOCAL_BROOD_UUID, 'name' => 'Local Brood', - 'url' => self::REMOTE_CEREBRATE_URL, + 'url' => $this->getWireMockBaseUrl() . self::REMOTE_CEREBRATE_URL, 'description' => $faker->text, 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, 'trusted' => true, @@ -228,10 +228,10 @@ class MispInterConnectionTest extends TestCase [ 'email' => self::REMOTE_MISP_SYNC_USER_EMAIL, 'authkey' => self::REMOTE_MISP_AUTHKEY, - 'url' => self::REMOTE_MISP_INSTANCE_URL, + 'url' => $this->getWireMockBaseUrl() . self::REMOTE_MISP_INSTANCE_URL, 'reflected_user_id' => self::REMOTE_MISP_SYNC_USER_ID, 'connectorName' => 'MispConnector', - 'cerebrateURL' => self::REMOTE_CEREBRATE_URL, + 'cerebrateURL' => $this->getWireMockBaseUrl() . self::REMOTE_CEREBRATE_URL, 'local_tool_id' => 1, 'remote_tool_id' => 1, 'tool_name' => 'MISP_REMOTE' @@ -250,7 +250,7 @@ class MispInterConnectionTest extends TestCase self::LOCAL_MISP_ADMIN_USER_AUTHKEY, [ 'authkey' => self::REMOTE_MISP_AUTHKEY, - 'url' => self::REMOTE_MISP_INSTANCE_URL, + 'url' => $this->getWireMockBaseUrl() . self::REMOTE_MISP_INSTANCE_URL, 'name' => 'MISP_REMOTE', 'remote_org_id' => OrganisationsFixture::ORGANISATION_A_ID ] diff --git a/tests/TestCase/Api/Users/DeleteUserApiTest.php b/tests/TestCase/Api/Users/DeleteUserApiTest.php index 9288b9a..2ffe0be 100644 --- a/tests/TestCase/Api/Users/DeleteUserApiTest.php +++ b/tests/TestCase/Api/Users/DeleteUserApiTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Test\TestCase\Api\Users; +use Cake\Core\Configure; use Cake\TestSuite\TestCase; use App\Test\Fixture\AuthKeysFixture; use App\Test\Fixture\UsersFixture; @@ -25,6 +26,7 @@ class DeleteUserApiTest extends TestCase public function testDeleteUser(): void { + Configure::write('user.allow-user-deletion', true); $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); $url = sprintf('%s/%d', self::ENDPOINT, UsersFixture::USER_REGULAR_USER_ID); $this->delete($url); diff --git a/tests/TestCase/ApplicationTest.php b/tests/TestCase/ApplicationTest.php index e2d3183..01afe27 100644 --- a/tests/TestCase/ApplicationTest.php +++ b/tests/TestCase/ApplicationTest.php @@ -40,14 +40,13 @@ class ApplicationTest extends IntegrationTestCase $app->bootstrap(); $plugins = $app->getPlugins(); - $this->assertCount(7, $plugins); + $this->assertCount(6, $plugins); $this->assertSame('Bake', $plugins->get('Bake')->getName()); $this->assertSame('DebugKit', $plugins->get('DebugKit')->getName()); $this->assertSame('Migrations', $plugins->get('Migrations')->getName()); $this->assertSame('Authentication', $plugins->get('Authentication')->getName()); $this->assertSame('ADmad/SocialAuth', $plugins->get('ADmad/SocialAuth')->getName()); $this->assertSame('Tags', $plugins->get('Tags')->getName()); - $this->assertSame('Cake/TwigView', $plugins->get('Cake/TwigView')->getName()); } /** From 9ba00dc3342605e5855a7cd423333eb4edf040d1 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Fri, 7 Apr 2023 11:39:37 +0200 Subject: [PATCH 06/11] fix: check for required plugins online, the number makes the test fragile as debug plugins may or not be present when running the tests --- tests/TestCase/ApplicationTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase/ApplicationTest.php b/tests/TestCase/ApplicationTest.php index 01afe27..5704f68 100644 --- a/tests/TestCase/ApplicationTest.php +++ b/tests/TestCase/ApplicationTest.php @@ -40,7 +40,6 @@ class ApplicationTest extends IntegrationTestCase $app->bootstrap(); $plugins = $app->getPlugins(); - $this->assertCount(6, $plugins); $this->assertSame('Bake', $plugins->get('Bake')->getName()); $this->assertSame('DebugKit', $plugins->get('DebugKit')->getName()); $this->assertSame('Migrations', $plugins->get('Migrations')->getName()); From 47ff4db826f3afae78cae3aee9d9f9f076fa5933 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Fri, 7 Apr 2023 14:08:27 +0200 Subject: [PATCH 07/11] fix: fix tests, move e2e tests to a separate suite --- phpunit.xml.dist | 3 + tests/Fixture/IndividualsFixture.php | 11 + tests/README.md | 1 + tests/TestCase/Api/Users/AddUserApiTest.php | 16 +- .../Broods/TestBroodConnectionApiTest.php | 65 +++ .../LocalTools/MispInterConnectionTest.php | 405 ++++++++++++++++++ 6 files changed, 494 insertions(+), 7 deletions(-) create mode 100644 tests/TestCase/Integration/Broods/TestBroodConnectionApiTest.php create mode 100644 tests/TestCase/Integration/LocalTools/MispInterConnectionTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 403e1e5..ae814bc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -20,6 +20,9 @@ ./tests/TestCase/Api + + ./tests/TestCase/Integration + diff --git a/tests/Fixture/IndividualsFixture.php b/tests/Fixture/IndividualsFixture.php index 13aecdf..9e33d53 100644 --- a/tests/Fixture/IndividualsFixture.php +++ b/tests/Fixture/IndividualsFixture.php @@ -15,6 +15,7 @@ class IndividualsFixture extends TestFixture public const INDIVIDUAL_ORG_ADMIN_ID = 3; public const INDIVIDUAL_REGULAR_USER_ID = 4; public const INDIVIDUAL_A_ID = 5; + public const INDIVIDUAL_B_ID = 6; public function init(): void { @@ -70,6 +71,16 @@ class IndividualsFixture extends TestFixture 'position' => 'user', 'created' => $faker->dateTime()->getTimestamp(), 'modified' => $faker->dateTime()->getTimestamp() + ], + [ + 'id' => self::INDIVIDUAL_B_ID, + 'uuid' => $faker->uuid(), + 'email' => $faker->email(), + 'first_name' => $faker->firstName, + 'last_name' => $faker->lastName, + 'position' => 'user', + 'created' => $faker->dateTime()->getTimestamp(), + 'modified' => $faker->dateTime()->getTimestamp() ] ]; parent::init(); diff --git a/tests/README.md b/tests/README.md index 434f84a..bfdead7 100644 --- a/tests/README.md +++ b/tests/README.md @@ -59,6 +59,7 @@ $ vendor/bin/phpunit --testsuite=api --testdox Available suites: * `app`: runs all test suites * `api`: runs only api tests +* `e2e`: runs only integration tests (requires wiremock running) * `controller`: runs only controller tests * _to be continued ..._ diff --git a/tests/TestCase/Api/Users/AddUserApiTest.php b/tests/TestCase/Api/Users/AddUserApiTest.php index 3437d29..8d10139 100644 --- a/tests/TestCase/Api/Users/AddUserApiTest.php +++ b/tests/TestCase/Api/Users/AddUserApiTest.php @@ -6,7 +6,7 @@ namespace App\Test\TestCase\Api\Users; use Cake\TestSuite\TestCase; use App\Test\Fixture\AuthKeysFixture; -use App\Test\Fixture\UsersFixture; +use App\Test\Fixture\IndividualsFixture; use App\Test\Fixture\OrganisationsFixture; use App\Test\Fixture\RolesFixture; use App\Test\Helper\ApiTestTrait; @@ -31,18 +31,20 @@ class AddUserApiTest extends TestCase $this->post( self::ENDPOINT, [ - 'individual_id' => UsersFixture::USER_REGULAR_USER_ID, + 'individual_id' => IndividualsFixture::INDIVIDUAL_B_ID, 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, 'role_id' => RolesFixture::ROLE_REGULAR_USER_ID, 'disabled' => false, - 'username' => 'test', + 'username' => 'test123', 'password' => 'Password123456!', ] ); + print_r($this->getJsonResponseAsArray()); + $this->assertResponseOk(); - $this->assertResponseContains('"username": "test"'); - $this->assertDbRecordExists('Users', ['username' => 'test']); + $this->assertResponseContains('"username": "test123"'); + $this->assertDbRecordExists('Users', ['username' => 'test123']); } public function testAddUserNotAllowedAsRegularUser(): void @@ -51,7 +53,7 @@ class AddUserApiTest extends TestCase $this->post( self::ENDPOINT, [ - 'individual_id' => UsersFixture::USER_REGULAR_USER_ID, + 'individual_id' => IndividualsFixture::INDIVIDUAL_B_ID, 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, 'role_id' => RolesFixture::ROLE_REGULAR_USER_ID, 'disabled' => false, @@ -61,6 +63,6 @@ class AddUserApiTest extends TestCase ); $this->assertResponseCode(405); - $this->assertDbRecordNotExists('Users', ['username' => 'test']); + $this->assertDbRecordNotExists('Users', ['username' => 'test123']); } } diff --git a/tests/TestCase/Integration/Broods/TestBroodConnectionApiTest.php b/tests/TestCase/Integration/Broods/TestBroodConnectionApiTest.php new file mode 100644 index 0000000..8f0a0df --- /dev/null +++ b/tests/TestCase/Integration/Broods/TestBroodConnectionApiTest.php @@ -0,0 +1,65 @@ +setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->initializeWireMock(); + $stub = $this->mockCerebrateStatusResponse(); + + $url = sprintf('%s/%d', self::ENDPOINT, BroodsFixture::BROOD_WIREMOCK_ID); + $this->get($url); + + $this->verifyStubCalled($stub); + $this->assertResponseOk(); + $this->assertResponseContains('"user": "wiremock"'); + } + + private function mockCerebrateStatusResponse(): \WireMock\Stubbing\StubMapping + { + return $this->getWireMock()->stubFor( + WireMock::get(WireMock::urlEqualTo('/instance/status.json')) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + "version" => "0.1", + "application" => "Cerebrate", + "user" => [ + "id" => 1, + "username" => "wiremock", + "role" => [ + "id" => 1 + ] + ] + ] + ))) + ); + } +} diff --git a/tests/TestCase/Integration/LocalTools/MispInterConnectionTest.php b/tests/TestCase/Integration/LocalTools/MispInterConnectionTest.php new file mode 100644 index 0000000..33f9eac --- /dev/null +++ b/tests/TestCase/Integration/LocalTools/MispInterConnectionTest.php @@ -0,0 +1,405 @@ +initializeWireMock(); + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + + $faker = \Faker\Factory::create(); + + /** + * 1. Create LocalTool connection to `MISP LOCAL` (local MISP instance) + */ + $this->post( + sprintf('%s/localTools/add', self::LOCAL_CEREBRATE_URL), + [ + 'name' => 'MISP_LOCAL', + 'connector' => 'MispConnector', + 'settings' => json_encode([ + 'url' => $this->getWireMockBaseUrl() . self::LOCAL_MISP_INSTANCE_URL, + 'authkey' => self::LOCAL_MISP_ADMIN_USER_AUTHKEY, + 'skip_ssl' => true, + ]), + 'description' => 'MISP local instance', + 'exposed' => true + ] + ); + $this->assertResponseOk(); + $this->assertDbRecordExists('LocalTools', ['name' => 'MISP_LOCAL']); + + /** + * 2. Create a new Brood (connect to a remote Cerebrate instance) + * This step assumes that the remote Cerebrate instance is already + * running and has a user created for the local Cerebrate instance. + * + * NOTE: Uses OrganisationsFixture::ORGANISATION_A_ID from the + * fixtures as the local Organisation. + */ + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $LOCAL_BROOD_UUID = $faker->uuid; + $this->post( + '/broods/add', + [ + 'uuid' => $LOCAL_BROOD_UUID, + 'name' => 'Local Brood', + 'url' => $this->getWireMockBaseUrl() . self::REMOTE_CEREBRATE_URL, + 'description' => $faker->text, + 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, + 'trusted' => true, + 'pull' => true, + 'skip_proxy' => true, + 'authkey' => self::REMOTE_CEREBRATE_AUTHKEY, + ] + ); + $this->assertResponseOk(); + $this->assertDbRecordExists('Broods', ['uuid' => $LOCAL_BROOD_UUID]); + $brood = $this->getJsonResponseAsArray(); + + /** + * 3. Create a new Cerebrate local user for the remote Cerebrate + * These includes: + * - 3.a: Create a new Organisation + * - 3.b: Create a new Individual + * - 3.c: Create a new User + * - 3.d: Create a new Authkey + */ + // Create Organisation + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $remoteOrgUuid = $faker->uuid; + $this->post( + '/organisations/add', + [ + 'name' => 'Remote Organisation', + 'description' => $faker->text, + 'uuid' => $remoteOrgUuid, + 'url' => 'http://cerebrate.remote', + 'nationality' => 'US', + 'sector' => 'sector', + 'type' => 'type', + ] + ); + $this->assertResponseOk(); + $this->assertDbRecordExists('Organisations', ['uuid' => $remoteOrgUuid]); + $remoteOrg = $this->getJsonResponseAsArray(); + + // Create Individual + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->post( + '/individuals/add', + [ + 'email' => 'sync@cerebrate.remote', + 'first_name' => 'Remote', + 'last_name' => 'Cerebrate' + ] + ); + $this->assertResponseOk(); + $this->assertDbRecordExists('Individuals', ['email' => 'sync@cerebrate.remote']); + $remoteIndividual = $this->getJsonResponseAsArray(); + + // Create User + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->post( + '/users/add', + [ + 'individual_id' => $remoteIndividual['id'], + 'organisation_id' => $remoteOrg['id'], + 'role_id' => RolesFixture::ROLE_SYNC_ID, + 'disabled' => false, + 'username' => 'remote_cerebrate', + 'password' => 'Password123456!', + ] + ); + $this->assertResponseOk(); + $this->assertDbRecordExists('Users', ['username' => 'remote_cerebrate']); + $user = $this->getJsonResponseAsArray(); + + // Create Authkey + $remoteCerebrateAuthkey = $faker->sha1; + $remoteAuthkeyUuid = $faker->uuid; + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->post( + '/authKeys/add', + [ + 'uuid' => $remoteAuthkeyUuid, + 'authkey' => $remoteCerebrateAuthkey, + 'expiration' => 0, + 'user_id' => $user['id'], + 'comment' => $faker->text + ] + ); + $this->assertResponseOk(); + $this->assertDbRecordExists('AuthKeys', ['uuid' => $remoteAuthkeyUuid]); + + /** + * 4. Get remote Cerebrate exposed tools + */ + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->mockCerebrateGetExposedToolsResponse('CEREBRATE_REMOTE', self::REMOTE_CEREBRATE_AUTHKEY); + $this->get(sprintf('/localTools/broodTools/%s', $brood['id'])); + $this->assertResponseOk(); + $tools = $this->getJsonResponseAsArray(); + + /** + * 5. Issue a connection request to the remote MISP instance + */ + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->mockCerebrateGetExposedToolsResponse('CEREBRATE_REMOTE', self::REMOTE_CEREBRATE_AUTHKEY); + $this->mockMispViewOrganisationByUuid( + 'MISP_LOCAL', + self::LOCAL_MISP_ADMIN_USER_AUTHKEY, + OrganisationsFixture::ORGANISATION_A_UUID, + OrganisationsFixture::ORGANISATION_A_ID + ); + $this->mockMispCreateSyncUser( + 'MISP_LOCAL', + self::LOCAL_MISP_ADMIN_USER_AUTHKEY, + self::REMOTE_MISP_SYNC_USER_ID, + self::REMOTE_MISP_SYNC_USER_EMAIL + ); + $this->mockCerebrateCreateMispIncommingConnectionRequest( + 'CEREBRATE_REMOTE', + UsersFixture::USER_ADMIN_ID, + self::LOCAL_CEREBRATE_URL, + self::REMOTE_CEREBRATE_AUTHKEY, + self::LOCAL_MISP_INSTANCE_URL + ); + $this->post( + sprintf('/localTools/connectionRequest/%s/%s', $brood['id'], $tools[0]['id']), + [ + 'local_tool_id' => 1 + ] + ); + $this->assertResponseOk(); + + /** + * 6. Remote Cerebrate accepts the connection request + */ + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->post( + '/inbox/createEntry/LocalTool/AcceptedRequest', + [ + 'email' => self::REMOTE_MISP_SYNC_USER_EMAIL, + 'authkey' => self::REMOTE_MISP_AUTHKEY, + 'url' => $this->getWireMockBaseUrl() . self::REMOTE_MISP_INSTANCE_URL, + 'reflected_user_id' => self::REMOTE_MISP_SYNC_USER_ID, + 'connectorName' => 'MispConnector', + 'cerebrateURL' => $this->getWireMockBaseUrl() . self::REMOTE_CEREBRATE_URL, + 'local_tool_id' => 1, + 'remote_tool_id' => 1, + 'tool_name' => 'MISP_REMOTE' + ] + ); + $this->assertResponseOk(); + $acceptRequest = $this->getJsonResponseAsArray(); + + /** + * 7. Finalize the connection + */ + $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); + $this->mockEnableMispSyncUser('MISP_LOCAL', self::LOCAL_MISP_ADMIN_USER_AUTHKEY, self::REMOTE_MISP_SYNC_USER_ID); + $this->mockAddMispServer( + 'MISP_LOCAL', + self::LOCAL_MISP_ADMIN_USER_AUTHKEY, + [ + 'authkey' => self::REMOTE_MISP_AUTHKEY, + 'url' => $this->getWireMockBaseUrl() . self::REMOTE_MISP_INSTANCE_URL, + 'name' => 'MISP_REMOTE', + 'remote_org_id' => OrganisationsFixture::ORGANISATION_A_ID + ] + ); + $this->post(sprintf('/inbox/process/%s', $acceptRequest['data']['id'])); + $this->assertResponseOk(); + $this->assertResponseContains('"success": true'); + $this->verifyAllStubsCalled(); + } + + private function mockCerebrateGetExposedToolsResponse(string $instance, string $cerebrateAuthkey): \WireMock\Stubbing\StubMapping + { + return $this->getWireMock()->stubFor( + WireMock::get(WireMock::urlEqualTo("/$instance/localTools/exposedTools")) + ->withHeader('Authorization', WireMock::equalTo($cerebrateAuthkey)) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + [ + "id" => 1, + "name" => "MISP ($instance)", + "connector" => "MispConnector", + ] + ] + ))) + ); + } + + private function mockMispViewOrganisationByUuid(string $instance, string $mispAuthkey, string $orgUuid, int $orgId): \WireMock\Stubbing\StubMapping + { + return $this->getWireMock()->stubFor( + WireMock::get(WireMock::urlEqualTo("/$instance/organisations/view/$orgUuid/limit:50")) + ->withHeader('Authorization', WireMock::equalTo($mispAuthkey)) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + "Organisation" => [ + "id" => $orgId, + "name" => $instance . ' Organisation', + "uuid" => $orgUuid, + "local" => true + ] + ] + ))) + ); + } + + private function mockMispCreateSyncUser(string $instance, string $mispAuthkey, int $userId, string $email): \WireMock\Stubbing\StubMapping + { + $faker = \Faker\Factory::create(); + return $this->getWireMock()->stubFor( + WireMock::post(WireMock::urlEqualTo("/$instance/admin/users/add")) + ->withHeader('Authorization', WireMock::equalTo($mispAuthkey)) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + "User" => [ + "id" => $userId, + "email" => $email, + "authkey" => $faker->sha1 + ] + ] + ))) + ); + } + + private function mockCerebrateCreateMispIncommingConnectionRequest( + string $instance, + int $userId, + string $cerebrateUrl, + string $cerebrateAuthkey, + string $mispUrl + ): \WireMock\Stubbing\StubMapping { + $faker = \Faker\Factory::create(); + + return $this->getWireMock()->stubFor( + WireMock::post(WireMock::urlEqualTo("/$instance/inbox/createEntry/LocalTool/IncomingConnectionRequest")) + ->withHeader('Authorization', WireMock::equalTo($cerebrateAuthkey)) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + 'data' => [ + 'id' => $faker->randomNumber(), + 'uuid' => $faker->uuid, + 'origin' => $cerebrateUrl, + 'user_id' => $userId, + 'data' => [ + 'connectorName' => 'MispConnector', + 'cerebrateURL' => $cerebrateUrl, + 'url' => $mispUrl, + 'tool_connector' => 'MispConnector', + 'local_tool_id' => 1, + 'remote_tool_id' => 1, + ], + 'title' => 'Request for MISP Inter-connection', + 'scope' => 'LocalTool', + 'action' => 'IncomingConnectionRequest', + 'description' => 'Handle Phase I of inter-connection when another cerebrate instance performs the request.', + 'local_tool_connector_name' => 'MispConnector', + 'created' => date('c'), + 'modified' => date('c') + ], + 'success' => true, + 'message' => 'LocalTool request for IncomingConnectionRequest created', + 'errors' => [], + ] + ))) + ); + } + + private function mockEnableMispSyncUser(string $instance, string $mispAuthkey, int $userId): \WireMock\Stubbing\StubMapping + { + return $this->getWireMock()->stubFor( + WireMock::post(WireMock::urlEqualTo("/$instance/admin/users/edit/$userId")) + ->withHeader('Authorization', WireMock::equalTo($mispAuthkey)) + ->withRequestBody(WireMock::equalToJson(json_encode(['disabled' => false]))) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + "User" => [ + "id" => $userId, + ] + ] + ))) + ); + } + + private function mockAddMispServer(string $instance, string $mispAuthkey, array $body): \WireMock\Stubbing\StubMapping + { + $faker = \Faker\Factory::create(); + + return $this->getWireMock()->stubFor( + WireMock::post(WireMock::urlEqualTo("/$instance/servers/add")) + ->withHeader('Authorization', WireMock::equalTo($mispAuthkey)) + ->withRequestBody(WireMock::equalToJson(json_encode($body))) + ->willReturn(WireMock::aResponse() + ->withHeader('Content-Type', 'application/json') + ->withBody((string)json_encode( + [ + 'Server' => [ + 'id' => $faker->randomNumber() + ] + ] + ))) + ); + } +} From a42599be326f4ed953a47dfde3b573ab5be3e8e9 Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 14 Sep 2023 17:12:36 +0200 Subject: [PATCH 08/11] fix: failing test, due to different user --- tests/Fixture/InboxFixture.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Fixture/InboxFixture.php b/tests/Fixture/InboxFixture.php index 40303b2..f26a4c8 100644 --- a/tests/Fixture/InboxFixture.php +++ b/tests/Fixture/InboxFixture.php @@ -45,7 +45,7 @@ class InboxFixture extends TestFixture 'origin' => 'http://127.0.0.1', 'comment' => null, 'description' => 'Handle Phase I of inter-connection when another cerebrate instance performs the request.', - 'user_id' => UsersFixture::USER_ORG_ADMIN_ID, + 'user_id' => UsersFixture::USER_ADMIN_ID, 'data' => [ 'connectorName' => 'MispConnector', 'cerebrateURL' => 'http://127.0.0.1', From 16f8ea8c2b8213cf7b61e3dfa4394deb7930f65d Mon Sep 17 00:00:00 2001 From: Luciano Righetti Date: Thu, 14 Sep 2023 17:47:56 +0200 Subject: [PATCH 09/11] fix: fix tests --- tests/Fixture/InboxFixture.php | 10 ++++------ tests/TestCase/Api/Inbox/IndexInboxApiTest.php | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/Fixture/InboxFixture.php b/tests/Fixture/InboxFixture.php index f26a4c8..d274a56 100644 --- a/tests/Fixture/InboxFixture.php +++ b/tests/Fixture/InboxFixture.php @@ -11,8 +11,8 @@ class InboxFixture extends TestFixture public $connection = 'test'; public $table = 'inbox'; - public const INBOX_USER_REGISTRATION_ID = 1; - public const INBOX_INCOMING_CONNECTION_REQUEST_ID = 2; + public const INBOX_USER_REGISTRATION_UUID = 'e783b13a-7019-48f5-848e-582bb930a833'; + public const INBOX_INCOMING_CONNECTION_REQUEST_UUID = '9810bd94-16f9-42e0-b364-af59dba50a34'; public function init(): void { @@ -20,8 +20,7 @@ class InboxFixture extends TestFixture $this->records = [ [ - 'id' => self::INBOX_USER_REGISTRATION_ID, - 'uuid' => $faker->uuid(), + 'uuid' => self::INBOX_USER_REGISTRATION_UUID, 'scope' => 'User', 'action' => 'Registration', 'title' => 'User account creation requested for foo@bar.com', @@ -37,8 +36,7 @@ class InboxFixture extends TestFixture 'modified' => $faker->dateTime()->getTimestamp() ], [ - 'id' => self::INBOX_INCOMING_CONNECTION_REQUEST_ID, - 'uuid' => $faker->uuid(), + 'uuid' => self::INBOX_INCOMING_CONNECTION_REQUEST_UUID, 'scope' => 'LocalTool', 'action' => 'IncomingConnectionRequest', 'title' => 'Request for MISP Inter-connection', diff --git a/tests/TestCase/Api/Inbox/IndexInboxApiTest.php b/tests/TestCase/Api/Inbox/IndexInboxApiTest.php index b8af1c6..0a60393 100644 --- a/tests/TestCase/Api/Inbox/IndexInboxApiTest.php +++ b/tests/TestCase/Api/Inbox/IndexInboxApiTest.php @@ -30,7 +30,7 @@ class IndexInboxApiTest extends TestCase $this->get(self::ENDPOINT); $this->assertResponseOk(); - $this->assertResponseContains(sprintf('"id": %d', InboxFixture::INBOX_USER_REGISTRATION_ID)); - $this->assertResponseContains(sprintf('"id": %d', InboxFixture::INBOX_INCOMING_CONNECTION_REQUEST_ID)); + $this->assertResponseContains(sprintf('"uuid": "%s"', InboxFixture::INBOX_USER_REGISTRATION_UUID)); + $this->assertResponseContains(sprintf('"uuid": "%s"', InboxFixture::INBOX_INCOMING_CONNECTION_REQUEST_UUID)); } } From 0d5dee35245e6af42e361cae2c1e588e7add8c9e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 27 Sep 2023 10:44:43 +0200 Subject: [PATCH 10/11] fix: [component:CRUD] Do not limit results if the limit query parameter is not provided --- src/Controller/Component/CRUDComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 47269ff..65dce89 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -102,7 +102,7 @@ class CRUDComponent extends Component if (!$this->Controller->ParamHandler->isRest()) { $this->setRequestedEntryAmount(); - } else if (!empty($this->request->getQuery('limit'))) { + } else if (empty($this->request->getQuery('limit'))) { $this->Controller->paginate['limit'] = PHP_INT_MAX; // Make sure to download the entire filtered table } $data = $this->Controller->paginate($query, $this->Controller->paginate ?? []); From b6b4310da4ef9a35ae576713c33414acfa1d024f Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 27 Sep 2023 12:05:48 +0200 Subject: [PATCH 11/11] chg: [indexes] Added `select all` option for some tables --- templates/Broods/index.php | 1 + templates/Individuals/index.php | 1 + templates/MetaTemplates/index.php | 1 + templates/OrgGroups/index.php | 1 + templates/Organisations/index.php | 1 + templates/Roles/index.php | 1 + templates/SharingGroups/index.php | 1 + 7 files changed, 7 insertions(+) diff --git a/templates/Broods/index.php b/templates/Broods/index.php index a78cf5a..d26db0d 100644 --- a/templates/Broods/index.php +++ b/templates/Broods/index.php @@ -61,6 +61,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('Broods Index'), 'description' => __('Cerebrate can connect to other Cerebrate instances to exchange trust information and to instrument interconnectivity between connected local tools. Each such Cerebrate instance with its connected tools is considered to be a brood.'), + 'includeAllPagination' => true, 'pull' => 'right', 'actions' => [ [ diff --git a/templates/Individuals/index.php b/templates/Individuals/index.php index d7d228f..463f4de 100644 --- a/templates/Individuals/index.php +++ b/templates/Individuals/index.php @@ -78,6 +78,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('ContactDB Individuals Index'), 'description' => __('A list of individuals known by your Cerebrate instance. This list can get populated either directly, by adding new individuals or by fetching them from trusted remote sources. Additionally, users created for the platform will always have an individual identity.'), + 'includeAllPagination' => true, 'actions' => [ [ 'url' => '/individuals/view', diff --git a/templates/MetaTemplates/index.php b/templates/MetaTemplates/index.php index 1ac384f..0c3acfc 100644 --- a/templates/MetaTemplates/index.php +++ b/templates/MetaTemplates/index.php @@ -169,6 +169,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('Meta Field Templates'), 'description' => __('The various templates used to enrich certain objects by a set of standardised fields.'), + 'includeAllPagination' => true, 'actions' => [ [ 'url' => '/metaTemplates/view', diff --git a/templates/OrgGroups/index.php b/templates/OrgGroups/index.php index 33569ad..ad714bf 100644 --- a/templates/OrgGroups/index.php +++ b/templates/OrgGroups/index.php @@ -62,6 +62,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('Organisation Groups Index'), 'description' => __('OrgGroups are an administrative concept, multiple organisations can belong to a grouping that allows common management by so called "GroupAdmins". This helps grouping organisations by sector, country or other commonalities into co-managed sub-communities.'), + 'includeAllPagination' => true, 'actions' => [ [ 'url' => '/orgGroups/view', diff --git a/templates/Organisations/index.php b/templates/Organisations/index.php index 2cf96de..c3fb2ae 100644 --- a/templates/Organisations/index.php +++ b/templates/Organisations/index.php @@ -97,6 +97,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('ContactDB Organisation Index'), 'description' => __('A list of organisations known by your Cerebrate instance. This list can get populated either directly, by adding new organisations or by fetching them from trusted remote sources.'), + 'includeAllPagination' => true, 'actions' => [ [ 'url' => '/organisations/view', diff --git a/templates/Roles/index.php b/templates/Roles/index.php index 8e12ed9..a5f2ffb 100644 --- a/templates/Roles/index.php +++ b/templates/Roles/index.php @@ -77,6 +77,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('Roles Index'), 'description' => __('A list of configurable user roles. Create or modify user access roles based on the settings below.'), + 'includeAllPagination' => true, 'pull' => 'right', 'actions' => [ [ diff --git a/templates/SharingGroups/index.php b/templates/SharingGroups/index.php index 0162c23..bb4843e 100644 --- a/templates/SharingGroups/index.php +++ b/templates/SharingGroups/index.php @@ -57,6 +57,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], 'title' => __('Sharing Groups Index'), 'description' => __('Sharing groups are distribution lists usable by tools that can exchange information with a list of trusted partners. Create recurring or ad hoc sharing groups and share them with the members of the sharing group.'), + 'includeAllPagination' => true, 'pull' => 'right', 'actions' => [ [