diff --git a/Makefile b/Makefile index bc69cb8216..af97ce5726 100644 --- a/Makefile +++ b/Makefile @@ -94,7 +94,7 @@ up_services: @echo "Logging into ECR..." $(ECR_LOGIN) @echo "Getting Notify API Key..." - $(NOTIFY) && $(COMPOSE) up -d --remove-orphans webpack service-pdf viewer-web viewer-app actor-web actor-app front-composer api-web api-app api-composer + $(NOTIFY) && $(COMPOSE) up -d --remove-orphans webpack service-pdf viewer-web viewer-app actor-web actor-app front-composer api-web api-app api-composer proxy .PHONY: up_services update_mock: diff --git a/docker-compose.dependencies.yml b/docker-compose.dependencies.yml index d62da9c7b3..9801c29df1 100644 --- a/docker-compose.dependencies.yml +++ b/docker-compose.dependencies.yml @@ -43,7 +43,7 @@ services: kms: image: nsmithuk/local-kms:3 volumes: - - ./local-config:/init + - ./local-config/kms:/init environment: KMS_REGION: eu-west-1 KMS_SEED_PATH: /init/kms-seed.yaml diff --git a/docker-compose.testing.yml b/docker-compose.testing.yml index a7ec320b51..de18050dca 100644 --- a/docker-compose.testing.yml +++ b/docker-compose.testing.yml @@ -21,10 +21,11 @@ services: - ./tests/smoke:/app - ./tests/features:/app/features environment: - BEHAT_VIEWER_URL: http://viewer-web - BEHAT_ACTOR_URL: http://actor-web - BEHAT_OLD_VIEWER_URL: http://viewer-web - BEHAT_OLD_ACTOR_URL: http://actor-web + BEHAT_VIEWER_URL: http://proxy:9001 + BEHAT_ACTOR_URL: http://proxy:9002 + BEHAT_OLD_VIEWER_URL: http://proxy:9001 + BEHAT_OLD_ACTOR_URL: http://proxy:9002 + BEHAT_PARAMS: '{"extensions":{"Smoke\\SmokeExtension":{"allow_insecure_https":true}}}' XDEBUG_CONFIG: client_host=host.docker.internal client_port=9000 XDEBUG_MODE: develop,debug,coverage diff --git a/docker-compose.yml b/docker-compose.yml index a7de271ca9..ff3f29d1bf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,8 +39,6 @@ services: dockerfile: service-front/docker/web/Dockerfile volumes: - webpack_dist:/web - ports: - - 9001:80 environment: WEB_DOMAIN: http://localhost:9001 APP_HOST: viewer-app @@ -81,7 +79,6 @@ services: XDEBUG_MODE: develop,debug,coverage SESSION_EXPIRES: 30 # session expiry length to support timeout message. COOKIE_EXPIRES: 1440 # cookie expiry for complete logout - initial value to be 24 hours. - COOKIE_SECURE: "false" depends_on: - redis @@ -96,8 +93,6 @@ services: dockerfile: service-front/docker/web/Dockerfile volumes: - webpack_dist:/web - ports: - - 9002:80 environment: WEB_DOMAIN: http://localhost:9002 APP_HOST: actor-app @@ -142,7 +137,6 @@ services: SESSION_EXPIRES: 20 # session expiry length to support timeout message. SESSION_EXPIRY_WARNING: 5 # session expiry warning time to trigger popup window. COOKIE_EXPIRES: 1440 # cookie expiry for complete logout - initial value to be 24 hours. - COOKIE_SECURE: "false" NOTIFY_API_KEY: # --------------------------- @@ -282,3 +276,16 @@ services: ports: - 9007:8080 entrypoint: /aws-lambda/aws-lambda-rie /usr/local/bin/python -m awslambdaric app.upload-statistics.lambda_handler + + proxy: + container_name: proxy + image: traefik:v2.10 + ports: + - "9001:9001" + - "9041:9041" + - "9002:9002" + - "9042:9042" + - "9080:8080" + volumes: + - ./local-config/proxy/traefik.toml:/etc/traefik/traefik.toml + - ./local-config/proxy/dynamic.toml:/etc/traefik/dynamic.toml diff --git a/local-config/kms-seed.yaml b/local-config/kms/kms-seed.yaml similarity index 100% rename from local-config/kms-seed.yaml rename to local-config/kms/kms-seed.yaml diff --git a/local-config/proxy/dynamic.toml b/local-config/proxy/dynamic.toml new file mode 100644 index 0000000000..36a554412f --- /dev/null +++ b/local-config/proxy/dynamic.toml @@ -0,0 +1,54 @@ +[http] +[http.middlewares] + +[http.middlewares.viewer-gov-redirect.redirectRegex] +regex="^https://[^/]+/$$" +replacement="https://www.gov.uk/view-lasting-power-of-attorney" + +[http.middlewares.actor-gov-redirect.redirectRegex] +regex="^https://[^/]+/$$" +replacement="https://www.gov.uk/use-lasting-power-of-attorney" + +[http.middlewares.viewer-web-https.redirectScheme] +scheme="https" +port=9041 + +[http.middlewares.actor-web-https.redirectScheme] +scheme="https" +port=9042 + +[http.routers] +[http.routers.viewer-web] +rule="PathPrefix(`/`)" +entryPoints="http-viewer" +middlewares=["viewer-web-https"] +service="viewer-web" + +[http.routers.viewer-web-secure] +rule="PathPrefix(`/`)" +entryPoints="https-viewer" +middlewares=["viewer-gov-redirect"] +service="viewer-web" +[http.routers.viewer-web-secure.tls] + +[http.routers.actor-web] +rule="PathPrefix(`/`)" +entryPoints="http-actor" +middlewares=["actor-web-https"] +service="actor-web" + +[http.routers.actor-web-secure] +rule="PathPrefix(`/`)" +entryPoints="https-actor" +middlewares=["actor-gov-redirect"] +service="actor-web" +[http.routers.actor-web-secure.tls] + +[http.services] +[http.services.actor-web.loadBalancer] +[[http.services.actor-web.loadBalancer.servers]] +url="http://actor-web" + +[http.services.viewer-web.loadBalancer] +[[http.services.viewer-web.loadBalancer.servers]] +url="http://viewer-web" diff --git a/local-config/proxy/traefik.toml b/local-config/proxy/traefik.toml new file mode 100644 index 0000000000..1c570189af --- /dev/null +++ b/local-config/proxy/traefik.toml @@ -0,0 +1,19 @@ +[api] +insecure=true + +[accessLog] + +[entryPoints] +[entryPoints.http-viewer] +address=":9001" +[entryPoints.https-viewer] +address=":9041" +[entryPoints.http-actor] +address=":9002" +[entryPoints.https-actor] +address=":9042" + +[providers] +[providers.file] +filename="/etc/traefik/dynamic.toml" +watch=true diff --git a/service-front/app/config/autoload/development.local.php.dist b/service-front/app/config/autoload/development.local.php.dist index 4e97eefb09..ca69603f3d 100644 --- a/service-front/app/config/autoload/development.local.php.dist +++ b/service-front/app/config/autoload/development.local.php.dist @@ -16,9 +16,6 @@ use Mezzio\Container; use Mezzio\Middleware\ErrorResponseGenerator; return [ - 'session' => [ - 'cookie_name' => 'session', // The normally configured "__Host-session" name does not work for dev due to SSL - ], 'dependencies' => [ 'invokables' => [], 'factories' => [ diff --git a/tests/features/always-use-the-live-service-url.feature b/tests/features/always-use-the-live-service-url.feature index 98c4eb29ed..ffafbd0d95 100644 --- a/tests/features/always-use-the-live-service-url.feature +++ b/tests/features/always-use-the-live-service-url.feature @@ -1,9 +1,14 @@ @smoke -Feature: User is sent to the live sevice URL +Feature: User is sent to the live service URL As a user of the service who has been given an old web address for the service, I want to be redirected to the new live service url, So that I can continue to use the service correctly. + Background: + # This feature is implemented at the load balancer level and can be found in the terraform + # terraform/environment/region/actor_load_balancer.tf:36 + # terraform/environment/region/viewer_load_balancer.tf:35 + @smoke @viewer Scenario: I start a view journey Given I access the service with the old web address diff --git a/tests/features/user-journeys-start-on-gov-uk.feature b/tests/features/user-journeys-start-on-gov-uk.feature index 592373bad7..f7e9e17cf4 100644 --- a/tests/features/user-journeys-start-on-gov-uk.feature +++ b/tests/features/user-journeys-start-on-gov-uk.feature @@ -4,6 +4,11 @@ Feature: User Journeys start on Gov.uk I want to have an introduction to the service, So that I know what it is about and what I will need to use it + Background: + # This feature is implemented at the load balancer level and can be found in the terraform + # terraform/environment/region/actor_load_balancer.tf:76 + # terraform/environment/region/viewer_load_balancer.tf:77 + @smoke @viewer Scenario: I start a view journey Given I access the service root path diff --git a/tests/smoke/context/BaseContextTrait.php b/tests/smoke/context/BaseContextTrait.php index 6a489e8d47..5f06b97049 100644 --- a/tests/smoke/context/BaseContextTrait.php +++ b/tests/smoke/context/BaseContextTrait.php @@ -85,4 +85,23 @@ public function assertJsonResponse(string $mimeType = 'application/json'): array return $json; } + + /** + * Asserts that the current url was accessed over a https connection + * + * @throws ExpectationException + */ + public function assertHttps(): void + { + $actual = $this->ui->getSession()->getDriver()->getCurrentUrl(); + + $scheme = parse_url($actual, PHP_URL_SCHEME); + + if ($scheme !== 'https') { + throw new ExpectationException( + sprintf('Current scheme is "%s", but "https" expected.', $scheme), + $this->ui->getSession()->getDriver() + ); + } + } } diff --git a/tests/smoke/context/CommonContext.php b/tests/smoke/context/CommonContext.php index e7fe359be6..252057899c 100644 --- a/tests/smoke/context/CommonContext.php +++ b/tests/smoke/context/CommonContext.php @@ -34,8 +34,12 @@ public function iAccessTheServiceHomepage(): void */ public function iAccessTheViewerServiceInsecurely(): void { - $baseUrlHost = parse_url($this->ui->getMinkParameter('base_url'), PHP_URL_HOST); - $insecureUrl = sprintf('http://%s/home', $baseUrlHost); + $urlParts = parse_url($this->ui->getMinkParameter('base_url')); + + $insecureUrl = sprintf( + 'http://%s/home', + $urlParts['host'] . (! empty($urlParts['port']) ? ':' . $urlParts['port'] : '') + ); $this->ui->visit($insecureUrl); } @@ -47,10 +51,15 @@ public function iAccessTheViewerServiceInsecurely(): void */ public function iAccessTheServiceRoot(): void { - $baseUrlHost = parse_url($this->ui->getMinkParameter('base_url'), PHP_URL_HOST); - $rootUrl = sprintf('http://%s/', $baseUrlHost); + $urlParts = parse_url($this->ui->getMinkParameter('base_url')); + + $url = sprintf( + '%s://%s/', + $urlParts['scheme'], + $urlParts['host'] . (! empty($urlParts['port']) ? ':' . $urlParts['port'] : '') + ); - $this->ui->visit($rootUrl); + $this->ui->visit($url); } /** @@ -58,10 +67,15 @@ public function iAccessTheServiceRoot(): void */ public function iAccessTheOldServiceUrl(): void { - $oldUrlHost = parse_url($this->ui->getMinkParameter('old_base_url'), PHP_URL_HOST); - $rootUrl = sprintf('http://%s/home', $oldUrlHost); + $urlParts = parse_url($this->ui->getMinkParameter('old_base_url')); - $this->ui->visit($rootUrl); + $url = sprintf( + '%s://%s/home', + $urlParts['scheme'], + $urlParts['host'] . (! empty($urlParts['port']) ? ':' . $urlParts['port'] : '') + ); + + $this->ui->visit($url); } /** @@ -183,10 +197,7 @@ public function theViewerServiceHomepageShouldBeShownSecurely(): void { $this->ui->assertResponseStatus(StatusCodeInterface::STATUS_OK); - $baseUrlHost = parse_url($this->ui->getMinkParameter('base_url'), PHP_URL_HOST); - $expectedUrl = sprintf('https://%s/home', $baseUrlHost); - - $this->assertExactUrl($expectedUrl); + $this->assertHttps(); } /** diff --git a/tests/smoke/src/Drivers/ChromeDriver.php b/tests/smoke/src/Drivers/ChromeDriver.php index 6bb2273024..59fac8d1a6 100644 --- a/tests/smoke/src/Drivers/ChromeDriver.php +++ b/tests/smoke/src/Drivers/ChromeDriver.php @@ -11,6 +11,7 @@ class ChromeDriver implements Driver private ?Process $process; public function __construct( + bool $allowInsecureHttps = false, private string $binPath = '/usr/bin/google-chrome-stable', private array $binArguments = [ '--disable-gpu', @@ -28,6 +29,10 @@ public function __construct( ], ) { $this->process = null; + + if ($allowInsecureHttps) { + $this->binArguments[] = '--ignore-certificate-errors'; + } } public function start(): void diff --git a/tests/smoke/src/SmokeExtension.php b/tests/smoke/src/SmokeExtension.php index 6340478fd6..8c9355d1bb 100644 --- a/tests/smoke/src/SmokeExtension.php +++ b/tests/smoke/src/SmokeExtension.php @@ -8,12 +8,11 @@ use Behat\Testwork\ServiceContainer\Extension; use Behat\Testwork\ServiceContainer\ExtensionManager; use Behat\Testwork\ServiceContainer\ServiceProcessor; +use Smoke\Drivers\ChromeDriver; use Smoke\Drivers\Driver; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Smoke\Drivers\ChromeDriver; -use Smoke\DriverSubscriber; class SmokeExtension implements Extension { @@ -60,6 +59,12 @@ public function initialize(ExtensionManager $extensionManager): void */ public function configure(ArrayNodeDefinition $builder): void { + $builder + ->children() + ->scalarNode('allow_insecure_https') + ->defaultFalse() + ->end() + ->end(); } /** @@ -84,6 +89,7 @@ public function process(ContainerBuilder $container): void public function load(ContainerBuilder $container, array $config): void { $definition = new Definition(ChromeDriver::class); + $definition->addArgument($config['allow_insecure_https']); $definition->addTag(Driver::DRIVER_TAG); $container->setDefinition('smokedriver.driver.chrome', $definition);