Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .env.dev.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ MONITORFISH_API_KEY=dummy-monitorfish-key
RAPPORTNAV_URL=http://localhost:8083

# OICD
MONITORENV_OIDC_ENABLED=false
MONITORENV_OIDC_ENABLED=true
MONITORENV_OIDC_LOGIN_URL=/oauth2/authorization/proconnect
MONITORENV_OIDC_SUCCESS_URL=http://localhost:3000
MONITORENV_OIDC_ERROR_URL=http://localhost:3000/register
Expand Down Expand Up @@ -138,7 +138,7 @@ FRONTEND_METABASE_URL=https://metabase.din.developpement-durable.gouv.fr/
################################################################################
# OICD

FRONTEND_OIDC_ENABLED=false
FRONTEND_OIDC_ENABLED=true

################################################################################
# MATOMO
Expand All @@ -158,3 +158,9 @@ MONITORENV_EXT_PASSWORD=Mot de passe MonitorEnvExt
# PERF

MONITORENV_BATCH_SIZE=30

################################################################################
# Cypress Tests

MONITORENV_OIDC_PROXY_URL=http://localhost:8085
MONITORENV_KEYCLOAK_PROXY_ENABLED=false
6 changes: 6 additions & 0 deletions .env.infra.example
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,9 @@ MONITORENV_EXT_PASSWORD=
# PERF

MONITORENV_BATCH_SIZE=100

################################################################################
# Cypress Tests

MONITORENV_OIDC_PROXY_URL=
MONITORENV_KEYCLOAK_PROXY_ENABLED=
24 changes: 15 additions & 9 deletions .env.test.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ BACKEND_HTTP_PORT=8880
ENVIRONMENT=test
PROJECT_NAME=monitorenv
MONITORENV_IMAGE=monitorenv-app
MONITORENV_URL=http://localhost:8880
MONITORENV_URL=127.0.0.1

MONITORENV_VERSION=0.0.0
MONITORENV_API_KEY=DUMMY-API-KEY
Expand All @@ -23,22 +23,22 @@ MONITORFISH_API_KEY=dummy-monitorfish-key
RAPPORTNAV_URL=http://mock-rapportnav:8080

# OICD
MONITORENV_OIDC_ENABLED=false
MONITORENV_OIDC_ENABLED=true
MONITORENV_OIDC_LOGIN_URL=/oauth2/authorization/proconnect
MONITORENV_OIDC_SUCCESS_URL=http://localhost:3000
MONITORENV_OIDC_ERROR_URL=http://localhost:3000/register
MONITORENV_OIDC_SUCCESS_URL=http://localhost:8880
MONITORENV_OIDC_ERROR_URL=http://localhost:8880/register
MONITORENV_OIDC_AUTHORIZED_SIRETS=1234567890,0987654321
MONITORENV_OIDC_CLIENT_ID=monitorenv
MONITORENV_OIDC_CLIENT_SECRET=PNKVjpo4DNCVMCVYUZiS3wepPJdtdFhH
MONITORENV_OIDC_REDIRECT_URI=http://localhost:8880/login/oauth2/code/proconnect
# Backend server-side calls go directly to Keycloak
MONITORENV_OIDC_ISSUER_URI=http://localhost:8085/realms/monitor
MONITORENV_OIDC_ISSUER_URI=http://keycloak:8080/realms/monitor
# Browser-facing authorization URL uses backend proxy (rewrites Keycloak URLs)
MONITORENV_OIDC_AUTHORIZATION_URI=http://localhost:8880/realms/monitor/protocol/openid-connect/auth
# Server-side token exchange goes directly to Keycloak
MONITORENV_OIDC_TOKEN_URI=http://localhost:8085/realms/monitor/protocol/openid-connect/token
MONITORENV_OIDC_USER_INFO_URI=http://localhost:8085/realms/monitor/protocol/openid-connect/userinfo
MONITORENV_OIDC_JWK_SET_URI=http://localhost:8085/realms/monitor/protocol/openid-connect/certs
MONITORENV_OIDC_TOKEN_URI=http://keycloak:8080/realms/monitor/protocol/openid-connect/token
MONITORENV_OIDC_USER_INFO_URI=http://keycloak:8080/realms/monitor/protocol/openid-connect/userinfo
MONITORENV_OIDC_JWK_SET_URI=http://keycloak:8080/realms/monitor/protocol/openid-connect/certs
MONITORENV_OIDC_PROVIDER=proconnect
# External issuer for JWT validation (tokens from Docker Keycloak have this issuer)
MONITORENV_OIDC_ISSUER_URI_EXTERNAL=http://keycloak:8080/realms/monitor
Expand Down Expand Up @@ -130,7 +130,7 @@ FRONTEND_METABASE_URL=https://metabase.din.developpement-durable.gouv.fr
################################################################################
# OICD

FRONTEND_OIDC_ENABLED=false
FRONTEND_OIDC_ENABLED=true

################################################################################

Expand All @@ -152,3 +152,9 @@ MONITORENV_EXT_PASSWORD=
# PERF

MONITORENV_BATCH_SIZE=100

################################################################################
# Cypress Tests

MONITORENV_OIDC_PROXY_URL=http://keycloak:8080
MONITORENV_KEYCLOAK_PROXY_ENABLED=true
8 changes: 2 additions & 6 deletions .github/workflows/cicd-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MONITORENV_VERSION: ${{ needs.version.outputs.VERSION }}
VERSION: ${{needs.version.outputs.VERSION}}
VITE_CYPRESS_PORT: 8880
PUPPETEER_SKIP_DOWNLOAD: "true"
steps:
- name: Checkout
Expand All @@ -230,17 +231,12 @@ jobs:
cache-dependency-path: ./frontend/package-lock.json
node-version: 20

- name: Setup Firefox
uses: browser-actions/setup-firefox@latest
with:
firefox-version: 119.0.1

- name: Curl stubbed geoserver check
run: until $(curl --output /dev/null --silent --fail "http://localhost:8081/geoserver/wfs?service=WFS&version=1.1.0&request=GetFeature&typename=monitorenv:regulations&outputFormat=application/json&CQL_FILTER=topic=%27Ouest%20Cotentin%20Bivalves%27%20AND%20zone=%27Praires%20Ouest%20cotentin%27"); do printf '.'; sleep 5; done;

- uses: cypress-io/github-action@v6
with:
browser: firefox
browser: chrome
cache-key: cypress-${{ hashFiles('package-lock.json') }}
config-file: config/cypress.config.ts
env: PORT=8880
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ docker-push-app:
test-init-infra-env:
npm i @import-meta-env/[email protected] && npx import-meta-env-prepare -u -x ./.env.infra.example -p ./.env.test.defaults
test-run-infra-for-frontend:
export MONITORENV_VERSION=$(VERSION) && docker compose --profile=test -f docker-compose.yml -f docker-compose-test.yml up -d
export MONITORENV_VERSION=$(VERSION) && docker compose --profile=test -f docker-compose.yml -f docker-compose-test.yml -f docker-compose-cypress.yml up -d
test: test-back
cd frontend && CI=true npm run test:unit

Expand Down
1 change: 1 addition & 0 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
val sentryVersion = "8.26.0"
val flywayVersion = "11.17.0"

dependencies {

Check warning on line 68 in backend/build.gradle.kts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Group dependencies by their destination.

See more on https://sonarcloud.io/project/issues?id=MTES-MCT_monitorenv&issues=AZq2Z3ZRmeQGQm5JNIJ_&open=AZq2Z3ZRmeQGQm5JNIJ_&pullRequest=2483
implementation(platform("org.springframework.boot:spring-boot-dependencies:3.5.7"))

// Spring Boot
Expand Down Expand Up @@ -127,6 +127,7 @@

// Devtools
runtimeOnly("org.springframework.boot:spring-boot-devtools")
implementation("org.springframework.cloud:spring-cloud-gateway-proxyexchange-webmvc:4.3.0")

Check warning on line 130 in backend/build.gradle.kts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not hardcode version numbers.

See more on https://sonarcloud.io/project/issues?id=MTES-MCT_monitorenv&issues=AZq2Z3ZRmeQGQm5JNIJ-&open=AZq2Z3ZRmeQGQm5JNIJ-&pullRequest=2483

// Testing
testImplementation("org.springframework.security:spring-security-test")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package fr.gouv.cacem.monitorenv.config

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.cloud.gateway.mvc.config.ProxyExchangeArgumentResolver
import org.springframework.context.annotation.Configuration
import org.springframework.web.client.RestTemplate
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

/**
* Used by the Keycloak proxy
*/
@Configuration
@ConditionalOnProperty(
value = ["monitorfish.keycloak.proxy.enabled"],
havingValue = "true",
matchIfMissing = false,
)
class CustomProxyExchangeConfig(
private val restTemplate: RestTemplate,
) : WebMvcConfigurer {
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
resolvers.add(ProxyExchangeArgumentResolver(restTemplate))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package fr.gouv.cacem.monitorenv.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

/**
* ⚠️ DEVELOPMENT ONLY - Keycloak Proxy Configuration
*
* This configuration is ONLY used for local development and E2E testing.
* It controls whether the KeycloakProxyController is enabled to proxy
* authentication requests to a Keycloak server during development.
*
* ❌ This should NEVER be enabled in production environments.
* ✅ Only used for local development and Cypress E2E tests.
*/
@Configuration
@ConfigurationProperties(prefix = "monitorenv.keycloak.proxy")
class KeycloakProxyProperties {
/**
* ⚠️ DEVELOPMENT ONLY - Enable Keycloak proxy controller
*
* This property is only used in development: local environment and cypress tests.
* When enabled, the KeycloakProxyController will proxy authentication requests
* to a Keycloak server and rewrite URLs for local development.
*
* Default: false (disabled)
* Production: Must always be false
*/
var enabled: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class OIDCProperties {
* ⚠️ DEVELOPMENT ONLY - Keycloak proxy URL
*
* This property is only used when running Keycloak locally in E2E tests
* and local development. It specifies the URL of the build.gtKeycloak server
* and local development. It specifies the URL of the Keycloak server
* that the KeycloakProxyController should proxy requests to.
*
* Example: "http://localhost:8085"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ class SecurityConfig(
"/robots.txt",
"/favicon-32.ico",
"/asset-manifest.json",
"/proxy/**",
"/realms/**",
"/resources/**",
"/swagger-ui/**",
"v3/**",
// Used to redirect to the frontend SPA, see Spa.kt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import java.nio.charset.StandardCharsets
* ⚠️ DEVELOPMENT ONLY - Keycloak Authentication Proxy
*
* This controller is ONLY used for local development and E2E testing.
* It provides a proxy between the monitorenv application and a Keycloak server
* It provides a proxy between the MonitorEnv application and a Keycloak server
* to handle authentication flows during development.
*
* ❌ This controller is NEVER enabled in production environments.
Expand Down
4 changes: 4 additions & 0 deletions backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
server:
port: 8880
use-forward-headers: true
forward-headers-strategy: framework
compression:
enabled: true
Expand Down Expand Up @@ -51,6 +52,9 @@ monitorenv:
legicem:
id: ${MONITORENV_LEGICEM_ID}
password: ${MONITORENV_LEGICEM_PASSWORD}
keycloak:
proxy:
enabled: ${MONITORENV_KEYCLOAK_PROXY_ENABLED:false}

spring:
jmx:
Expand Down
41 changes: 41 additions & 0 deletions docker-compose-cypress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
services:
app:
environment:
- MONITORENV_OIDC_PROXY_URL=${MONITORENV_OIDC_PROXY_URL}
- MONITORENV_KEYCLOAK_PROXY_ENABLED=${MONITORENV_KEYCLOAK_PROXY_ENABLED}
- SPRING_CLOUD_GATEWAY_MVC_FORM_FILTER_ENABLED=false
depends_on:
keycloak:
condition: service_healthy
keycloak:
container_name: monitorenv_keycloak
profiles: [ test ]
image: quay.io/keycloak/keycloak:23.0.0
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
- KC_HOSTNAME_STRICT=false # See https://www.keycloak.org/server/reverseproxy for vars below
- KC_HOSTNAME=keycloak:8080
- KC_HTTP_ENABLED=true
- PROXY_ADDRESS_FORWARDING=true
- KC_PROXY=edge
- KC_PROXY_HEADERS=forwarded
- KC_HEALTH_ENABLED=true
- KC_FRONTEND_URL=http://localhost:8880/
healthcheck:
test: [ "CMD-SHELL",
"exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '200 OK'" ]
interval: 30s
timeout: 5s
retries: 20
networks:
- backend_network
ports:
- "8085:8080"
volumes:
- ./infra/configurations/keycloack:/opt/keycloak/data/import
command:
- start-dev
- --import-realm


2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ services:
keycloak:
container_name: monitorenv_keycloak
profiles: [ dev ]
image: quay.io/keycloak/keycloak:latest
image: quay.io/keycloak/keycloak:23.0.0
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
Expand Down
5 changes: 2 additions & 3 deletions frontend/config/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
import { defineConfig } from 'cypress'

const IS_CI = Boolean(process.env.CI)
const WEBAPP_PORT = IS_CI ? 8880 : 3000
const WEBAPP_HOST = 'localhost'

export default defineConfig({
chromeWebSecurity: false,
e2e: {
baseUrl: `http://${WEBAPP_HOST}:${WEBAPP_PORT}`,
baseUrl: `http://${IS_CI ? '0.0.0.0:8880' : 'localhost:3000'}`,
specPattern: ['cypress/e2e/**/*.spec.ts']
},
pageLoadTimeout: 120000,
Expand Down
3 changes: 1 addition & 2 deletions frontend/cypress/e2e/back_office/administration_form.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
context('Back Office > Administration Form', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v1/administrations`).as('getAdministrations')

cy.visit(`/backoffice/administrations`)

cy.wait('@getAdministrations')
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
context('Back Office > Administration Table > Filters', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v1/administrations`).as('getAdministrations')

cy.visit(`/backoffice/administrations`)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Successful archiving and deleting use cases are tested in `administration_form.spec.ts` for Test Idempotency purpose
context('Back Office > Administration Table > Row Actions', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v1/administrations`).as('getAdministrations')

cy.visit(`/backoffice/administrations`)
Expand Down
3 changes: 1 addition & 2 deletions frontend/cypress/e2e/back_office/control_unit_form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { expectPathToBe } from '../utils/expectPathToBe'

context('Back Office > Control Unit Form', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v2/control_units`).as('getControlUnits')

cy.visit(`/backoffice/control_units`)

cy.wait('@getControlUnits')
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
context('Back Office > Control Unit Table > Filters', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v2/control_units`).as('getControlUnits')

cy.visit(`/backoffice/control_units`)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Successful archiving and deleting use cases are tested in `control_unit_form.spec.ts` for Test Idempotency purpose
context('Back Office > Control Unit Table > Row Actions', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v2/control_units`).as('getControlUnits')

cy.visit(`/backoffice/control_units`)
Expand Down
3 changes: 1 addition & 2 deletions frontend/cypress/e2e/back_office/station_form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { faker } from '@faker-js/faker'

context('Back Office > Station Form', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v1/stations`).as('getStations')

cy.visit(`/backoffice/stations`)

cy.wait('@getStations')
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
context('Back Office > Station Table > Filters', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v1/stations`).as('getStations')

cy.visit(`/backoffice/stations`)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Successful archiving and deleting use cases are tested in `base_form.spec.ts` for Test Idempotency purpose
context('Back Office > Station Table > Row Actions', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', `/api/v1/stations`).as('getStations')

cy.visit(`/backoffice/stations`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FAKE_MAPBOX_RESPONSE } from '../../constants'

context('Main Window > Control Unit List Dialog > Filters', () => {
beforeEach(() => {
cy.login('superuser')
cy.intercept('GET', 'https://api.mapbox.com/**', FAKE_MAPBOX_RESPONSE)

cy.visit(`/`).wait(1000)
Expand Down
Loading
Loading