Merge pull request #151 from righel/fix-test-action

fix: fix test workflow action
refacto/CRUDComponent
Luciano Righetti 2023-09-14 16:16:55 +02:00 committed by GitHub
commit eb95c44528
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 556 additions and 35 deletions

View File

@ -2,7 +2,7 @@ name: test
on: on:
push: push:
branches: [main, develop] branches: [main, develop, fix-test-action]
pull_request: pull_request:
branches: [main, develop] branches: [main, develop]
@ -14,9 +14,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-20.04] os: [ubuntu-20.04]
php: ["7.4"] php: ["8.2"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Create config files - name: Create config files
run: | run: |

View File

@ -178,7 +178,6 @@ return [
*/ */
'Error' => [ 'Error' => [
'errorLevel' => E_ALL, 'errorLevel' => E_ALL,
'exceptionRenderer' => ExceptionRenderer::class,
'skipLog' => [], 'skipLog' => [],
'log' => true, 'log' => true,
'trace' => true, 'trace' => true,

View File

@ -20,6 +20,9 @@
<testsuite name="api"> <testsuite name="api">
<directory>./tests/TestCase/Api</directory> <directory>./tests/TestCase/Api</directory>
</testsuite> </testsuite>
<testsuite name="e2e">
<directory>./tests/TestCase/Integration</directory>
</testsuite>
</testsuites> </testsuites>
<extensions> <extensions>

View File

@ -106,7 +106,7 @@ class TagHelper extends Helper
$deleteButton = $this->Bootstrap->button([ $deleteButton = $this->Bootstrap->button([
'size' => 'sm', 'size' => 'sm',
'icon' => 'times', 'icon' => 'times',
'class' => ['ms-1', 'border-0', "text-${textColour}"], 'class' => ['ms-1', 'border-0', "text-$textColour"],
'variant' => 'text', 'variant' => 'text',
'title' => __('Delete tag'), 'title' => __('Delete tag'),
'onclick' => sprintf('deleteTag(\'%s\', \'%s\', this)', 'onclick' => sprintf('deleteTag(\'%s\', \'%s\', this)',

View File

@ -136,7 +136,7 @@ class ImporterCommand extends Command
$entity = null; $entity = null;
if (isset($item[$primary_key])) { if (isset($item[$primary_key])) {
$query = $table->find('all') $query = $table->find('all')
->where(["${primary_key}" => $item[$primary_key]]); ->where(["$primary_key" => $item[$primary_key]]);
$entity = $query->first(); $entity = $query->first();
} }
if (is_null($entity)) { if (is_null($entity)) {

View File

@ -1489,7 +1489,7 @@ class CRUDComponent extends Component
{ {
$prefixedConditions = []; $prefixedConditions = [];
foreach ($conditions as $condField => $condValue) { foreach ($conditions as $condField => $condValue) {
$prefixedConditions["${prefix}.${condField}"] = $condValue; $prefixedConditions["$prefix.$condField"] = $condValue;
} }
return $prefixedConditions; return $prefixedConditions;
} }
@ -1613,13 +1613,13 @@ class CRUDComponent extends Component
[sprintf('%s.id = %s.%s', $this->Table->getAlias(), $associatedTable->getAlias(), $association->getForeignKey())] [sprintf('%s.id = %s.%s', $this->Table->getAlias(), $associatedTable->getAlias(), $association->getForeignKey())]
) )
->where([ ->where([
["${field} IS NOT" => NULL] ["$field IS NOT" => NULL]
]); ]);
} else if ($associationType == 'manyToOne') { } else if ($associationType == 'manyToOne') {
$fieldToExtract = sprintf('%s.%s', Inflector::singularize(strtolower($model)), $subField); $fieldToExtract = sprintf('%s.%s', Inflector::singularize(strtolower($model)), $subField);
$query = $this->Table->find()->contain($model); $query = $this->Table->find()->contain($model);
} else { } else {
throw new Exception("Association ${associationType} not supported in CRUD Component"); throw new Exception("Association $associationType not supported in CRUD Component");
} }
} else { } else {
$fieldToExtract = $field; $fieldToExtract = $field;

View File

@ -390,7 +390,7 @@ class RestResponseComponent extends Component
return '[]'; return '[]';
} }
public function saveFailResponse($controller, $action, $id = false, $validationErrors, $format = false) public function saveFailResponse($controller, $action, $id, $validationErrors, $format = false)
{ {
$this->autoRender = false; $this->autoRender = false;
$response = array(); $response = array();

View File

@ -56,7 +56,7 @@ class BroodsFixture extends TestFixture
'id' => self::BROOD_WIREMOCK_ID, 'id' => self::BROOD_WIREMOCK_ID,
'uuid' => $faker->uuid(), 'uuid' => $faker->uuid(),
'name' => 'wiremock', 'name' => 'wiremock',
'url' => 'http://localhost:8080', 'url' => sprintf('http://%s:%s', $_ENV['WIREMOCK_HOST'] ?? 'localhost', $_ENV['WIREMOCK_PORT'] ?? '8080'),
'description' => $faker->text, 'description' => $faker->text,
'organisation_id' => OrganisationsFixture::ORGANISATION_B_ID, 'organisation_id' => OrganisationsFixture::ORGANISATION_B_ID,
'trusted' => true, 'trusted' => true,

View File

@ -15,6 +15,7 @@ class IndividualsFixture extends TestFixture
public const INDIVIDUAL_ORG_ADMIN_ID = 3; public const INDIVIDUAL_ORG_ADMIN_ID = 3;
public const INDIVIDUAL_REGULAR_USER_ID = 4; public const INDIVIDUAL_REGULAR_USER_ID = 4;
public const INDIVIDUAL_A_ID = 5; public const INDIVIDUAL_A_ID = 5;
public const INDIVIDUAL_B_ID = 6;
public function init(): void public function init(): void
{ {
@ -70,6 +71,16 @@ class IndividualsFixture extends TestFixture
'position' => 'user', 'position' => 'user',
'created' => $faker->dateTime()->getTimestamp(), 'created' => $faker->dateTime()->getTimestamp(),
'modified' => $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(); parent::init();

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class MetaTemplateDirectoryFixture extends TestFixture
{
public $connection = 'test';
public const META_TEMPLATE_DIRECTORY_ID = 1;
public const META_TEMPLATE_DIRECTORY_UUID = '99c7b7c0-23e2-4ba8-9ad4-97bcdadf4c81';
public function init(): void
{
$this->records = [
[
'id' => self::META_TEMPLATE_DIRECTORY_ID,
'uuid' => self::META_TEMPLATE_DIRECTORY_UUID,
'name' => 'Test Meta Template Directory',
'namespace' => 'cerebrate',
'version' => '1'
]
];
parent::init();
}
}

View File

@ -17,6 +17,7 @@ class MetaTemplateFieldsFixture extends TestFixture
'field' => 'test_field_1', 'field' => 'test_field_1',
'type' => 'text', 'type' => 'text',
'meta_template_id' => MetaTemplatesFixture::ENABLED_TEST_ORG_META_TEMPLATE_ID, 'meta_template_id' => MetaTemplatesFixture::ENABLED_TEST_ORG_META_TEMPLATE_ID,
'meta_template_directory_id' => MetaTemplateDirectoryFixture::META_TEMPLATE_DIRECTORY_ID,
'regex' => null, 'regex' => null,
'multiple' => 1, 'multiple' => 1,
'enabled' => 1, 'enabled' => 1,

View File

@ -44,6 +44,7 @@ class MetaTemplatesFixture extends TestFixture
$this->records = [ $this->records = [
[ [
'id' => self::ENABLED_TEST_ORG_META_TEMPLATE_ID, 'id' => self::ENABLED_TEST_ORG_META_TEMPLATE_ID,
'meta_template_directory_id' => MetaTemplateDirectoryFixture::META_TEMPLATE_DIRECTORY_ID,
'scope' => 'organisation', 'scope' => 'organisation',
'name' => 'Test Meta Template (enabled)', 'name' => 'Test Meta Template (enabled)',
'namespace' => 'cerebrate', 'namespace' => 'cerebrate',
@ -58,6 +59,7 @@ class MetaTemplatesFixture extends TestFixture
], ],
[ [
'id' => self::DISABLED_TEST_ORG_META_TEMPLATE_ID, 'id' => self::DISABLED_TEST_ORG_META_TEMPLATE_ID,
'meta_template_directory_id' => MetaTemplateDirectoryFixture::META_TEMPLATE_DIRECTORY_ID,
'scope' => 'organisation', 'scope' => 'organisation',
'name' => 'Test Meta Template (disabled)', 'name' => 'Test Meta Template (disabled)',
'namespace' => 'cerebrate', 'namespace' => 'cerebrate',

View File

@ -15,16 +15,18 @@ trait WireMockTestTrait
private $wiremock; private $wiremock;
/** @var array<mixed> */ /** @var array<mixed> */
private $config = [ private $config;
'hostname' => 'localhost',
'port' => 8080
];
public function initializeWireMock(): void public function initializeWireMock(): void
{ {
$this->config = [
'hostname' => $_ENV['WIREMOCK_HOST'] ?? 'localhost',
'port' => $_ENV['WIREMOCK_PORT'] ?? 8080
];
$this->wiremock = WireMock::create( $this->wiremock = WireMock::create(
$_ENV['WIREMOCK_HOST'] ?? $this->config['hostname'], $this->config['hostname'],
$_ENV['WIREMOCK_PORT'] ?? $this->config['port'] $this->config['port']
); );
if (!$this->wiremock->isAlive()) { if (!$this->wiremock->isAlive()) {

View File

@ -59,6 +59,7 @@ $ vendor/bin/phpunit --testsuite=api --testdox
Available suites: Available suites:
* `app`: runs all test suites * `app`: runs all test suites
* `api`: runs only api tests * `api`: runs only api tests
* `e2e`: runs only integration tests (requires wiremock running)
* `controller`: runs only controller tests * `controller`: runs only controller tests
* _to be continued ..._ * _to be continued ..._

View File

@ -31,20 +31,20 @@ class MispInterConnectionTest extends TestCase
]; ];
/** constants related to the local Cerebrate instance */ /** 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 */ /** 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'; private const LOCAL_MISP_ADMIN_USER_AUTHKEY = 'b17ce79ac0f05916f382ab06ea4790665dbc174c';
/** constants related to the remote Cerebrate instance */ /** 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'; private const REMOTE_CEREBRATE_AUTHKEY = 'a192ba3c749b545f9cec6b6bba0643736f6c3022';
/** constants related to the remote MISP instance */ /** constants related to the remote MISP instance */
private const REMOTE_MISP_SYNC_USER_ID = 333; private const REMOTE_MISP_SYNC_USER_ID = 333;
private const REMOTE_MISP_SYNC_USER_EMAIL = 'sync@misp.remote'; 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'; private const REMOTE_MISP_AUTHKEY = '19ca57ecebd2fe34c1c17d729980678eb648d541';
@ -64,7 +64,7 @@ class MispInterConnectionTest extends TestCase
'name' => 'MISP_LOCAL', 'name' => 'MISP_LOCAL',
'connector' => 'MispConnector', 'connector' => 'MispConnector',
'settings' => json_encode([ 'settings' => json_encode([
'url' => self::LOCAL_MISP_INSTANCE_URL, 'url' => $this->getWireMockBaseUrl() . self::LOCAL_MISP_INSTANCE_URL,
'authkey' => self::LOCAL_MISP_ADMIN_USER_AUTHKEY, 'authkey' => self::LOCAL_MISP_ADMIN_USER_AUTHKEY,
'skip_ssl' => true, 'skip_ssl' => true,
]), ]),
@ -90,7 +90,7 @@ class MispInterConnectionTest extends TestCase
[ [
'uuid' => $LOCAL_BROOD_UUID, 'uuid' => $LOCAL_BROOD_UUID,
'name' => 'Local Brood', 'name' => 'Local Brood',
'url' => self::REMOTE_CEREBRATE_URL, 'url' => $this->getWireMockBaseUrl() . self::REMOTE_CEREBRATE_URL,
'description' => $faker->text, 'description' => $faker->text,
'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
'trusted' => true, 'trusted' => true,
@ -228,10 +228,10 @@ class MispInterConnectionTest extends TestCase
[ [
'email' => self::REMOTE_MISP_SYNC_USER_EMAIL, 'email' => self::REMOTE_MISP_SYNC_USER_EMAIL,
'authkey' => self::REMOTE_MISP_AUTHKEY, '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, 'reflected_user_id' => self::REMOTE_MISP_SYNC_USER_ID,
'connectorName' => 'MispConnector', 'connectorName' => 'MispConnector',
'cerebrateURL' => self::REMOTE_CEREBRATE_URL, 'cerebrateURL' => $this->getWireMockBaseUrl() . self::REMOTE_CEREBRATE_URL,
'local_tool_id' => 1, 'local_tool_id' => 1,
'remote_tool_id' => 1, 'remote_tool_id' => 1,
'tool_name' => 'MISP_REMOTE' 'tool_name' => 'MISP_REMOTE'
@ -250,7 +250,7 @@ class MispInterConnectionTest extends TestCase
self::LOCAL_MISP_ADMIN_USER_AUTHKEY, self::LOCAL_MISP_ADMIN_USER_AUTHKEY,
[ [
'authkey' => self::REMOTE_MISP_AUTHKEY, 'authkey' => self::REMOTE_MISP_AUTHKEY,
'url' => self::REMOTE_MISP_INSTANCE_URL, 'url' => $this->getWireMockBaseUrl() . self::REMOTE_MISP_INSTANCE_URL,
'name' => 'MISP_REMOTE', 'name' => 'MISP_REMOTE',
'remote_org_id' => OrganisationsFixture::ORGANISATION_A_ID 'remote_org_id' => OrganisationsFixture::ORGANISATION_A_ID
] ]

View File

@ -6,7 +6,7 @@ namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\TestCase; use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture; use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\UsersFixture; use App\Test\Fixture\IndividualsFixture;
use App\Test\Fixture\OrganisationsFixture; use App\Test\Fixture\OrganisationsFixture;
use App\Test\Fixture\RolesFixture; use App\Test\Fixture\RolesFixture;
use App\Test\Helper\ApiTestTrait; use App\Test\Helper\ApiTestTrait;
@ -31,18 +31,20 @@ class AddUserApiTest extends TestCase
$this->post( $this->post(
self::ENDPOINT, self::ENDPOINT,
[ [
'individual_id' => UsersFixture::USER_REGULAR_USER_ID, 'individual_id' => IndividualsFixture::INDIVIDUAL_B_ID,
'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
'role_id' => RolesFixture::ROLE_REGULAR_USER_ID, 'role_id' => RolesFixture::ROLE_REGULAR_USER_ID,
'disabled' => false, 'disabled' => false,
'username' => 'test', 'username' => 'test123',
'password' => 'Password123456!', 'password' => 'Password123456!',
] ]
); );
print_r($this->getJsonResponseAsArray());
$this->assertResponseOk(); $this->assertResponseOk();
$this->assertResponseContains('"username": "test"'); $this->assertResponseContains('"username": "test123"');
$this->assertDbRecordExists('Users', ['username' => 'test']); $this->assertDbRecordExists('Users', ['username' => 'test123']);
} }
public function testAddUserNotAllowedAsRegularUser(): void public function testAddUserNotAllowedAsRegularUser(): void
@ -51,7 +53,7 @@ class AddUserApiTest extends TestCase
$this->post( $this->post(
self::ENDPOINT, self::ENDPOINT,
[ [
'individual_id' => UsersFixture::USER_REGULAR_USER_ID, 'individual_id' => IndividualsFixture::INDIVIDUAL_B_ID,
'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID, 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
'role_id' => RolesFixture::ROLE_REGULAR_USER_ID, 'role_id' => RolesFixture::ROLE_REGULAR_USER_ID,
'disabled' => false, 'disabled' => false,
@ -61,6 +63,6 @@ class AddUserApiTest extends TestCase
); );
$this->assertResponseCode(405); $this->assertResponseCode(405);
$this->assertDbRecordNotExists('Users', ['username' => 'test']); $this->assertDbRecordNotExists('Users', ['username' => 'test123']);
} }
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Test\TestCase\Api\Users; namespace App\Test\TestCase\Api\Users;
use Cake\Core\Configure;
use Cake\TestSuite\TestCase; use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture; use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\UsersFixture; use App\Test\Fixture\UsersFixture;
@ -25,6 +26,7 @@ class DeleteUserApiTest extends TestCase
public function testDeleteUser(): void public function testDeleteUser(): void
{ {
Configure::write('user.allow-user-deletion', true);
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY); $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, UsersFixture::USER_REGULAR_USER_ID); $url = sprintf('%s/%d', self::ENDPOINT, UsersFixture::USER_REGULAR_USER_ID);
$this->delete($url); $this->delete($url);

View File

@ -40,14 +40,12 @@ class ApplicationTest extends IntegrationTestCase
$app->bootstrap(); $app->bootstrap();
$plugins = $app->getPlugins(); $plugins = $app->getPlugins();
$this->assertCount(7, $plugins);
$this->assertSame('Bake', $plugins->get('Bake')->getName()); $this->assertSame('Bake', $plugins->get('Bake')->getName());
$this->assertSame('DebugKit', $plugins->get('DebugKit')->getName()); $this->assertSame('DebugKit', $plugins->get('DebugKit')->getName());
$this->assertSame('Migrations', $plugins->get('Migrations')->getName()); $this->assertSame('Migrations', $plugins->get('Migrations')->getName());
$this->assertSame('Authentication', $plugins->get('Authentication')->getName()); $this->assertSame('Authentication', $plugins->get('Authentication')->getName());
$this->assertSame('ADmad/SocialAuth', $plugins->get('ADmad/SocialAuth')->getName()); $this->assertSame('ADmad/SocialAuth', $plugins->get('ADmad/SocialAuth')->getName());
$this->assertSame('Tags', $plugins->get('Tags')->getName()); $this->assertSame('Tags', $plugins->get('Tags')->getName());
$this->assertSame('Cake/TwigView', $plugins->get('Cake/TwigView')->getName());
} }
/** /**

View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Integration\Broods;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\BroodsFixture;
use App\Test\Helper\ApiTestTrait;
use App\Test\Helper\WireMockTestTrait;
use \WireMock\Client\WireMock;
class TestBroodConnectionApiTest extends TestCase
{
use ApiTestTrait;
use WireMockTestTrait;
protected const ENDPOINT = '/broods/testConnection';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys',
'app.Broods'
];
public function testTestBroodConnection(): void
{
$this->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
]
]
]
)))
);
}
}

View File

@ -0,0 +1,405 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Integration\LocalTools;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\OrganisationsFixture;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\UsersFixture;
use App\Test\Fixture\RolesFixture;
use App\Test\Helper\ApiTestTrait;
use App\Test\Helper\WireMockTestTrait;
use \WireMock\Client\WireMock;
class MispInterConnectionTest extends TestCase
{
use ApiTestTrait;
use WireMockTestTrait;
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys',
'app.Broods',
'app.LocalTools',
'app.RemoteToolConnections',
'app.Inbox'
];
/** constants related to the local Cerebrate instance */
private const LOCAL_CEREBRATE_URL = 'http://localhost';
/** constants related to the local MISP instance */
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 = '/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 = '/MISP_REMOTE';
private const REMOTE_MISP_AUTHKEY = '19ca57ecebd2fe34c1c17d729980678eb648d541';
public function testInterConnectMispViaCerebrate(): void
{
$this->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()
]
]
)))
);
}
}