From d7287029a6cf1723c8831329f331b1d11cb8c7a1 Mon Sep 17 00:00:00 2001 From: Bas Date: Tue, 19 Nov 2024 13:35:15 +0100 Subject: [PATCH 1/3] Make behat replay logic more verbose When provisioning the tests output the commands and stop when this fails. So you are aware if something is broken. --- .../features/bootstrap/FeatureContext.php | 39 +++++++++++++++---- .../bootstrap/SecondFactorAuthContext.php | 8 ---- .../features/bootstrap/SelfServiceContext.php | 2 +- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/stepup/tests/behat/features/bootstrap/FeatureContext.php b/stepup/tests/behat/features/bootstrap/FeatureContext.php index 74064c6..14b3918 100644 --- a/stepup/tests/behat/features/bootstrap/FeatureContext.php +++ b/stepup/tests/behat/features/bootstrap/FeatureContext.php @@ -44,6 +44,26 @@ class FeatureContext implements Context */ private $institutionConfiguration; + private static function execCommand(string $command): void + { + $output = []; + $returnCode = -1; + $result = exec($command, $output, $returnCode); + + if($result === false) { + echo "Failed executing command\n"; + die(); + } + + foreach ($output as $line) { + echo $line."\n"; + } + + if ($returnCode !== 0) { + die(); + } + } + /** * @BeforeFeature */ @@ -51,17 +71,20 @@ public static function setupDatabase(BeforeFeatureScope $scope) { // Generate test databases echo "Preparing test schemas\n"; - shell_exec("docker exec -t stepup-middleware-1 bin/console doctrine:schema:drop --env=smoketest --force"); - shell_exec("docker exec -t stepup-gateway-1 bin/console doctrine:schema:drop --env=smoketest --force"); - shell_exec("docker exec -t stepup-middleware-1 bin/console doctrine:schema:create --env=smoketest"); - shell_exec("docker exec -t stepup-gateway-1 bin/console doctrine:schema:create --env=smoketest"); + self::execCommand('docker exec -t stepup-middleware-1 bin/console doctrine:schema:drop --em=middleware --env=smoketest --force'); + self::execCommand('docker exec -t stepup-middleware-1 bin/console doctrine:schema:drop --em=gateway --env=smoketest --force'); + self::execCommand('docker exec -t stepup-middleware-1 bin/console doctrine:schema:create --em=middleware --env=smoketest'); + self::execCommand('docker exec -t stepup-middleware-1 bin/console doctrine:schema:create --em=gateway --env=smoketest'); - echo "Replaying event stream\n"; // Import the events.sql into middleware - shell_exec("mysql -uroot -psecret middleware_test -h mariadb < ./fixtures/events.sql"); - shell_exec("./fixtures/middleware-push-config.sh"); + echo "Add events to test database\n"; + self::execCommand("mysql -uroot -psecret middleware_test -h mariadb < ./fixtures/events.sql"); + // Perform an event replay - shell_exec("docker exec -t stepup-middleware-1 bin/console middleware:event:replay --env=smoketest_event_replay --no-interaction -q"); + echo "Replaying event stream\n"; + self::execCommand("docker exec -t stepup-middleware-1 bin/console middleware:event:replay --env=smoketest_event_replay --no-interaction -vvv"); + // Push config + self::execCommand("./fixtures/middleware-push-config.sh"); } /** diff --git a/stepup/tests/behat/features/bootstrap/SecondFactorAuthContext.php b/stepup/tests/behat/features/bootstrap/SecondFactorAuthContext.php index 1cc2ff6..129ce64 100644 --- a/stepup/tests/behat/features/bootstrap/SecondFactorAuthContext.php +++ b/stepup/tests/behat/features/bootstrap/SecondFactorAuthContext.php @@ -387,14 +387,6 @@ public function authenticateWithIdentityProviderForWithStepup($userName) $this->minkContext->pressButton('Yes, continue'); } - private function passTroughIdentityProviderAssertionConsumerService() - { - $this->minkContext->assertPageAddress('https://gateway.dev.openconext.local/authentication/consume-assertion'); - - $this->minkContext->assertPageNotContainsText('Incorrect username or password'); - $this->minkContext->pressButton('Submit'); - } - /** * @Then I am logged on the service provider */ diff --git a/stepup/tests/behat/features/bootstrap/SelfServiceContext.php b/stepup/tests/behat/features/bootstrap/SelfServiceContext.php index e591679..d2b0538 100644 --- a/stepup/tests/behat/features/bootstrap/SelfServiceContext.php +++ b/stepup/tests/behat/features/bootstrap/SelfServiceContext.php @@ -508,7 +508,7 @@ private function getLastSentEmail() } $messages = json_decode($response); - if (!$messages) { + if (!is_array($messages)) { throw new Exception( 'Unable to parse mailcatcher response' ); From 5d18d495740845276a335a62bbc75e05f12d2327 Mon Sep 17 00:00:00 2001 From: Bas Date: Tue, 19 Nov 2024 13:42:25 +0100 Subject: [PATCH 2/3] Run all Behat features in isolation The initialization config for the tests are now using the same config as middleware is using when a new environment is bootstrapped. After the initial setup of the tests a mysqldump is created in order to run all test in isolation and prevent unwanted side effects. And the event stream is no only used to boostrap an SRAA with a token. --- stepup/tests/behat/.gitignore | 3 +- .../features/bootstrap/FeatureContext.php | 22 ++- stepup/tests/behat/features/identity.feature | 5 + .../tests/behat/features/ra_candidate.feature | 13 -- .../behat/features/ra_multiple_tokens.feature | 16 --- stepup/tests/behat/fixtures/events.sql | 78 ++++++---- .../fixtures/middleware-institution.json | 133 ++++++++++++++++++ .../fixtures/middleware-push-institution.sh | 50 +++++++ .../fixtures/middleware-push-whitelist.sh | 51 +++++++ .../behat/fixtures/middleware-whitelist.json | 15 ++ 10 files changed, 323 insertions(+), 63 deletions(-) create mode 100644 stepup/tests/behat/fixtures/middleware-institution.json create mode 100755 stepup/tests/behat/fixtures/middleware-push-institution.sh create mode 100755 stepup/tests/behat/fixtures/middleware-push-whitelist.sh create mode 100644 stepup/tests/behat/fixtures/middleware-whitelist.json diff --git a/stepup/tests/behat/.gitignore b/stepup/tests/behat/.gitignore index 49ce3c1..a75e37d 100644 --- a/stepup/tests/behat/.gitignore +++ b/stepup/tests/behat/.gitignore @@ -1 +1,2 @@ -/vendor \ No newline at end of file +/vendor +setup.sql diff --git a/stepup/tests/behat/features/bootstrap/FeatureContext.php b/stepup/tests/behat/features/bootstrap/FeatureContext.php index 14b3918..3370baa 100644 --- a/stepup/tests/behat/features/bootstrap/FeatureContext.php +++ b/stepup/tests/behat/features/bootstrap/FeatureContext.php @@ -4,6 +4,7 @@ use Behat\Behat\Hook\Scope\BeforeScenarioScope; use Behat\MinkExtension\Context\MinkContext; use Behat\Behat\Hook\Scope\BeforeFeatureScope; +use Behat\Testwork\Hook\Scope\BeforeSuiteScope; use Ramsey\Uuid\Uuid; use Surfnet\StepupBehat\Factory\CommandPayloadFactory; use Surfnet\StepupBehat\Repository\SecondFactorRepository; @@ -65,9 +66,9 @@ private static function execCommand(string $command): void } /** - * @BeforeFeature + * @BeforeSuite */ - public static function setupDatabase(BeforeFeatureScope $scope) + public static function setupDatabase(BeforeSuiteScope $scope) { // Generate test databases echo "Preparing test schemas\n"; @@ -83,8 +84,25 @@ public static function setupDatabase(BeforeFeatureScope $scope) // Perform an event replay echo "Replaying event stream\n"; self::execCommand("docker exec -t stepup-middleware-1 bin/console middleware:event:replay --env=smoketest_event_replay --no-interaction -vvv"); + // Push config + echo "Push Middleware config\n"; self::execCommand("./fixtures/middleware-push-config.sh"); + self::execCommand("./fixtures/middleware-push-whitelist.sh"); + self::execCommand("./fixtures/middleware-push-institution.sh"); + + // Write base setup for initializing features + echo "Dump empty setup to mysql file\n"; + self::execCommand("mysqldump -h mariadb -u root -psecret --single-transaction --databases middleware_test gateway_test > setup.sql"); + } + + /** + * @BeforeFeature + */ + public static function load(BeforeFeatureScope $scope) + { + // restore base setup + self::execCommand("mysql -h mariadb -u root -psecret < setup.sql"); } /** diff --git a/stepup/tests/behat/features/identity.feature b/stepup/tests/behat/features/identity.feature index 5f52180..8abc300 100644 --- a/stepup/tests/behat/features/identity.feature +++ b/stepup/tests/behat/features/identity.feature @@ -3,6 +3,11 @@ Feature: A (S)RA(A) user reads identities of StepUp users in the middleware API As a (S)RA(A) user I must be able to read from the middleware API + Scenario: Provision the following users: + Given a user "jane-a1" identified by "urn:collab:person:institution-a.example.com:jane-a-ra" from institution "institution-a.example.com" with UUID "00000000-0000-4000-8000-000000000001" + And a user "joe-a1" identified by "urn:collab:person:institution-a.example.com:jane-a-ra" from institution "institution-a.example.com" with UUID "00000000-0000-4000-8000-000000000002" + And a user "jill-a1" identified by "urn:collab:person:institution-a.example.com:jane-a-ra" from institution "institution-a.example.com" with UUID "00000000-0000-4000-8000-000000000003" + Scenario: A (S)RA(A) user reads identities without additional authorization context Given I authenticate with user "ra" and password "secret" When I request "GET /identity?institution=institution-a.example.com" diff --git a/stepup/tests/behat/features/ra_candidate.feature b/stepup/tests/behat/features/ra_candidate.feature index ba700e0..4078140 100644 --- a/stepup/tests/behat/features/ra_candidate.feature +++ b/stepup/tests/behat/features/ra_candidate.feature @@ -23,11 +23,7 @@ Feature: A RAA manages ra candidates in the ra environment Then I should see the following candidates: | name | institution | | jane-a-ra | institution-a.example.com | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | | Admin | dev.openconext.local | - | SRAA2 | dev.openconext.local | Scenario: SRAA user checks if "Jane Toppan" is a candidate for all institutions (with filtering on institution-a) Given I am logged in into the ra portal as "admin" with a "yubikey" token @@ -42,9 +38,6 @@ Feature: A RAA manages ra candidates in the ra environment When I visit the RA promotion page Then I should see the following candidates for "institution-b.example.com": | name | institution | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | Scenario: SRAA user demotes "jane-a-ra" to no longer be an RAA for "institution-a" Given I am logged in into the ra portal as "admin" with a "yubikey" token @@ -57,18 +50,12 @@ Feature: A RAA manages ra candidates in the ra environment Then I should see the following candidates for "institution-a.example.com": | name | institution | | jane-a-ra | institution-a.example.com | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | Scenario: SRAA user checks if "Jane Toppan" is not a candidate for "institution-b" Given I am logged in into the ra portal as "admin" with a "yubikey" token When I visit the RA promotion page Then I should see the following candidates for "institution-b.example.com": | name | institution | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | Scenario: SRAA user checks if "Jane Toppan" is not listed for "institution-a" Given I am logged in into the ra portal as "admin" with a "yubikey" token diff --git a/stepup/tests/behat/features/ra_multiple_tokens.feature b/stepup/tests/behat/features/ra_multiple_tokens.feature index b2deea6..ecbd23b 100644 --- a/stepup/tests/behat/features/ra_multiple_tokens.feature +++ b/stepup/tests/behat/features/ra_multiple_tokens.feature @@ -54,11 +54,7 @@ Feature: A RAA (jane a ra) has two loa 3 tokens which makes her a valid RA candi Then I should see the following candidates: | name | institution | | jane-a-ra | institution-a.example.com | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | | Admin | dev.openconext.local | - | SRAA2 | dev.openconext.local | Scenario: SRAA user checks if "jane-a-ra" is a candidate for institutions if relieved from the RAA role Given I am logged in into the ra portal as "admin" with a "yubikey" token @@ -68,11 +64,7 @@ Feature: A RAA (jane a ra) has two loa 3 tokens which makes her a valid RA candi And I should see the following candidates: | name | institution | | jane-a-ra | institution-a.example.com | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | | Admin | dev.openconext.local | - | SRAA2 | dev.openconext.local | Scenario: Sraa revokes only one vetted token from "jane-a-ra" and that shouldn't remove her as candidate Given I am logged in into the ra portal as "admin" with a "yubikey" token @@ -82,11 +74,7 @@ Feature: A RAA (jane a ra) has two loa 3 tokens which makes her a valid RA candi And I should see the following candidates: | name | institution | | jane-a-ra | institution-a.example.com | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | | Admin | dev.openconext.local | - | SRAA2 | dev.openconext.local | Scenario: Sraa revokes the last vetted token from "Jane Toppan" and that must remove her as candidate Given I am logged in into the ra portal as "admin" with a "yubikey" token @@ -95,8 +83,4 @@ Feature: A RAA (jane a ra) has two loa 3 tokens which makes her a valid RA candi Then I visit the RA promotion page And I should see the following candidates: | name | institution | - | jane-b1 institution-b.example.com | institution-b.example.com | - | user-b5 institution-b.example.com | institution-b.example.com | - | user-b-ra institution-b.example.com | institution-b.example.com | | Admin | dev.openconext.local | - | SRAA2 | dev.openconext.local | diff --git a/stepup/tests/behat/fixtures/events.sql b/stepup/tests/behat/fixtures/events.sql index 5629575..862e917 100644 --- a/stepup/tests/behat/fixtures/events.sql +++ b/stepup/tests/behat/fixtures/events.sql @@ -20,30 +20,39 @@ -- DROP TABLE IF EXISTS `event_stream`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `event_stream` ( - `uuid` varchar(36) COLLATE utf8_unicode_ci NOT NULL, - `playhead` int(11) NOT NULL, - `metadata` text COLLATE utf8_unicode_ci NOT NULL, - `payload` longtext COLLATE utf8_unicode_ci NOT NULL, - `recorded_on` varchar(32) COLLATE utf8_unicode_ci NOT NULL, - `type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - PRIMARY KEY (`uuid`,`playhead`), - KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client=@@character_set_client */; +/*!40101 SET character_set_client=utf8 */; +CREATE TABLE `event_stream` +( + `uuid` varchar(36) COLLATE utf8_unicode_ci NOT NULL, + `playhead` int(11) NOT NULL, + `metadata` text COLLATE utf8_unicode_ci NOT NULL, + `payload` longtext COLLATE utf8_unicode_ci NOT NULL, + `recorded_on` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`uuid`, `playhead`), + KEY `type` (`type`) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8 + COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client=@saved_cs_client */; -- -- Dumping data for table `event_stream` -- LOCK TABLES `event_stream` WRITE; -/*!40000 ALTER TABLE `event_stream` DISABLE KEYS */; -INSERT INTO `event_stream` VALUES ('0007699b-7a29-4526-9e08-fe291806361f',0,'{\"class\":\"Broadway\\\\Domain\\\\Metadata\",\"payload\":[]}','{\"class\":\"Surfnet\\\\Stepup\\\\Identity\\\\Event\\\\IdentityCreatedEvent\",\"payload\":{\"id\":\"0007699b-7a29-4526-9e08-fe291806361f\",\"institution\":\"dev.openconext.local\",\"name_id\":\"urn:collab:person:dev.openconext.local:sraa2\",\"preferred_locale\":\"en_GB\"}}','2023-09-14T12:22:22.541496+00:00','Surfnet.Stepup.Identity.Event.IdentityCreatedEvent'),('0007699b-7a29-4526-9e08-fe291806361f',1,'{\"class\":\"Broadway\\\\Domain\\\\Metadata\",\"payload\":[]}','{\"class\":\"Surfnet\\\\Stepup\\\\Identity\\\\Event\\\\YubikeySecondFactorBootstrappedEvent\",\"payload\":{\"identity_id\":\"0007699b-7a29-4526-9e08-fe291806361f\",\"name_id\":\"urn:collab:person:dev.openconext.local:sraa2\",\"identity_institution\":\"dev.openconext.local\",\"preferred_locale\":\"en_GB\",\"second_factor_id\":\"f2b1e616-ecde-458b-9f12-1536ad63ded0\"}}','2023-09-14T12:22:22.545350+00:00','Surfnet.Stepup.Identity.Event.YubikeySecondFactorBootstrappedEvent'),('12345678-abcd-4321-abcd-123456789012',0,'{\"class\":\"Broadway\\\\Domain\\\\Metadata\",\"payload\":[]}','{\"class\":\"Surfnet\\\\Stepup\\\\Configuration\\\\Event\\\\NewConfigurationCreatedEvent\",\"payload\":{\"id\":\"12345678-abcd-4321-abcd-123456789012\"}}','2023-07-19T06:46:34.940735+00:00','Surfnet.Stepup.Configuration.Event.NewConfigurationCreatedEvent'),('12345678-abcd-4321-abcd-123456789012',1,'{\"class\":\"Broadway\\\\Domain\\\\Metadata\",\"payload\":[]}','{\"class\":\"Surfnet\\\\Stepup\\\\Configuration\\\\Event\\\\ConfigurationUpdatedEvent\",\"payload\":{\"id\":\"12345678-abcd-4321-abcd-123456789012\",\"new_configuration\":{\"sraa\":[\"urn:collab:person:dev.openconext.local:admin\",\"urn:collab:person:dev.openconext.local:pieter\",\"urn:collab:person:dev.openconext.local:joost\"],\"email_templates\":{\"confirm_email\":{\"en_GB\":\"

Dear {{ commonName }},<\\/p>

Thank you for registering your token. Please visit this link to verify your email address:<\\/p>

{{ verificationUrl }}<\\/a><\\/p>

If you can not click on the URL, please copy the link and paste it in the address bar of your browser.<\\/p>\",\"nl_NL\":\"

Beste {{ commonName }},<\\/p>

Bedankt voor het registreren van je token. Klik op onderstaande link om je e-mailadres te bevestigen:<\\/p>

{{ verificationUrl }}<\\/a><\\/p>

Is klikken op de link niet mogelijk? Kopieer dan de link en plak deze in de adresbalk van je browser.<\\/p>\"},\"registration_code_with_ras\":{\"en_GB\":\"

Dear {{ commonName }},<\\/p>

Thank you for registering your token. Please visit one of the locations below within 14 days to get your token activated. After {{ expirationDate | localizeddate(\'full\', \'none\', locale) }} your activation code is no longer valid.<\\/p>

Please bring the following:<\\/p>