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()
+ ]
+ ]
+ )))
+ );
+ }
+}