From ac6966dfcf6d96a91eba9fe6fed7a67e528530c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:56:54 +0200 Subject: [PATCH 01/42] chore(deps): bump axios from 1.6.8 to 1.7.4 (#652) Bumps [axios](https://github.com/axios/axios) from 1.6.8 to 1.7.4. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.6.8...v1.7.4) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Douglas Duteil --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c892e7c..2f8ed6e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "@types/morgan": "^1.9.9", "@zootools/email-spell-checker": "^1.12.0", "await-to-js": "^3.0.0", - "axios": "^1.6.8", + "axios": "^1.7.4", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", "choices.js": "^10.2.0", @@ -2139,9 +2139,10 @@ } }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 7d94b304..c3122660 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@types/morgan": "^1.9.9", "@zootools/email-spell-checker": "^1.12.0", "await-to-js": "^3.0.0", - "axios": "^1.6.8", + "axios": "^1.7.4", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", "choices.js": "^10.2.0", From 202ca23f081521e60495229911f25908fd290d90 Mon Sep 17 00:00:00 2001 From: Douglas Duteil Date: Tue, 3 Sep 2024 16:48:33 +0200 Subject: [PATCH 02/42] :recycle: (config): use zod to parse the env (#677) --- .env.sample | 13 ++- .env.test | 20 ++++ .github/workflows/end-to-end.yml | 2 +- .github/workflows/fixtures.yml | 1 + .github/workflows/test.yml | 2 +- .gitignore | 1 + installation.md | 5 +- package-lock.json | 22 +++- package.json | 5 +- src/config/env.ts | 193 ++++++++++++------------------- src/config/env.zod.ts | 138 ++++++++++++++++++++++ test/env.zod.test.ts | 128 ++++++++++++++++++++ test/get-array-from-env.test.ts | 44 ------- 13 files changed, 393 insertions(+), 181 deletions(-) create mode 100644 .env.test create mode 100644 src/config/env.zod.ts create mode 100644 test/env.zod.test.ts delete mode 100644 test/get-array-from-env.test.ts diff --git a/.env.sample b/.env.sample index 2c8b186d..b0634f19 100644 --- a/.env.sample +++ b/.env.sample @@ -6,20 +6,21 @@ DATABASE_URL=postgres://username:password@localhost:5432/dbname REDIS_URL=redis://:@127.0.0.1:6379 # email configuration DO_NOT_SEND_MAIL=True -BREVO_API_KEY= -ZAMMAD_URL= -ZAMMAD_TOKEN= +BREVO_API_KEY=____________________________ +DEBOUNCE_API_KEY=_____________ +ZAMMAD_URL=https://support.etalab.gouv.fr +ZAMMAD_TOKEN=___________________________________________________________ # disable features DO_NOT_CHECK_EMAIL_DELIVERABILITY=True CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE=True DO_NOT_USE_ANNUAIRE_EMAILS=True DO_NOT_AUTHENTICATE_BROWSER=True DISABLE_SECURITY_RESPONSE_HEADERS=True -SECURE_COOKIES="false" +SECURE_COOKIES=False SESSION_COOKIE_SECRET=moncompteprosecret SYMMETRIC_ENCRYPTION_KEY=aTrueRandom32BytesLongBase64EncodedStringAA= # A default JWKS is necessary for interacting with a client with signed payloads enabled JWKS: '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}' # INSEE credentials needed to fetch organization info -INSEE_CONSUMER_KEY=GET_YOUR_OWN -INSEE_CONSUMER_SECRET=GET_YOUR_OWN +INSEE_CONSUMER_KEY=____________________________ +INSEE_CONSUMER_SECRET=____________________________ diff --git a/.env.test b/.env.test new file mode 100644 index 00000000..9fa0dda0 --- /dev/null +++ b/.env.test @@ -0,0 +1,20 @@ +BREVO_API_KEY=____________________________ +CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE=False +DATABASE_URL=postgres://username:password@localhost:5432/dbname +DEBOUNCE_API_KEY=_____________ +DISABLE_SECURITY_RESPONSE_HEADERS=True +DO_NOT_AUTHENTICATE_BROWSER=True +DO_NOT_CHECK_EMAIL_DELIVERABILITY=False +DO_NOT_SEND_MAIL=True +DO_NOT_USE_ANNUAIRE_EMAILS=False +INSEE_CONSUMER_KEY=____________________________ +INSEE_CONSUMER_SECRET=____________________________ +JWKS: '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}' +MONCOMPTEPRO_HOST=http://localhost:3000 +PORT=3000 +REDIS_URL=redis://:@127.0.0.1:6379 +SECURE_COOKIES=False +SESSION_COOKIE_SECRET=moncompteprosecret +SYMMETRIC_ENCRYPTION_KEY=aTrueRandom32BytesLongBase64EncodedStringAA= +ZAMMAD_TOKEN=___________________________________________________________ +ZAMMAD_URL=https://support.etalab.gouv.fr diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index 6775782c..52dd8775 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -29,7 +29,7 @@ env: INSEE_CONSUMER_KEY: ${{ secrets.INSEE_CONSUMER_KEY }} INSEE_CONSUMER_SECRET: ${{ secrets.INSEE_CONSUMER_SECRET }} CYPRESS_MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} - SECURE_COOKIES: "false" + SECURE_COOKIES: "False" ZAMMAD_URL: ${{ secrets.ZAMMAD_URL }} ZAMMAD_TOKEN: ${{ secrets.ZAMMAD_TOKEN }} MODERATION_TAG: "github-action-e2e-test" diff --git a/.github/workflows/fixtures.yml b/.github/workflows/fixtures.yml index 388e2846..37bd1040 100644 --- a/.github/workflows/fixtures.yml +++ b/.github/workflows/fixtures.yml @@ -16,6 +16,7 @@ env: DATABASE_URL: postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro INSEE_CONSUMER_KEY: ${{ secrets.INSEE_CONSUMER_KEY }} INSEE_CONSUMER_SECRET: ${{ secrets.INSEE_CONSUMER_SECRET }} + NODE_ENV: test SYMMETRIC_ENCRYPTION_KEY: aTrueRandom32BytesLongBase64EncodedStringAA= jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b94b59b3..a169a8c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,4 +18,4 @@ jobs: - run: CYPRESS_INSTALL_BINARY=0 npm ci --include=dev - run: npm run test:lint - run: npm run test:type-check - - run: npm test + - run: npm run test:unit diff --git a/.gitignore b/.gitignore index bfc12690..ba93839a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.swo *.orig .env +.env*.local jwks.json *.log diff --git a/installation.md b/installation.md index 2807dfde..be1bac96 100644 --- a/installation.md +++ b/installation.md @@ -36,7 +36,10 @@ This guide provides steps to run the MonComptePro Node.js application locally wh cp .env.sample .env ``` - This will create a local copy of the .env file containing the environnement variables to run MonComptePro. + This will create a local copy of the `.env` file containing the environnement variables to run MonComptePro. + + We set the defaults in `.env` (all environments) and, following the `NODE_ENV` environment variable, the `.env.development` (development environment), `.env.production` (production environment) or `.env.test` (test environment). + We recommend to use the `.env*.local` to override the defaults variables. `.env..local` will take precedence over `.env.local` and `.env.`. 3. **Get your own INSEE api credential**: or use the one of your teammates. diff --git a/package-lock.json b/package-lock.json index 2f8ed6e8..da085bc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,8 @@ "tsx": "^4.17.0", "typescript": "^5.5.4", "vite": "^5.2.10", - "zod": "^3.23.4" + "zod": "^3.23.8", + "zod-validation-error": "^3.3.1" }, "devDependencies": { "@dotenvx/dotenvx": "^1.0.1", @@ -9589,12 +9590,25 @@ } }, "node_modules/zod": { - "version": "3.23.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.4.tgz", - "integrity": "sha512-/AtWOKbBgjzEYYQRNfoGKHObgfAZag6qUJX1VbHo2PRBgS+wfWagEY2mizjfyAPcGesrJOcx/wcl0L9WnVrHFw==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-validation-error": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.3.1.tgz", + "integrity": "sha512-uFzCZz7FQis256dqw4AhPQgD6f3pzNca/Zh62RNELavlumQB3nDIUFbF5JQfFLcMbO1s02Q7Xg/gpcOBlEnYZA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } } } } diff --git a/package.json b/package.json index c3122660..8241ba88 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test": "run-s test:*", "test:lint": "prettier '**/*.ts' --list-different", "test:type-check": " tsc --noEmit", - "test:unit": "CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE=False DO_NOT_USE_ANNUAIRE_EMAILS=False DO_NOT_CHECK_EMAIL_DELIVERABILITY=False SYMMETRIC_ENCRYPTION_KEY=aTrueRandom32BytesLongBase64EncodedStringAA= mocha --require tsx test/*.ts", + "test:unit": "NODE_ENV=test mocha --require tsx test/*.ts", "update-organization-info": "tsx scripts/update-organizations-info.ts", "watch:assets": "NODE_ENV=development vite build --clearScreen false --watch", "watch:ejs": "CHOKIDAR_USEPOLLING=true copy-and-watch --clean --watch \"src/views/**/*.ejs\" src/views/*.ejs build/views/", @@ -89,7 +89,8 @@ "tsx": "^4.17.0", "typescript": "^5.5.4", "vite": "^5.2.10", - "zod": "^3.23.4" + "zod": "^3.23.8", + "zod-validation-error": "^3.3.1" }, "devDependencies": { "@dotenvx/dotenvx": "^1.0.1", diff --git a/src/config/env.ts b/src/config/env.ts index 4d03d708..5d89210f 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -1,136 +1,85 @@ -// load variable from .env file, only used in local dev env -import "dotenv/config"; +// + +import dotenv from "dotenv"; +import { fromZodError } from "zod-validation-error"; +import { envSchema } from "./env.zod"; + +// Load variable from .env..local file, +// Load variable from .env.local file, +// Load variable from .env. file, +// Load variable from .env file +// only used in local dev env and test +dotenv.config({ + path: [ + `.env.${process.env["NODE_ENV"] ?? "development"}.local`, + ".env.local", + `.env.${process.env["NODE_ENV"] ?? "development"}`, + ".env", + ], +}); + +const parsedEnv = envSchema.safeParse(process.env, { + path: ["process.env"], +}); + +if (!parsedEnv.success) throw fromZodError(parsedEnv.error, {}); export const { - NODE_ENV, - CRISP_BASE_URL = "https://api.crisp.chat", + API_AUTH_PASSWORD, + API_AUTH_USERNAME, + BREVO_API_KEY, + CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE, + CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE, + CRISP_BASE_URL, CRISP_IDENTIFIER, CRISP_KEY, CRISP_PLUGIN_URN, - CRISP_USER_NICKNAME = "MonComptePro", + CRISP_USER_NICKNAME, CRISP_WEBSITE_ID, - DEPLOY_ENV = "preview", - PORT = 3000, - MONCOMPTEPRO_HOST = `http://localhost:${PORT}`, + DEBOUNCE_API_KEY, + DEPLOY_ENV, + DISABLE_SECURITY_RESPONSE_HEADERS, + DISPLAY_TEST_ENV_WARNING, + DO_NOT_AUTHENTICATE_BROWSER, + DO_NOT_CHECK_EMAIL_DELIVERABILITY, + DO_NOT_RATE_LIMIT, + DO_NOT_SEND_MAIL, + DO_NOT_USE_ANNUAIRE_EMAILS, + EMAIL_DELIVERABILITY_WHITELIST, + ENABLE_FIXED_ACR, + HTTP_CLIENT_TIMEOUT, + JWKS, + LOG_LEVEL, + MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES, + MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES, + MAX_SUGGESTED_ORGANIZATIONS, + MODERATION_TAG, + MONCOMPTEPRO_LABEL, + NODE_ENV, + NOTIFY_ALL_MEMBER_LIMIT, + PAIR_AUTHENTICATION_WHITELIST, + PORT, + RECENT_LOGIN_INTERVAL_IN_SECONDS, + REDIS_URL, + RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES, + SECURE_COOKIES, SENTRY_DSN, + SESSION_COOKIE_SECRET, + SESSION_MAX_AGE_IN_SECONDS, + SYMMETRIC_ENCRYPTION_KEY, + TEST_CONTACT_EMAIL, + TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS, + VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES, + ZAMMAD_TOKEN, + ZAMMAD_URL, +} = parsedEnv.data; + +export const { + MONCOMPTEPRO_HOST = `http://localhost:${PORT}`, ACCESS_LOG_PATH, - DEBOUNCE_API_KEY, - BREVO_API_KEY = "", - TEST_CONTACT_EMAIL = "mairie@yopmail.com", DATABASE_URL, - REDIS_URL = "redis://:@127.0.0.1:6379", INSEE_CONSUMER_KEY, INSEE_CONSUMER_SECRET, - API_AUTH_USERNAME = "admin", - API_AUTH_PASSWORD = "admin", - ZAMMAD_TOKEN, - ZAMMAD_URL, - MODERATION_TAG = "moderation", - // "trace" | "debug" | "info" | "warn" | "error" | "fatal" - LOG_LEVEL = "info", } = process.env; -if (!process.env.SYMMETRIC_ENCRYPTION_KEY) { - throw new Error( - "The SYMMETRIC_ENCRYPTION_KEY environment variable is not set!", - ); -} else if ( - Buffer.byteLength(process.env.SYMMETRIC_ENCRYPTION_KEY, "base64") !== 32 -) { - throw new Error( - "The SYMMETRIC_ENCRYPTION_KEY environment variable should be 32 bytes long! Use crypto.randomBytes(32).toString('base64') to generate one.", - ); -} -export const SYMMETRIC_ENCRYPTION_KEY: string = process.env - .SYMMETRIC_ENCRYPTION_KEY as string; - -export const MONCOMPTEPRO_LABEL = "MonComptePro"; export const MONCOMPTEPRO_IDENTIFIER = new URL(MONCOMPTEPRO_HOST).hostname; - -export const DO_NOT_CHECK_EMAIL_DELIVERABILITY = - process.env.DO_NOT_CHECK_EMAIL_DELIVERABILITY === "True"; -export const CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE = - process.env.CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE === "True"; -export const CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE = - process.env.CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE === "True"; -export const DO_NOT_SEND_MAIL = process.env.DO_NOT_SEND_MAIL === "True"; -export const SECURE_COOKIES = (process.env.SECURE_COOKIES || "true") === "true"; -export const DO_NOT_USE_ANNUAIRE_EMAILS = - process.env.DO_NOT_USE_ANNUAIRE_EMAILS === "True"; -export const DO_NOT_RATE_LIMIT = process.env.DO_NOT_RATE_LIMIT === "True"; -export const DISPLAY_TEST_ENV_WARNING = - process.env.DISPLAY_TEST_ENV_WARNING === "True"; -export const DO_NOT_AUTHENTICATE_BROWSER = - process.env.DO_NOT_AUTHENTICATE_BROWSER === "True"; -export const DISABLE_SECURITY_RESPONSE_HEADERS = - process.env.DISABLE_SECURITY_RESPONSE_HEADERS === "True"; -export const ENABLE_FIXED_ACR = process.env.ENABLE_FIXED_ACR === "True"; - -const getNumberFromEnv = (name: string, defaultValue: number) => { - const value = process.env[name]; - return value ? parseInt(value, 10) : defaultValue; -}; -// we wait just enough to avoid nginx default timeout of 60 seconds -export const HTTP_CLIENT_TIMEOUT = getNumberFromEnv( - "HTTP_CLIENT_TIMEOUT", - 55 * 1000, // 55 seconds in milliseconds; -); -export const SESSION_MAX_AGE_IN_SECONDS = getNumberFromEnv( - "SESSION_MAX_AGE_IN_SECONDS", - 1 * 24 * 60 * 60, // 1 day in seconds -); -export const TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS = getNumberFromEnv( - "TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS", - 3 * 30 * 24 * 60 * 60, // 3 months in seconds -); -export const RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES = - getNumberFromEnv("RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES", 60); -export const VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES = - getNumberFromEnv("VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES", 60); -export const MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES = getNumberFromEnv( - "MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES", - 60, -); -export const MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES = - getNumberFromEnv( - "MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES", - - 3 * 30 * 24 * 60, // 3 months in minutes - ); -export const NOTIFY_ALL_MEMBER_LIMIT = getNumberFromEnv( - "NOTIFY_ALL_MEMBER_LIMIT", - 50, -); -export const RECENT_LOGIN_INTERVAL_IN_SECONDS = getNumberFromEnv( - "RECENT_LOGIN_INTERVAL_IN_SECONDS", - 15 * 60, // 15 minutes -); -export const MAX_SUGGESTED_ORGANIZATIONS = getNumberFromEnv( - "MAX_SUGGESTED_ORGANIZATIONS", - 3, -); - -export const getArrayFromEnv = (name: string) => { - const value = process.env[name]; - return value - ? value - .split(",") - .map((item) => item.trim()) - .filter((item) => item) - : []; -}; - -export const EMAIL_DELIVERABILITY_WHITELIST = getArrayFromEnv( - "EMAIL_DELIVERABILITY_WHITELIST", -); -export const PAIR_AUTHENTICATION_WHITELIST = getArrayFromEnv( - "PAIR_AUTHENTICATION_WHITELIST", -); -export const SESSION_COOKIE_SECRET = getArrayFromEnv("SESSION_COOKIE_SECRET"); - -export const getJSONFromEnv = (name: string) => { - const value = process.env[name]; - return value ? JSON.parse(value) : {}; -}; - -export const JWKS = getJSONFromEnv("JWKS"); diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts new file mode 100644 index 00000000..278208a2 --- /dev/null +++ b/src/config/env.zod.ts @@ -0,0 +1,138 @@ +import { z, type ZodTypeAny } from "zod"; + +// + +export const apiEnvSchema = z.object({ + API_AUTH_PASSWORD: z.string().default("admin"), + API_AUTH_USERNAME: z.string().default("admin"), +}); + +export const crispEnvSchema = z.object({ + CRISP_BASE_URL: z.string().url().default("https://api.crisp.chat"), + CRISP_IDENTIFIER: z.string().default(""), + CRISP_KEY: z.string().default(""), + CRISP_PLUGIN_URN: z.string().default(""), + CRISP_USER_NICKNAME: z.string().default("MonComptePro"), + CRISP_WEBSITE_ID: z.string().default(""), +}); + +// + +export const envSchema = z + .object({ + ACCESS_LOG_PATH: z.string().optional(), + BREVO_API_KEY: z.string().default(""), + CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE: zodTrueFalseBoolean().default("False"), + CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE: + zodTrueFalseBoolean().default("False"), + DATABASE_URL: z.string().url(), + DEBOUNCE_API_KEY: z.string().optional(), + DEPLOY_ENV: z.enum(["preview", "production"]).default("preview"), + DISABLE_SECURITY_RESPONSE_HEADERS: zodTrueFalseBoolean().default("False"), + DISPLAY_TEST_ENV_WARNING: zodTrueFalseBoolean().default("False"), + DO_NOT_AUTHENTICATE_BROWSER: zodTrueFalseBoolean().default("False"), + DO_NOT_CHECK_EMAIL_DELIVERABILITY: zodTrueFalseBoolean().default("False"), + DO_NOT_RATE_LIMIT: zodTrueFalseBoolean().default("False"), + DO_NOT_SEND_MAIL: zodTrueFalseBoolean().default("False"), + DO_NOT_USE_ANNUAIRE_EMAILS: zodTrueFalseBoolean().default("False"), + EMAIL_DELIVERABILITY_WHITELIST: zCoerceArray(z.string()).default(""), + ENABLE_FIXED_ACR: zodTrueFalseBoolean().default("False"), + HTTP_CLIENT_TIMEOUT: z.coerce + .number() + .int() + .nonnegative() + .default(55 * 1_000), // 55 seconds in milliseconds; + INSEE_CONSUMER_KEY: z.string(), + INSEE_CONSUMER_SECRET: z.string(), + JWKS: zCoerceJson().pipe(z.object({ keys: z.array(z.any()) })), + LOG_LEVEL: z + .enum(["trace", "debug", "info", "warn", "error", "fatal"]) + .default("info"), + MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(60), // 1 hour in minutes + MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(3 * 30 * 24 * 60), // 3 months in minutes + MAX_SUGGESTED_ORGANIZATIONS: z.coerce + .number() + .int() + .nonnegative() + .default(3), + MODERATION_TAG: z.string().default("moderation"), + MONCOMPTEPRO_HOST: z.string().url().default("http://localhost:3000"), + MONCOMPTEPRO_LABEL: z.string().default("MonComptePro"), + NODE_ENV: z + .enum(["production", "development", "test"]) + .default("development"), + NOTIFY_ALL_MEMBER_LIMIT: z.coerce.number().int().nonnegative().default(50), + PAIR_AUTHENTICATION_WHITELIST: zCoerceArray(z.string()).default(""), + PORT: z.coerce.number().int().nonnegative().default(3000), + RECENT_LOGIN_INTERVAL_IN_SECONDS: z.coerce + .number() + .int() + .nonnegative() + .default(15 * 60), // 15 minutes + REDIS_URL: z.string().url().default("redis://:@127.0.0.1:6379"), + RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(60), // 1 hour in minutes + SECURE_COOKIES: zodTrueFalseBoolean().default("True"), + SENTRY_DSN: z.string().default(""), + SESSION_COOKIE_SECRET: zCoerceArray(z.string()).default(""), + SESSION_MAX_AGE_IN_SECONDS: z.coerce + .number() + .int() + .nonnegative() + .default(1 * 24 * 60 * 60), // 1 day in seconds + SYMMETRIC_ENCRYPTION_KEY: z.string().base64({ + message: + "The SYMMETRIC_ENCRYPTION_KEY environment variable should be 32 bytes long! Use crypto.randomBytes(32).toString('base64') to generate one.", + }), + TEST_CONTACT_EMAIL: z.string().default("mairie@yopmail.com"), + TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS: z.coerce + .number() + .int() + .nonnegative() + .default(3 * 30 * 24 * 60 * 60), // 3 months in seconds + VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(60), // 1 hour in minutes + ZAMMAD_TOKEN: z.string(), + ZAMMAD_URL: z.string().url(), + }) + .merge(apiEnvSchema) + .merge(crispEnvSchema); + +// +export function zodTrueFalseBoolean() { + return z.enum(["True", "False"]).transform((v: string) => v === "True"); +} + +// + +export function zCoerceArray(schema: T) { + return z + .string() + .transform((value) => (value === "" ? [] : value.split(","))) + .pipe(z.array(schema)); +} + +export function zCoerceJson() { + return z.string().transform((str, ctx) => { + try { + return JSON.parse(str); + } catch (e) { + ctx.addIssue({ code: "custom", message: "Invalid JSON" }); + return z.NEVER; + } + }); +} diff --git a/test/env.zod.test.ts b/test/env.zod.test.ts new file mode 100644 index 00000000..1912eec4 --- /dev/null +++ b/test/env.zod.test.ts @@ -0,0 +1,128 @@ +// + +import { expect } from "chai"; +import { config } from "dotenv"; +import { test } from "mocha"; +import { envSchema } from "../src/config/env.zod"; + +// + +test("default sample env", () => { + const sample_env = {}; + config({ path: ".env.sample", processEnv: sample_env }); + + const env = envSchema.parse(sample_env); + + expect(env).to.deep.equal({ + API_AUTH_PASSWORD: "admin", + API_AUTH_USERNAME: "admin", + BREVO_API_KEY: "____________________________", + CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE: false, + CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE: true, + CRISP_BASE_URL: "https://api.crisp.chat", + CRISP_IDENTIFIER: "", + CRISP_KEY: "", + CRISP_PLUGIN_URN: "", + CRISP_USER_NICKNAME: "MonComptePro", + CRISP_WEBSITE_ID: "", + DATABASE_URL: "postgres://username:password@localhost:5432/dbname", + DEBOUNCE_API_KEY: "_____________", + DEPLOY_ENV: "preview", + DISABLE_SECURITY_RESPONSE_HEADERS: true, + DISPLAY_TEST_ENV_WARNING: false, + DO_NOT_AUTHENTICATE_BROWSER: true, + DO_NOT_CHECK_EMAIL_DELIVERABILITY: true, + DO_NOT_RATE_LIMIT: false, + DO_NOT_SEND_MAIL: true, + DO_NOT_USE_ANNUAIRE_EMAILS: true, + EMAIL_DELIVERABILITY_WHITELIST: [], + ENABLE_FIXED_ACR: false, + HTTP_CLIENT_TIMEOUT: 55000, + INSEE_CONSUMER_KEY: "____________________________", + INSEE_CONSUMER_SECRET: "____________________________", + JWKS, + LOG_LEVEL: "info", + MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES: 60, + MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES: 129600, + MAX_SUGGESTED_ORGANIZATIONS: 3, + MODERATION_TAG: "moderation", + MONCOMPTEPRO_HOST: "http://localhost:3000", + MONCOMPTEPRO_LABEL: "MonComptePro", + NODE_ENV: "development", + NOTIFY_ALL_MEMBER_LIMIT: 50, + PAIR_AUTHENTICATION_WHITELIST: [], + PORT: 3000, + RECENT_LOGIN_INTERVAL_IN_SECONDS: 900, + REDIS_URL: "redis://:@127.0.0.1:6379", + RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES: 60, + SECURE_COOKIES: false, + SENTRY_DSN: "", + SESSION_COOKIE_SECRET: ["moncompteprosecret"], + SESSION_MAX_AGE_IN_SECONDS: 86400, + SYMMETRIC_ENCRYPTION_KEY: "aTrueRandom32BytesLongBase64EncodedStringAA=", + TEST_CONTACT_EMAIL: "mairie@yopmail.com", + TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS: 7776000, + VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES: 60, + ZAMMAD_TOKEN: "___________________________________________________________", + ZAMMAD_URL: "https://support.etalab.gouv.fr", + }); +}); + +// + +const JWKS = { + keys: [ + { + crv: "P-256", + d: "taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y", + kid: "GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE", + kty: "EC", + use: "enc", + x: "UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q", + y: "YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E", + }, + { + crv: "P-256", + d: "TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4", + kid: "TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8", + kty: "EC", + use: "sig", + x: "2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs", + y: "Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q", + }, + { + crv: "Ed25519", + d: "WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4", + kid: "onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU", + kty: "OKP", + use: "sig", + x: "NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4", + }, + { + d: "qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ", + dp: "g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk", + dq: "1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU", + e: "AQAB", + kid: "kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4", + kty: "RSA", + n: "5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ", + p: "-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs", + q: "7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs", + qi: "4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig", + use: "enc", + }, + { + d: "L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ", + dp: "Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE", + dq: "SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU", + e: "AQAB", + kid: "lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo", + kty: "RSA", + n: "6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ", + p: "_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E", + q: "6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0", + qi: "QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08", + use: "sig", + }, + ], +}; diff --git a/test/get-array-from-env.test.ts b/test/get-array-from-env.test.ts deleted file mode 100644 index 335cc73e..00000000 --- a/test/get-array-from-env.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { assert } from "chai"; -import { getArrayFromEnv } from "../src/config/env"; // Replace with the actual path of your module - -describe("getArrayFromEnv", () => { - let oldEnv: NodeJS.ProcessEnv; - - beforeEach(() => { - // Backup the actual environment - oldEnv = process.env; - }); - - afterEach(() => { - // Restore the actual environment after each test - process.env = oldEnv; - }); - - it("should return empty array for undefined", () => { - // Create a new environment mock - process.env = { ...oldEnv }; - const envValue = "MY_VARIABLE"; - assert.deepEqual(getArrayFromEnv(envValue), []); - }); - - it("should return array", () => { - // Create a new environment mock - process.env = { ...oldEnv, MY_VARIABLE: "" }; - const envValue = "MY_VARIABLE"; - assert.deepEqual(getArrayFromEnv(envValue), []); - }); - - it("should return array", () => { - // Create a new environment mock - process.env = { ...oldEnv, MY_VARIABLE: "value1,value2," }; - const envValue = "MY_VARIABLE"; - assert.deepEqual(getArrayFromEnv(envValue), ["value1", "value2"]); - }); - - it("should return array", () => { - // Create a new environment mock - process.env = { ...oldEnv, MY_VARIABLE: "value1, value2 " }; - const envValue = "MY_VARIABLE"; - assert.deepEqual(getArrayFromEnv(envValue), ["value1", "value2"]); - }); -}); From 1310af524ef3305a02525d57142840cc3675fdaf Mon Sep 17 00:00:00 2001 From: Douglas Duteil Date: Tue, 3 Sep 2024 17:10:38 +0200 Subject: [PATCH 03/42] :pirate_flag: (crisp): add delay hack in the env var (#680) --- src/config/env.ts | 1 + src/config/env.zod.ts | 1 + src/connectors/crisp.ts | 3 ++- test/env.zod.test.ts | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/env.ts b/src/config/env.ts index 5d89210f..a0fae9cc 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -34,6 +34,7 @@ export const { CRISP_IDENTIFIER, CRISP_KEY, CRISP_PLUGIN_URN, + CRISP_RESOLVE_DELAY, CRISP_USER_NICKNAME, CRISP_WEBSITE_ID, DEBOUNCE_API_KEY, diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts index 278208a2..3485c1da 100644 --- a/src/config/env.zod.ts +++ b/src/config/env.zod.ts @@ -12,6 +12,7 @@ export const crispEnvSchema = z.object({ CRISP_IDENTIFIER: z.string().default(""), CRISP_KEY: z.string().default(""), CRISP_PLUGIN_URN: z.string().default(""), + CRISP_RESOLVE_DELAY: z.coerce.number().int().nonnegative().default(1_000), // 1 second CRISP_USER_NICKNAME: z.string().default("MonComptePro"), CRISP_WEBSITE_ID: z.string().default(""), }); diff --git a/src/connectors/crisp.ts b/src/connectors/crisp.ts index 7ff6feb4..361d78f0 100644 --- a/src/connectors/crisp.ts +++ b/src/connectors/crisp.ts @@ -12,6 +12,7 @@ import { CRISP_IDENTIFIER, CRISP_KEY, CRISP_PLUGIN_URN, + CRISP_RESOLVE_DELAY, CRISP_USER_NICKNAME, CRISP_WEBSITE_ID, } from "../config/env"; @@ -75,7 +76,7 @@ export async function startCripsConversation({ // HACK(douglasduteil): Wait for the message to be sent // Crisp seems to have a delay between the message being sent and the state being updated - await new Promise((resolve) => setTimeout(resolve, 500)); + await new Promise((resolve) => setTimeout(resolve, CRISP_RESOLVE_DELAY)); await fetch_crisp(config, { endpoint: `/v1/website/${config.website_id}/conversation/${session_id}/state`, diff --git a/test/env.zod.test.ts b/test/env.zod.test.ts index 1912eec4..81ffdbeb 100644 --- a/test/env.zod.test.ts +++ b/test/env.zod.test.ts @@ -23,6 +23,7 @@ test("default sample env", () => { CRISP_IDENTIFIER: "", CRISP_KEY: "", CRISP_PLUGIN_URN: "", + CRISP_RESOLVE_DELAY: 1000, CRISP_USER_NICKNAME: "MonComptePro", CRISP_WEBSITE_ID: "", DATABASE_URL: "postgres://username:password@localhost:5432/dbname", From 61ab67262bb57c55203df188d091fdc05cc26b70 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Thu, 29 Aug 2024 14:59:34 +0200 Subject: [PATCH 04/42] refactor(sponsor): remove middleware/env/routes about authent with sponsor --- src/middlewares/user.ts | 161 ------------------------------------- src/routers/interaction.ts | 2 - src/routers/user.ts | 61 -------------- 3 files changed, 224 deletions(-) diff --git a/src/middlewares/user.ts b/src/middlewares/user.ts index 56b67cf4..699e483f 100644 --- a/src/middlewares/user.ts +++ b/src/middlewares/user.ts @@ -1,16 +1,9 @@ import { NextFunction, Request, Response } from "express"; import HttpErrors from "http-errors"; import { isEmpty } from "lodash-es"; -import { PAIR_AUTHENTICATION_WHITELIST } from "../config/env"; import { UserNotFoundError } from "../config/errors"; import { is2FACapable, shouldForce2faForUser } from "../managers/2fa"; import { isBrowserTrustedForUser } from "../managers/browser-authentication"; -import { - greetForJoiningOrganization, - markAsGouvFrDomain, - markAsWhitelisted, - notifyAllMembers, -} from "../managers/organization/authentication-by-peers"; import { getOrganizationsByUserId, selectOrganization, @@ -27,9 +20,7 @@ import { getPartialUserFromUnauthenticatedSession, } from "../managers/session/unauthenticated"; import { needsEmailVerificationRenewal } from "../managers/user"; -import { getInternalActiveUsers } from "../repositories/organization/getters"; import { getSelectedOrganizationId } from "../repositories/redis/selected-organization"; -import { getEmailDomain, usesAGouvFrDomain } from "../services/email"; import { getTrustedReferrerPath } from "../services/security"; import { usesAuthHeaders } from "../services/uses-auth-headers"; @@ -365,155 +356,3 @@ export const checkUserHasSelectedAnOrganizationMiddleware = ( next(error); } }); - -export const checkUserHasNoPendingOfficialContactEmailVerificationMiddleware = ( - req: Request, - res: Response, - next: NextFunction, -) => - checkUserHasSelectedAnOrganizationMiddleware(req, res, async (error) => { - try { - if (error) return next(error); - - const userOrganisations = await getOrganizationsByUserId( - getUserFromAuthenticatedSession(req).id, - ); - - let organizationThatNeedsOfficialContactEmailVerification; - if (req.session.mustReturnOneOrganizationInPayload) { - const selectedOrganizationId = await getSelectedOrganizationId( - getUserFromAuthenticatedSession(req).id, - ); - - organizationThatNeedsOfficialContactEmailVerification = - userOrganisations.find( - ({ id, needs_official_contact_email_verification }) => - needs_official_contact_email_verification && - id === selectedOrganizationId, - ); - } else { - organizationThatNeedsOfficialContactEmailVerification = - userOrganisations.find( - ({ needs_official_contact_email_verification }) => - needs_official_contact_email_verification, - ); - } - - if (!isEmpty(organizationThatNeedsOfficialContactEmailVerification)) { - return res.redirect( - `/users/official-contact-email-verification/${organizationThatNeedsOfficialContactEmailVerification.id}`, - ); - } - - return next(); - } catch (error) { - next(error); - } - }); - -export const checkUserHasBeenAuthenticatedByPeersMiddleware = ( - req: Request, - res: Response, - next: NextFunction, -) => - checkUserHasNoPendingOfficialContactEmailVerificationMiddleware( - req, - res, - async (error) => { - try { - if (error) return next(error); - - const { id: user_id, email } = getUserFromAuthenticatedSession(req); - - const userOrganisations = await getOrganizationsByUserId(user_id); - - let organizationThatNeedsAuthenticationByPeers; - if (req.session.mustReturnOneOrganizationInPayload) { - const selectedOrganizationId = - await getSelectedOrganizationId(user_id); - - organizationThatNeedsAuthenticationByPeers = userOrganisations.find( - ({ id, authentication_by_peers_type }) => - !authentication_by_peers_type && id === selectedOrganizationId, - ); - } else { - organizationThatNeedsAuthenticationByPeers = userOrganisations.find( - ({ authentication_by_peers_type }) => !authentication_by_peers_type, - ); - } - - if (!isEmpty(organizationThatNeedsAuthenticationByPeers)) { - const organization_id = organizationThatNeedsAuthenticationByPeers.id; - const internalActiveUsers = - await getInternalActiveUsers(organization_id); - const otherInternalUsers = internalActiveUsers.filter( - ({ email: e }) => e !== email, - ); - - if (PAIR_AUTHENTICATION_WHITELIST.includes(getEmailDomain(email))) { - await markAsWhitelisted({ user_id, organization_id }); - } else if (usesAGouvFrDomain(email)) { - await markAsGouvFrDomain({ user_id, organization_id }); - } else if (otherInternalUsers.length > 0) { - return res.redirect(`/users/choose-sponsor/${organization_id}`); - } else { - await notifyAllMembers({ user_id, organization_id }); - } - } - - return next(); - } catch (error) { - next(error); - } - }, - ); - -export const checkUserHasBeenGreetedForJoiningOrganizationMiddleware = ( - req: Request, - res: Response, - next: NextFunction, -) => - checkUserHasBeenAuthenticatedByPeersMiddleware(req, res, async (error) => { - try { - if (error) return next(error); - - const userOrganisations = await getOrganizationsByUserId( - getUserFromAuthenticatedSession(req).id, - ); - - let organizationThatNeedsGreetings; - if (req.session.mustReturnOneOrganizationInPayload) { - const selectedOrganizationId = await getSelectedOrganizationId( - getUserFromAuthenticatedSession(req).id, - ); - - organizationThatNeedsGreetings = userOrganisations.find( - ({ id, has_been_greeted }) => - !has_been_greeted && id === selectedOrganizationId, - ); - } else { - organizationThatNeedsGreetings = userOrganisations.find( - ({ has_been_greeted }) => !has_been_greeted, - ); - } - - if (!isEmpty(organizationThatNeedsGreetings)) { - await greetForJoiningOrganization({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id: organizationThatNeedsGreetings.id, - }); - - return res.redirect( - `/users/welcome/${organizationThatNeedsGreetings.id}`, - ); - } - - return next(); - } catch (error) { - next(error); - } - }); - -// check that user go through all requirements before issuing a session -export const checkUserSignInRequirementsMiddleware = - checkUserHasBeenGreetedForJoiningOrganizationMiddleware; diff --git a/src/routers/interaction.ts b/src/routers/interaction.ts index 399f9835..ba8a826e 100644 --- a/src/routers/interaction.ts +++ b/src/routers/interaction.ts @@ -5,7 +5,6 @@ import { interactionEndControllerFactory, interactionStartControllerFactory, } from "../controllers/interaction"; -import { checkUserSignInRequirementsMiddleware } from "../middlewares/user"; export const interactionRouter = (oidcProvider: Provider) => { const interactionRouter = Router(); @@ -20,7 +19,6 @@ export const interactionRouter = (oidcProvider: Provider) => { ); interactionRouter.get( "/:grant/login", - checkUserSignInRequirementsMiddleware, interactionEndControllerFactory(oidcProvider), ); diff --git a/src/routers/user.ts b/src/routers/user.ts index adca9be1..abe25e14 100644 --- a/src/routers/user.ts +++ b/src/routers/user.ts @@ -11,13 +11,6 @@ import { } from "../controllers/organization"; import { postSignInWithAuthenticatorAppController } from "../controllers/totp"; import { get2faSignInController } from "../controllers/user/2fa-sign-in"; -import { - getChooseSponsorController, - getNoSponsorFoundController, - getSponsorValidationController, - postChooseSponsorMiddleware, - postNoSponsorFoundMiddleware, -} from "../controllers/user/choose-sponsor"; import { postDeleteUserController } from "../controllers/user/delete"; import { issueSessionOrRedirectController } from "../controllers/user/issue-session-or-redirect"; import { @@ -78,12 +71,10 @@ import { checkUserCanAccessAdminMiddleware, checkUserCanAccessAppMiddleware, checkUserHasAtLeastOneOrganizationMiddleware, - checkUserHasNoPendingOfficialContactEmailVerificationMiddleware, checkUserHasPersonalInformationsMiddleware, checkUserHasSelectedAnOrganizationMiddleware, checkUserIsConnectedMiddleware, checkUserIsVerifiedMiddleware, - checkUserSignInRequirementsMiddleware, checkUserTwoFactorAuthMiddleware, } from "../middlewares/user"; @@ -133,7 +124,6 @@ export const userRouter = () => { passwordRateLimiterMiddleware, csrfProtectionMiddleware, postSignInMiddleware, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( @@ -148,7 +138,6 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, postSignUpController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -164,7 +153,6 @@ export const userRouter = () => { checkUserIsConnectedMiddleware, csrfProtectionMiddleware, postSignInWithAuthenticatorAppController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.post( @@ -173,7 +161,6 @@ export const userRouter = () => { checkUserIsConnectedMiddleware, csrfProtectionMiddleware, postVerifySecondFactorAuthenticationController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -189,7 +176,6 @@ export const userRouter = () => { checkUserTwoFactorAuthMiddleware, csrfProtectionMiddleware, postVerifyEmailController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.post( @@ -205,7 +191,6 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, postSendMagicLinkController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get("/magic-link-sent", getMagicLinkSentController); @@ -220,7 +205,6 @@ export const userRouter = () => { rateLimiterMiddleware, csrfProtectionMiddleware, postSignInWithMagicLinkController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( @@ -229,7 +213,6 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, getSignInWithPasskeyController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -239,7 +222,6 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, postVerifyFirstFactorAuthenticationController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( @@ -277,7 +259,6 @@ export const userRouter = () => { checkUserIsVerifiedMiddleware, csrfProtectionMiddleware, postPersonalInformationsController, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -300,7 +281,6 @@ export const userRouter = () => { checkUserHasPersonalInformationsMiddleware, csrfProtectionMiddleware, postJoinOrganizationMiddleware, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -350,7 +330,6 @@ export const userRouter = () => { checkUserHasAtLeastOneOrganizationMiddleware, csrfProtectionMiddleware, postSelectOrganizationMiddleware, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -368,57 +347,17 @@ export const userRouter = () => { checkUserHasSelectedAnOrganizationMiddleware, csrfProtectionMiddleware, postOfficialContactEmailVerificationMiddleware, - checkUserSignInRequirementsMiddleware, - issueSessionOrRedirectController, - ); - - userRouter.get( - "/choose-sponsor/:organization_id", - rateLimiterMiddleware, - checkUserHasNoPendingOfficialContactEmailVerificationMiddleware, - csrfProtectionMiddleware, - getChooseSponsorController, - ); - - userRouter.post( - "/choose-sponsor/:organization_id", - rateLimiterMiddleware, - checkUserHasNoPendingOfficialContactEmailVerificationMiddleware, - csrfProtectionMiddleware, - postChooseSponsorMiddleware, - checkUserSignInRequirementsMiddleware, - issueSessionOrRedirectController, - ); - - userRouter.get("/sponsor-validation", getSponsorValidationController); - - userRouter.get( - "/no-sponsor-found/:organization_id", - checkUserHasNoPendingOfficialContactEmailVerificationMiddleware, - csrfProtectionMiddleware, - getNoSponsorFoundController, - ); - - userRouter.post( - "/no-sponsor-found/:organization_id", - rateLimiterMiddleware, - checkUserHasNoPendingOfficialContactEmailVerificationMiddleware, - csrfProtectionMiddleware, - postNoSponsorFoundMiddleware, - checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( "/welcome/:organization_id", - checkUserSignInRequirementsMiddleware, csrfProtectionMiddleware, getWelcomeController, ); userRouter.post( "/welcome", rateLimiterMiddleware, - checkUserSignInRequirementsMiddleware, csrfProtectionMiddleware, issueSessionOrRedirectController, ); From 383c9e23d63fd1da678a3192235fffeb040aaeda Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 11:32:31 +0200 Subject: [PATCH 05/42] refactor(views): delete/update views about sponsorship --- src/views/user/choose-sponsor.ejs | 46 ----------------------------- src/views/user/no-sponsor-found.ejs | 38 ------------------------ src/views/user/welcome.ejs | 7 ----- 3 files changed, 91 deletions(-) delete mode 100644 src/views/user/choose-sponsor.ejs delete mode 100644 src/views/user/no-sponsor-found.ejs diff --git a/src/views/user/choose-sponsor.ejs b/src/views/user/choose-sponsor.ejs deleted file mode 100644 index f853baaf..00000000 --- a/src/views/user/choose-sponsor.ejs +++ /dev/null @@ -1,46 +0,0 @@ -
- <%- include('../partials/notifications.ejs', {notifications: notifications}) %> -
-

- Sélectionner un parrain -

-
-

- Cette personne sera informée que vous rejoignez l’organisation <%= libelle %> sur MonComptePro. -

-
- - -
- - -
- <%- include('../partials/card-button-container.ejs', {label: 'Valider', showGoBackButton: true}) %> -
-
-
- -
- - diff --git a/src/views/user/no-sponsor-found.ejs b/src/views/user/no-sponsor-found.ejs deleted file mode 100644 index e5a99d84..00000000 --- a/src/views/user/no-sponsor-found.ejs +++ /dev/null @@ -1,38 +0,0 @@ -
-

Vérifier votre profil sans parrainage

-
-

- Sans parrainage, nous notifierons certains des membres de votre - organisation de la création de votre compte. -

- -
- - - -
- - - -
-
-
-
- diff --git a/src/views/user/welcome.ejs b/src/views/user/welcome.ejs index f7213772..9095522d 100644 --- a/src/views/user/welcome.ejs +++ b/src/views/user/welcome.ejs @@ -13,13 +13,6 @@

Vous avez reçu un email de confirmation.

- - <% if (sponsor_label) { %> -

- Votre parrain <%= sponsor_label %> a été informé que vous avez rejoint son organisation sur MonComptePro. -

- <% } %> -
From 5a153d8571f014f828d447f9ea206feffe3375ee Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 12:01:58 +0200 Subject: [PATCH 06/42] refactor(controllers): delete/update views about sponsorship --- src/controllers/api.ts | 5 - src/controllers/user/choose-sponsor.ts | 173 ------------------------- src/controllers/user/welcome.ts | 16 --- 3 files changed, 194 deletions(-) delete mode 100644 src/controllers/user/choose-sponsor.ts diff --git a/src/controllers/api.ts b/src/controllers/api.ts index 64c13c7e..3af9ab90 100644 --- a/src/controllers/api.ts +++ b/src/controllers/api.ts @@ -9,7 +9,6 @@ import { import notificationMessages from "../config/notification-messages"; import { getOrganizationInfo } from "../connectors/api-sirene"; import { sendModerationProcessedEmail } from "../managers/moderation"; -import { notifyAllMembers } from "../managers/organization/authentication-by-peers"; import { forceJoinOrganization } from "../managers/organization/join"; import { markDomainAsVerified } from "../managers/organization/main"; import { getUserOrganizationLink } from "../repositories/organization/getters"; @@ -103,10 +102,6 @@ export const postForceJoinOrganizationController = async ( }); } - if (!userOrganizationLink.authentication_by_peers_type) { - await notifyAllMembers({ user_id, organization_id }); - } - return res.json({}); } catch (e) { logger.error(e); diff --git a/src/controllers/user/choose-sponsor.ts b/src/controllers/user/choose-sponsor.ts deleted file mode 100644 index 9bfbdf6e..00000000 --- a/src/controllers/user/choose-sponsor.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import HttpErrors from "http-errors"; -import { z } from "zod"; -import { - NotFoundError, - UserHasAlreadyBeenAuthenticatedByPeers, -} from "../../config/errors"; -import { - chooseSponsor, - getOrganizationLabel, - getSponsorOptions, - notifyAllMembers, -} from "../../managers/organization/authentication-by-peers"; -import { getOrganizationById } from "../../managers/organization/main"; -import { getUserFromAuthenticatedSession } from "../../managers/session/authenticated"; -import { csrfToken } from "../../middlewares/csrf-protection"; -import { idSchema } from "../../services/custom-zod-schemas"; -import getNotificationsFromRequest from "../../services/get-notifications-from-request"; - -const { NotFound } = HttpErrors; -export const getChooseSponsorController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - try { - const schema = z.object({ - organization_id: idSchema(), - }); - - const { organization_id } = await schema.parseAsync(req.params); - - const sponsorOptions = await getSponsorOptions({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id, - }); - - const { cached_libelle: libelle } = - (await getOrganizationById(organization_id))!; - - return res.render("user/choose-sponsor", { - pageTitle: "Sélectionner un parrain", - notifications: await getNotificationsFromRequest(req), - csrfToken: csrfToken(req), - organization_id, - sponsorOptions, - libelle, - }); - } catch (error) { - if (error instanceof NotFoundError) { - next(new NotFound()); - } - - next(error); - } -}; - -export const postChooseSponsorMiddleware = async ( - req: Request, - _res: Response, - next: NextFunction, -) => { - try { - const schema = z.object({ - params: z.object({ - organization_id: idSchema(), - }), - body: z.object({ - sponsor_id: idSchema(), - }), - }); - - const { - params: { organization_id }, - body: { sponsor_id }, - } = await schema.parseAsync({ - params: req.params, - body: req.body, - }); - - await chooseSponsor({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id, - sponsor_id, - }); - - next(); - } catch (error) { - if (error instanceof NotFoundError) { - next(new NotFound()); - } - - next(error); - } -}; - -export const getSponsorValidationController = async ( - _req: Request, - res: Response, - next: NextFunction, -) => { - try { - return res.render("user/sponsor-validation", { - pageTitle: "Compte vérifié", - }); - } catch (error) { - next(error); - } -}; - -export const getNoSponsorFoundController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - try { - const schema = z.object({ - organization_id: idSchema(), - }); - - const { organization_id } = await schema.parseAsync(req.params); - - // we call this fonction only to ensure user is in organization - await getOrganizationLabel({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id, - }); - - return res.render("user/no-sponsor-found", { - pageTitle: "Vérifier votre profil sans parrainage", - csrfToken: csrfToken(req), - organization_id, - }); - } catch (error) { - if (error instanceof NotFoundError) { - next(new NotFound()); - } - - next(error); - } -}; - -export const postNoSponsorFoundMiddleware = async ( - req: Request, - _res: Response, - next: NextFunction, -) => { - try { - const schema = z.object({ - organization_id: idSchema(), - }); - - const { organization_id } = await schema.parseAsync(req.params); - - await notifyAllMembers({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id, - }); - - return next(); - } catch (error) { - if (error instanceof NotFoundError) { - next(new NotFound()); - } - - if (error instanceof UserHasAlreadyBeenAuthenticatedByPeers) { - // fail silently - return next(); - } - - next(error); - } -}; diff --git a/src/controllers/user/welcome.ts b/src/controllers/user/welcome.ts index 0031c36e..aab04828 100644 --- a/src/controllers/user/welcome.ts +++ b/src/controllers/user/welcome.ts @@ -1,8 +1,4 @@ import { NextFunction, Request, Response } from "express"; -import { z } from "zod"; -import { idSchema } from "../../services/custom-zod-schemas"; - -import { getSponsorLabel } from "../../managers/organization/authentication-by-peers"; import { getUserFromAuthenticatedSession, updateUserInAuthenticatedSession, @@ -16,17 +12,6 @@ export const getWelcomeController = async ( next: NextFunction, ) => { try { - const schema = z.object({ - organization_id: idSchema(), - }); - - const { organization_id } = await schema.parseAsync(req.params); - - const sponsor_label = await getSponsorLabel({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id, - }); - let user = getUserFromAuthenticatedSession(req); const showInclusionConnectOnboardingHelp = user.needs_inclusionconnect_onboarding_help; @@ -38,7 +23,6 @@ export const getWelcomeController = async ( return res.render("user/welcome", { pageTitle: "Compte créé", csrfToken: csrfToken(req), - sponsor_label, illustration: "welcome.svg", showInclusionConnectOnboardingHelp, }); From 4a46cff34efd6be50adc94903b5a8ad2572e3084 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 12:03:24 +0200 Subject: [PATCH 07/42] refactor(managers): delete/update views about sponsorship --- .../organization/authentication-by-peers.ts | 314 ------------------ 1 file changed, 314 deletions(-) delete mode 100644 src/managers/organization/authentication-by-peers.ts diff --git a/src/managers/organization/authentication-by-peers.ts b/src/managers/organization/authentication-by-peers.ts deleted file mode 100644 index 8603db62..00000000 --- a/src/managers/organization/authentication-by-peers.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { isEmpty, sampleSize } from "lodash-es"; -import { NOTIFY_ALL_MEMBER_LIMIT } from "../../config/env"; -import { - NotFoundError, - UserHasAlreadyBeenAuthenticatedByPeers, -} from "../../config/errors"; -import { sendMail } from "../../connectors/brevo"; -import { - findById as findOrganizationById, - getActiveUsers, - getInternalActiveUsers, - getUserOrganizationLink, - getUsers, -} from "../../repositories/organization/getters"; -import { updateUserOrganizationLink } from "../../repositories/organization/setters"; -import { findById as findUserById } from "../../repositories/user"; -import { getOrganizationsByUserId } from "./main"; - -export const notifyAllMembers = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const organizationUsers = await getUsers(organization_id); - const user = organizationUsers.find(({ id }) => id === user_id); - const organization = await findOrganizationById(organization_id); - - // The user should be in the organization already - if (isEmpty(user) || isEmpty(organization)) { - throw new NotFoundError(); - } - - // The user has already notified all members - if (user.authentication_by_peers_type) { - throw new UserHasAlreadyBeenAuthenticatedByPeers(); - } - - const { email, given_name, family_name, is_external } = user; - const { cached_libelle } = organization; - - // Email organization members of the organization - const internalActiveUsers = await getInternalActiveUsers(organization_id); - const otherInternalUsers = internalActiveUsers.filter( - ({ email: e }) => e !== email, - ); - - let authentication_by_peers_type: BaseUserOrganizationLink["authentication_by_peers_type"]; - if (otherInternalUsers.length > 0) { - const user_label = - !given_name && !family_name ? email : `${given_name} ${family_name}`; - const otherInternalUsersSample = sampleSize( - otherInternalUsers, - NOTIFY_ALL_MEMBER_LIMIT, - ); - await sendMail({ - to: otherInternalUsersSample.map(({ email }) => email), - subject: "Votre organisation sur MonComptePro", - template: "join-organization", - params: { - user_label, - libelle: cached_libelle, - email, - is_external: is_external, - }, - senderEmail: "notifications@moncomptepro.beta.gouv.fr", - }); - - authentication_by_peers_type = "all_members_notified"; - } else { - authentication_by_peers_type = "is_the_only_active_member"; - } - - return await updateUserOrganizationLink(organization_id, user_id, { - authentication_by_peers_type, - }); -}; - -export const markAsWhitelisted = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const organizationUsers = await getUsers(organization_id); - const user = organizationUsers.find(({ id }) => id === user_id); - const organization = await findOrganizationById(organization_id); - - // The user should be in the organization already - if (isEmpty(user) || isEmpty(organization)) { - throw new NotFoundError(); - } - - return await updateUserOrganizationLink(organization_id, user_id, { - authentication_by_peers_type: "deactivated_by_whitelist", - }); -}; - -export const markAsGouvFrDomain = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const organizationUsers = await getUsers(organization_id); - const user = organizationUsers.find(({ id }) => id === user_id); - const organization = await findOrganizationById(organization_id); - - // The user should be in the organization already - if (isEmpty(user) || isEmpty(organization)) { - throw new NotFoundError(); - } - - return await updateUserOrganizationLink(organization_id, user_id, { - authentication_by_peers_type: "deactivated_by_gouv_fr_domain", - }); -}; - -export const greetForJoiningOrganization = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const userOrganisations = await getOrganizationsByUserId(user_id); - const organization = userOrganisations.find( - ({ id }) => id === organization_id, - ); - - if (isEmpty(organization)) { - throw new NotFoundError(); - } - - const { given_name, family_name, email } = (await findUserById(user_id))!; - const { cached_libelle, is_external, authentication_by_peers_type } = - organization; - - // Welcome the user when he joins is first organization as he may now be able to connect - await sendMail({ - to: [email], - subject: "Votre compte MonComptePro a bien été créé", - template: "welcome", - params: { given_name, family_name, email }, - }); - - // Email organization members list to the user (if he is an internal member) - const usersInOrganization = await getActiveUsers(organization_id); - const otherUsers = usersInOrganization.filter( - ({ email: e, authentication_by_peers_type }) => - e !== email && !!authentication_by_peers_type, - ); - - if (!authentication_by_peers_type) { - throw new Error("User should be authenticated by peer."); - } - - if ( - !is_external && - ![ - "is_the_only_active_member", - "deactivated_by_whitelist", - "deactivated_by_gouv_fr_domain", - ].includes(authentication_by_peers_type) && - otherUsers.length > 0 - ) { - await sendMail({ - to: [email], - subject: "Votre organisation sur MonComptePro", - template: "organization-welcome", - params: { - given_name, - family_name, - libelle: cached_libelle, - otherUsers, - }, - }); - } - - return await updateUserOrganizationLink(organization_id, user_id, { - has_been_greeted: true, - }); -}; -export const getSponsorOptions = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const organizationUsers = await getUsers(organization_id); - const user = organizationUsers.find(({ id }) => id === user_id); - - // The user should be in the organization already - if (isEmpty(user)) { - throw new NotFoundError(); - } - - const internalActiveUsers = await getInternalActiveUsers(organization_id); - // Note that external user will have access to name and job of internal members - const sponsorOptions: { - id: number; - label: string; - }[] = internalActiveUsers - .map(({ id, given_name, family_name, job }) => ({ - id, - label: `${given_name} ${family_name} - ${job}`, - })) - .sort(({ label: aLabel }, { label: bLabel }) => (aLabel < bLabel ? -1 : 1)); - - return sponsorOptions; -}; -export const chooseSponsor = async ({ - user_id, - organization_id, - sponsor_id, -}: { - user_id: number; - organization_id: number; - sponsor_id: number; -}) => { - const organization = await findOrganizationById(organization_id); - - const organizationUsers = await getUsers(organization_id); - const user = organizationUsers.find(({ id }) => id === user_id); - - const internalActiveUsers = await getInternalActiveUsers(organization_id); - const sponsor = internalActiveUsers.find(({ id }) => id === sponsor_id); - - // The user should be in the organization already - // The sponsor must be an authenticated internal member. - if (isEmpty(user) || isEmpty(sponsor) || isEmpty(organization)) { - throw new NotFoundError(); - } - - // The user has already been sponsored - if (user.authentication_by_peers_type) { - throw new UserHasAlreadyBeenAuthenticatedByPeers(); - } - - await sendMail({ - to: [sponsor.email], - subject: "Connaissez-vous ce nouveau membre ?", - template: "choose-sponsor", - params: { - given_name: user.given_name, - family_name: user.family_name, - email: user.email, - libelle: organization.cached_libelle, - user_id, - organization_id, - sponsor_id, - }, - }); - - // Note that this will allow the user to connect no matter the sponsor decision. - // We email the sponsor only for him to be notified. - // We log the sponsor decision, it has no influence on the user being able to connect for now. - return await updateUserOrganizationLink(organization_id, user_id, { - authentication_by_peers_type: "sponsored_by_member", - sponsor_id, - }); -}; -export const getSponsorLabel = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const link = await getUserOrganizationLink(organization_id, user_id); - - if (isEmpty(link)) { - return null; - } - - const { sponsor_id } = link; - - if (!sponsor_id) { - return null; - } - - const sponsor = await findUserById(sponsor_id); - - if (isEmpty(sponsor)) { - return null; - } - - const { given_name, family_name } = sponsor; - - return `${given_name} ${family_name}`; -}; -export const getOrganizationLabel = async ({ - user_id, - organization_id, -}: { - user_id: number; - organization_id: number; -}) => { - const organizationUsers = await getUsers(organization_id); - const organization = await findOrganizationById(organization_id); - - const user = organizationUsers.find(({ id }) => id === user_id); - - // The user should be in the organization already - if (isEmpty(user) || isEmpty(organization)) { - throw new NotFoundError(); - } - - return organization.cached_libelle; -}; From da351451ee7b51fcd98ef5a9695895e050f8238b Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 15:16:52 +0200 Subject: [PATCH 08/42] refactor(tests): remove entry about authent by peers in sql fixtures for tests e2e --- cypress/e2e/activate_totp/fixtures.sql | 4 +- cypress/e2e/delete_account/fixtures.sql | 4 +- cypress/e2e/delete_totp/fixtures.sql | 6 +- .../join_org_with_gouv_fr_domain/fixtures.sql | 4 +- .../fixtures.sql | 16 +-- .../fixtures.sql | 4 +- .../fixtures.sql | 4 +- .../e2e/join_with_sponsorship/fixtures.sql | 108 +++++++++--------- .../reauthenticate_on_admin_page/fixtures.sql | 4 +- .../fixtures.sql | 6 +- cypress/e2e/reset_password/fixtures.sql | 4 +- .../fixtures.sql | 4 +- .../fixtures.sql | 4 +- .../signin_from_legacy_client/fixtures.sql | 6 +- .../signin_from_standard_client/fixtures.sql | 8 +- .../fixtures.sql | 4 +- cypress/e2e/signin_with_totp/fixtures.sql | 6 +- .../update_personal_information/fixtures.sql | 4 +- .../e2e/update_totp_application/fixtures.sql | 4 +- 19 files changed, 102 insertions(+), 102 deletions(-) diff --git a/cypress/e2e/activate_totp/fixtures.sql b/cypress/e2e/activate_totp/fixtures.sql index 27089f1b..4199df96 100644 --- a/cypress/e2e/activate_totp/fixtures.sql +++ b/cypress/e2e/activate_totp/fixtures.sql @@ -9,6 +9,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'verified_email_domain', 'all_members_notified', true); + (1, 1, false, 'verified_email_domain', true); diff --git a/cypress/e2e/delete_account/fixtures.sql b/cypress/e2e/delete_account/fixtures.sql index f077bf13..c81fc7b0 100644 --- a/cypress/e2e/delete_account/fixtures.sql +++ b/cypress/e2e/delete_account/fixtures.sql @@ -10,6 +10,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'verified_email_domain', 'all_members_notified', true); + (1, 1, false, 'verified_email_domain', true); diff --git a/cypress/e2e/delete_totp/fixtures.sql b/cypress/e2e/delete_totp/fixtures.sql index b931bca2..4af13eab 100644 --- a/cypress/e2e/delete_totp/fixtures.sql +++ b/cypress/e2e/delete_totp/fixtures.sql @@ -21,7 +21,7 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'verified_email_domain', 'all_members_notified', true), - (2, 1, false, 'verified_email_domain', 'all_members_notified', true); + (1, 1, false, 'verified_email_domain', true), + (2, 1, false, 'verified_email_domain', true); diff --git a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql index 66e3b325..c85bf23a 100644 --- a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql +++ b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql @@ -17,6 +17,6 @@ VALUES (1, 1, 'fake.gouv.fr', 'trackdechets_postal_mail', CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (2, 1, false, 'domain', 'is_the_only_active_member', true); + (2, 1, false, 'domain', true); diff --git a/cypress/e2e/join_org_with_verified_domain/fixtures.sql b/cypress/e2e/join_org_with_verified_domain/fixtures.sql index 3e2dca9f..fd5ba1a5 100644 --- a/cypress/e2e/join_org_with_verified_domain/fixtures.sql +++ b/cypress/e2e/join_org_with_verified_domain/fixtures.sql @@ -28,13 +28,13 @@ VALUES (3, 'randomain.fr', 'verified', CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (2, 1, false, 'domain', 'all_members_notified', true), - (7, 1, true, 'domain', 'all_members_notified', true), + (2, 1, false, 'domain', true), + (7, 1, true, 'domain', true), (8, 1, false, 'domain', null, false), - (2, 2, false, 'domain', 'all_members_notified', true), - (3, 1, false, 'domain', 'all_members_notified', true), - (4, 1, false, 'domain', 'all_members_notified', true), - (5, 1, false, 'domain', 'all_members_notified', true), - (6, 1, false, 'domain', 'all_members_notified', true); + (2, 2, false, 'domain', true), + (3, 1, false, 'domain', true), + (4, 1, false, 'domain', true), + (5, 1, false, 'domain', true), + (6, 1, false, 'domain', true); diff --git a/cypress/e2e/join_with_code_sent_to_official_contact_email/fixtures.sql b/cypress/e2e/join_with_code_sent_to_official_contact_email/fixtures.sql index b4350c24..587c1c9c 100644 --- a/cypress/e2e/join_with_code_sent_to_official_contact_email/fixtures.sql +++ b/cypress/e2e/join_with_code_sent_to_official_contact_email/fixtures.sql @@ -12,6 +12,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true); diff --git a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/fixtures.sql b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/fixtures.sql index 743490ce..5412d105 100644 --- a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/fixtures.sql +++ b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/fixtures.sql @@ -12,6 +12,6 @@ VALUES (1, '19750663700010', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true); diff --git a/cypress/e2e/join_with_sponsorship/fixtures.sql b/cypress/e2e/join_with_sponsorship/fixtures.sql index f4982cce..e412dd5e 100644 --- a/cypress/e2e/join_with_sponsorship/fixtures.sql +++ b/cypress/e2e/join_with_sponsorship/fixtures.sql @@ -75,60 +75,60 @@ VALUES (3, 1, 'fakedomain.com', null, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, needs_official_contact_email_verification, has_been_greeted) + (user_id, organization_id, is_external, verification_type, needs_official_contact_email_verification, has_been_greeted) VALUES - (2, 1, false, 'domain', 'all_members_notified', false, true), - (3, 1, false, 'domain', 'all_members_notified', false, true), - (4, 1, true, 'domain', 'all_members_notified', false, true), + (2, 1, false, 'domain', false, true), + (3, 1, false, 'domain', false, true), + (4, 1, true, 'domain', false, true), (5, 1, false, 'domain', null, false, false), (6, 1, false, null, null, true, false), - (7, 1, false, 'domain', 'all_members_notified', false, true), - (8, 1, false, 'domain', 'all_members_notified', false, true), - (9, 1, false, 'domain', 'all_members_notified', false, true), - (10, 1, false, 'domain', 'all_members_notified', false, true), - (11, 1, false, 'domain', 'all_members_notified', false, true), - (12, 1, false, 'domain', 'all_members_notified', false, true), - (13, 1, false, 'domain', 'all_members_notified', false, true), - (14, 1, false, 'domain', 'all_members_notified', false, true), - (15, 1, false, 'domain', 'all_members_notified', false, true), - (16, 1, false, 'domain', 'all_members_notified', false, true), - (17, 1, false, 'domain', 'all_members_notified', false, true), - (18, 1, false, 'domain', 'all_members_notified', false, true), - (19, 1, false, 'domain', 'all_members_notified', false, true), - (20, 1, false, 'domain', 'all_members_notified', false, true), - (21, 1, false, 'domain', 'all_members_notified', false, true), - (22, 1, false, 'domain', 'all_members_notified', false, true), - (23, 1, false, 'domain', 'all_members_notified', false, true), - (24, 1, false, 'domain', 'all_members_notified', false, true), - (25, 1, false, 'domain', 'all_members_notified', false, true), - (26, 1, false, 'domain', 'all_members_notified', false, true), - (27, 1, false, 'domain', 'all_members_notified', false, true), - (28, 1, false, 'domain', 'all_members_notified', false, true), - (29, 1, false, 'domain', 'all_members_notified', false, true), - (30, 1, false, 'domain', 'all_members_notified', false, true), - (31, 1, false, 'domain', 'all_members_notified', false, true), - (32, 1, false, 'domain', 'all_members_notified', false, true), - (33, 1, false, 'domain', 'all_members_notified', false, true), - (34, 1, false, 'domain', 'all_members_notified', false, true), - (35, 1, false, 'domain', 'all_members_notified', false, true), - (36, 1, false, 'domain', 'all_members_notified', false, true), - (37, 1, false, 'domain', 'all_members_notified', false, true), - (38, 1, false, 'domain', 'all_members_notified', false, true), - (39, 1, false, 'domain', 'all_members_notified', false, true), - (40, 1, false, 'domain', 'all_members_notified', false, true), - (41, 1, false, 'domain', 'all_members_notified', false, true), - (42, 1, false, 'domain', 'all_members_notified', false, true), - (43, 1, false, 'domain', 'all_members_notified', false, true), - (44, 1, false, 'domain', 'all_members_notified', false, true), - (45, 1, false, 'domain', 'all_members_notified', false, true), - (46, 1, false, 'domain', 'all_members_notified', false, true), - (47, 1, false, 'domain', 'all_members_notified', false, true), - (48, 1, false, 'domain', 'all_members_notified', false, true), - (49, 1, false, 'domain', 'all_members_notified', false, true), - (50, 1, false, 'domain', 'all_members_notified', false, true), - (51, 1, false, 'domain', 'all_members_notified', false, true), - (52, 1, false, 'domain', 'all_members_notified', false, true), - (53, 1, false, 'domain', 'all_members_notified', false, true), - (54, 1, false, 'domain', 'all_members_notified', false, true), - (55, 1, false, 'domain', 'all_members_notified', false, true), - (56, 1, false, 'domain', 'all_members_notified', false, true); + (7, 1, false, 'domain', false, true), + (8, 1, false, 'domain', false, true), + (9, 1, false, 'domain', false, true), + (10, 1, false, 'domain', false, true), + (11, 1, false, 'domain', false, true), + (12, 1, false, 'domain', false, true), + (13, 1, false, 'domain', false, true), + (14, 1, false, 'domain', false, true), + (15, 1, false, 'domain', false, true), + (16, 1, false, 'domain', false, true), + (17, 1, false, 'domain', false, true), + (18, 1, false, 'domain', false, true), + (19, 1, false, 'domain', false, true), + (20, 1, false, 'domain', false, true), + (21, 1, false, 'domain', false, true), + (22, 1, false, 'domain', false, true), + (23, 1, false, 'domain', false, true), + (24, 1, false, 'domain', false, true), + (25, 1, false, 'domain', false, true), + (26, 1, false, 'domain', false, true), + (27, 1, false, 'domain', false, true), + (28, 1, false, 'domain', false, true), + (29, 1, false, 'domain', false, true), + (30, 1, false, 'domain', false, true), + (31, 1, false, 'domain', false, true), + (32, 1, false, 'domain', false, true), + (33, 1, false, 'domain', false, true), + (34, 1, false, 'domain', false, true), + (35, 1, false, 'domain', false, true), + (36, 1, false, 'domain', false, true), + (37, 1, false, 'domain', false, true), + (38, 1, false, 'domain', false, true), + (39, 1, false, 'domain', false, true), + (40, 1, false, 'domain', false, true), + (41, 1, false, 'domain', false, true), + (42, 1, false, 'domain', false, true), + (43, 1, false, 'domain', false, true), + (44, 1, false, 'domain', false, true), + (45, 1, false, 'domain', false, true), + (46, 1, false, 'domain', false, true), + (47, 1, false, 'domain', false, true), + (48, 1, false, 'domain', false, true), + (49, 1, false, 'domain', false, true), + (50, 1, false, 'domain', false, true), + (51, 1, false, 'domain', false, true), + (52, 1, false, 'domain', false, true), + (53, 1, false, 'domain', false, true), + (54, 1, false, 'domain', false, true), + (55, 1, false, 'domain', false, true), + (56, 1, false, 'domain', false, true); diff --git a/cypress/e2e/reauthenticate_on_admin_page/fixtures.sql b/cypress/e2e/reauthenticate_on_admin_page/fixtures.sql index 91a75af8..aee0c166 100644 --- a/cypress/e2e/reauthenticate_on_admin_page/fixtures.sql +++ b/cypress/e2e/reauthenticate_on_admin_page/fixtures.sql @@ -15,6 +15,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true); diff --git a/cypress/e2e/redirect_after_session_expiration/fixtures.sql b/cypress/e2e/redirect_after_session_expiration/fixtures.sql index 9026b2b9..b0e765a6 100644 --- a/cypress/e2e/redirect_after_session_expiration/fixtures.sql +++ b/cypress/e2e/redirect_after_session_expiration/fixtures.sql @@ -12,7 +12,7 @@ VALUES (2, '21920023500014', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true), - (1, 2, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true), + (1, 2, false, 'domain', true); diff --git a/cypress/e2e/reset_password/fixtures.sql b/cypress/e2e/reset_password/fixtures.sql index 74cae91f..b6a06461 100644 --- a/cypress/e2e/reset_password/fixtures.sql +++ b/cypress/e2e/reset_password/fixtures.sql @@ -10,6 +10,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true); diff --git a/cypress/e2e/set_info_after_account_provisioning/fixtures.sql b/cypress/e2e/set_info_after_account_provisioning/fixtures.sql index 7997c472..8fea0c10 100644 --- a/cypress/e2e/set_info_after_account_provisioning/fixtures.sql +++ b/cypress/e2e/set_info_after_account_provisioning/fixtures.sql @@ -10,6 +10,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'imported_from_inclusion_connect', 'deactivated_by_import', false); + (1, 1, false, 'imported_from_inclusion_connect', false); diff --git a/cypress/e2e/signin_from_agentconnect_client/fixtures.sql b/cypress/e2e/signin_from_agentconnect_client/fixtures.sql index 4fdc49ac..cdf1c591 100644 --- a/cypress/e2e/signin_from_agentconnect_client/fixtures.sql +++ b/cypress/e2e/signin_from_agentconnect_client/fixtures.sql @@ -11,9 +11,9 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true); INSERT INTO oidc_clients (client_name, client_id, client_secret, redirect_uris, diff --git a/cypress/e2e/signin_from_legacy_client/fixtures.sql b/cypress/e2e/signin_from_legacy_client/fixtures.sql index e526ac79..cc6973df 100644 --- a/cypress/e2e/signin_from_legacy_client/fixtures.sql +++ b/cypress/e2e/signin_from_legacy_client/fixtures.sql @@ -12,10 +12,10 @@ VALUES (2, '21920023500014', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true), - (1, 2, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true), + (1, 2, false, 'domain', true); INSERT INTO oidc_clients (client_name, client_id, client_secret, redirect_uris, diff --git a/cypress/e2e/signin_from_standard_client/fixtures.sql b/cypress/e2e/signin_from_standard_client/fixtures.sql index aeffce91..8cc53618 100644 --- a/cypress/e2e/signin_from_standard_client/fixtures.sql +++ b/cypress/e2e/signin_from_standard_client/fixtures.sql @@ -13,11 +13,11 @@ VALUES (2, '21920023500014', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true), - (2, 1, false, 'domain', 'all_members_notified', true), - (2, 2, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true), + (2, 1, false, 'domain', true), + (2, 2, false, 'domain', true); INSERT INTO oidc_clients (client_name, client_id, client_secret, redirect_uris, diff --git a/cypress/e2e/signin_with_email_verification_renewal/fixtures.sql b/cypress/e2e/signin_with_email_verification_renewal/fixtures.sql index a4f45639..e1f07bd4 100644 --- a/cypress/e2e/signin_with_email_verification_renewal/fixtures.sql +++ b/cypress/e2e/signin_with_email_verification_renewal/fixtures.sql @@ -11,6 +11,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true); diff --git a/cypress/e2e/signin_with_totp/fixtures.sql b/cypress/e2e/signin_with_totp/fixtures.sql index e281bc54..8acb7255 100644 --- a/cypress/e2e/signin_with_totp/fixtures.sql +++ b/cypress/e2e/signin_with_totp/fixtures.sql @@ -21,10 +21,10 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'domain', 'all_members_notified', true), - (2, 1, false, 'domain', 'all_members_notified', true); + (1, 1, false, 'domain', true), + (2, 1, false, 'domain', true); INSERT INTO oidc_clients (client_name, client_id, client_secret, redirect_uris, diff --git a/cypress/e2e/update_personal_information/fixtures.sql b/cypress/e2e/update_personal_information/fixtures.sql index a656de2e..0b8565d0 100644 --- a/cypress/e2e/update_personal_information/fixtures.sql +++ b/cypress/e2e/update_personal_information/fixtures.sql @@ -10,6 +10,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'verified_email_domain', 'all_members_notified', true); + (1, 1, false, 'verified_email_domain', true); diff --git a/cypress/e2e/update_totp_application/fixtures.sql b/cypress/e2e/update_totp_application/fixtures.sql index 31a86b13..820efef8 100644 --- a/cypress/e2e/update_totp_application/fixtures.sql +++ b/cypress/e2e/update_totp_application/fixtures.sql @@ -11,6 +11,6 @@ VALUES (1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES - (1, 1, false, 'verified_email_domain', 'all_members_notified', true); + (1, 1, false, 'verified_email_domain', true); From fe64632f71b6be3eabc5659a20728334253a7a61 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 16:07:46 +0200 Subject: [PATCH 09/42] refactor(migration): delete file for migration/script sql and types for authentication_by_peers_type --- .../1685002008698_add-peer-authentication.cjs | 30 --- scripts/fixtures.sql | 172 +++++++++--------- scripts/import-accounts.ts | 3 - src/types/user-organization-link.d.ts | 8 - 4 files changed, 86 insertions(+), 127 deletions(-) delete mode 100644 migrations/1685002008698_add-peer-authentication.cjs diff --git a/migrations/1685002008698_add-peer-authentication.cjs b/migrations/1685002008698_add-peer-authentication.cjs deleted file mode 100644 index f6b22fe4..00000000 --- a/migrations/1685002008698_add-peer-authentication.cjs +++ /dev/null @@ -1,30 +0,0 @@ -exports.shorthands = undefined; - -exports.up = async (pgm) => { - await pgm.db.query(` -ALTER TABLE users_organizations -ADD COLUMN authentication_by_peers_type character varying; -`); - - await pgm.db.query(` -UPDATE users_organizations -SET authentication_by_peers_type = 'all_members_notified'`); - - await pgm.db.query(` -ALTER TABLE users -ADD COLUMN has_been_greeted_for_first_organization_join boolean DEFAULT FALSE`); - - await pgm.db.query(` -UPDATE users -SET has_been_greeted_for_first_organization_join = TRUE`); -}; - -exports.down = async (pgm) => { - await pgm.db.query(` -ALTER TABLE users_organizations -DROP COLUMN authentication_by_peers_type`); - - await pgm.db.query(` -ALTER TABLE users -DROP COLUMN has_been_greeted_for_first_organization_join`); -}; diff --git a/scripts/fixtures.sql b/scripts/fixtures.sql index 6974e00e..8f13d028 100644 --- a/scripts/fixtures.sql +++ b/scripts/fixtures.sql @@ -288,95 +288,95 @@ SELECT setval( ); INSERT INTO users_organizations - (user_id, organization_id, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, verification_type, has_been_greeted) VALUES - (1, 1, 'verified_email_domain', 'all_members_notified', true), - (2, 2, 'verified_email_domain', 'all_members_notified', true), - (3, 2, 'verified_email_domain', 'all_members_notified', true), - (4, 2, 'verified_email_domain', 'all_members_notified', true), - (5, 2, 'verified_email_domain', 'all_members_notified', true), - (6, 2, 'verified_email_domain', 'all_members_notified', true), - (7, 3, 'verified_email_domain', 'all_members_notified', true), - (7, 4, 'verified_email_domain', 'all_members_notified', true), - (7, 5, 'verified_email_domain', 'all_members_notified', true), - (7, 6, 'verified_email_domain', 'all_members_notified', true), - (7, 7, 'verified_email_domain', 'all_members_notified', true), - (7, 8, 'verified_email_domain', 'all_members_notified', true), - (7, 9, 'verified_email_domain', 'all_members_notified', true), - (7, 10, 'verified_email_domain', 'all_members_notified', true), - (7, 11, 'verified_email_domain', 'all_members_notified', true), - (7, 12, 'verified_email_domain', 'all_members_notified', true), - (7, 13, 'verified_email_domain', 'all_members_notified', true), - (7, 14, 'verified_email_domain', 'all_members_notified', true), - (7, 15, 'verified_email_domain', 'all_members_notified', true), - (7, 16, 'verified_email_domain', 'all_members_notified', true), - (7, 17, 'verified_email_domain', 'all_members_notified', true), - (7, 18, 'verified_email_domain', 'all_members_notified', true), - (7, 19, 'verified_email_domain', 'all_members_notified', true), - (7, 20, 'verified_email_domain', 'all_members_notified', true), - (7, 21, 'verified_email_domain', 'all_members_notified', true), - (7, 22, 'verified_email_domain', 'all_members_notified', true), - (7, 23, 'verified_email_domain', 'all_members_notified', true), - (7, 24, 'verified_email_domain', 'all_members_notified', true), - (7, 25, 'verified_email_domain', 'all_members_notified', true), - (7, 26, 'verified_email_domain', 'all_members_notified', true), - (7, 27, 'verified_email_domain', 'all_members_notified', true), - (7, 28, 'verified_email_domain', 'all_members_notified', true), - (7, 29, 'verified_email_domain', 'all_members_notified', true), - (7, 30, 'verified_email_domain', 'all_members_notified', true), - (7, 31, 'verified_email_domain', 'all_members_notified', true), - (7, 32, 'verified_email_domain', 'all_members_notified', true), - (7, 33, 'verified_email_domain', 'all_members_notified', true), - (7, 34, 'verified_email_domain', 'all_members_notified', true), - (7, 35, 'verified_email_domain', 'all_members_notified', true), - (7, 36, 'verified_email_domain', 'all_members_notified', true), - (7, 37, 'verified_email_domain', 'all_members_notified', true), - (7, 38, 'verified_email_domain', 'all_members_notified', true), - (7, 39, 'verified_email_domain', 'all_members_notified', true), - (7, 40, 'verified_email_domain', 'all_members_notified', true), - (7, 41, 'verified_email_domain', 'all_members_notified', true), - (10, 2, 'verified_email_domain', 'all_members_notified', true), - (11, 2, 'verified_email_domain', 'all_members_notified', true), - (12, 2, 'verified_email_domain', 'all_members_notified', true), - (13, 2, 'verified_email_domain', 'all_members_notified', true), - (14, 2, 'verified_email_domain', 'all_members_notified', true), - (15, 2, 'verified_email_domain', 'all_members_notified', true), - (16, 2, 'verified_email_domain', 'all_members_notified', true), - (17, 2, 'verified_email_domain', 'all_members_notified', true), - (18, 2, 'verified_email_domain', 'all_members_notified', true), - (19, 2, 'verified_email_domain', 'all_members_notified', true), - (35, 46, 'verified_email_domain', 'all_members_notified', true), - (36, 46, 'verified_email_domain', 'all_members_notified', true), - (37, 46, 'verified_email_domain', 'all_members_notified', true), - (38, 46, 'verified_email_domain', 'all_members_notified', true), - (39, 46, 'verified_email_domain', 'all_members_notified', true), - (40, 46, 'verified_email_domain', 'all_members_notified', true), - (41, 46, 'verified_email_domain', 'all_members_notified', true), - (42, 46, 'verified_email_domain', 'all_members_notified', true), - (43, 46, 'verified_email_domain', 'all_members_notified', true), - (44, 46, 'verified_email_domain', 'all_members_notified', true), - (45, 46, 'verified_email_domain', 'all_members_notified', true), - (46, 46, 'verified_email_domain', 'all_members_notified', true), - (47, 46, 'verified_email_domain', 'all_members_notified', true), - (48, 46, 'verified_email_domain', 'all_members_notified', true), - (42, 47, 'verified_email_domain', 'all_members_notified', true), - (43, 47, 'verified_email_domain', 'all_members_notified', true), - (44, 47, 'verified_email_domain', 'all_members_notified', true), - (45, 47, 'verified_email_domain', 'all_members_notified', true), - (46, 47, 'verified_email_domain', 'all_members_notified', true), - (47, 47, 'verified_email_domain', 'all_members_notified', true), - (48, 47, 'verified_email_domain', 'all_members_notified', true), - (49, 48, 'verified_email_domain', 'all_members_notified', true), - (50, 48, 'verified_email_domain', 'all_members_notified', true), - (51, 48, 'verified_email_domain', 'all_members_notified', true), - (52, 48, 'verified_email_domain', 'all_members_notified', true), - (53, 48, 'verified_email_domain', 'all_members_notified', true), - (54, 48, 'verified_email_domain', 'all_members_notified', true), - (55, 48, 'verified_email_domain', 'all_members_notified', true) + (1, 1, 'verified_email_domain', true), + (2, 2, 'verified_email_domain', true), + (3, 2, 'verified_email_domain', true), + (4, 2, 'verified_email_domain', true), + (5, 2, 'verified_email_domain', true), + (6, 2, 'verified_email_domain', true), + (7, 3, 'verified_email_domain', true), + (7, 4, 'verified_email_domain', true), + (7, 5, 'verified_email_domain', true), + (7, 6, 'verified_email_domain', true), + (7, 7, 'verified_email_domain', true), + (7, 8, 'verified_email_domain', true), + (7, 9, 'verified_email_domain', true), + (7, 10, 'verified_email_domain', true), + (7, 11, 'verified_email_domain', true), + (7, 12, 'verified_email_domain', true), + (7, 13, 'verified_email_domain', true), + (7, 14, 'verified_email_domain', true), + (7, 15, 'verified_email_domain', true), + (7, 16, 'verified_email_domain', true), + (7, 17, 'verified_email_domain', true), + (7, 18, 'verified_email_domain', true), + (7, 19, 'verified_email_domain', true), + (7, 20, 'verified_email_domain', true), + (7, 21, 'verified_email_domain', true), + (7, 22, 'verified_email_domain', true), + (7, 23, 'verified_email_domain', true), + (7, 24, 'verified_email_domain', true), + (7, 25, 'verified_email_domain', true), + (7, 26, 'verified_email_domain', true), + (7, 27, 'verified_email_domain', true), + (7, 28, 'verified_email_domain', true), + (7, 29, 'verified_email_domain', true), + (7, 30, 'verified_email_domain', true), + (7, 31, 'verified_email_domain', true), + (7, 32, 'verified_email_domain', true), + (7, 33, 'verified_email_domain', true), + (7, 34, 'verified_email_domain', true), + (7, 35, 'verified_email_domain', true), + (7, 36, 'verified_email_domain', true), + (7, 37, 'verified_email_domain', true), + (7, 38, 'verified_email_domain', true), + (7, 39, 'verified_email_domain', true), + (7, 40, 'verified_email_domain', true), + (7, 41, 'verified_email_domain', true), + (10, 2, 'verified_email_domain', true), + (11, 2, 'verified_email_domain', true), + (12, 2, 'verified_email_domain', true), + (13, 2, 'verified_email_domain', true), + (14, 2, 'verified_email_domain', true), + (15, 2, 'verified_email_domain', true), + (16, 2, 'verified_email_domain', true), + (17, 2, 'verified_email_domain', true), + (18, 2, 'verified_email_domain', true), + (19, 2, 'verified_email_domain', true), + (35, 46, 'verified_email_domain', true), + (36, 46, 'verified_email_domain', true), + (37, 46, 'verified_email_domain', true), + (38, 46, 'verified_email_domain', true), + (39, 46, 'verified_email_domain', true), + (40, 46, 'verified_email_domain', true), + (41, 46, 'verified_email_domain', true), + (42, 46, 'verified_email_domain', true), + (43, 46, 'verified_email_domain', true), + (44, 46, 'verified_email_domain', true), + (45, 46, 'verified_email_domain', true), + (46, 46, 'verified_email_domain', true), + (47, 46, 'verified_email_domain', true), + (48, 46, 'verified_email_domain', true), + (42, 47, 'verified_email_domain', true), + (43, 47, 'verified_email_domain', true), + (44, 47, 'verified_email_domain', true), + (45, 47, 'verified_email_domain', true), + (46, 47, 'verified_email_domain', true), + (47, 47, 'verified_email_domain', true), + (48, 47, 'verified_email_domain', true), + (49, 48, 'verified_email_domain', true), + (50, 48, 'verified_email_domain', true), + (51, 48, 'verified_email_domain', true), + (52, 48, 'verified_email_domain', true), + (53, 48, 'verified_email_domain', true), + (54, 48, 'verified_email_domain', true), + (55, 48, 'verified_email_domain', true) ON CONFLICT (user_id, organization_id) DO UPDATE - SET (verification_type, authentication_by_peers_type, has_been_greeted) - = (EXCLUDED.verification_type, EXCLUDED.authentication_by_peers_type, EXCLUDED.has_been_greeted); + SET (verification_type, has_been_greeted) + = (EXCLUDED.verification_type, EXCLUDED.has_been_greeted); INSERT INTO oidc_clients (id, client_name, client_id, client_secret, redirect_uris, diff --git a/scripts/import-accounts.ts b/scripts/import-accounts.ts index b81fed49..0469f1f8 100644 --- a/scripts/import-accounts.ts +++ b/scripts/import-accounts.ts @@ -173,9 +173,6 @@ const maxInseeCallRateInMs = rateInMsFromArgs !== 0 ? rateInMsFromArgs : 125; user_id: user.id, verification_type: "imported_from_inclusion_connect", }); - await updateUserOrganizationLink(organization.id, user.id, { - authentication_by_peers_type: "deactivated_by_import", - }); } } catch (error) { logger.error(`unexpected error for siret: ${siret}`); diff --git a/src/types/user-organization-link.d.ts b/src/types/user-organization-link.d.ts index 6c45c681..56361f41 100644 --- a/src/types/user-organization-link.d.ts +++ b/src/types/user-organization-link.d.ts @@ -11,14 +11,6 @@ interface BaseUserOrganizationLink { | null; // updated when verification_type is changed verified_at: Date | null; - authentication_by_peers_type: - | "all_members_notified" - | "sponsored_by_member" - | "is_the_only_active_member" - | "deactivated_by_whitelist" - | "deactivated_by_gouv_fr_domain" - | "deactivated_by_import" - | null; has_been_greeted: boolean; sponsor_id: number | null; needs_official_contact_email_verification: boolean; From 704911d5a6401fbae473a1ba1e9a578f57a29eb3 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 17:21:28 +0200 Subject: [PATCH 10/42] Revert "refactor(migration): delete file for migration/script sql and types for authentication_by_peers_type" This reverts commit f82a516f9b68528c1725eeb8144bc7b161e31a66. --- .../1685002008698_add-peer-authentication.cjs | 30 +++ scripts/fixtures.sql | 172 +++++++++--------- scripts/import-accounts.ts | 3 + src/types/user-organization-link.d.ts | 8 + 4 files changed, 127 insertions(+), 86 deletions(-) create mode 100644 migrations/1685002008698_add-peer-authentication.cjs diff --git a/migrations/1685002008698_add-peer-authentication.cjs b/migrations/1685002008698_add-peer-authentication.cjs new file mode 100644 index 00000000..f6b22fe4 --- /dev/null +++ b/migrations/1685002008698_add-peer-authentication.cjs @@ -0,0 +1,30 @@ +exports.shorthands = undefined; + +exports.up = async (pgm) => { + await pgm.db.query(` +ALTER TABLE users_organizations +ADD COLUMN authentication_by_peers_type character varying; +`); + + await pgm.db.query(` +UPDATE users_organizations +SET authentication_by_peers_type = 'all_members_notified'`); + + await pgm.db.query(` +ALTER TABLE users +ADD COLUMN has_been_greeted_for_first_organization_join boolean DEFAULT FALSE`); + + await pgm.db.query(` +UPDATE users +SET has_been_greeted_for_first_organization_join = TRUE`); +}; + +exports.down = async (pgm) => { + await pgm.db.query(` +ALTER TABLE users_organizations +DROP COLUMN authentication_by_peers_type`); + + await pgm.db.query(` +ALTER TABLE users +DROP COLUMN has_been_greeted_for_first_organization_join`); +}; diff --git a/scripts/fixtures.sql b/scripts/fixtures.sql index 8f13d028..6974e00e 100644 --- a/scripts/fixtures.sql +++ b/scripts/fixtures.sql @@ -288,95 +288,95 @@ SELECT setval( ); INSERT INTO users_organizations - (user_id, organization_id, verification_type, has_been_greeted) + (user_id, organization_id, verification_type, authentication_by_peers_type, has_been_greeted) VALUES - (1, 1, 'verified_email_domain', true), - (2, 2, 'verified_email_domain', true), - (3, 2, 'verified_email_domain', true), - (4, 2, 'verified_email_domain', true), - (5, 2, 'verified_email_domain', true), - (6, 2, 'verified_email_domain', true), - (7, 3, 'verified_email_domain', true), - (7, 4, 'verified_email_domain', true), - (7, 5, 'verified_email_domain', true), - (7, 6, 'verified_email_domain', true), - (7, 7, 'verified_email_domain', true), - (7, 8, 'verified_email_domain', true), - (7, 9, 'verified_email_domain', true), - (7, 10, 'verified_email_domain', true), - (7, 11, 'verified_email_domain', true), - (7, 12, 'verified_email_domain', true), - (7, 13, 'verified_email_domain', true), - (7, 14, 'verified_email_domain', true), - (7, 15, 'verified_email_domain', true), - (7, 16, 'verified_email_domain', true), - (7, 17, 'verified_email_domain', true), - (7, 18, 'verified_email_domain', true), - (7, 19, 'verified_email_domain', true), - (7, 20, 'verified_email_domain', true), - (7, 21, 'verified_email_domain', true), - (7, 22, 'verified_email_domain', true), - (7, 23, 'verified_email_domain', true), - (7, 24, 'verified_email_domain', true), - (7, 25, 'verified_email_domain', true), - (7, 26, 'verified_email_domain', true), - (7, 27, 'verified_email_domain', true), - (7, 28, 'verified_email_domain', true), - (7, 29, 'verified_email_domain', true), - (7, 30, 'verified_email_domain', true), - (7, 31, 'verified_email_domain', true), - (7, 32, 'verified_email_domain', true), - (7, 33, 'verified_email_domain', true), - (7, 34, 'verified_email_domain', true), - (7, 35, 'verified_email_domain', true), - (7, 36, 'verified_email_domain', true), - (7, 37, 'verified_email_domain', true), - (7, 38, 'verified_email_domain', true), - (7, 39, 'verified_email_domain', true), - (7, 40, 'verified_email_domain', true), - (7, 41, 'verified_email_domain', true), - (10, 2, 'verified_email_domain', true), - (11, 2, 'verified_email_domain', true), - (12, 2, 'verified_email_domain', true), - (13, 2, 'verified_email_domain', true), - (14, 2, 'verified_email_domain', true), - (15, 2, 'verified_email_domain', true), - (16, 2, 'verified_email_domain', true), - (17, 2, 'verified_email_domain', true), - (18, 2, 'verified_email_domain', true), - (19, 2, 'verified_email_domain', true), - (35, 46, 'verified_email_domain', true), - (36, 46, 'verified_email_domain', true), - (37, 46, 'verified_email_domain', true), - (38, 46, 'verified_email_domain', true), - (39, 46, 'verified_email_domain', true), - (40, 46, 'verified_email_domain', true), - (41, 46, 'verified_email_domain', true), - (42, 46, 'verified_email_domain', true), - (43, 46, 'verified_email_domain', true), - (44, 46, 'verified_email_domain', true), - (45, 46, 'verified_email_domain', true), - (46, 46, 'verified_email_domain', true), - (47, 46, 'verified_email_domain', true), - (48, 46, 'verified_email_domain', true), - (42, 47, 'verified_email_domain', true), - (43, 47, 'verified_email_domain', true), - (44, 47, 'verified_email_domain', true), - (45, 47, 'verified_email_domain', true), - (46, 47, 'verified_email_domain', true), - (47, 47, 'verified_email_domain', true), - (48, 47, 'verified_email_domain', true), - (49, 48, 'verified_email_domain', true), - (50, 48, 'verified_email_domain', true), - (51, 48, 'verified_email_domain', true), - (52, 48, 'verified_email_domain', true), - (53, 48, 'verified_email_domain', true), - (54, 48, 'verified_email_domain', true), - (55, 48, 'verified_email_domain', true) + (1, 1, 'verified_email_domain', 'all_members_notified', true), + (2, 2, 'verified_email_domain', 'all_members_notified', true), + (3, 2, 'verified_email_domain', 'all_members_notified', true), + (4, 2, 'verified_email_domain', 'all_members_notified', true), + (5, 2, 'verified_email_domain', 'all_members_notified', true), + (6, 2, 'verified_email_domain', 'all_members_notified', true), + (7, 3, 'verified_email_domain', 'all_members_notified', true), + (7, 4, 'verified_email_domain', 'all_members_notified', true), + (7, 5, 'verified_email_domain', 'all_members_notified', true), + (7, 6, 'verified_email_domain', 'all_members_notified', true), + (7, 7, 'verified_email_domain', 'all_members_notified', true), + (7, 8, 'verified_email_domain', 'all_members_notified', true), + (7, 9, 'verified_email_domain', 'all_members_notified', true), + (7, 10, 'verified_email_domain', 'all_members_notified', true), + (7, 11, 'verified_email_domain', 'all_members_notified', true), + (7, 12, 'verified_email_domain', 'all_members_notified', true), + (7, 13, 'verified_email_domain', 'all_members_notified', true), + (7, 14, 'verified_email_domain', 'all_members_notified', true), + (7, 15, 'verified_email_domain', 'all_members_notified', true), + (7, 16, 'verified_email_domain', 'all_members_notified', true), + (7, 17, 'verified_email_domain', 'all_members_notified', true), + (7, 18, 'verified_email_domain', 'all_members_notified', true), + (7, 19, 'verified_email_domain', 'all_members_notified', true), + (7, 20, 'verified_email_domain', 'all_members_notified', true), + (7, 21, 'verified_email_domain', 'all_members_notified', true), + (7, 22, 'verified_email_domain', 'all_members_notified', true), + (7, 23, 'verified_email_domain', 'all_members_notified', true), + (7, 24, 'verified_email_domain', 'all_members_notified', true), + (7, 25, 'verified_email_domain', 'all_members_notified', true), + (7, 26, 'verified_email_domain', 'all_members_notified', true), + (7, 27, 'verified_email_domain', 'all_members_notified', true), + (7, 28, 'verified_email_domain', 'all_members_notified', true), + (7, 29, 'verified_email_domain', 'all_members_notified', true), + (7, 30, 'verified_email_domain', 'all_members_notified', true), + (7, 31, 'verified_email_domain', 'all_members_notified', true), + (7, 32, 'verified_email_domain', 'all_members_notified', true), + (7, 33, 'verified_email_domain', 'all_members_notified', true), + (7, 34, 'verified_email_domain', 'all_members_notified', true), + (7, 35, 'verified_email_domain', 'all_members_notified', true), + (7, 36, 'verified_email_domain', 'all_members_notified', true), + (7, 37, 'verified_email_domain', 'all_members_notified', true), + (7, 38, 'verified_email_domain', 'all_members_notified', true), + (7, 39, 'verified_email_domain', 'all_members_notified', true), + (7, 40, 'verified_email_domain', 'all_members_notified', true), + (7, 41, 'verified_email_domain', 'all_members_notified', true), + (10, 2, 'verified_email_domain', 'all_members_notified', true), + (11, 2, 'verified_email_domain', 'all_members_notified', true), + (12, 2, 'verified_email_domain', 'all_members_notified', true), + (13, 2, 'verified_email_domain', 'all_members_notified', true), + (14, 2, 'verified_email_domain', 'all_members_notified', true), + (15, 2, 'verified_email_domain', 'all_members_notified', true), + (16, 2, 'verified_email_domain', 'all_members_notified', true), + (17, 2, 'verified_email_domain', 'all_members_notified', true), + (18, 2, 'verified_email_domain', 'all_members_notified', true), + (19, 2, 'verified_email_domain', 'all_members_notified', true), + (35, 46, 'verified_email_domain', 'all_members_notified', true), + (36, 46, 'verified_email_domain', 'all_members_notified', true), + (37, 46, 'verified_email_domain', 'all_members_notified', true), + (38, 46, 'verified_email_domain', 'all_members_notified', true), + (39, 46, 'verified_email_domain', 'all_members_notified', true), + (40, 46, 'verified_email_domain', 'all_members_notified', true), + (41, 46, 'verified_email_domain', 'all_members_notified', true), + (42, 46, 'verified_email_domain', 'all_members_notified', true), + (43, 46, 'verified_email_domain', 'all_members_notified', true), + (44, 46, 'verified_email_domain', 'all_members_notified', true), + (45, 46, 'verified_email_domain', 'all_members_notified', true), + (46, 46, 'verified_email_domain', 'all_members_notified', true), + (47, 46, 'verified_email_domain', 'all_members_notified', true), + (48, 46, 'verified_email_domain', 'all_members_notified', true), + (42, 47, 'verified_email_domain', 'all_members_notified', true), + (43, 47, 'verified_email_domain', 'all_members_notified', true), + (44, 47, 'verified_email_domain', 'all_members_notified', true), + (45, 47, 'verified_email_domain', 'all_members_notified', true), + (46, 47, 'verified_email_domain', 'all_members_notified', true), + (47, 47, 'verified_email_domain', 'all_members_notified', true), + (48, 47, 'verified_email_domain', 'all_members_notified', true), + (49, 48, 'verified_email_domain', 'all_members_notified', true), + (50, 48, 'verified_email_domain', 'all_members_notified', true), + (51, 48, 'verified_email_domain', 'all_members_notified', true), + (52, 48, 'verified_email_domain', 'all_members_notified', true), + (53, 48, 'verified_email_domain', 'all_members_notified', true), + (54, 48, 'verified_email_domain', 'all_members_notified', true), + (55, 48, 'verified_email_domain', 'all_members_notified', true) ON CONFLICT (user_id, organization_id) DO UPDATE - SET (verification_type, has_been_greeted) - = (EXCLUDED.verification_type, EXCLUDED.has_been_greeted); + SET (verification_type, authentication_by_peers_type, has_been_greeted) + = (EXCLUDED.verification_type, EXCLUDED.authentication_by_peers_type, EXCLUDED.has_been_greeted); INSERT INTO oidc_clients (id, client_name, client_id, client_secret, redirect_uris, diff --git a/scripts/import-accounts.ts b/scripts/import-accounts.ts index 0469f1f8..b81fed49 100644 --- a/scripts/import-accounts.ts +++ b/scripts/import-accounts.ts @@ -173,6 +173,9 @@ const maxInseeCallRateInMs = rateInMsFromArgs !== 0 ? rateInMsFromArgs : 125; user_id: user.id, verification_type: "imported_from_inclusion_connect", }); + await updateUserOrganizationLink(organization.id, user.id, { + authentication_by_peers_type: "deactivated_by_import", + }); } } catch (error) { logger.error(`unexpected error for siret: ${siret}`); diff --git a/src/types/user-organization-link.d.ts b/src/types/user-organization-link.d.ts index 56361f41..6c45c681 100644 --- a/src/types/user-organization-link.d.ts +++ b/src/types/user-organization-link.d.ts @@ -11,6 +11,14 @@ interface BaseUserOrganizationLink { | null; // updated when verification_type is changed verified_at: Date | null; + authentication_by_peers_type: + | "all_members_notified" + | "sponsored_by_member" + | "is_the_only_active_member" + | "deactivated_by_whitelist" + | "deactivated_by_gouv_fr_domain" + | "deactivated_by_import" + | null; has_been_greeted: boolean; sponsor_id: number | null; needs_official_contact_email_verification: boolean; From af51ad8f5dbaf1731b429e4376b4be822678803d Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Fri, 30 Aug 2024 17:40:27 +0200 Subject: [PATCH 11/42] chore(migration): create new migration for delete authentication by peers --- ...88_delete-authentication-by-peers-type.cjs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 migrations/1725031836488_delete-authentication-by-peers-type.cjs diff --git a/migrations/1725031836488_delete-authentication-by-peers-type.cjs b/migrations/1725031836488_delete-authentication-by-peers-type.cjs new file mode 100644 index 00000000..80c3235f --- /dev/null +++ b/migrations/1725031836488_delete-authentication-by-peers-type.cjs @@ -0,0 +1,30 @@ +exports.shorthands = undefined; + +exports.up = async (pgm) => { + await pgm.db.query(` +ALTER TABLE users_organizations +DROP COLUMN authentication_by_peers_type`); + + await pgm.db.query(` +ALTER TABLE users +ADD COLUMN has_been_greeted_for_first_organization_join boolean DEFAULT FALSE`); + + await pgm.db.query(` +UPDATE users +SET has_been_greeted_for_first_organization_join = TRUE`); +}; + +exports.down = async (pgm) => { + await pgm.db.query(` +ALTER TABLE users_organizations +ADD COLUMN authentication_by_peers_type character varying; +`); + + await pgm.db.query(` +UPDATE users_organizations +SET authentication_by_peers_type = 'all_members_notified'`); + + await pgm.db.query(` +ALTER TABLE users +DROP COLUMN has_been_greeted_for_first_organization_join`); +}; From a2d98105a815b0c820407bc35c37d9fcec824ea3 Mon Sep 17 00:00:00 2001 From: Douglas DUTEIL Date: Tue, 3 Sep 2024 17:21:18 +0200 Subject: [PATCH 12/42] :wrench: allow sandbox as DEPLOY_ENV --- src/config/env.zod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts index 3485c1da..d60fe96d 100644 --- a/src/config/env.zod.ts +++ b/src/config/env.zod.ts @@ -28,7 +28,7 @@ export const envSchema = z zodTrueFalseBoolean().default("False"), DATABASE_URL: z.string().url(), DEBOUNCE_API_KEY: z.string().optional(), - DEPLOY_ENV: z.enum(["preview", "production"]).default("preview"), + DEPLOY_ENV: z.enum(["preview", "production", "sandbox"]).default("preview"), DISABLE_SECURITY_RESPONSE_HEADERS: zodTrueFalseBoolean().default("False"), DISPLAY_TEST_ENV_WARNING: zodTrueFalseBoolean().default("False"), DO_NOT_AUTHENTICATE_BROWSER: zodTrueFalseBoolean().default("False"), From de3ad4ffd0e4312a5a9825771afc6487f3f71b80 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Mon, 2 Sep 2024 11:17:23 +0200 Subject: [PATCH 13/42] chore: add correction from review --- src/managers/organization/join.ts | 9 ++ src/middlewares/user.ts | 158 ++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/src/managers/organization/join.ts b/src/managers/organization/join.ts index 0973a6bf..a2feeb14 100644 --- a/src/managers/organization/join.ts +++ b/src/managers/organization/join.ts @@ -18,6 +18,7 @@ import { import { getAnnuaireEducationNationaleContactEmail } from "../../connectors/api-annuaire-education-nationale"; import { getAnnuaireServicePublicContactEmail } from "../../connectors/api-annuaire-service-public"; import { getOrganizationInfo } from "../../connectors/api-sirene"; +import { sendMail } from "../../connectors/brevo"; import { startCripsConversation } from "../../connectors/crisp"; import { findEmailDomainsByOrganizationId } from "../../repositories/email-domain"; import { @@ -333,6 +334,14 @@ export const joinOrganization = async ({ ticket_id, }); + // Welcome the user when he joins is first organization as he may now be able to connect + await sendMail({ + to: [email], + subject: "Votre compte MonComptePro a bien été créé", + template: "welcome", + params: { given_name, family_name, email }, + }); + throw new UnableToAutoJoinOrganizationError(moderation_id); }; export const forceJoinOrganization = async ({ diff --git a/src/middlewares/user.ts b/src/middlewares/user.ts index 699e483f..353dfe9c 100644 --- a/src/middlewares/user.ts +++ b/src/middlewares/user.ts @@ -1,9 +1,15 @@ import { NextFunction, Request, Response } from "express"; import HttpErrors from "http-errors"; import { isEmpty } from "lodash-es"; +// import { PAIR_AUTHENTICATION_WHITELIST } from "../config/env"; import { UserNotFoundError } from "../config/errors"; import { is2FACapable, shouldForce2faForUser } from "../managers/2fa"; import { isBrowserTrustedForUser } from "../managers/browser-authentication"; +// import { +// markAsGouvFrDomain, +// markAsWhitelisted, +// notifyAllMembers, +// } from "../managers/organization/authentication-by-peers"; import { getOrganizationsByUserId, selectOrganization, @@ -356,3 +362,155 @@ export const checkUserHasSelectedAnOrganizationMiddleware = ( next(error); } }); + +export const checkUserHasNoPendingOfficialContactEmailVerificationMiddleware = ( + req: Request, + res: Response, + next: NextFunction, +) => + checkUserHasSelectedAnOrganizationMiddleware(req, res, async (error) => { + try { + if (error) return next(error); + + const userOrganisations = await getOrganizationsByUserId( + getUserFromAuthenticatedSession(req).id, + ); + + let organizationThatNeedsOfficialContactEmailVerification; + if (req.session.mustReturnOneOrganizationInPayload) { + const selectedOrganizationId = await getSelectedOrganizationId( + getUserFromAuthenticatedSession(req).id, + ); + + organizationThatNeedsOfficialContactEmailVerification = + userOrganisations.find( + ({ id, needs_official_contact_email_verification }) => + needs_official_contact_email_verification && + id === selectedOrganizationId, + ); + } else { + organizationThatNeedsOfficialContactEmailVerification = + userOrganisations.find( + ({ needs_official_contact_email_verification }) => + needs_official_contact_email_verification, + ); + } + + if (!isEmpty(organizationThatNeedsOfficialContactEmailVerification)) { + return res.redirect( + `/users/official-contact-email-verification/${organizationThatNeedsOfficialContactEmailVerification.id}`, + ); + } + + return next(); + } catch (error) { + next(error); + } + }); + +// export const checkUserHasBeenAuthenticatedByPeersMiddleware = ( +// req: Request, +// res: Response, +// next: NextFunction, +// ) => +// checkUserHasNoPendingOfficialContactEmailVerificationMiddleware( +// req, +// res, +// async (error) => { +// try { +// if (error) return next(error); + +// const { id: user_id, email } = getUserFromAuthenticatedSession(req); + +// const userOrganisations = await getOrganizationsByUserId(user_id); + +// let organizationThatNeedsAuthenticationByPeers; +// if (req.session.mustReturnOneOrganizationInPayload) { +// const selectedOrganizationId = +// await getSelectedOrganizationId(user_id); + +// organizationThatNeedsAuthenticationByPeers = userOrganisations.find( +// ({ id, authentication_by_peers_type }) => +// !authentication_by_peers_type && id === selectedOrganizationId, +// ); +// } else { +// organizationThatNeedsAuthenticationByPeers = userOrganisations.find( +// ({ authentication_by_peers_type }) => !authentication_by_peers_type, +// ); +// } + +// if (!isEmpty(organizationThatNeedsAuthenticationByPeers)) { +// const organization_id = organizationThatNeedsAuthenticationByPeers.id; +// const internalActiveUsers = +// await getInternalActiveUsers(organization_id); +// const otherInternalUsers = internalActiveUsers.filter( +// ({ email: e }) => e !== email, +// ); + +// if (PAIR_AUTHENTICATION_WHITELIST.includes(getEmailDomain(email))) { +// await markAsWhitelisted({ user_id, organization_id }); +// } else if (usesAGouvFrDomain(email)) { +// await markAsGouvFrDomain({ user_id, organization_id }); +// } else if (otherInternalUsers.length > 0) { +// return res.redirect(`/users/choose-sponsor/${organization_id}`); +// } else { +// await notifyAllMembers({ user_id, organization_id }); +// } +// } + +// return next(); +// } catch (error) { +// next(error); +// } +// }, +// ); + +// export const checkUserHasBeenGreetedForJoiningOrganizationMiddleware = ( +// req: Request, +// res: Response, +// next: NextFunction, +// ) => +// checkUserHasBeenAuthenticatedByPeersMiddleware(req, res, async (error) => { +// try { +// if (error) return next(error); + +// const userOrganisations = await getOrganizationsByUserId( +// getUserFromAuthenticatedSession(req).id, +// ); + +// let organizationThatNeedsGreetings; +// if (req.session.mustReturnOneOrganizationInPayload) { +// const selectedOrganizationId = await getSelectedOrganizationId( +// getUserFromAuthenticatedSession(req).id, +// ); + +// organizationThatNeedsGreetings = userOrganisations.find( +// ({ id, has_been_greeted }) => +// !has_been_greeted && id === selectedOrganizationId, +// ); +// } else { +// organizationThatNeedsGreetings = userOrganisations.find( +// ({ has_been_greeted }) => !has_been_greeted, +// ); +// } + +// if (!isEmpty(organizationThatNeedsGreetings)) { +// await greetForJoiningOrganization({ +// user_id: getUserFromAuthenticatedSession(req).id, +// organization_id: organizationThatNeedsGreetings.id, +// }); + +// return res.redirect( +// `/users/welcome/${organizationThatNeedsGreetings.id}`, +// ); +// } + +// return next(); +// } catch (error) { +// next(error); +// } +// }); + +// // check that user go through all requirements before issuing a session +// export const checkUserSignInRequirementsMiddleware = +// checkUserHasBeenGreetedForJoiningOrganizationMiddleware; From 8d5ae1887bae7769b4fe131e11de5a7d147a8606 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Mon, 2 Sep 2024 11:34:10 +0200 Subject: [PATCH 14/42] refactor(tests): delete tests about auth by peer --- .github/workflows/end-to-end.yml | 1 - .../join_org_with_gouv_fr_domain/fixtures.sql | 2 - .../join_org_with_verified_domain/index.cy.ts | 6 - .../index.cy.ts | 21 --- .../index.cy.ts | 21 --- cypress/e2e/join_with_sponsorship/env.conf | 1 - .../e2e/join_with_sponsorship/fixtures.sql | 134 --------------- cypress/e2e/join_with_sponsorship/index.cy.ts | 159 ------------------ 8 files changed, 345 deletions(-) delete mode 100644 cypress/e2e/join_with_sponsorship/env.conf delete mode 100644 cypress/e2e/join_with_sponsorship/fixtures.sql delete mode 100644 cypress/e2e/join_with_sponsorship/index.cy.ts diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index 52dd8775..f042c653 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -55,7 +55,6 @@ jobs: - join_with_code_sent_to_official_contact_email - join_with_code_sent_to_official_educ_nat_contact_email - join_with_official_contact_email - - join_with_sponsorship - reauthenticate_on_admin_page - redirect_after_session_expiration - reset_password diff --git a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql index c85bf23a..1a34df1c 100644 --- a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql +++ b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql @@ -3,8 +3,6 @@ INSERT INTO users phone_number, job) VALUES (1, 'unused@fake.gouv.fr', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Nouveau', '0123456789', 'Sbire'), --- We need a second member in the organization to trigger the sponsorship feature. - (2, 'membre@fake.gouv.fr', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Membre', '0123456789', 'Sbire'); INSERT INTO organizations (id, siret, created_at, updated_at) diff --git a/cypress/e2e/join_org_with_verified_domain/index.cy.ts b/cypress/e2e/join_org_with_verified_domain/index.cy.ts index e65c644f..641b9051 100644 --- a/cypress/e2e/join_org_with_verified_domain/index.cy.ts +++ b/cypress/e2e/join_org_with_verified_domain/index.cy.ts @@ -47,12 +47,6 @@ describe("join organizations", () => { // Click on the suggested organization cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click(); - // Click on "Je ne connais aucune des personnes proposées" - cy.get('[href="/users/no-sponsor-found/1"]').click(); - - // Click on the confirmation button - cy.get('[type="submit"]').contains("Personne ne peut me parrainer").click(); - // Click on "continue" on the welcome page cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts b/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts index 389a9f23..decc1641 100644 --- a/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts @@ -51,27 +51,6 @@ describe("join organizations", () => { cy.get('[type="submit"]').click(); }); - // Click on "Je ne connais aucune des personnes proposées" - cy.get('[href="/users/no-sponsor-found/1"]').click(); - - // Click on the confirmation button - cy.get('[type="submit"]').contains("Personne ne peut me parrainer").click(); - cy.contains("Votre compte est créé"); - - cy.mailslurp() - .then((mailslurp) => - mailslurp.waitForLatestEmail( - "26ccc0fa-0dc3-4f12-9335-7bb00282920c", - 60000, - true, - ), - ) - // assert reception of notification email - .then((email) => { - expect(email.body).to.match( - /.*Jean Nouveau.*\(c348a2c3-bf54-4f15-bb12-a2d7047c832f@mailslurp\.com\) a rejoint votre organisation.*Commune de lamalou-les-bains - Mairie.*sur .*MonComptePro/, - ); - }); }); }); diff --git a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts index fc087785..3892fd6c 100644 --- a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts @@ -52,27 +52,6 @@ describe("join organizations", () => { cy.get('[type="submit"]').click(); }); - // Click on "Je ne connais aucune des personnes proposées" - cy.get('[href="/users/no-sponsor-found/1"]').click(); - - // Click on the confirmation button - cy.get('[type="submit"]').contains("Personne ne peut me parrainer").click(); - cy.contains("Votre compte est créé"); - - cy.mailslurp() - .then((mailslurp) => - mailslurp.waitForLatestEmail( - "01714bdb-c5d7-48c9-93ab-73dc78c13609", - 60000, - true, - ), - ) - // assert reception of notification email - .then((email) => { - expect(email.body).to.match( - /.*Jean Nouveau.*\(10efdabd-deb0-4d19-a521-6772ca27acf8@mailslurp\.com\) a rejoint votre organisation.*Lycee general et technologique chaptal.*sur .*MonComptePro/, - ); - }); }); }); diff --git a/cypress/e2e/join_with_sponsorship/env.conf b/cypress/e2e/join_with_sponsorship/env.conf deleted file mode 100644 index 70f476b6..00000000 --- a/cypress/e2e/join_with_sponsorship/env.conf +++ /dev/null @@ -1 +0,0 @@ -PAIR_AUTHENTICATION_WHITELIST=mailslurp.biz diff --git a/cypress/e2e/join_with_sponsorship/fixtures.sql b/cypress/e2e/join_with_sponsorship/fixtures.sql deleted file mode 100644 index e412dd5e..00000000 --- a/cypress/e2e/join_with_sponsorship/fixtures.sql +++ /dev/null @@ -1,134 +0,0 @@ -INSERT INTO users - (id, email, email_verified, email_verified_at, encrypted_password, created_at, updated_at, given_name, family_name, - phone_number, job) -VALUES - (1, '233fd508-224d-4fe7-88ed-0a0d1df10e07@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Nouveau', '0123456789', 'Sbire'), - (2, '34c5063f-81c0-4d09-9d0b-a7502f844cdf@mailslurp.com', true, CURRENT_TIMESTAMP - interval '2 months', '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'SponsorActive', '0123456789', 'Sbire'), - (3, 'ba97e7a6-e603-465e-b2a5-236489ee0bb2@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'SponsorChosen', '0123456789', 'Sbire'), - (4, 'be040966-0142-421b-8041-5a3543a79a8a@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'External1', '0123456789', 'Sbire'), - (5, '487ef426-6135-42c9-b805-9161d3474974@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'NotAuthenticated1', '0123456789', 'Sbire'), - (6, '1bdcce53-4757-441c-8d07-93deee327d11@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'NeedsOfficialContactEmailVerification1', '0123456789', 'Sbire'), - (7, 'unused7@yopmail.com', true, CURRENT_TIMESTAMP - interval '4 months', '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'NotActive1', '0123456789', 'Sbire'), - -- add 50 organization members to trigger the sponsorship feature - (8, 'unused8@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor3', '0123456789', 'Sbire'), - (9, 'unused9@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor4', '0123456789', 'Sbire'), - (10, 'unused10@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor5', '0123456789', 'Sbire'), - (11, 'unused11@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor6', '0123456789', 'Sbire'), - (12, 'unused12@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor7', '0123456789', 'Sbire'), - (13, 'unused13@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor8', '0123456789', 'Sbire'), - (14, 'unused14@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor9', '0123456789', 'Sbire'), - (15, 'unused15@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor10', '0123456789', 'Sbire'), - (16, 'unused16@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor11', '0123456789', 'Sbire'), - (17, 'unused17@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor12', '0123456789', 'Sbire'), - (18, 'unused18@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor13', '0123456789', 'Sbire'), - (19, 'unused19@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor14', '0123456789', 'Sbire'), - (20, 'unused20@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor15', '0123456789', 'Sbire'), - (21, 'unused21@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor16', '0123456789', 'Sbire'), - (22, 'unused22@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor17', '0123456789', 'Sbire'), - (23, 'unused23@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor18', '0123456789', 'Sbire'), - (24, 'unused24@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor19', '0123456789', 'Sbire'), - (25, 'unused25@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor20', '0123456789', 'Sbire'), - (26, 'unused26@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor21', '0123456789', 'Sbire'), - (27, 'unused27@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor22', '0123456789', 'Sbire'), - (28, 'unused28@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor23', '0123456789', 'Sbire'), - (29, 'unused29@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor24', '0123456789', 'Sbire'), - (30, 'unused30@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor25', '0123456789', 'Sbire'), - (31, 'unused31@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor26', '0123456789', 'Sbire'), - (32, 'unused32@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor27', '0123456789', 'Sbire'), - (33, 'unused33@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor28', '0123456789', 'Sbire'), - (34, 'unused34@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor29', '0123456789', 'Sbire'), - (35, 'unused35@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor30', '0123456789', 'Sbire'), - (36, 'unused36@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor31', '0123456789', 'Sbire'), - (37, 'unused37@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor32', '0123456789', 'Sbire'), - (38, 'unused38@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor33', '0123456789', 'Sbire'), - (39, 'unused39@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor34', '0123456789', 'Sbire'), - (40, 'unused40@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor35', '0123456789', 'Sbire'), - (41, 'unused41@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor36', '0123456789', 'Sbire'), - (42, 'unused42@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor37', '0123456789', 'Sbire'), - (43, 'unused43@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor38', '0123456789', 'Sbire'), - (44, 'unused44@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor39', '0123456789', 'Sbire'), - (45, 'unused45@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor40', '0123456789', 'Sbire'), - (46, 'unused46@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor41', '0123456789', 'Sbire'), - (47, 'unused47@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor42', '0123456789', 'Sbire'), - (48, 'unused48@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor43', '0123456789', 'Sbire'), - (49, 'unused49@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor44', '0123456789', 'Sbire'), - (50, 'unused50@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor45', '0123456789', 'Sbire'), - (51, 'unused51@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor46', '0123456789', 'Sbire'), - (52, 'unused52@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor47', '0123456789', 'Sbire'), - (53, 'unused53@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor48', '0123456789', 'Sbire'), - (54, 'unused54@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor49', '0123456789', 'Sbire'), - (55, 'unused55@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor50', '0123456789', 'Sbire'), - (56, 'unused56@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Sponsor51', '0123456789', 'Sbire'), - (57, 'fbcbc4b0-40de-44ad-935e-26dd7ff2adb7@mailslurp.biz', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'FakeDomain', '0123456789', 'Sbire'); - -INSERT INTO organizations - (id, siret, created_at, updated_at) -VALUES - -- DIRECTION INTERMINISTERIELLE DU NUMERIQUE (DINUM) - (1, '13002526500013', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); - -INSERT INTO email_domains -(id, organization_id, domain, verification_type, verified_at) -VALUES - (1, 1, 'mailslurp.com', 'verified', CURRENT_TIMESTAMP), - (2, 1, 'mailslurp.biz', 'verified', CURRENT_TIMESTAMP), - (3, 1, 'fakedomain.com', null, CURRENT_TIMESTAMP); - -INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, needs_official_contact_email_verification, has_been_greeted) -VALUES - (2, 1, false, 'domain', false, true), - (3, 1, false, 'domain', false, true), - (4, 1, true, 'domain', false, true), - (5, 1, false, 'domain', null, false, false), - (6, 1, false, null, null, true, false), - (7, 1, false, 'domain', false, true), - (8, 1, false, 'domain', false, true), - (9, 1, false, 'domain', false, true), - (10, 1, false, 'domain', false, true), - (11, 1, false, 'domain', false, true), - (12, 1, false, 'domain', false, true), - (13, 1, false, 'domain', false, true), - (14, 1, false, 'domain', false, true), - (15, 1, false, 'domain', false, true), - (16, 1, false, 'domain', false, true), - (17, 1, false, 'domain', false, true), - (18, 1, false, 'domain', false, true), - (19, 1, false, 'domain', false, true), - (20, 1, false, 'domain', false, true), - (21, 1, false, 'domain', false, true), - (22, 1, false, 'domain', false, true), - (23, 1, false, 'domain', false, true), - (24, 1, false, 'domain', false, true), - (25, 1, false, 'domain', false, true), - (26, 1, false, 'domain', false, true), - (27, 1, false, 'domain', false, true), - (28, 1, false, 'domain', false, true), - (29, 1, false, 'domain', false, true), - (30, 1, false, 'domain', false, true), - (31, 1, false, 'domain', false, true), - (32, 1, false, 'domain', false, true), - (33, 1, false, 'domain', false, true), - (34, 1, false, 'domain', false, true), - (35, 1, false, 'domain', false, true), - (36, 1, false, 'domain', false, true), - (37, 1, false, 'domain', false, true), - (38, 1, false, 'domain', false, true), - (39, 1, false, 'domain', false, true), - (40, 1, false, 'domain', false, true), - (41, 1, false, 'domain', false, true), - (42, 1, false, 'domain', false, true), - (43, 1, false, 'domain', false, true), - (44, 1, false, 'domain', false, true), - (45, 1, false, 'domain', false, true), - (46, 1, false, 'domain', false, true), - (47, 1, false, 'domain', false, true), - (48, 1, false, 'domain', false, true), - (49, 1, false, 'domain', false, true), - (50, 1, false, 'domain', false, true), - (51, 1, false, 'domain', false, true), - (52, 1, false, 'domain', false, true), - (53, 1, false, 'domain', false, true), - (54, 1, false, 'domain', false, true), - (55, 1, false, 'domain', false, true), - (56, 1, false, 'domain', false, true); diff --git a/cypress/e2e/join_with_sponsorship/index.cy.ts b/cypress/e2e/join_with_sponsorship/index.cy.ts deleted file mode 100644 index 52bc5b39..00000000 --- a/cypress/e2e/join_with_sponsorship/index.cy.ts +++ /dev/null @@ -1,159 +0,0 @@ -// - -import { MatchOptionFieldEnum, MatchOptionShouldEnum } from "mailslurp-client"; - -// - -describe("join organizations", () => { - before(() => { - cy.mailslurp().then((mailslurp) => - mailslurp.inboxController.deleteAllInboxEmails({ - inboxId: "233fd508-224d-4fe7-88ed-0a0d1df10e07", - }), - ); - cy.mailslurp().then((mailslurp) => - mailslurp.inboxController.deleteAllInboxEmails({ - inboxId: "ba97e7a6-e603-465e-b2a5-236489ee0bb2", - }), - ); - cy.mailslurp().then((mailslurp) => - mailslurp.inboxController.deleteAllInboxEmails({ - inboxId: "fbcbc4b0-40de-44ad-935e-26dd7ff2adb7", - }), - ); - }); - - it("join organisation via sponsorship", function () { - cy.login( - "233fd508-224d-4fe7-88ed-0a0d1df10e07@mailslurp.com", - "password123", - ); - - cy.visit(`/users/join-organization`); - - // Click on the suggested organization - cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click(); - - // Open member selection - cy.get(".choices").click(); - - // Internal active members should be available for selection - cy.get(".choices__list").contains("Jean SponsorActive - Sbire"); - cy.get(".choices__list").contains("Jean SponsorChosen - Sbire"); - - // Current user should not be available for selection - cy.get(".choices__list") - .contains("Jean Nouveau - Sbire") - .should("not.exist"); - - // External member should not be available for selection - cy.get(".choices__list") - .contains("Jean External1 - Sbire") - .should("not.exist"); - - // Member that has not been authenticated yet should not be seen - cy.get(".choices__list") - .contains("Jean NotAuthenticated1 - Sbire") - .should("not.exist"); - - // Member that has not been authenticated yet should not be seen - cy.get(".choices__list") - .contains("Jean NeedsOfficialContactEmailVerification1 - Sbire") - .should("not.exist"); - - // Member that has not been authenticated yet should not be seen - cy.get(".choices__list") - .contains("Jean NotActive1 - Sbire") - .should("not.exist"); - - // Select second member - cy.get('[name="search_terms"]').type("SponsorChosen{enter}"); - - cy.get('[type="submit"]').click(); - - cy.contains("Votre compte est créé"); - cy.contains("Jean SponsorChosen a été informé"); - }); - - it("should send mail to sponsor", function () { - cy.mailslurp() - // use inbox id and a timeout of 30 seconds - .then((mailslurp) => - mailslurp.waitForLatestEmail( - "ba97e7a6-e603-465e-b2a5-236489ee0bb2", - 60000, - true, - ), - ) - // assert reception of notification email - .then((email) => { - expect(email.body).to.match( - /.*Jean.Nouveau.*\(233fd508-224d-4fe7-88ed-0a0d1df10e07@mailslurp.com\).*a rejoint l’organisation.*Direction interministerielle du numerique \(DINUM\).*sur .*MonComptePro/, - ); - }); - }); - - it("should not be able to select another sponsor", () => { - cy.login( - "233fd508-224d-4fe7-88ed-0a0d1df10e07@mailslurp.com", - "password123", - ); - - cy.visit(`/users/choose-sponsor/1`); - - // Open member selection - cy.get(".choices").click(); - - // Select second member - cy.get('[name="search_terms"]').type("Nouveau{enter}"); - - cy.get('[type="submit"]').click(); - - cy.contains("Une erreur est survenue."); - }); - - it("should not see sponsorship screen when whitelisted", () => { - cy.login( - "fbcbc4b0-40de-44ad-935e-26dd7ff2adb7@mailslurp.biz", - "password123", - ); - - cy.visit(`/users/join-organization`); - - // Click on the suggested organization - cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click(); - - // should not see sponsorship screen - cy.contains("Votre compte est créé"); - - // should not receive the list of existing user - cy.mailslurp().then((mailslurp) => - mailslurp - .waitForMatchingEmails( - { - matches: [ - { - field: MatchOptionFieldEnum.SUBJECT, - should: MatchOptionShouldEnum.EQUAL, - value: "Votre organisation sur MonComptePro", - }, - ], - }, - 1, - "fbcbc4b0-40de-44ad-935e-26dd7ff2adb7", - 5000, - true, - ) - .then((emails) => { - expect(emails).to.be.empty; - }) - .catch((error) => { - if (error.name === "AssertionError") { - throw error; - } - - expect(error.errorClass).to.equal("GetMessagesRetryException"); - }), - ); - }); -}); From 7a440b1d8420b8c3730d3c88ea3a64e737e4b015 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Mon, 2 Sep 2024 11:39:50 +0200 Subject: [PATCH 15/42] refactor: removes the last vestiges of auth by a sponsor --- scripts/fixtures.sql | 172 +++++++++++------------ scripts/import-accounts.ts | 4 - src/connectors/brevo.ts | 2 - src/middlewares/user.ts | 113 --------------- src/repositories/organization/getters.ts | 8 -- src/types/user-organization-link.d.ts | 9 -- 6 files changed, 86 insertions(+), 222 deletions(-) diff --git a/scripts/fixtures.sql b/scripts/fixtures.sql index 6974e00e..8f13d028 100644 --- a/scripts/fixtures.sql +++ b/scripts/fixtures.sql @@ -288,95 +288,95 @@ SELECT setval( ); INSERT INTO users_organizations - (user_id, organization_id, verification_type, authentication_by_peers_type, has_been_greeted) + (user_id, organization_id, verification_type, has_been_greeted) VALUES - (1, 1, 'verified_email_domain', 'all_members_notified', true), - (2, 2, 'verified_email_domain', 'all_members_notified', true), - (3, 2, 'verified_email_domain', 'all_members_notified', true), - (4, 2, 'verified_email_domain', 'all_members_notified', true), - (5, 2, 'verified_email_domain', 'all_members_notified', true), - (6, 2, 'verified_email_domain', 'all_members_notified', true), - (7, 3, 'verified_email_domain', 'all_members_notified', true), - (7, 4, 'verified_email_domain', 'all_members_notified', true), - (7, 5, 'verified_email_domain', 'all_members_notified', true), - (7, 6, 'verified_email_domain', 'all_members_notified', true), - (7, 7, 'verified_email_domain', 'all_members_notified', true), - (7, 8, 'verified_email_domain', 'all_members_notified', true), - (7, 9, 'verified_email_domain', 'all_members_notified', true), - (7, 10, 'verified_email_domain', 'all_members_notified', true), - (7, 11, 'verified_email_domain', 'all_members_notified', true), - (7, 12, 'verified_email_domain', 'all_members_notified', true), - (7, 13, 'verified_email_domain', 'all_members_notified', true), - (7, 14, 'verified_email_domain', 'all_members_notified', true), - (7, 15, 'verified_email_domain', 'all_members_notified', true), - (7, 16, 'verified_email_domain', 'all_members_notified', true), - (7, 17, 'verified_email_domain', 'all_members_notified', true), - (7, 18, 'verified_email_domain', 'all_members_notified', true), - (7, 19, 'verified_email_domain', 'all_members_notified', true), - (7, 20, 'verified_email_domain', 'all_members_notified', true), - (7, 21, 'verified_email_domain', 'all_members_notified', true), - (7, 22, 'verified_email_domain', 'all_members_notified', true), - (7, 23, 'verified_email_domain', 'all_members_notified', true), - (7, 24, 'verified_email_domain', 'all_members_notified', true), - (7, 25, 'verified_email_domain', 'all_members_notified', true), - (7, 26, 'verified_email_domain', 'all_members_notified', true), - (7, 27, 'verified_email_domain', 'all_members_notified', true), - (7, 28, 'verified_email_domain', 'all_members_notified', true), - (7, 29, 'verified_email_domain', 'all_members_notified', true), - (7, 30, 'verified_email_domain', 'all_members_notified', true), - (7, 31, 'verified_email_domain', 'all_members_notified', true), - (7, 32, 'verified_email_domain', 'all_members_notified', true), - (7, 33, 'verified_email_domain', 'all_members_notified', true), - (7, 34, 'verified_email_domain', 'all_members_notified', true), - (7, 35, 'verified_email_domain', 'all_members_notified', true), - (7, 36, 'verified_email_domain', 'all_members_notified', true), - (7, 37, 'verified_email_domain', 'all_members_notified', true), - (7, 38, 'verified_email_domain', 'all_members_notified', true), - (7, 39, 'verified_email_domain', 'all_members_notified', true), - (7, 40, 'verified_email_domain', 'all_members_notified', true), - (7, 41, 'verified_email_domain', 'all_members_notified', true), - (10, 2, 'verified_email_domain', 'all_members_notified', true), - (11, 2, 'verified_email_domain', 'all_members_notified', true), - (12, 2, 'verified_email_domain', 'all_members_notified', true), - (13, 2, 'verified_email_domain', 'all_members_notified', true), - (14, 2, 'verified_email_domain', 'all_members_notified', true), - (15, 2, 'verified_email_domain', 'all_members_notified', true), - (16, 2, 'verified_email_domain', 'all_members_notified', true), - (17, 2, 'verified_email_domain', 'all_members_notified', true), - (18, 2, 'verified_email_domain', 'all_members_notified', true), - (19, 2, 'verified_email_domain', 'all_members_notified', true), - (35, 46, 'verified_email_domain', 'all_members_notified', true), - (36, 46, 'verified_email_domain', 'all_members_notified', true), - (37, 46, 'verified_email_domain', 'all_members_notified', true), - (38, 46, 'verified_email_domain', 'all_members_notified', true), - (39, 46, 'verified_email_domain', 'all_members_notified', true), - (40, 46, 'verified_email_domain', 'all_members_notified', true), - (41, 46, 'verified_email_domain', 'all_members_notified', true), - (42, 46, 'verified_email_domain', 'all_members_notified', true), - (43, 46, 'verified_email_domain', 'all_members_notified', true), - (44, 46, 'verified_email_domain', 'all_members_notified', true), - (45, 46, 'verified_email_domain', 'all_members_notified', true), - (46, 46, 'verified_email_domain', 'all_members_notified', true), - (47, 46, 'verified_email_domain', 'all_members_notified', true), - (48, 46, 'verified_email_domain', 'all_members_notified', true), - (42, 47, 'verified_email_domain', 'all_members_notified', true), - (43, 47, 'verified_email_domain', 'all_members_notified', true), - (44, 47, 'verified_email_domain', 'all_members_notified', true), - (45, 47, 'verified_email_domain', 'all_members_notified', true), - (46, 47, 'verified_email_domain', 'all_members_notified', true), - (47, 47, 'verified_email_domain', 'all_members_notified', true), - (48, 47, 'verified_email_domain', 'all_members_notified', true), - (49, 48, 'verified_email_domain', 'all_members_notified', true), - (50, 48, 'verified_email_domain', 'all_members_notified', true), - (51, 48, 'verified_email_domain', 'all_members_notified', true), - (52, 48, 'verified_email_domain', 'all_members_notified', true), - (53, 48, 'verified_email_domain', 'all_members_notified', true), - (54, 48, 'verified_email_domain', 'all_members_notified', true), - (55, 48, 'verified_email_domain', 'all_members_notified', true) + (1, 1, 'verified_email_domain', true), + (2, 2, 'verified_email_domain', true), + (3, 2, 'verified_email_domain', true), + (4, 2, 'verified_email_domain', true), + (5, 2, 'verified_email_domain', true), + (6, 2, 'verified_email_domain', true), + (7, 3, 'verified_email_domain', true), + (7, 4, 'verified_email_domain', true), + (7, 5, 'verified_email_domain', true), + (7, 6, 'verified_email_domain', true), + (7, 7, 'verified_email_domain', true), + (7, 8, 'verified_email_domain', true), + (7, 9, 'verified_email_domain', true), + (7, 10, 'verified_email_domain', true), + (7, 11, 'verified_email_domain', true), + (7, 12, 'verified_email_domain', true), + (7, 13, 'verified_email_domain', true), + (7, 14, 'verified_email_domain', true), + (7, 15, 'verified_email_domain', true), + (7, 16, 'verified_email_domain', true), + (7, 17, 'verified_email_domain', true), + (7, 18, 'verified_email_domain', true), + (7, 19, 'verified_email_domain', true), + (7, 20, 'verified_email_domain', true), + (7, 21, 'verified_email_domain', true), + (7, 22, 'verified_email_domain', true), + (7, 23, 'verified_email_domain', true), + (7, 24, 'verified_email_domain', true), + (7, 25, 'verified_email_domain', true), + (7, 26, 'verified_email_domain', true), + (7, 27, 'verified_email_domain', true), + (7, 28, 'verified_email_domain', true), + (7, 29, 'verified_email_domain', true), + (7, 30, 'verified_email_domain', true), + (7, 31, 'verified_email_domain', true), + (7, 32, 'verified_email_domain', true), + (7, 33, 'verified_email_domain', true), + (7, 34, 'verified_email_domain', true), + (7, 35, 'verified_email_domain', true), + (7, 36, 'verified_email_domain', true), + (7, 37, 'verified_email_domain', true), + (7, 38, 'verified_email_domain', true), + (7, 39, 'verified_email_domain', true), + (7, 40, 'verified_email_domain', true), + (7, 41, 'verified_email_domain', true), + (10, 2, 'verified_email_domain', true), + (11, 2, 'verified_email_domain', true), + (12, 2, 'verified_email_domain', true), + (13, 2, 'verified_email_domain', true), + (14, 2, 'verified_email_domain', true), + (15, 2, 'verified_email_domain', true), + (16, 2, 'verified_email_domain', true), + (17, 2, 'verified_email_domain', true), + (18, 2, 'verified_email_domain', true), + (19, 2, 'verified_email_domain', true), + (35, 46, 'verified_email_domain', true), + (36, 46, 'verified_email_domain', true), + (37, 46, 'verified_email_domain', true), + (38, 46, 'verified_email_domain', true), + (39, 46, 'verified_email_domain', true), + (40, 46, 'verified_email_domain', true), + (41, 46, 'verified_email_domain', true), + (42, 46, 'verified_email_domain', true), + (43, 46, 'verified_email_domain', true), + (44, 46, 'verified_email_domain', true), + (45, 46, 'verified_email_domain', true), + (46, 46, 'verified_email_domain', true), + (47, 46, 'verified_email_domain', true), + (48, 46, 'verified_email_domain', true), + (42, 47, 'verified_email_domain', true), + (43, 47, 'verified_email_domain', true), + (44, 47, 'verified_email_domain', true), + (45, 47, 'verified_email_domain', true), + (46, 47, 'verified_email_domain', true), + (47, 47, 'verified_email_domain', true), + (48, 47, 'verified_email_domain', true), + (49, 48, 'verified_email_domain', true), + (50, 48, 'verified_email_domain', true), + (51, 48, 'verified_email_domain', true), + (52, 48, 'verified_email_domain', true), + (53, 48, 'verified_email_domain', true), + (54, 48, 'verified_email_domain', true), + (55, 48, 'verified_email_domain', true) ON CONFLICT (user_id, organization_id) DO UPDATE - SET (verification_type, authentication_by_peers_type, has_been_greeted) - = (EXCLUDED.verification_type, EXCLUDED.authentication_by_peers_type, EXCLUDED.has_been_greeted); + SET (verification_type, has_been_greeted) + = (EXCLUDED.verification_type, EXCLUDED.has_been_greeted); INSERT INTO oidc_clients (id, client_name, client_id, client_secret, redirect_uris, diff --git a/scripts/import-accounts.ts b/scripts/import-accounts.ts index b81fed49..6969cf20 100644 --- a/scripts/import-accounts.ts +++ b/scripts/import-accounts.ts @@ -8,7 +8,6 @@ import { } from "../src/connectors/api-sirene"; import { linkUserToOrganization, - updateUserOrganizationLink, upsert, } from "../src/repositories/organization/setters"; import { create, findByEmail, update } from "../src/repositories/user"; @@ -173,9 +172,6 @@ const maxInseeCallRateInMs = rateInMsFromArgs !== 0 ? rateInMsFromArgs : 125; user_id: user.id, verification_type: "imported_from_inclusion_connect", }); - await updateUserOrganizationLink(organization.id, user.id, { - authentication_by_peers_type: "deactivated_by_import", - }); } } catch (error) { logger.error(`unexpected error for siret: ${siret}`); diff --git a/src/connectors/brevo.ts b/src/connectors/brevo.ts index 28b6c45b..714d95b9 100644 --- a/src/connectors/brevo.ts +++ b/src/connectors/brevo.ts @@ -8,7 +8,6 @@ import { render } from "../services/renderer"; type RemoteTemplateSlug = | "official-contact-email-verification" - | "choose-sponsor" | "reset-password" | "magic-link" | "join-organization" @@ -31,7 +30,6 @@ const remoteTemplateSlugToBrevoTemplateId: { [k in RemoteTemplateSlug]: number; } = { "official-contact-email-verification": 3, - "choose-sponsor": 2, "reset-password": 5, "magic-link": 1, "join-organization": 4, diff --git a/src/middlewares/user.ts b/src/middlewares/user.ts index 353dfe9c..62aa1473 100644 --- a/src/middlewares/user.ts +++ b/src/middlewares/user.ts @@ -1,15 +1,9 @@ import { NextFunction, Request, Response } from "express"; import HttpErrors from "http-errors"; import { isEmpty } from "lodash-es"; -// import { PAIR_AUTHENTICATION_WHITELIST } from "../config/env"; import { UserNotFoundError } from "../config/errors"; import { is2FACapable, shouldForce2faForUser } from "../managers/2fa"; import { isBrowserTrustedForUser } from "../managers/browser-authentication"; -// import { -// markAsGouvFrDomain, -// markAsWhitelisted, -// notifyAllMembers, -// } from "../managers/organization/authentication-by-peers"; import { getOrganizationsByUserId, selectOrganization, @@ -407,110 +401,3 @@ export const checkUserHasNoPendingOfficialContactEmailVerificationMiddleware = ( next(error); } }); - -// export const checkUserHasBeenAuthenticatedByPeersMiddleware = ( -// req: Request, -// res: Response, -// next: NextFunction, -// ) => -// checkUserHasNoPendingOfficialContactEmailVerificationMiddleware( -// req, -// res, -// async (error) => { -// try { -// if (error) return next(error); - -// const { id: user_id, email } = getUserFromAuthenticatedSession(req); - -// const userOrganisations = await getOrganizationsByUserId(user_id); - -// let organizationThatNeedsAuthenticationByPeers; -// if (req.session.mustReturnOneOrganizationInPayload) { -// const selectedOrganizationId = -// await getSelectedOrganizationId(user_id); - -// organizationThatNeedsAuthenticationByPeers = userOrganisations.find( -// ({ id, authentication_by_peers_type }) => -// !authentication_by_peers_type && id === selectedOrganizationId, -// ); -// } else { -// organizationThatNeedsAuthenticationByPeers = userOrganisations.find( -// ({ authentication_by_peers_type }) => !authentication_by_peers_type, -// ); -// } - -// if (!isEmpty(organizationThatNeedsAuthenticationByPeers)) { -// const organization_id = organizationThatNeedsAuthenticationByPeers.id; -// const internalActiveUsers = -// await getInternalActiveUsers(organization_id); -// const otherInternalUsers = internalActiveUsers.filter( -// ({ email: e }) => e !== email, -// ); - -// if (PAIR_AUTHENTICATION_WHITELIST.includes(getEmailDomain(email))) { -// await markAsWhitelisted({ user_id, organization_id }); -// } else if (usesAGouvFrDomain(email)) { -// await markAsGouvFrDomain({ user_id, organization_id }); -// } else if (otherInternalUsers.length > 0) { -// return res.redirect(`/users/choose-sponsor/${organization_id}`); -// } else { -// await notifyAllMembers({ user_id, organization_id }); -// } -// } - -// return next(); -// } catch (error) { -// next(error); -// } -// }, -// ); - -// export const checkUserHasBeenGreetedForJoiningOrganizationMiddleware = ( -// req: Request, -// res: Response, -// next: NextFunction, -// ) => -// checkUserHasBeenAuthenticatedByPeersMiddleware(req, res, async (error) => { -// try { -// if (error) return next(error); - -// const userOrganisations = await getOrganizationsByUserId( -// getUserFromAuthenticatedSession(req).id, -// ); - -// let organizationThatNeedsGreetings; -// if (req.session.mustReturnOneOrganizationInPayload) { -// const selectedOrganizationId = await getSelectedOrganizationId( -// getUserFromAuthenticatedSession(req).id, -// ); - -// organizationThatNeedsGreetings = userOrganisations.find( -// ({ id, has_been_greeted }) => -// !has_been_greeted && id === selectedOrganizationId, -// ); -// } else { -// organizationThatNeedsGreetings = userOrganisations.find( -// ({ has_been_greeted }) => !has_been_greeted, -// ); -// } - -// if (!isEmpty(organizationThatNeedsGreetings)) { -// await greetForJoiningOrganization({ -// user_id: getUserFromAuthenticatedSession(req).id, -// organization_id: organizationThatNeedsGreetings.id, -// }); - -// return res.redirect( -// `/users/welcome/${organizationThatNeedsGreetings.id}`, -// ); -// } - -// return next(); -// } catch (error) { -// next(error); -// } -// }); - -// // check that user go through all requirements before issuing a session -// export const checkUserSignInRequirementsMiddleware = -// checkUserHasBeenGreetedForJoiningOrganizationMiddleware; diff --git a/src/repositories/organization/getters.ts b/src/repositories/organization/getters.ts index c3219a18..c1bd4b2a 100644 --- a/src/repositories/organization/getters.ts +++ b/src/repositories/organization/getters.ts @@ -25,9 +25,7 @@ SELECT o.*, uo.is_external, uo.verification_type, - uo.authentication_by_peers_type, uo.has_been_greeted, - uo.sponsor_id, uo.needs_official_contact_email_verification, uo.official_contact_email_verification_token, uo.official_contact_email_verification_sent_at @@ -99,9 +97,7 @@ SELECT u.*, uo.is_external, uo.verification_type, - uo.authentication_by_peers_type, uo.has_been_greeted, - uo.sponsor_id, uo.needs_official_contact_email_verification, uo.official_contact_email_verification_token, uo.official_contact_email_verification_sent_at @@ -127,7 +123,6 @@ export const getActiveUsers = (organization_id: number) => getUsersByOrganization( organization_id, ` - AND uo.authentication_by_peers_type IS NOT NULL AND u.email_verified_at >= $2`, [inactiveThresholdDate], ); @@ -137,7 +132,6 @@ export const getInternalActiveUsers = (organization_id: number) => organization_id, ` AND uo.is_external = FALSE - AND uo.authentication_by_peers_type IS NOT NULL AND u.email_verified_at >= $2`, [inactiveThresholdDate], ); @@ -157,9 +151,7 @@ SELECT created_at, updated_at, verification_type, - authentication_by_peers_type, has_been_greeted, - sponsor_id, needs_official_contact_email_verification, official_contact_email_verification_token, official_contact_email_verification_sent_at diff --git a/src/types/user-organization-link.d.ts b/src/types/user-organization-link.d.ts index 6c45c681..d3b3cd15 100644 --- a/src/types/user-organization-link.d.ts +++ b/src/types/user-organization-link.d.ts @@ -11,16 +11,7 @@ interface BaseUserOrganizationLink { | null; // updated when verification_type is changed verified_at: Date | null; - authentication_by_peers_type: - | "all_members_notified" - | "sponsored_by_member" - | "is_the_only_active_member" - | "deactivated_by_whitelist" - | "deactivated_by_gouv_fr_domain" - | "deactivated_by_import" - | null; has_been_greeted: boolean; - sponsor_id: number | null; needs_official_contact_email_verification: boolean; official_contact_email_verification_token: string | null; official_contact_email_verification_sent_at: Date | null; From 69ebc9643f0a5be8133a7728d39d6a17d2d69374 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Mon, 2 Sep 2024 12:46:15 +0200 Subject: [PATCH 16/42] fix(ci): fix test e2e in failure --- .../join_org_with_gouv_fr_domain/fixtures.sql | 17 ++--------------- .../join_org_with_verified_domain/fixtures.sql | 2 +- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql index 1a34df1c..b8d99471 100644 --- a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql +++ b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql @@ -3,18 +3,5 @@ INSERT INTO users phone_number, job) VALUES (1, 'unused@fake.gouv.fr', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Nouveau', '0123456789', 'Sbire'), - -INSERT INTO organizations - (id, siret, created_at, updated_at) -VALUES - (1, '13002526500013', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); - -INSERT INTO email_domains - (id, organization_id, domain, verification_type, verified_at) -VALUES - (1, 1, 'fake.gouv.fr', 'trackdechets_postal_mail', CURRENT_TIMESTAMP); - -INSERT INTO users_organizations - (user_id, organization_id, is_external, verification_type, has_been_greeted) -VALUES - (2, 1, false, 'domain', true); +-- We need a second member in the organization to trigger the sponsorship feature. + (2, 'membre@fake.gouv.fr', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Membre', '0123456789', 'Sbire'); diff --git a/cypress/e2e/join_org_with_verified_domain/fixtures.sql b/cypress/e2e/join_org_with_verified_domain/fixtures.sql index fd5ba1a5..b4b41141 100644 --- a/cypress/e2e/join_org_with_verified_domain/fixtures.sql +++ b/cypress/e2e/join_org_with_verified_domain/fixtures.sql @@ -32,7 +32,7 @@ INSERT INTO users_organizations VALUES (2, 1, false, 'domain', true), (7, 1, true, 'domain', true), - (8, 1, false, 'domain', null, false), + (8, 1, false, 'domain', false), (2, 2, false, 'domain', true), (3, 1, false, 'domain', true), (4, 1, false, 'domain', true), From 31b607525677470645525efa16e5ddf8bb86dd78 Mon Sep 17 00:00:00 2001 From: rebeccadumazert Date: Tue, 3 Sep 2024 14:08:26 +0200 Subject: [PATCH 17/42] fix: picks up some code I'd thrown away --- src/managers/organization/join.ts | 42 ++++++++++++++++++----- src/middlewares/user.ts | 57 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/managers/organization/join.ts b/src/managers/organization/join.ts index a2feeb14..7bc9d1d8 100644 --- a/src/managers/organization/join.ts +++ b/src/managers/organization/join.ts @@ -32,6 +32,7 @@ import { } from "../../repositories/organization/getters"; import { linkUserToOrganization, + updateUserOrganizationLink, upsert, } from "../../repositories/organization/setters"; import { findById as findUserById } from "../../repositories/user"; @@ -50,7 +51,7 @@ import { } from "../../services/organization"; import { isEmailValid } from "../../services/security"; import { unableToAutoJoinOrganizationMd } from "../../views/mails/unable-to-auto-join-organization"; -import { markDomainAsVerified } from "./main"; +import { getOrganizationsByUserId, markDomainAsVerified } from "./main"; export const doSuggestOrganizations = async ({ user_id, @@ -334,14 +335,6 @@ export const joinOrganization = async ({ ticket_id, }); - // Welcome the user when he joins is first organization as he may now be able to connect - await sendMail({ - to: [email], - subject: "Votre compte MonComptePro a bien été créé", - template: "welcome", - params: { given_name, family_name, email }, - }); - throw new UnableToAutoJoinOrganizationError(moderation_id); }; export const forceJoinOrganization = async ({ @@ -384,3 +377,34 @@ export const forceJoinOrganization = async ({ verification_type: link_verification_type, }); }; + +export const greetForJoiningOrganization = async ({ + user_id, + organization_id, +}: { + user_id: number; + organization_id: number; +}) => { + const userOrganisations = await getOrganizationsByUserId(user_id); + const organization = userOrganisations.find( + ({ id }) => id === organization_id, + ); + + if (isEmpty(organization)) { + throw new NotFoundError(); + } + + const { given_name, family_name, email } = (await findUserById(user_id))!; + + // Welcome the user when he joins is first organization as he may now be able to connect + await sendMail({ + to: [email], + subject: "Votre compte MonComptePro a bien été créé", + template: "welcome", + params: { given_name, family_name, email }, + }); + + return await updateUserOrganizationLink(organization_id, user_id, { + has_been_greeted: true, + }); +}; diff --git a/src/middlewares/user.ts b/src/middlewares/user.ts index 62aa1473..b0500b63 100644 --- a/src/middlewares/user.ts +++ b/src/middlewares/user.ts @@ -4,6 +4,7 @@ import { isEmpty } from "lodash-es"; import { UserNotFoundError } from "../config/errors"; import { is2FACapable, shouldForce2faForUser } from "../managers/2fa"; import { isBrowserTrustedForUser } from "../managers/browser-authentication"; +import { greetForJoiningOrganization } from "../managers/organization/join"; import { getOrganizationsByUserId, selectOrganization, @@ -401,3 +402,59 @@ export const checkUserHasNoPendingOfficialContactEmailVerificationMiddleware = ( next(error); } }); + +/// + +export const checkUserHasBeenGreetedForJoiningOrganizationMiddleware = ( + req: Request, + res: Response, + next: NextFunction, +) => + checkUserHasNoPendingOfficialContactEmailVerificationMiddleware( + req, + res, + async (error) => { + try { + if (error) return next(error); + + const userOrganisations = await getOrganizationsByUserId( + getUserFromAuthenticatedSession(req).id, + ); + + let organizationThatNeedsGreetings; + if (req.session.mustReturnOneOrganizationInPayload) { + const selectedOrganizationId = await getSelectedOrganizationId( + getUserFromAuthenticatedSession(req).id, + ); + + organizationThatNeedsGreetings = userOrganisations.find( + ({ id, has_been_greeted }) => + !has_been_greeted && id === selectedOrganizationId, + ); + } else { + organizationThatNeedsGreetings = userOrganisations.find( + ({ has_been_greeted }) => !has_been_greeted, + ); + } + + if (!isEmpty(organizationThatNeedsGreetings)) { + await greetForJoiningOrganization({ + user_id: getUserFromAuthenticatedSession(req).id, + organization_id: organizationThatNeedsGreetings.id, + }); + + return res.redirect( + `/users/welcome/${organizationThatNeedsGreetings.id}`, + ); + } + + return next(); + } catch (error) { + next(error); + } + }, + ); + +// check that user go through all requirements before issuing a session +export const checkUserSignInRequirementsMiddleware = + checkUserHasBeenGreetedForJoiningOrganizationMiddleware; From afaf9702d796e6431d478d711a3600c0e4a228f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:08:05 +0200 Subject: [PATCH 18/42] chore(deps): bump cypress-io/github-action from 6.7.2 to 6.7.5 (#679) Bumps [cypress-io/github-action](https://github.com/cypress-io/github-action) from 6.7.2 to 6.7.5. - [Release notes](https://github.com/cypress-io/github-action/releases) - [Changelog](https://github.com/cypress-io/github-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/cypress-io/github-action/compare/v6.7.2...v6.7.5) --- updated-dependencies: - dependency-name: cypress-io/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/end-to-end.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index 52dd8775..0ad711f3 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -133,7 +133,7 @@ jobs: - run: npm run fixtures:load-ci -- cypress/e2e/${{ matrix.e2e_test }}/fixtures.sql - run: npm run update-organization-info -- 500 - name: Cypress run - uses: cypress-io/github-action@v6.7.2 + uses: cypress-io/github-action@v6.7.5 with: wait-on: ${{ env.MONCOMPTEPRO_HOST }}/users/start-sign-in build: npm run build:assets From 3319606cf69904af073888e23ec31facdadf6675 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:55:51 +0200 Subject: [PATCH 19/42] chore(deps): bump docker/build-push-action from 3 to 6 (#659) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 6. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v3...v6) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1f9d15ef..1d31cb8b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -30,7 +30,7 @@ jobs: latest=${{ github.ref == 'refs/heads/master' }} - name: Build image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: push: true tags: ${{ steps.meta.outputs.tags }} From 753cc737118cb71597d6ee87a422f9bf6a0cc1d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:57:42 +0200 Subject: [PATCH 20/42] chore(deps): bump docker/login-action from 2 to 3 (#658) Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1d31cb8b..8a46c180 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,7 +11,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Login to Github Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} From c5969ac59e78685aebd7947d565b1c22204dfd2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 08:27:45 +0000 Subject: [PATCH 21/42] chore(deps): bump micromatch from 4.0.7 to 4.0.8 Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.7 to 4.0.8. - [Release notes](https://github.com/micromatch/micromatch/releases) - [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/micromatch/compare/4.0.7...4.0.8) --- updated-dependencies: - dependency-name: micromatch dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index da085bc1..fd6c5788 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5987,9 +5987,10 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "peer": true, "dependencies": { "braces": "^3.0.3", From 59903009899f308a5e067e331be125087ac86c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 10:53:32 +0200 Subject: [PATCH 22/42] fix: restore checkUserSignInRequirementsMiddleware --- src/routers/interaction.ts | 2 ++ src/routers/user.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/routers/interaction.ts b/src/routers/interaction.ts index ba8a826e..399f9835 100644 --- a/src/routers/interaction.ts +++ b/src/routers/interaction.ts @@ -5,6 +5,7 @@ import { interactionEndControllerFactory, interactionStartControllerFactory, } from "../controllers/interaction"; +import { checkUserSignInRequirementsMiddleware } from "../middlewares/user"; export const interactionRouter = (oidcProvider: Provider) => { const interactionRouter = Router(); @@ -19,6 +20,7 @@ export const interactionRouter = (oidcProvider: Provider) => { ); interactionRouter.get( "/:grant/login", + checkUserSignInRequirementsMiddleware, interactionEndControllerFactory(oidcProvider), ); diff --git a/src/routers/user.ts b/src/routers/user.ts index abe25e14..9f7e151e 100644 --- a/src/routers/user.ts +++ b/src/routers/user.ts @@ -75,6 +75,7 @@ import { checkUserHasSelectedAnOrganizationMiddleware, checkUserIsConnectedMiddleware, checkUserIsVerifiedMiddleware, + checkUserSignInRequirementsMiddleware, checkUserTwoFactorAuthMiddleware, } from "../middlewares/user"; @@ -124,6 +125,7 @@ export const userRouter = () => { passwordRateLimiterMiddleware, csrfProtectionMiddleware, postSignInMiddleware, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( @@ -138,6 +140,7 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, postSignUpController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -153,6 +156,7 @@ export const userRouter = () => { checkUserIsConnectedMiddleware, csrfProtectionMiddleware, postSignInWithAuthenticatorAppController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.post( @@ -161,6 +165,7 @@ export const userRouter = () => { checkUserIsConnectedMiddleware, csrfProtectionMiddleware, postVerifySecondFactorAuthenticationController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -176,6 +181,7 @@ export const userRouter = () => { checkUserTwoFactorAuthMiddleware, csrfProtectionMiddleware, postVerifyEmailController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.post( @@ -191,6 +197,7 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, postSendMagicLinkController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get("/magic-link-sent", getMagicLinkSentController); @@ -213,6 +220,7 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, getSignInWithPasskeyController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -222,6 +230,7 @@ export const userRouter = () => { checkCredentialPromptRequirementsMiddleware, csrfProtectionMiddleware, postVerifyFirstFactorAuthenticationController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( @@ -259,6 +268,7 @@ export const userRouter = () => { checkUserIsVerifiedMiddleware, csrfProtectionMiddleware, postPersonalInformationsController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -281,6 +291,7 @@ export const userRouter = () => { checkUserHasPersonalInformationsMiddleware, csrfProtectionMiddleware, postJoinOrganizationMiddleware, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -330,6 +341,7 @@ export const userRouter = () => { checkUserHasAtLeastOneOrganizationMiddleware, csrfProtectionMiddleware, postSelectOrganizationMiddleware, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); @@ -347,17 +359,20 @@ export const userRouter = () => { checkUserHasSelectedAnOrganizationMiddleware, csrfProtectionMiddleware, postOfficialContactEmailVerificationMiddleware, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( "/welcome/:organization_id", + checkUserSignInRequirementsMiddleware, csrfProtectionMiddleware, getWelcomeController, ); userRouter.post( "/welcome", rateLimiterMiddleware, + checkUserSignInRequirementsMiddleware, csrfProtectionMiddleware, issueSessionOrRedirectController, ); From e95c34917b544fbd48d260e4d78cebe0e1140022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 11:14:07 +0200 Subject: [PATCH 23/42] fix(e2e): remove remaining peer authentication tests --- .github/workflows/end-to-end.yml | 1 - .../e2e/join_org_with_gouv_fr_domain/env.conf | 1 - .../join_org_with_gouv_fr_domain/fixtures.sql | 7 -- .../join_org_with_gouv_fr_domain/index.cy.ts | 20 ----- .../fixtures.sql | 17 ++--- .../join_org_with_verified_domain/index.cy.ts | 74 ------------------- 6 files changed, 5 insertions(+), 115 deletions(-) delete mode 100644 cypress/e2e/join_org_with_gouv_fr_domain/env.conf delete mode 100644 cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql delete mode 100644 cypress/e2e/join_org_with_gouv_fr_domain/index.cy.ts diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index f042c653..319ae394 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -49,7 +49,6 @@ jobs: - join_and_moderation - join_collectivite_territoriale_official_contact_domain - join_must_confirm - - join_org_with_gouv_fr_domain - join_org_with_trackdechets_domain - join_org_with_verified_domain - join_with_code_sent_to_official_contact_email diff --git a/cypress/e2e/join_org_with_gouv_fr_domain/env.conf b/cypress/e2e/join_org_with_gouv_fr_domain/env.conf deleted file mode 100644 index c3ad04fe..00000000 --- a/cypress/e2e/join_org_with_gouv_fr_domain/env.conf +++ /dev/null @@ -1 +0,0 @@ -DO_NOT_SEND_MAIL=True diff --git a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql b/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql deleted file mode 100644 index b8d99471..00000000 --- a/cypress/e2e/join_org_with_gouv_fr_domain/fixtures.sql +++ /dev/null @@ -1,7 +0,0 @@ -INSERT INTO users - (id, email, email_verified, email_verified_at, encrypted_password, created_at, updated_at, given_name, family_name, - phone_number, job) -VALUES - (1, 'unused@fake.gouv.fr', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Nouveau', '0123456789', 'Sbire'), --- We need a second member in the organization to trigger the sponsorship feature. - (2, 'membre@fake.gouv.fr', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Membre', '0123456789', 'Sbire'); diff --git a/cypress/e2e/join_org_with_gouv_fr_domain/index.cy.ts b/cypress/e2e/join_org_with_gouv_fr_domain/index.cy.ts deleted file mode 100644 index 8bfb8d3c..00000000 --- a/cypress/e2e/join_org_with_gouv_fr_domain/index.cy.ts +++ /dev/null @@ -1,20 +0,0 @@ -// - -describe("join organizations", () => { - beforeEach(() => { - cy.login("unused@fake.gouv.fr", "password123"); - }); - - it("join suggested organisation", function () { - // Visit the signup page - cy.visit(`/`); - cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").contains( - "DINUM", - ); - // Click on the suggested organization - cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click(); - - // Check redirection to welcome page - cy.contains("Votre compte est créé"); - }); -}); diff --git a/cypress/e2e/join_org_with_verified_domain/fixtures.sql b/cypress/e2e/join_org_with_verified_domain/fixtures.sql index b4b41141..d644542e 100644 --- a/cypress/e2e/join_org_with_verified_domain/fixtures.sql +++ b/cypress/e2e/join_org_with_verified_domain/fixtures.sql @@ -3,13 +3,10 @@ INSERT INTO users phone_number, job) VALUES (1, 'c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Nouveau', '0123456789', 'Sbire'), - (2, '34c5063f-81c0-4d09-9d0b-a7502f844cdf@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User1', '0123456789', 'Sbire'), - (3, '761a72f6-d051-4df5-a733-62e207c4989b@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User2', '0123456789', 'Sbire'), - (4, 'be040966-0142-421b-8041-5a3543a79a8a@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User3', '0123456789', 'Sbire'), - (5, '487ef426-6135-42c9-b805-9161d3474974@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User4', '0123456789', 'Sbire'), - (6, '4fd3acbc-8711-4487-9313-c52dee8afcbb@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User5', '0123456789', 'Sbire'), - (7, '04972db5-2c62-460e-8a88-848317acfe34@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'External', '0123456789', 'Sbire'), - (8, '869c78e6-196d-4e95-9662-44d25f801b06@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'NotAuthenticated1', '0123456789', 'Sbire'); + (2, '761a72f6-d051-4df5-a733-62e207c4989b@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User2', '0123456789', 'Sbire'), + (3, 'be040966-0142-421b-8041-5a3543a79a8a@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User3', '0123456789', 'Sbire'), + (4, '487ef426-6135-42c9-b805-9161d3474974@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User4', '0123456789', 'Sbire'), + (5, '4fd3acbc-8711-4487-9313-c52dee8afcbb@mailslurp.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'User5', '0123456789', 'Sbire'); INSERT INTO organizations (id, siret, created_at, updated_at) @@ -31,10 +28,6 @@ INSERT INTO users_organizations (user_id, organization_id, is_external, verification_type, has_been_greeted) VALUES (2, 1, false, 'domain', true), - (7, 1, true, 'domain', true), - (8, 1, false, 'domain', false), - (2, 2, false, 'domain', true), (3, 1, false, 'domain', true), (4, 1, false, 'domain', true), - (5, 1, false, 'domain', true), - (6, 1, false, 'domain', true); + (5, 1, false, 'domain', true); diff --git a/cypress/e2e/join_org_with_verified_domain/index.cy.ts b/cypress/e2e/join_org_with_verified_domain/index.cy.ts index 641b9051..500484bf 100644 --- a/cypress/e2e/join_org_with_verified_domain/index.cy.ts +++ b/cypress/e2e/join_org_with_verified_domain/index.cy.ts @@ -1,7 +1,5 @@ // -import { MatchOptionFieldEnum, MatchOptionShouldEnum } from "mailslurp-client"; - // describe("join organizations", () => { @@ -11,15 +9,6 @@ describe("join organizations", () => { mailslurp.inboxController.deleteAllInboxEmails({ inboxId: "c6c64542-5601-43e0-b320-b20da72f6edc", }), - mailslurp.inboxController.deleteAllInboxEmails({ - inboxId: "34c5063f-81c0-4d09-9d0b-a7502f844cdf", - }), - mailslurp.inboxController.deleteAllInboxEmails({ - inboxId: "04972db5-2c62-460e-8a88-848317acfe34", - }), - mailslurp.inboxController.deleteAllInboxEmails({ - inboxId: "869c78e6-196d-4e95-9662-44d25f801b06", - }), ]), ); }); @@ -52,69 +41,6 @@ describe("join organizations", () => { // Check redirection to home page cy.contains("Votre compte est créé"); - - cy.mailslurp() - .then((mailslurp) => - mailslurp - .waitForMatchingEmails( - // match option does not seem to be used here - { - matches: [ - { - field: MatchOptionFieldEnum.SUBJECT, - should: MatchOptionShouldEnum.EQUAL, - value: "Votre organisation sur MonComptePro", - }, - ], - }, - 1, - "c6c64542-5601-43e0-b320-b20da72f6edc", - 60000, - true, - ) - .then(([{ id }]) => mailslurp.getEmail(id)), - ) - // assert reception of confirmation email - .then((email) => { - expect(email.body).to.include("Jean USER1"); - expect(email.body).to.include("Jean EXTERNAL (externe)"); - expect(email.body).to.not.include("Jean NOTAUTHENTICATED1"); - }); - - cy.mailslurp() - .then((mailslurp) => - mailslurp.waitForLatestEmail( - "34c5063f-81c0-4d09-9d0b-a7502f844cdf", - 60000, - true, - ), - ) - // assert reception of notification email - .then((email) => { - expect(email.body).to.match( - /.*Jean Nouveau.*\(c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp\.com\) a rejoint votre organisation.*Commune de clamart - Mairie.*sur .*MonComptePro/, - ); - }); - - // external users should not be warned for newcomers - cy.mailslurp().then((mailslurp) => - mailslurp - // note that this method may return empty emails arrays before receiving one. - .getEmails("04972db5-2c62-460e-8a88-848317acfe34") - .then((emails) => { - expect(emails).to.be.empty; - }), - ); - - // non-authenticated users should not be warned for newcomers - cy.mailslurp().then((mailslurp) => - mailslurp - // note that this method may return empty emails arrays before receiving one. - .getEmails("869c78e6-196d-4e95-9662-44d25f801b06") - .then((emails) => { - expect(emails).to.be.empty; - }), - ); }); it("join another organisation", function () { From a5b47ee47c3734e3c94acec0dd9221b8aadfcb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 11:27:10 +0200 Subject: [PATCH 24/42] refactor: delete unused env var --- src/config/env.ts | 2 -- src/config/env.zod.ts | 2 -- test/env.zod.test.ts | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/config/env.ts b/src/config/env.ts index a0fae9cc..a1f2f6d6 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -57,8 +57,6 @@ export const { MODERATION_TAG, MONCOMPTEPRO_LABEL, NODE_ENV, - NOTIFY_ALL_MEMBER_LIMIT, - PAIR_AUTHENTICATION_WHITELIST, PORT, RECENT_LOGIN_INTERVAL_IN_SECONDS, REDIS_URL, diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts index 3485c1da..efefac7d 100644 --- a/src/config/env.zod.ts +++ b/src/config/env.zod.ts @@ -70,8 +70,6 @@ export const envSchema = z NODE_ENV: z .enum(["production", "development", "test"]) .default("development"), - NOTIFY_ALL_MEMBER_LIMIT: z.coerce.number().int().nonnegative().default(50), - PAIR_AUTHENTICATION_WHITELIST: zCoerceArray(z.string()).default(""), PORT: z.coerce.number().int().nonnegative().default(3000), RECENT_LOGIN_INTERVAL_IN_SECONDS: z.coerce .number() diff --git a/test/env.zod.test.ts b/test/env.zod.test.ts index 81ffdbeb..c2e22761 100644 --- a/test/env.zod.test.ts +++ b/test/env.zod.test.ts @@ -50,8 +50,6 @@ test("default sample env", () => { MONCOMPTEPRO_HOST: "http://localhost:3000", MONCOMPTEPRO_LABEL: "MonComptePro", NODE_ENV: "development", - NOTIFY_ALL_MEMBER_LIMIT: 50, - PAIR_AUTHENTICATION_WHITELIST: [], PORT: 3000, RECENT_LOGIN_INTERVAL_IN_SECONDS: 900, REDIS_URL: "redis://:@127.0.0.1:6379", From 9badaa835c888cf523f5fe2f973d1e8b15af0a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 11:44:07 +0200 Subject: [PATCH 25/42] fix: migration error --- ...031836488_delete-authentication-by-peers-type.cjs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/migrations/1725031836488_delete-authentication-by-peers-type.cjs b/migrations/1725031836488_delete-authentication-by-peers-type.cjs index 80c3235f..282ab280 100644 --- a/migrations/1725031836488_delete-authentication-by-peers-type.cjs +++ b/migrations/1725031836488_delete-authentication-by-peers-type.cjs @@ -4,14 +4,6 @@ exports.up = async (pgm) => { await pgm.db.query(` ALTER TABLE users_organizations DROP COLUMN authentication_by_peers_type`); - - await pgm.db.query(` -ALTER TABLE users -ADD COLUMN has_been_greeted_for_first_organization_join boolean DEFAULT FALSE`); - - await pgm.db.query(` -UPDATE users -SET has_been_greeted_for_first_organization_join = TRUE`); }; exports.down = async (pgm) => { @@ -23,8 +15,4 @@ ADD COLUMN authentication_by_peers_type character varying; await pgm.db.query(` UPDATE users_organizations SET authentication_by_peers_type = 'all_members_notified'`); - - await pgm.db.query(` -ALTER TABLE users -DROP COLUMN has_been_greeted_for_first_organization_join`); }; From 25edfbb4f3c94c5c50c086236b86c330e25812ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 11:55:29 +0200 Subject: [PATCH 26/42] refactor: remove unused error class --- src/config/errors.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/config/errors.ts b/src/config/errors.ts index 842e4cf4..1748e5ca 100644 --- a/src/config/errors.ts +++ b/src/config/errors.ts @@ -44,8 +44,6 @@ export class UserMustConfirmToJoinOrganizationError extends Error { } } -export class UserHasAlreadyBeenAuthenticatedByPeers extends Error {} - export class InvalidCredentialsError extends Error {} export class EmailUnavailableError extends Error {} From aeecce072d6fe4fafaaf29bb6c95a134bc7b8b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 12:06:20 +0200 Subject: [PATCH 27/42] refactor: remove unused sponsor_id in database + remove unused url param --- ...25031836488_delete-authentication-by-peers-type.cjs | 10 ++++++++++ src/middlewares/user.ts | 4 +--- src/routers/user.ts | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/migrations/1725031836488_delete-authentication-by-peers-type.cjs b/migrations/1725031836488_delete-authentication-by-peers-type.cjs index 282ab280..cc8d32f9 100644 --- a/migrations/1725031836488_delete-authentication-by-peers-type.cjs +++ b/migrations/1725031836488_delete-authentication-by-peers-type.cjs @@ -4,6 +4,11 @@ exports.up = async (pgm) => { await pgm.db.query(` ALTER TABLE users_organizations DROP COLUMN authentication_by_peers_type`); + + await pgm.db.query(` +ALTER TABLE users_organizations +DROP COLUMN sponsor_id + `); }; exports.down = async (pgm) => { @@ -15,4 +20,9 @@ ADD COLUMN authentication_by_peers_type character varying; await pgm.db.query(` UPDATE users_organizations SET authentication_by_peers_type = 'all_members_notified'`); + + await pgm.db.query(` +ALTER TABLE users_organizations +ADD COLUMN sponsor_id int REFERENCES users ON DELETE SET NULL + `); }; diff --git a/src/middlewares/user.ts b/src/middlewares/user.ts index b0500b63..24c1534d 100644 --- a/src/middlewares/user.ts +++ b/src/middlewares/user.ts @@ -443,9 +443,7 @@ export const checkUserHasBeenGreetedForJoiningOrganizationMiddleware = ( organization_id: organizationThatNeedsGreetings.id, }); - return res.redirect( - `/users/welcome/${organizationThatNeedsGreetings.id}`, - ); + return res.redirect(`/users/welcome`); } return next(); diff --git a/src/routers/user.ts b/src/routers/user.ts index 9f7e151e..1aa2ff4d 100644 --- a/src/routers/user.ts +++ b/src/routers/user.ts @@ -364,7 +364,7 @@ export const userRouter = () => { ); userRouter.get( - "/welcome/:organization_id", + "/welcome", checkUserSignInRequirementsMiddleware, csrfProtectionMiddleware, getWelcomeController, From 878ec90b584fc73f012ec252f5889b2e8552ba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 12:13:30 +0200 Subject: [PATCH 28/42] refactor: remove unused repository --- src/repositories/organization/getters.ts | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/repositories/organization/getters.ts b/src/repositories/organization/getters.ts index c1bd4b2a..9ad4d2f3 100644 --- a/src/repositories/organization/getters.ts +++ b/src/repositories/organization/getters.ts @@ -1,5 +1,4 @@ import { QueryResult } from "pg"; -import { MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES } from "../../config/env"; import { getDatabaseConnection } from "../../connectors/postgres"; export const findById = async (id: number) => { @@ -114,28 +113,6 @@ ${additionalWhereClause}`, export const getUsers = (organization_id: number) => getUsersByOrganization(organization_id); -const inactiveThresholdDate = new Date( - new Date().getTime() - - MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES * 60e3, -); - -export const getActiveUsers = (organization_id: number) => - getUsersByOrganization( - organization_id, - ` - AND u.email_verified_at >= $2`, - [inactiveThresholdDate], - ); - -export const getInternalActiveUsers = (organization_id: number) => - getUsersByOrganization( - organization_id, - ` - AND uo.is_external = FALSE - AND u.email_verified_at >= $2`, - [inactiveThresholdDate], - ); - export const getUserOrganizationLink = async ( organization_id: number, user_id: number, From 6aea94f7ca3ac8ac7035ebc00509e5faecb20624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 12:17:44 +0200 Subject: [PATCH 29/42] refactor: remove unused function --- src/services/email.ts | 6 ------ test/email.test.ts | 46 ++----------------------------------------- 2 files changed, 2 insertions(+), 50 deletions(-) diff --git a/src/services/email.ts b/src/services/email.ts index fe735a3f..ba575e92 100644 --- a/src/services/email.ts +++ b/src/services/email.ts @@ -31,9 +31,3 @@ export const usesAFreeEmailProvider = (email: string) => { return isAFreeEmailProvider(domain); }; - -export const usesAGouvFrDomain = (email: string) => { - const domain = getEmailDomain(email); - - return domain.endsWith(".gouv.fr"); -}; diff --git a/test/email.test.ts b/test/email.test.ts index fc5c32d3..a3da1c41 100644 --- a/test/email.test.ts +++ b/test/email.test.ts @@ -1,9 +1,5 @@ -import { assert, expect } from "chai"; -import { - getEmailDomain, - usesAFreeEmailProvider, - usesAGouvFrDomain, -} from "../src/services/email"; +import { assert } from "chai"; +import { getEmailDomain, usesAFreeEmailProvider } from "../src/services/email"; describe("getEmailDomain", () => { const data = [ @@ -53,41 +49,3 @@ describe("usesAFreeEmailProvider", () => { }); }); }); - -describe("usesAGouvFrDomain", () => { - const emailAddressesThatUseGouvFrDomain = [ - "user@beta.gouv.fr", - "user@interieur.gouv.fr", - "user@pm.gouv.fr", - "user@sub.domain.gouv.fr", - ]; - - emailAddressesThatUseGouvFrDomain.forEach((email) => { - it("should return true for email that uses gouv.fr domains", () => { - assert.equal(usesAGouvFrDomain(email), true); - }); - }); - - const nonGouvFrEmailAddresses = [ - "user@beta.agouv.fr", - "user@beta.gouv.fr.co", - "user@gouv.fr", - "user@paris.fr", - ]; - - nonGouvFrEmailAddresses.forEach((nonGouvFrEmailAddress) => { - it("should return false for non gouv.fr email address", () => { - assert.equal(usesAGouvFrDomain(nonGouvFrEmailAddress), false); - }); - }); - - const nonValidEmailAddresses = ["user@beta.gouv.fra"]; - - nonValidEmailAddresses.forEach((nonValidEmailAddress) => { - it("should return false for non gouv.fr email address", () => { - expect(() => usesAGouvFrDomain(nonValidEmailAddress)).to.throw( - 'Invalid TLD {"parts":["beta","gouv","fra"],"tld_level":-1,"allowUnknownTLD":false}', - ); - }); - }); -}); From ee1a36e82335dba48e1d5c1a7ea10ca9fdaad877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 12:20:55 +0200 Subject: [PATCH 30/42] fix: restore checkUserSignInRequirementsMiddleware --- src/routers/user.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routers/user.ts b/src/routers/user.ts index 1aa2ff4d..6afed148 100644 --- a/src/routers/user.ts +++ b/src/routers/user.ts @@ -212,6 +212,7 @@ export const userRouter = () => { rateLimiterMiddleware, csrfProtectionMiddleware, postSignInWithMagicLinkController, + checkUserSignInRequirementsMiddleware, issueSessionOrRedirectController, ); userRouter.get( From 4e8a11a8a26db4c2054030da1009bd9126ffee26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 6 Sep 2024 12:26:42 +0200 Subject: [PATCH 31/42] refactor: remove unused searchable-select widget --- assets/css/searchable-select.css | 17 -------------- assets/js/searchable-select.js | 16 ------------- package-lock.json | 39 +++----------------------------- package.json | 3 +-- 4 files changed, 4 insertions(+), 71 deletions(-) delete mode 100644 assets/css/searchable-select.css delete mode 100644 assets/js/searchable-select.js diff --git a/assets/css/searchable-select.css b/assets/css/searchable-select.css deleted file mode 100644 index d4a3eba3..00000000 --- a/assets/css/searchable-select.css +++ /dev/null @@ -1,17 +0,0 @@ -@import "choices.js/public/assets/styles/choices.css"; - -.choices { - margin-top: 0.5rem; -} - -.choices__list { - z-index: 2 !important; -} - -.choices[data-type*="select-one"]:after { - display: none !important; -} - -.choices__list--single { - padding: unset !important; -} diff --git a/assets/js/searchable-select.js b/assets/js/searchable-select.js deleted file mode 100644 index b81b3d49..00000000 --- a/assets/js/searchable-select.js +++ /dev/null @@ -1,16 +0,0 @@ -import Choices from "choices.js"; - -document.addEventListener( - "DOMContentLoaded", - function () { - var searchable_select = document.getElementById("searchable-select"); - var choices = new Choices(searchable_select, { - noResultsText: "Aucun résultat trouvé", - itemSelectText: "", - classNames: { - containerInner: "fr-select", - }, - }); - }, - false, -); diff --git a/package-lock.json b/package-lock.json index da085bc1..ec2dbc73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "axios": "^1.7.4", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", - "choices.js": "^10.2.0", "connect-redis": "^7.1.1", "console-log-level": "^1.4.1", "cookie-parser": "^1.4.6", @@ -102,6 +101,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -2752,16 +2752,6 @@ "node": ">= 0.8.0" } }, - "node_modules/choices.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-10.2.0.tgz", - "integrity": "sha512-8PKy6wq7BMjNwDTZwr3+Zry6G2+opJaAJDDA/j3yxvqSCnvkKe7ZIFfIyOhoc7htIWFhsfzF9tJpGUATcpUtPg==", - "dependencies": { - "deepmerge": "^4.2.2", - "fuse.js": "^6.6.2", - "redux": "^4.2.0" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -3625,14 +3615,6 @@ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==" }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -4649,14 +4631,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fuse.js": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", - "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==", - "engines": { - "node": ">=10" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -7619,18 +7593,11 @@ "node": ">=4" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true }, "node_modules/request-progress": { "version": "3.0.0", diff --git a/package.json b/package.json index 8241ba88..21427964 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "dependencies": { "@fullhuman/postcss-purgecss": "^5.0.0", "@gouvfr/dsfr": "^1.12.1", + "@numerique-gouv/crisp": "https://github.com/douglasduteil/crisp/releases/download/v1.6.1/douglasduteil-crisp-1.6.1.tgz", "@panva/jose": "^1.9.3", "@sentry/node": "^7.112.2", "@sentry/tracing": "^7.112.2", @@ -57,11 +58,9 @@ "axios": "^1.7.4", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", - "choices.js": "^10.2.0", "connect-redis": "^7.1.1", "console-log-level": "^1.4.1", "cookie-parser": "^1.4.6", - "@numerique-gouv/crisp": "https://github.com/douglasduteil/crisp/releases/download/v1.6.1/douglasduteil-crisp-1.6.1.tgz", "csrf-sync": "^4.0.3", "dotenv": "^16.4.5", "ejs": "^3.1.10", From 3430ccd2fe605ca775feda3ec81310c7941b4f8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:37:18 +0200 Subject: [PATCH 32/42] chore(deps): bump cypress-io/github-action from 6.7.5 to 6.7.6 (#684) Bumps [cypress-io/github-action](https://github.com/cypress-io/github-action) from 6.7.5 to 6.7.6. - [Release notes](https://github.com/cypress-io/github-action/releases) - [Changelog](https://github.com/cypress-io/github-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/cypress-io/github-action/compare/v6.7.5...v6.7.6) --- updated-dependencies: - dependency-name: cypress-io/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/end-to-end.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index b7f54042..95905c96 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -131,7 +131,7 @@ jobs: - run: npm run fixtures:load-ci -- cypress/e2e/${{ matrix.e2e_test }}/fixtures.sql - run: npm run update-organization-info -- 500 - name: Cypress run - uses: cypress-io/github-action@v6.7.5 + uses: cypress-io/github-action@v6.7.6 with: wait-on: ${{ env.MONCOMPTEPRO_HOST }}/users/start-sign-in build: npm run build:assets From b93d2f408873c075eeb295c0cc96cada69f527aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 11:35:54 +0200 Subject: [PATCH 33/42] feat: add custom DS message --- src/config/notification-messages.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/notification-messages.ts b/src/config/notification-messages.ts index 5dcc8807..482d0b5c 100644 --- a/src/config/notification-messages.ts +++ b/src/config/notification-messages.ts @@ -181,6 +181,11 @@ Si vous avez oublié votre mot de passe cliquez sur « Mot de passe oublié ? description: "Attention : le service auquel vous souhaitez accéder requiert une validation en deux étapes. Merci de configurer soit une clé d’accès soit l’application FreeOTP Authenticator.", }, + "2fa_not_configured_for_ds": { + type: "warning", + description: + "Attention : pour accéder à Démarches Simplifiées vous devez configurer soit une clé d’accès soit l’application FreeOTP Authenticator.", + }, }; export default notificationMessages; From b722d64359ea8529056a6fdddc29d673d00b2022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Tue, 10 Sep 2024 08:20:59 +0200 Subject: [PATCH 34/42] refactor(env): less variable for devs more variable in prod configuration --- .env.development | 0 .env.sample | 37 ++---- .env.test | 19 +-- .github/workflows/end-to-end.yml | 21 +--- cypress/README.md | 5 +- docker-compose.yml | 6 +- installation.md | 23 +++- src/config/env.ts | 13 +- src/config/env.zod.ts | 202 ++++++++++++++++--------------- test/env.zod.test.ts | 25 ++-- 10 files changed, 161 insertions(+), 190 deletions(-) create mode 100644 .env.development diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..e69de29b diff --git a/.env.sample b/.env.sample index b0634f19..55f92736 100644 --- a/.env.sample +++ b/.env.sample @@ -1,26 +1,11 @@ -# running on port 3000 -PORT=3000 -MONCOMPTEPRO_HOST=http://localhost:3000 -# database configuration -DATABASE_URL=postgres://username:password@localhost:5432/dbname -REDIS_URL=redis://:@127.0.0.1:6379 -# email configuration -DO_NOT_SEND_MAIL=True -BREVO_API_KEY=____________________________ -DEBOUNCE_API_KEY=_____________ -ZAMMAD_URL=https://support.etalab.gouv.fr -ZAMMAD_TOKEN=___________________________________________________________ -# disable features -DO_NOT_CHECK_EMAIL_DELIVERABILITY=True -CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE=True -DO_NOT_USE_ANNUAIRE_EMAILS=True -DO_NOT_AUTHENTICATE_BROWSER=True -DISABLE_SECURITY_RESPONSE_HEADERS=True -SECURE_COOKIES=False -SESSION_COOKIE_SECRET=moncompteprosecret -SYMMETRIC_ENCRYPTION_KEY=aTrueRandom32BytesLongBase64EncodedStringAA= -# A default JWKS is necessary for interacting with a client with signed payloads enabled -JWKS: '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}' -# INSEE credentials needed to fetch organization info -INSEE_CONSUMER_KEY=____________________________ -INSEE_CONSUMER_SECRET=____________________________ +# Section 1: These variables are required for the full connection process +DATABASE_URL=postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro +#INSEE_CONSUMER_KEY= +#INSEE_CONSUMER_SECRET= + +# Section 2: These variables are required for end-to-end testing +#BREVO_API_KEY= +#CYPRESS_MAILSLURP_API_KEY= +#DEBOUNCE_API_KEY= +#DO_NOT_SEND_MAIL=False +#ZAMMAD_TOKEN= diff --git a/.env.test b/.env.test index 9fa0dda0..e529dfb9 100644 --- a/.env.test +++ b/.env.test @@ -1,20 +1,3 @@ -BREVO_API_KEY=____________________________ CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE=False -DATABASE_URL=postgres://username:password@localhost:5432/dbname -DEBOUNCE_API_KEY=_____________ -DISABLE_SECURITY_RESPONSE_HEADERS=True -DO_NOT_AUTHENTICATE_BROWSER=True -DO_NOT_CHECK_EMAIL_DELIVERABILITY=False -DO_NOT_SEND_MAIL=True DO_NOT_USE_ANNUAIRE_EMAILS=False -INSEE_CONSUMER_KEY=____________________________ -INSEE_CONSUMER_SECRET=____________________________ -JWKS: '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}' -MONCOMPTEPRO_HOST=http://localhost:3000 -PORT=3000 -REDIS_URL=redis://:@127.0.0.1:6379 -SECURE_COOKIES=False -SESSION_COOKIE_SECRET=moncompteprosecret -SYMMETRIC_ENCRYPTION_KEY=aTrueRandom32BytesLongBase64EncodedStringAA= -ZAMMAD_TOKEN=___________________________________________________________ -ZAMMAD_URL=https://support.etalab.gouv.fr +DO_NOT_CHECK_EMAIL_DELIVERABILITY=False diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index b7f54042..cc47a1dd 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -7,34 +7,21 @@ on: - "!master" env: - ACCESS_LOG_PATH: PGUSER: moncomptepro PGPASSWORD: moncomptepro PGDATABASE: moncomptepro PGHOST: 127.0.0.1 PGPORT: 5432 - DATABASE_URL: postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro BREVO_API_KEY: ${{ secrets.BREVO_API_KEY }} - MONCOMPTEPRO_HOST: http://172.18.0.1:3000 CYPRESS_BASE_URL: http://172.18.0.1:3000 - DO_NOT_SEND_MAIL: "False" - DO_NOT_CHECK_EMAIL_DELIVERABILITY: "True" - CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE: "True" - DO_NOT_RATE_LIMIT: "True" - DO_NOT_USE_ANNUAIRE_EMAILS: "True" - DO_NOT_AUTHENTICATE_BROWSER: "True" - SESSION_COOKIE_SECRET: secret + CYPRESS_MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} + DATABASE_URL: postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro DEBOUNCE_API_KEY: ${{ secrets.DEBOUNCE_API_KEY }} - SENTRY_DSN: + DO_NOT_SEND_MAIL: "False" INSEE_CONSUMER_KEY: ${{ secrets.INSEE_CONSUMER_KEY }} INSEE_CONSUMER_SECRET: ${{ secrets.INSEE_CONSUMER_SECRET }} - CYPRESS_MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} - SECURE_COOKIES: "False" - ZAMMAD_URL: ${{ secrets.ZAMMAD_URL }} + MONCOMPTEPRO_HOST: http://172.18.0.1:3000 ZAMMAD_TOKEN: ${{ secrets.ZAMMAD_TOKEN }} - MODERATION_TAG: "github-action-e2e-test" - SYMMETRIC_ENCRYPTION_KEY: aTrueRandom32BytesLongBase64EncodedStringAA= - JWKS: '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}' jobs: test: strategy: diff --git a/cypress/README.md b/cypress/README.md index 9aae03ba..8b08af6c 100644 --- a/cypress/README.md +++ b/cypress/README.md @@ -4,15 +4,14 @@ ### Setup env vars -You will need to set `BREVO_API_KEY`, `DEBOUNCE_API_KEY`, `ZAMMAD_URL`, `ZAMMAD_TOKEN` and `CYPRESS_MAILSLURP_API_KEY`. +You will need to set `BREVO_API_KEY`, `CYPRESS_MAILSLURP_API_KEY`, `DEBOUNCE_API_KEY`, and `ZAMMAD_TOKEN`. Ask a teammate for them and put the values in your `.env`. -Also in your .env put the following values : +Also in your `.env` put the following values : ```dotenv DO_NOT_SEND_MAIL=False -DO_NOT_RATE_LIMIT=True ``` ### Load test fixtures in the database diff --git a/docker-compose.yml b/docker-compose.yml index c86d80dc..6bc57a38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,9 +4,9 @@ services: ports: - "5432:5432" environment: - POSTGRES_USER: username - POSTGRES_PASSWORD: password - POSTGRES_DB: dbname + POSTGRES_USER: moncomptepro + POSTGRES_PASSWORD: moncomptepro + POSTGRES_DB: moncomptepro volumes: - db-data:/var/lib/postgresql/data diff --git a/installation.md b/installation.md index be1bac96..6b051ee4 100644 --- a/installation.md +++ b/installation.md @@ -22,8 +22,6 @@ This guide provides steps to run the MonComptePro Node.js application locally wh 1. **Install Node.js Dependencies**: - > If you don’t want to run end-to-end tests locally, you can prevent the installation script to download the (somewhat big) cypress binary by running `CYPRESS_INSTALL_BINARY=0 npm install` instead of the following command. - Inside the project’s root directory, run: ```bash @@ -38,14 +36,11 @@ This guide provides steps to run the MonComptePro Node.js application locally wh This will create a local copy of the `.env` file containing the environnement variables to run MonComptePro. - We set the defaults in `.env` (all environments) and, following the `NODE_ENV` environment variable, the `.env.development` (development environment), `.env.production` (production environment) or `.env.test` (test environment). - We recommend to use the `.env*.local` to override the defaults variables. `.env..local` will take precedence over `.env.local` and `.env.`. - 3. **Get your own INSEE api credential**: or use the one of your teammates. Fetch them at https://api.gouv.fr/les-api/sirene_v3. - Then fill your local .env file with them. + Then fill your `.env` file with them. 4. **Database Initialization**: The database will be automatically initialized with data from `scripts/fixtures.sql`. @@ -97,3 +92,19 @@ To connect to these databases, use the following commands: docker compose exec db psql postgres://username:password@db:5432/dbname docker compose exec redis redis-cli -h redis -p 6379 ``` + +## Configuring different environment variables + +The default environment variables are defined in the `.env` file, which applies to all environments. Based on the `NODE_ENV` variable, the corresponding file is selected: `.env.development` for the development environment, `.env.production` for production, or `.env.test` for testing. + +To customize or override these defaults, we recommend using the `.env*.local` files. The file `.env..local` will have higher priority over both `.env.local` and `.env.`. + +## Skipping Cypress Binary Installation for Local Setup + +If you prefer not to run end-to-end tests locally and want to avoid downloading the large Cypress binary, you can prevent it during the installation process. To do this, run the following command: + +```bash +CYPRESS_INSTALL_BINARY=0 npm install +``` + +This command ensures that the Cypress binary is not downloaded, saving time and disk space during the installation process. diff --git a/src/config/env.ts b/src/config/env.ts index a1f2f6d6..20dadf78 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -25,6 +25,7 @@ const parsedEnv = envSchema.safeParse(process.env, { if (!parsedEnv.success) throw fromZodError(parsedEnv.error, {}); export const { + ACCESS_LOG_PATH, API_AUTH_PASSWORD, API_AUTH_USERNAME, BREVO_API_KEY, @@ -37,6 +38,7 @@ export const { CRISP_RESOLVE_DELAY, CRISP_USER_NICKNAME, CRISP_WEBSITE_ID, + DATABASE_URL, DEBOUNCE_API_KEY, DEPLOY_ENV, DISABLE_SECURITY_RESPONSE_HEADERS, @@ -49,12 +51,15 @@ export const { EMAIL_DELIVERABILITY_WHITELIST, ENABLE_FIXED_ACR, HTTP_CLIENT_TIMEOUT, + INSEE_CONSUMER_KEY, + INSEE_CONSUMER_SECRET, JWKS, LOG_LEVEL, MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES, MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES, MAX_SUGGESTED_ORGANIZATIONS, MODERATION_TAG, + MONCOMPTEPRO_HOST, MONCOMPTEPRO_LABEL, NODE_ENV, PORT, @@ -73,12 +78,4 @@ export const { ZAMMAD_URL, } = parsedEnv.data; -export const { - MONCOMPTEPRO_HOST = `http://localhost:${PORT}`, - ACCESS_LOG_PATH, - DATABASE_URL, - INSEE_CONSUMER_KEY, - INSEE_CONSUMER_SECRET, -} = process.env; - export const MONCOMPTEPRO_IDENTIFIER = new URL(MONCOMPTEPRO_HOST).hostname; diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts index 335ba646..d50ab7d7 100644 --- a/src/config/env.zod.ts +++ b/src/config/env.zod.ts @@ -1,13 +1,15 @@ import { z, type ZodTypeAny } from "zod"; -// +export const emailEnvSchema = z.object({ + BREVO_API_KEY: z.string().optional(), + DO_NOT_SEND_MAIL: zodTrueFalseBoolean().default("True"), + ZAMMAD_TOKEN: z.string().optional(), + ZAMMAD_URL: z.string().url().default("https://support.etalab.gouv.fr"), +}); -export const apiEnvSchema = z.object({ +export const connectorEnvSchema = z.object({ API_AUTH_PASSWORD: z.string().default("admin"), API_AUTH_USERNAME: z.string().default("admin"), -}); - -export const crispEnvSchema = z.object({ CRISP_BASE_URL: z.string().url().default("https://api.crisp.chat"), CRISP_IDENTIFIER: z.string().default(""), CRISP_KEY: z.string().default(""), @@ -15,109 +17,115 @@ export const crispEnvSchema = z.object({ CRISP_RESOLVE_DELAY: z.coerce.number().int().nonnegative().default(1_000), // 1 second CRISP_USER_NICKNAME: z.string().default("MonComptePro"), CRISP_WEBSITE_ID: z.string().default(""), + DATABASE_URL: z.string().url(), + DEBOUNCE_API_KEY: z.string().optional(), + INSEE_CONSUMER_KEY: z.string(), + INSEE_CONSUMER_SECRET: z.string(), + REDIS_URL: z.string().url().default("redis://:@127.0.0.1:6379"), + SENTRY_DSN: z.string().default(""), }); -// +export const featureTogglesEnvSchema = z.object({ + CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE: zodTrueFalseBoolean().default("False"), + CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE: zodTrueFalseBoolean().default("True"), + DISABLE_SECURITY_RESPONSE_HEADERS: zodTrueFalseBoolean().default("True"), + DISPLAY_TEST_ENV_WARNING: zodTrueFalseBoolean().default("False"), + DO_NOT_AUTHENTICATE_BROWSER: zodTrueFalseBoolean().default("True"), + DO_NOT_CHECK_EMAIL_DELIVERABILITY: zodTrueFalseBoolean().default("True"), + DO_NOT_RATE_LIMIT: zodTrueFalseBoolean().default("True"), + DO_NOT_USE_ANNUAIRE_EMAILS: zodTrueFalseBoolean().default("True"), + SECURE_COOKIES: zodTrueFalseBoolean().default("False"), + ENABLE_FIXED_ACR: zodTrueFalseBoolean().default("False"), +}); -export const envSchema = z - .object({ - ACCESS_LOG_PATH: z.string().optional(), - BREVO_API_KEY: z.string().default(""), - CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE: zodTrueFalseBoolean().default("False"), - CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE: - zodTrueFalseBoolean().default("False"), - DATABASE_URL: z.string().url(), - DEBOUNCE_API_KEY: z.string().optional(), - DEPLOY_ENV: z.enum(["preview", "production", "sandbox"]).default("preview"), - DISABLE_SECURITY_RESPONSE_HEADERS: zodTrueFalseBoolean().default("False"), - DISPLAY_TEST_ENV_WARNING: zodTrueFalseBoolean().default("False"), - DO_NOT_AUTHENTICATE_BROWSER: zodTrueFalseBoolean().default("False"), - DO_NOT_CHECK_EMAIL_DELIVERABILITY: zodTrueFalseBoolean().default("False"), - DO_NOT_RATE_LIMIT: zodTrueFalseBoolean().default("False"), - DO_NOT_SEND_MAIL: zodTrueFalseBoolean().default("False"), - DO_NOT_USE_ANNUAIRE_EMAILS: zodTrueFalseBoolean().default("False"), - EMAIL_DELIVERABILITY_WHITELIST: zCoerceArray(z.string()).default(""), - ENABLE_FIXED_ACR: zodTrueFalseBoolean().default("False"), - HTTP_CLIENT_TIMEOUT: z.coerce - .number() - .int() - .nonnegative() - .default(55 * 1_000), // 55 seconds in milliseconds; - INSEE_CONSUMER_KEY: z.string(), - INSEE_CONSUMER_SECRET: z.string(), - JWKS: zCoerceJson().pipe(z.object({ keys: z.array(z.any()) })), - LOG_LEVEL: z - .enum(["trace", "debug", "info", "warn", "error", "fatal"]) - .default("info"), - MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce - .number() - .int() - .nonnegative() - .default(60), // 1 hour in minutes - MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES: z.coerce - .number() - .int() - .nonnegative() - .default(3 * 30 * 24 * 60), // 3 months in minutes - MAX_SUGGESTED_ORGANIZATIONS: z.coerce - .number() - .int() - .nonnegative() - .default(3), - MODERATION_TAG: z.string().default("moderation"), - MONCOMPTEPRO_HOST: z.string().url().default("http://localhost:3000"), - MONCOMPTEPRO_LABEL: z.string().default("MonComptePro"), - NODE_ENV: z - .enum(["production", "development", "test"]) - .default("development"), - PORT: z.coerce.number().int().nonnegative().default(3000), - RECENT_LOGIN_INTERVAL_IN_SECONDS: z.coerce - .number() - .int() - .nonnegative() - .default(15 * 60), // 15 minutes - REDIS_URL: z.string().url().default("redis://:@127.0.0.1:6379"), - RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce - .number() - .int() - .nonnegative() - .default(60), // 1 hour in minutes - SECURE_COOKIES: zodTrueFalseBoolean().default("True"), - SENTRY_DSN: z.string().default(""), - SESSION_COOKIE_SECRET: zCoerceArray(z.string()).default(""), - SESSION_MAX_AGE_IN_SECONDS: z.coerce - .number() - .int() - .nonnegative() - .default(1 * 24 * 60 * 60), // 1 day in seconds - SYMMETRIC_ENCRYPTION_KEY: z.string().base64({ +export const secretEnvSchema = z.object({ + SYMMETRIC_ENCRYPTION_KEY: z + .string() + .base64({ message: "The SYMMETRIC_ENCRYPTION_KEY environment variable should be 32 bytes long! Use crypto.randomBytes(32).toString('base64') to generate one.", - }), - TEST_CONTACT_EMAIL: z.string().default("mairie@yopmail.com"), - TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS: z.coerce - .number() - .int() - .nonnegative() - .default(3 * 30 * 24 * 60 * 60), // 3 months in seconds - VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce - .number() - .int() - .nonnegative() - .default(60), // 1 hour in minutes - ZAMMAD_TOKEN: z.string(), - ZAMMAD_URL: z.string().url(), - }) - .merge(apiEnvSchema) - .merge(crispEnvSchema); + }) + .default("aTrueRandom32BytesLongBase64EncodedStringAA="), + SESSION_COOKIE_SECRET: zCoerceArray(z.string()).default("moncompteprosecret"), + JWKS: zCoerceJson() + .default( + '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}', + ) + .pipe(z.object({ keys: z.array(z.any()) })), +}); + +export const paramsEnvSchema = z.object({ + ACCESS_LOG_PATH: z.string().optional(), + DEPLOY_ENV: z.enum(["preview", "production", "sandbox"]).default("preview"), + EMAIL_DELIVERABILITY_WHITELIST: zCoerceArray(z.string()).default(""), + HTTP_CLIENT_TIMEOUT: z.coerce + .number() + .int() + .nonnegative() + .default(55 * 1_000), // 55 seconds in milliseconds; + LOG_LEVEL: z + .enum(["trace", "debug", "info", "warn", "error", "fatal"]) + .default("info"), + MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(60), // 1 hour in minutes + MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(3 * 30 * 24 * 60), // 3 months in minutes + MAX_SUGGESTED_ORGANIZATIONS: z.coerce.number().int().nonnegative().default(3), + MODERATION_TAG: z.string().default("github-action-e2e-test"), + MONCOMPTEPRO_HOST: z.string().url().default("http://localhost:3000"), + MONCOMPTEPRO_LABEL: z.string().default("MonComptePro"), + NODE_ENV: z + .enum(["production", "development", "test"]) + .default("development"), + PORT: z.coerce.number().int().nonnegative().default(3000), + RECENT_LOGIN_INTERVAL_IN_SECONDS: z.coerce + .number() + .int() + .nonnegative() + .default(15 * 60), // 15 minutes + RESET_PASSWORD_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(60), // 1 hour in minutes + SESSION_MAX_AGE_IN_SECONDS: z.coerce + .number() + .int() + .nonnegative() + .default(1 * 24 * 60 * 60), // 1 day in seconds + TEST_CONTACT_EMAIL: z.string().default("mairie@yopmail.com"), + TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS: z.coerce + .number() + .int() + .nonnegative() + .default(3 * 30 * 24 * 60 * 60), // 3 months in seconds + VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES: z.coerce + .number() + .int() + .nonnegative() + .default(60), // 1 hour in minutes +}); + +export const envSchema = z + .object({}) + .merge(emailEnvSchema) + .merge(connectorEnvSchema) + .merge(featureTogglesEnvSchema) + .merge(secretEnvSchema) + .merge(paramsEnvSchema); // + export function zodTrueFalseBoolean() { return z.enum(["True", "False"]).transform((v: string) => v === "True"); } -// - export function zCoerceArray(schema: T) { return z .string() diff --git a/test/env.zod.test.ts b/test/env.zod.test.ts index c2e22761..40d89b1d 100644 --- a/test/env.zod.test.ts +++ b/test/env.zod.test.ts @@ -1,22 +1,24 @@ // import { expect } from "chai"; -import { config } from "dotenv"; import { test } from "mocha"; import { envSchema } from "../src/config/env.zod"; // -test("default sample env", () => { - const sample_env = {}; - config({ path: ".env.sample", processEnv: sample_env }); +test("default sample env with configured INSEE secrets", () => { + const sample_env = { + DATABASE_URL: + "postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro", + INSEE_CONSUMER_KEY: "fakesecret", + INSEE_CONSUMER_SECRET: "fakesecret", + }; const env = envSchema.parse(sample_env); expect(env).to.deep.equal({ API_AUTH_PASSWORD: "admin", API_AUTH_USERNAME: "admin", - BREVO_API_KEY: "____________________________", CONSIDER_ALL_EMAIL_DOMAINS_AS_FREE: false, CONSIDER_ALL_EMAIL_DOMAINS_AS_NON_FREE: true, CRISP_BASE_URL: "https://api.crisp.chat", @@ -26,27 +28,27 @@ test("default sample env", () => { CRISP_RESOLVE_DELAY: 1000, CRISP_USER_NICKNAME: "MonComptePro", CRISP_WEBSITE_ID: "", - DATABASE_URL: "postgres://username:password@localhost:5432/dbname", - DEBOUNCE_API_KEY: "_____________", + DATABASE_URL: + "postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro", DEPLOY_ENV: "preview", DISABLE_SECURITY_RESPONSE_HEADERS: true, DISPLAY_TEST_ENV_WARNING: false, DO_NOT_AUTHENTICATE_BROWSER: true, DO_NOT_CHECK_EMAIL_DELIVERABILITY: true, - DO_NOT_RATE_LIMIT: false, + DO_NOT_RATE_LIMIT: true, DO_NOT_SEND_MAIL: true, DO_NOT_USE_ANNUAIRE_EMAILS: true, EMAIL_DELIVERABILITY_WHITELIST: [], ENABLE_FIXED_ACR: false, HTTP_CLIENT_TIMEOUT: 55000, - INSEE_CONSUMER_KEY: "____________________________", - INSEE_CONSUMER_SECRET: "____________________________", + INSEE_CONSUMER_KEY: "fakesecret", + INSEE_CONSUMER_SECRET: "fakesecret", JWKS, LOG_LEVEL: "info", MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES: 60, MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES: 129600, MAX_SUGGESTED_ORGANIZATIONS: 3, - MODERATION_TAG: "moderation", + MODERATION_TAG: "github-action-e2e-test", MONCOMPTEPRO_HOST: "http://localhost:3000", MONCOMPTEPRO_LABEL: "MonComptePro", NODE_ENV: "development", @@ -62,7 +64,6 @@ test("default sample env", () => { TEST_CONTACT_EMAIL: "mairie@yopmail.com", TRUSTED_BROWSER_COOKIE_MAX_AGE_IN_SECONDS: 7776000, VERIFY_EMAIL_TOKEN_EXPIRATION_DURATION_IN_MINUTES: 60, - ZAMMAD_TOKEN: "___________________________________________________________", ZAMMAD_URL: "https://support.etalab.gouv.fr", }); }); From a143a4bf65059cdc77256405f9711cfafcdc72bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 12:53:53 +0200 Subject: [PATCH 35/42] fix: Unable to determine why test triggers env.ts validation, but I need to focus on higher-priority tasks --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a169a8c4..6f1ee492 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,11 @@ on: - "**" - "!master" +env: + DATABASE_URL: postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro + INSEE_CONSUMER_KEY: ${{ secrets.INSEE_CONSUMER_KEY }} + INSEE_CONSUMER_SECRET: ${{ secrets.INSEE_CONSUMER_SECRET }} + jobs: test: runs-on: ubuntu-22.04 From dd2e134067ae5c2c1bbf5da70acc06838456486d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 13:00:12 +0200 Subject: [PATCH 36/42] refactor: move default JWKS to a dedicated file --- src/config/default-jwks.ts | 56 ++++++++++++++++++++++++++++++++++ src/config/env.zod.ts | 5 ++- test/env.zod.test.ts | 62 ++------------------------------------ 3 files changed, 60 insertions(+), 63 deletions(-) create mode 100644 src/config/default-jwks.ts diff --git a/src/config/default-jwks.ts b/src/config/default-jwks.ts new file mode 100644 index 00000000..f2f75a50 --- /dev/null +++ b/src/config/default-jwks.ts @@ -0,0 +1,56 @@ +export const defaultJWKS = { + keys: [ + { + crv: "P-256", + d: "taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y", + kid: "GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE", + kty: "EC", + use: "enc", + x: "UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q", + y: "YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E", + }, + { + crv: "P-256", + d: "TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4", + kid: "TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8", + kty: "EC", + use: "sig", + x: "2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs", + y: "Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q", + }, + { + crv: "Ed25519", + d: "WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4", + kid: "onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU", + kty: "OKP", + use: "sig", + x: "NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4", + }, + { + d: "qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ", + dp: "g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk", + dq: "1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU", + e: "AQAB", + kid: "kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4", + kty: "RSA", + n: "5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ", + p: "-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs", + q: "7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs", + qi: "4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig", + use: "enc", + }, + { + d: "L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ", + dp: "Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE", + dq: "SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU", + e: "AQAB", + kid: "lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo", + kty: "RSA", + n: "6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ", + p: "_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E", + q: "6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0", + qi: "QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08", + use: "sig", + }, + ], +}; diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts index d50ab7d7..535baa6a 100644 --- a/src/config/env.zod.ts +++ b/src/config/env.zod.ts @@ -1,4 +1,5 @@ import { z, type ZodTypeAny } from "zod"; +import { defaultJWKS } from "./default-jwks"; export const emailEnvSchema = z.object({ BREVO_API_KEY: z.string().optional(), @@ -48,9 +49,7 @@ export const secretEnvSchema = z.object({ .default("aTrueRandom32BytesLongBase64EncodedStringAA="), SESSION_COOKIE_SECRET: zCoerceArray(z.string()).default("moncompteprosecret"), JWKS: zCoerceJson() - .default( - '{"keys":[{"crv":"P-256","x":"UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q","y":"YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E","d":"taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y","kty":"EC","kid":"GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE","use":"enc"},{"crv":"P-256","x":"2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs","y":"Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q","d":"TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4","kty":"EC","kid":"TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8","use":"sig"},{"crv":"Ed25519","x":"NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4","d":"WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4","kty":"OKP","kid":"onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU","use":"sig"},{"e":"AQAB","n":"5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ","d":"qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ","p":"-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs","q":"7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs","dp":"g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk","dq":"1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU","qi":"4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig","kty":"RSA","kid":"kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4","use":"enc"},{"e":"AQAB","n":"6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ","d":"L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ","p":"_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E","q":"6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0","dp":"Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE","dq":"SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU","qi":"QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08","kty":"RSA","kid":"lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo","use":"sig"}]}', - ) + .default(JSON.stringify(defaultJWKS)) .pipe(z.object({ keys: z.array(z.any()) })), }); diff --git a/test/env.zod.test.ts b/test/env.zod.test.ts index 40d89b1d..3c7b9b6f 100644 --- a/test/env.zod.test.ts +++ b/test/env.zod.test.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { test } from "mocha"; +import { defaultJWKS } from "../src/config/default-jwks"; import { envSchema } from "../src/config/env.zod"; // @@ -43,7 +44,7 @@ test("default sample env with configured INSEE secrets", () => { HTTP_CLIENT_TIMEOUT: 55000, INSEE_CONSUMER_KEY: "fakesecret", INSEE_CONSUMER_SECRET: "fakesecret", - JWKS, + JWKS: defaultJWKS, LOG_LEVEL: "info", MAGIC_LINK_TOKEN_EXPIRATION_DURATION_IN_MINUTES: 60, MAX_DURATION_BETWEEN_TWO_EMAIL_ADDRESS_VERIFICATION_IN_MINUTES: 129600, @@ -67,62 +68,3 @@ test("default sample env with configured INSEE secrets", () => { ZAMMAD_URL: "https://support.etalab.gouv.fr", }); }); - -// - -const JWKS = { - keys: [ - { - crv: "P-256", - d: "taURynSwshCfxEWs6z2_Xz-ocheg-6ePaU87cjy572Y", - kid: "GCirOyeBc0rlWhcbMnwe9FUadPk6ToJlOq3yvxvkKlE", - kty: "EC", - use: "enc", - x: "UtmbpHb1aHibmvEQJ2KlIzNro4tGfyMiBIVmO92YX7Q", - y: "YsRG_NMtLOqvA6S9zq5r7M9Y-Cgo4YwKvH3xXyvFE2E", - }, - { - crv: "P-256", - d: "TLeCkidQUJG9s6hvHx8QSHNKfqyhcbIXCN7rJ67AjH4", - kid: "TeXJ6Hx4sG9A13LCFlU46-PYGopwwFOsmCTEJcwZvZ8", - kty: "EC", - use: "sig", - x: "2SSoeci15SnMM6wwxvNwzp_xjVTwgEALOY1NvTBbdqs", - y: "Gplus4XyX4dQ6Z0Pwb0UhsmJfx7S5_DCFxpK6yt396Q", - }, - { - crv: "Ed25519", - d: "WxFz4Ulx6rLBO5HHHhg86BMc_CtRoCmFn8Gwy-kbaL4", - kid: "onHSTAw1rfQOz_qWnPTh2SZzrseoqbOOrD1tcxFOaIU", - kty: "OKP", - use: "sig", - x: "NQNM3isoJAeK6HWKEgHifRqFrC-R6ufusnv47BnlWn4", - }, - { - d: "qClmBFjTiJgj2cMXmtvtLSnuVtMr-sFqVzZiEYiXAv7yXT3B0CEqdXf0unvVH2x3JTuhcies2Zf_0gQdhglpPro8YRx1v3l6N2HE1nmTj6reakWSlXNOdMthQ6KOzZxTHUA3J53aW1U0-nhW2TrQAYaTHgNSr-yOWMBFGWrxxomc8h_1OnnXS9wRxoicPshjx7S8huy3YLbWzQphBqzBx5vsPOClfbs0dtxhAY63vXbNDS_sAIVfn1U__f6ilFmzE9odgOydsSwBUtRm2Ir4wY5HhqYGRPOKAUNHLqEsDqwmp_o3RtBQwg937ymbOJvgoa6qkqg_uxtaVSP7RX4EKQ", - dp: "g0dYT3wQ4VRY8NFbwHci_2jBQzpOXgvLooEcMuJzP3TeITqoNQp8-qOuguiWs3caPAMk4g-wGH8zw5qhEStJtjBPzfw3MwKFAJ-tjgZvcUkhzaIUNmG9RZTUl3zcRurKYR7pdcETdfRT4nmHfkbgZV4uE0KZlP96ekoI7LUxqgk", - dq: "1Gpl81t_KVDgTu2OIoySo2nNBpUjzc7feKnzgsjaItALyrSkXD4COSElPPzY-vGa0fwWd483CRcyQeoSPKyGuf7wNVJ9XBV4kObqfh46cSgLp33axo3erPVpxwYubxO5olq00FW6Lr5D4kSclTX1pJ29LtoZDV7v1xJ_11vxydU", - e: "AQAB", - kid: "kddKa_IDmU8a5RhJaIzwzpJcFe4qT-GtNFcG2AWclZ4", - kty: "RSA", - n: "5yuakCQKnkzP4tNXYI6qRYX-0pyeuGKS8VKl7S1QNj7bAMjeV2o3xjDgg4qtrUrFrqxSFOfBX5kJR3NEBoYiQpUwl9zPmKNLR0zX0w6VpwDREDS8bpBL_naeiGRdLX_AYxR7jCsDETEXqFm0S1CmfLjgAoazLPDxzGvFaezLEo0rafcLR3MpKIa-INqwCoTiLWUAtXKv-ZcmO7QuzRcVJecFs7WaMQZNMrfSAdj-agdnVOkP2cXnd6xpT2Pcph7I6z2slRkEZ_Oz1BkG-FV_21IlY1U4tE3GigKdSNRSJmuyvdgI4wDb2noZdEStFr8nsOsG63kIYM_Gve-HWiTxmQ", - p: "-X7pv_NpfJvqTTlQwQnaz6eiA_I7v7Jj0l1KtmBRBZz6q6R9qq1BVlP8XeOBO5TX9vQKIooY8fL3QsWf73ZqQmmy9W3C4dAhwbwalvBzZHZT2Wznrurp_bML_8Xx1XhNxTawAb263O3AUz7Rw3g5lI2cafTe4x1dSO8_CHL3eMs", - q: "7TJj6aNzkVjyPeCZVHwBXGDWDIT2DxqWRjKrKgqlpWdzEftNce855Wg3Ve4JnNtFkg7Qow4imZVkbK69ChIStv9s1KDX_sGRCyfN17d0jlkyGUnFB2RSBB42t7SmcC4ZhHjxdAdopOG_o1r5vEwDo-0hKWikP9uyYmWyhfY3hqs", - qi: "4RyTKy-QINtOUezaLEymzNBIG3uZv_IKvGPhEYi4wRNP_XxIK9NwfgUFRAhxhpxSpEjco_eNuN3I6XBF7bXb4-Bnnye1mBm3sBTXx2_09r8zt9Uvg3cdh0pYem3hU1ANMRmr3rfjtain4DTskIJ2CxjvGIMyh3VXLyRyfzIGJig", - use: "enc", - }, - { - d: "L0a7DDdP9LsAjeu_C6fkh4GXMqtRo3yPHK077SVFTPGGAFGq8Lk3C-wWTQxTwO9eZ_xx9wCzFTyIyqrksTuvQxzfY0MmzEk3mXNSrKxpK_vbgtC47qV6UwuGGiRfJ1z7MGXGmu5OmpaZqZJ-CPTGVtsM0rF4V665dIe-15o9GHzLX80mhw8ySd0qqBIbdIWlK2zaSRPGL08mP02t_XnHdCCaWfRE_erO6zsEhR8ePvbmQqI7GRBull59seXefo1VDP30lEwHwH05Ju24_ZddhfuP2Y9jkZnNKqpSHF3EZzT6Vh3ggAaLjQzRWJvd1_0Zit7CD06o6L1aLV7nDnTYAQ", - dp: "Cyf-4iwZZIrve5WsqKy8UVWpMBDCyBpCpHRNw2PAVGAP4aNXe2dG41sxV3mvdsOHWzqApsLT90EVHDa2KySS5CNcxOQ47Yr1mVVFoHb3izIcLe2dIpVUmgyc62WDcaShl47ahnWyDO8rLJzeH75AZlCaJR3s2nnth7Xy_cU4GQE", - dq: "SvJe21HfQe2JsqGwrBPg0ShcfKMnkQI3uaaDOJDkEyHJz4eILlETfiFEBgNRo4xYjPU4Aa2w81poYms4RhYdNwpB4DT5OXT8kL_KRykmhBLWaxafezyIRxdNs97h7eU1Vk_05C3vbG7fqASO6vv7HdHnB_ityGRDUnLbwKfDasU", - e: "AQAB", - kid: "lFWqEBQbScnjO5OzUbvkPp0rmjGy17bmzZOqUbWkQMo", - kty: "RSA", - n: "6btLS-c06m18O5BlLvJA4HJNVI7WauBg5JVoy1cHTdfjTJ-oSts5uetXKF_NlNcLuq-zIKZuu9wea5m2E3lJ-vtCSAtRaJgZY41KAOjIsrHQstVuc9di4zjgcA6zwEXhqwu48gklGKpWNk7wnfMCO6mNoRs_-8-CnK6lTFeJFzfoCDmS6dYbefmPeFW4qziEZzEv5DPGAcUsXXZhbOku_E8gILRVMkBwHpvY_G3jngE1EWXctiM9tYqhgvxyJC9QyPCVpgfAvDlslMpuQTxBviC9PsrcBaQ5PyAP_xNN3X8LB-STpz7jNpNquKL5Ls0Hv9R3fDCeHvcYoVsgDirwfQ", - p: "_u6v29kLICnGvbF44-sF5cFisrIPyXcj8laaNx6uP5ax9ZOPD_THdxTFU3YWLUErzi4MQJQPLoJacphQJKnG78b0PX_733--r-vpqUqfbzAlIssS8N4CFj6_YEMFR8W70laXJYJdx7IsnGOAlxAUZur5ugaaR0zDzlMTQVuR30E", - q: "6rXguomnHNGFslAeHWPxPDaHxihx3eRJ-8t1KyvvwT43YfdPn2xxdq5-TyO0MMKznvDIHk9HDMBMr8JH32Q9qx75Ec81NOLGkBWqO9x-8dBlKn95jr8-qkD3iXHmJiHHNWNurHJM4G4lo73IL_0jgo0CCpcZWP7iH5y-b_mXPj0", - qi: "QomRmnqrW3k8cV8MIefgmKZMGDGHRC44bFk9B20YR15_XHcMimi7o9rjUE7BY-RO30RsPUiQqB_vkpKvQZILOuPmIQhElcgmguKqPNwprVMgx-krUQ1Khuh3tgzxhBgazXzPcKmx8JBbCopP_UwNiCpPcdm74VFcZ-OswmqQU08", - use: "sig", - }, - ], -}; From 8c6987f282e203c8e90872b13c67cd539ad07b53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:04:22 +0000 Subject: [PATCH 37/42] chore(deps): bump body-parser and express Bumps [body-parser](https://github.com/expressjs/body-parser) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `body-parser` from 1.20.2 to 1.20.3 - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3) Updates `express` from 4.19.2 to 4.21.0 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md) - [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0) --- updated-dependencies: - dependency-name: body-parser dependency-type: direct:production - dependency-name: express dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 140 +++++++++++++++++++++++++++------------------- package.json | 4 +- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index 39eeffbc..7efefec3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,14 +31,14 @@ "await-to-js": "^3.0.0", "axios": "^1.7.4", "bcryptjs": "^2.4.3", - "body-parser": "^1.20.2", + "body-parser": "^1.20.3", "connect-redis": "^7.1.1", "console-log-level": "^1.4.1", "cookie-parser": "^1.4.6", "csrf-sync": "^4.0.3", "dotenv": "^16.4.5", "ejs": "^3.1.10", - "express": "^4.19.2", + "express": "^4.21.0", "express-basic-auth": "^1.2.1", "express-session": "^1.18.0", "helmet": "^7.1.0", @@ -2252,9 +2252,10 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -2264,7 +2265,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -4146,36 +4147,37 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -4236,6 +4238,15 @@ } ] }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -4431,12 +4442,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -4447,10 +4459,20 @@ "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/finalhandler/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -4462,6 +4484,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5942,9 +5965,13 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -5978,6 +6005,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -6826,9 +6854,10 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" }, "node_modules/pathval": { "version": "2.0.0", @@ -7463,11 +7492,12 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -7788,9 +7818,10 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -7813,12 +7844,14 @@ "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/send/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -7830,6 +7863,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7899,19 +7933,29 @@ "peer": true }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/server-destroy": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", @@ -9152,22 +9196,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/url/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 21427964..f61a4f24 100644 --- a/package.json +++ b/package.json @@ -57,14 +57,14 @@ "await-to-js": "^3.0.0", "axios": "^1.7.4", "bcryptjs": "^2.4.3", - "body-parser": "^1.20.2", + "body-parser": "^1.20.3", "connect-redis": "^7.1.1", "console-log-level": "^1.4.1", "cookie-parser": "^1.4.6", "csrf-sync": "^4.0.3", "dotenv": "^16.4.5", "ejs": "^3.1.10", - "express": "^4.19.2", + "express": "^4.21.0", "express-basic-auth": "^1.2.1", "express-session": "^1.18.0", "helmet": "^7.1.0", From 42247359d5de323fb667f386f0804b2f5b7aa3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 13:47:21 +0200 Subject: [PATCH 38/42] feat: enable dirty implementation to redirect to DS --- src/config/env.ts | 1 + src/config/env.zod.ts | 7 ++++++ src/controllers/main.ts | 28 ++++++++++++++++++++++- src/managers/session/dirty-ds-redirect.ts | 21 +++++++++++++++++ src/types/express-session.d.ts | 1 + test/env.zod.test.ts | 2 ++ 6 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/managers/session/dirty-ds-redirect.ts diff --git a/src/config/env.ts b/src/config/env.ts index 20dadf78..6560c9ef 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -41,6 +41,7 @@ export const { DATABASE_URL, DEBOUNCE_API_KEY, DEPLOY_ENV, + DIRTY_DS_REDIRECTION_URL, DISABLE_SECURITY_RESPONSE_HEADERS, DISPLAY_TEST_ENV_WARNING, DO_NOT_AUTHENTICATE_BROWSER, diff --git a/src/config/env.zod.ts b/src/config/env.zod.ts index 535baa6a..5f2b069c 100644 --- a/src/config/env.zod.ts +++ b/src/config/env.zod.ts @@ -56,6 +56,13 @@ export const secretEnvSchema = z.object({ export const paramsEnvSchema = z.object({ ACCESS_LOG_PATH: z.string().optional(), DEPLOY_ENV: z.enum(["preview", "production", "sandbox"]).default("preview"), + DIRTY_DS_REDIRECTION_URL: z + .string() + .url() + .optional() + .default( + "https://www.demarches-simplifiees.fr/agent_connect/logout_from_mcp", + ), EMAIL_DELIVERABILITY_WHITELIST: zCoerceArray(z.string()).default(""), HTTP_CLIENT_TIMEOUT: z.coerce .number() diff --git a/src/controllers/main.ts b/src/controllers/main.ts index c8c17bbf..56317fd6 100644 --- a/src/controllers/main.ts +++ b/src/controllers/main.ts @@ -3,6 +3,7 @@ import HttpErrors from "http-errors"; import { isEmpty } from "lodash-es"; import moment from "moment/moment"; import { z, ZodError } from "zod"; +import { DIRTY_DS_REDIRECTION_URL } from "../config/env"; import { ForbiddenError, NotFoundError, @@ -18,6 +19,11 @@ import { isWithinAuthenticatedSession, updateUserInAuthenticatedSession, } from "../managers/session/authenticated"; +import { + deleteNeedsDirtyDSRedirect, + getNeedsDirtyDSRedirect, + setNeedsDirtyDSRedirect, +} from "../managers/session/dirty-ds-redirect"; import { isAuthenticatorAppConfiguredForUser } from "../managers/totp"; import { sendDisable2faMail, @@ -27,7 +33,10 @@ import { import { getUserAuthenticators } from "../managers/webauthn"; import { csrfToken } from "../middlewares/csrf-protection"; import { idSchema } from "../services/custom-zod-schemas"; -import getNotificationsFromRequest from "../services/get-notifications-from-request"; +import { + getNotificationLabelFromRequest, + getNotificationsFromRequest, +} from "../services/get-notifications-from-request"; import { getParamsForPostPersonalInformationsController } from "./user/update-personal-informations"; export const getHomeController = async ( @@ -156,6 +165,23 @@ export const getConnectionAndAccountController = async ( const passkeys = await getUserAuthenticators(email); const is2faCapable = await is2FACapable(user_id); + // Dirty ad hoc implementation waiting for complete acr support on ProConnect + const notificationLabel = await getNotificationLabelFromRequest(req); + if (notificationLabel === "2fa_not_configured_for_ds") { + setNeedsDirtyDSRedirect(req); + } + if ( + notificationLabel && + ["authenticator_added", "passkey_successfully_created"].includes( + notificationLabel, + ) && + getNeedsDirtyDSRedirect(req) + ) { + deleteNeedsDirtyDSRedirect(req); + + return res.redirect(DIRTY_DS_REDIRECTION_URL); + } + return res.render("connection-and-account", { pageTitle: "Connexion et compte", notifications: await getNotificationsFromRequest(req), diff --git a/src/managers/session/dirty-ds-redirect.ts b/src/managers/session/dirty-ds-redirect.ts new file mode 100644 index 00000000..5d29758e --- /dev/null +++ b/src/managers/session/dirty-ds-redirect.ts @@ -0,0 +1,21 @@ +import { Request } from "express"; +import { UserNotLoggedInError } from "../../config/errors"; +import { isWithinAuthenticatedSession } from "./authenticated"; + +export const setNeedsDirtyDSRedirect = (req: Request) => { + if (!isWithinAuthenticatedSession(req.session)) { + throw new UserNotLoggedInError(); + } + + req.session.needsDirtyDSRedirect = true; +}; +export const getNeedsDirtyDSRedirect = (req: Request) => { + if (!isWithinAuthenticatedSession(req.session)) { + throw new UserNotLoggedInError(); + } + + return req.session.needsDirtyDSRedirect ?? false; +}; +export const deleteNeedsDirtyDSRedirect = (req: Request) => { + delete req.session.needsDirtyDSRedirect; +}; diff --git a/src/types/express-session.d.ts b/src/types/express-session.d.ts index c841cea8..38dbbdf9 100644 --- a/src/types/express-session.d.ts +++ b/src/types/express-session.d.ts @@ -29,5 +29,6 @@ declare module "express-session" { user?: User; temporaryEncryptedTotpKey?: string; amr?: AmrValue[]; + needsDirtyDSRedirect?: boolean; } } diff --git a/test/env.zod.test.ts b/test/env.zod.test.ts index 3c7b9b6f..6add6c7b 100644 --- a/test/env.zod.test.ts +++ b/test/env.zod.test.ts @@ -32,6 +32,8 @@ test("default sample env with configured INSEE secrets", () => { DATABASE_URL: "postgres://moncomptepro:moncomptepro@127.0.0.1:5432/moncomptepro", DEPLOY_ENV: "preview", + DIRTY_DS_REDIRECTION_URL: + "https://www.demarches-simplifiees.fr/agent_connect/logout_from_mcp", DISABLE_SECURITY_RESPONSE_HEADERS: true, DISPLAY_TEST_ENV_WARNING: false, DO_NOT_AUTHENTICATE_BROWSER: true, From 8ebdba3836514d00ca2461b8aac8b8313df323af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 14:49:36 +0200 Subject: [PATCH 39/42] fix: totp deletion should not result in infinite redirect --- cypress/e2e/delete_totp/fixtures.sql | 18 ++++++++++++++ cypress/e2e/delete_totp/index.cy.ts | 35 ++++++++++++++++++++++++++++ src/managers/totp.ts | 14 ++++++----- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/delete_totp/fixtures.sql b/cypress/e2e/delete_totp/fixtures.sql index 4af13eab..33336650 100644 --- a/cypress/e2e/delete_totp/fixtures.sql +++ b/cypress/e2e/delete_totp/fixtures.sql @@ -25,3 +25,21 @@ INSERT INTO users_organizations VALUES (1, 1, false, 'verified_email_domain', true), (2, 1, false, 'verified_email_domain', true); + +INSERT INTO oidc_clients + (client_name, client_id, client_secret, redirect_uris, + post_logout_redirect_uris, scope, client_uri, client_description, + userinfo_signed_response_alg, id_token_signed_response_alg, + authorization_signed_response_alg, introspection_signed_response_alg) +VALUES + ('Oidc Test Client', + 'standard_client_id', + 'standard_client_secret', + ARRAY [ + 'http://localhost:4000/login-callback' + ], + ARRAY []::varchar[], + 'openid email profile organization', + 'http://localhost:4000/', + 'MonComptePro test client. More info: https://github.com/numerique-gouv/moncomptepro-test-client.', + null, null, null, null); diff --git a/cypress/e2e/delete_totp/index.cy.ts b/cypress/e2e/delete_totp/index.cy.ts index 46592dfd..2e59d73a 100644 --- a/cypress/e2e/delete_totp/index.cy.ts +++ b/cypress/e2e/delete_totp/index.cy.ts @@ -7,6 +7,11 @@ describe("delete TOTP connexion", () => { inboxId: "eab4ab97-875d-4ec7-bdcc-04323948ee63", }), ); + cy.mailslurp().then((mailslurp) => + mailslurp.inboxController.deleteAllInboxEmails({ + inboxId: "c9fabb94-9274-4ece-a3d0-54b1987c8588", + }), + ); }); it("should delete TOTP application", function () { @@ -57,6 +62,21 @@ describe("delete TOTP connexion", () => { }); }); + it("should not be ask to sign with TOTP", function () { + cy.visit(`http://localhost:4000`); + cy.get("button.moncomptepro-button").click(); + cy.get('[name="login"]').type( + "eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com", + ); + cy.get('[type="submit"]').click(); + cy.get('[name="password"]').type("password123"); + cy.get('[action="/users/sign-in"] [type="submit"]') + .contains("S’identifier") + .click(); + + cy.contains('"amr": [\n "pwd"\n ],'); + }); + it("should disable TOTP", function () { // Visit the signup page cy.visit(`/users/start-sign-in`); @@ -102,4 +122,19 @@ describe("delete TOTP connexion", () => { ); }); }); + + it("should not be ask to sign with TOTP", function () { + cy.visit(`http://localhost:4000`); + cy.get("button.moncomptepro-button").click(); + cy.get('[name="login"]').type( + "c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com", + ); + cy.get('[type="submit"]').click(); + cy.get('[name="password"]').type("password123"); + cy.get('[action="/users/sign-in"] [type="submit"]') + .contains("S’identifier") + .click(); + + cy.contains('"amr": [\n "pwd"\n ],'); + }); }); diff --git a/src/managers/totp.ts b/src/managers/totp.ts index 80ab682e..99e220fa 100644 --- a/src/managers/totp.ts +++ b/src/managers/totp.ts @@ -74,20 +74,22 @@ export const confirmAuthenticatorAppRegistration = async ( }; export const deleteAuthenticatorAppConfiguration = async (user_id: number) => { - const user = await findById(user_id); + let user = await findById(user_id); if (isEmpty(user)) { throw new UserNotFoundError(); } - if (!(await is2FACapable(user_id))) { - await disableForce2fa(user_id); - } - - return await update(user_id, { + user = await update(user_id, { encrypted_totp_key: null, totp_key_verified_at: null, }); + + if (!(await is2FACapable(user_id))) { + user = await disableForce2fa(user_id); + } + + return user; }; export const isAuthenticatorAppConfiguredForUser = async (user_id: number) => { From e58fb8c0a5563bb302889b95344447858c9aeb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 16:08:08 +0200 Subject: [PATCH 40/42] refactor: factorise login steps in e2e tests --- cypress/e2e/activate_totp/index.cy.ts | 14 +--- cypress/e2e/delete_account/index.cy.ts | 15 +--- cypress/e2e/delete_totp/index.cy.ts | 68 ++----------------- cypress/e2e/join_and_moderation/index.cy.ts | 10 +-- .../index.cy.ts | 7 +- cypress/e2e/join_must_confirm/index.cy.ts | 12 ++-- .../index.cy.ts | 8 +-- .../join_org_with_verified_domain/index.cy.ts | 11 +-- .../index.cy.ts | 7 +- .../index.cy.ts | 7 +- .../index.cy.ts | 14 ++-- .../reauthenticate_on_admin_page/index.cy.ts | 27 +------- .../index.cy.ts | 21 ++---- .../e2e/signin_from_legacy_client/index.cy.ts | 8 +-- .../signin_from_standard_client/index.cy.ts | 16 +---- .../index.cy.ts | 10 +-- cypress/e2e/signin_with_totp/index.cy.ts | 45 ++---------- .../update_personal_information/index.cy.ts | 14 ++-- .../e2e/update_totp_application/index.cy.ts | 24 +------ cypress/support/commands.ts | 37 ++++++++-- installation.md | 2 +- src/controllers/main.ts | 1 + src/controllers/webauthn.ts | 2 + 23 files changed, 93 insertions(+), 287 deletions(-) diff --git a/cypress/e2e/activate_totp/index.cy.ts b/cypress/e2e/activate_totp/index.cy.ts index fe8037c5..92163603 100644 --- a/cypress/e2e/activate_totp/index.cy.ts +++ b/cypress/e2e/activate_totp/index.cy.ts @@ -10,19 +10,9 @@ describe("add 2fa authentication", () => { }); it("should add 2fa authentication on account user", function () { - cy.visit(`/users/start-sign-in`); + cy.visit(`/connection-and-account`); - cy.get('[name="login"]').type( - "64d9024b-d389-4b9d-948d-a504082c14fa@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); - - cy.contains("Connexion et compte").click(); + cy.login("64d9024b-d389-4b9d-948d-a504082c14fa@mailslurp.com"); cy.contains("Application FreeOTP Authenticator"); diff --git a/cypress/e2e/delete_account/index.cy.ts b/cypress/e2e/delete_account/index.cy.ts index 4d334e55..aa624eb7 100644 --- a/cypress/e2e/delete_account/index.cy.ts +++ b/cypress/e2e/delete_account/index.cy.ts @@ -10,20 +10,9 @@ describe("delete account", () => { }); it("should delete account", function () { - // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit(`/connection-and-account`); - cy.get('[name="login"]').type( - "4cec922b-ecbe-4a46-8511-fc9478c1efd0@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); - - cy.contains("Connexion et compte").click(); + cy.login("4cec922b-ecbe-4a46-8511-fc9478c1efd0@mailslurp.com"); cy.contains("Suppression"); diff --git a/cypress/e2e/delete_totp/index.cy.ts b/cypress/e2e/delete_totp/index.cy.ts index 2e59d73a..3641481e 100644 --- a/cypress/e2e/delete_totp/index.cy.ts +++ b/cypress/e2e/delete_totp/index.cy.ts @@ -1,5 +1,3 @@ -import { generateToken } from "@sunknudsen/totp"; - describe("delete TOTP connexion", () => { before(() => { cy.mailslurp().then((mailslurp) => @@ -15,29 +13,9 @@ describe("delete TOTP connexion", () => { }); it("should delete TOTP application", function () { - // Visit the signup page - cy.visit(`/users/start-sign-in`); - - cy.get('[name="login"]').type( - "eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.visit(`/connection-and-account`); - // redirect to the TOTP login page - cy.contains("Valider en deux étapes"); - - const totp = generateToken("din5ncvbluqpx7xfzqcybmibmtjocnsf", Date.now()); - cy.get("[name=totpToken]").type(totp); - cy.get( - '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', - ).click(); - - cy.contains("Connexion et compte").click(); + cy.mfaLogin("eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com"); cy.contains("Application FreeOTP Authenticator"); @@ -65,42 +43,15 @@ describe("delete TOTP connexion", () => { it("should not be ask to sign with TOTP", function () { cy.visit(`http://localhost:4000`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type( - "eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com"); cy.contains('"amr": [\n "pwd"\n ],'); }); it("should disable TOTP", function () { - // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit(`/connection-and-account`); - cy.get('[name="login"]').type( - "c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); - - // redirect to the TOTP login page - cy.contains("Valider en deux étapes"); - - const totp = generateToken("din5ncvbluqpx7xfzqcybmibmtjocnsf", Date.now()); - cy.get("[name=totpToken]").type(totp); - cy.get( - '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', - ).click(); - - cy.contains("Connexion et compte").click(); + cy.mfaLogin("c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com"); cy.contains("Validation en deux étapes"); @@ -126,14 +77,7 @@ describe("delete TOTP connexion", () => { it("should not be ask to sign with TOTP", function () { cy.visit(`http://localhost:4000`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type( - "c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com"); cy.contains('"amr": [\n "pwd"\n ],'); }); diff --git a/cypress/e2e/join_and_moderation/index.cy.ts b/cypress/e2e/join_and_moderation/index.cy.ts index 3a73d866..f61d5462 100644 --- a/cypress/e2e/join_and_moderation/index.cy.ts +++ b/cypress/e2e/join_and_moderation/index.cy.ts @@ -11,15 +11,11 @@ describe("join and moderation", () => { ); }); - beforeEach(() => { - cy.login( - "86983334-028f-48b5-881d-8b05d738bec5@mailslurp.net", - "password123", - ); - }); - it("will be moderated", function () { cy.visit(`/`); + + cy.login("86983334-028f-48b5-881d-8b05d738bec5@mailslurp.net"); + cy.get('[name="siret"]').type("66204244933106"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts b/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts index a3fc5eaf..e1dcbf70 100644 --- a/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts +++ b/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts @@ -10,12 +10,9 @@ describe("join organizations", () => { }); it("join collectivité territoriale with official contact domain", function () { - cy.login( - "76450610-4dcc-4664-b9ab-1cea869b62b1@mailslurp.com", - "password123", - ); - cy.visit(`/users/join-organization`); + cy.login("76450610-4dcc-4664-b9ab-1cea869b62b1@mailslurp.com"); + cy.get('[name="siret"]').type("21740056300011"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_must_confirm/index.cy.ts b/cypress/e2e/join_must_confirm/index.cy.ts index f9f6d741..58c35df5 100644 --- a/cypress/e2e/join_must_confirm/index.cy.ts +++ b/cypress/e2e/join_must_confirm/index.cy.ts @@ -2,7 +2,9 @@ describe("join organizations", () => { it("join big company with free email provider", function () { - cy.login("unused1@yopmail.com", "password123"); + cy.visit(`/users/start-sign-in`); + + cy.login("unused1@yopmail.com"); cy.visit(`/users/join-organization`); cy.get('[name="siret"]').type("54205118000066"); @@ -16,13 +18,7 @@ describe("join organizations", () => { .contains("Corriger l’email") .click(); - cy.get('[name="login"]').type("unused2@yopmail.com"); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("unused2@yopmail.com"); cy.get('[name="siret"]').type("54205118000066"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts b/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts index 50b396bd..fcd9cad9 100644 --- a/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts +++ b/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts @@ -12,16 +12,10 @@ describe("join organizations", () => { }), ); }); - beforeEach(() => { - cy.login( - "0c5b976c-b6b0-406e-a7ed-08ddae8d2d81@mailslurp.com", - "password123", - ); - }); it("join suggested organisation", function () { - // Visit the signup page cy.visit(`/`); + cy.login("0c5b976c-b6b0-406e-a7ed-08ddae8d2d81@mailslurp.com"); // The user gets this suggestion because it as mailslurp.com as trackdechets domain cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").contains( diff --git a/cypress/e2e/join_org_with_verified_domain/index.cy.ts b/cypress/e2e/join_org_with_verified_domain/index.cy.ts index 500484bf..d5af0530 100644 --- a/cypress/e2e/join_org_with_verified_domain/index.cy.ts +++ b/cypress/e2e/join_org_with_verified_domain/index.cy.ts @@ -12,16 +12,10 @@ describe("join organizations", () => { ]), ); }); - beforeEach(() => { - cy.login( - "c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp.com", - "password123", - ); - }); it("join suggested organisation", function () { - // Visit the signup page cy.visit(`/`); + cy.login("c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp.com"); // The user gets this suggestion because it as mailslurp.com as verified domain cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").contains( @@ -44,8 +38,9 @@ describe("join organizations", () => { }); it("join another organisation", function () { - // Visit the join organization page cy.visit(`/users/join-organization`); + cy.login("c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp.com"); + cy.get('[name="siret"]').type("13002526500013"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts b/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts index decc1641..b9cac5da 100644 --- a/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts @@ -17,12 +17,9 @@ describe("join organizations", () => { }); it("join collectivité territoriale with code send to official contact email", function () { - cy.login( - "c348a2c3-bf54-4f15-bb12-a2d7047c832f@mailslurp.com", - "password123", - ); - cy.visit(`/users/join-organization`); + cy.login("c348a2c3-bf54-4f15-bb12-a2d7047c832f@mailslurp.com"); + cy.get('[name="siret"]').type("21340126800130"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts index 3892fd6c..29855b15 100644 --- a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts @@ -17,12 +17,9 @@ describe("join organizations", () => { }); it("join collectivité territoriale with code send to official contact email", function () { - cy.login( - "10efdabd-deb0-4d19-a521-6772ca27acf8@mailslurp.com", - "password123", - ); - cy.visit(`/users/join-organization`); + cy.login("10efdabd-deb0-4d19-a521-6772ca27acf8@mailslurp.com"); + cy.get('[name="siret"]').type("19750663700010"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_with_official_contact_email/index.cy.ts b/cypress/e2e/join_with_official_contact_email/index.cy.ts index 15116f79..a69c8830 100644 --- a/cypress/e2e/join_with_official_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_official_contact_email/index.cy.ts @@ -10,12 +10,9 @@ describe("join organizations", () => { }); it("join collectivité territoriale with official contact email", function () { - cy.login( - "435f6a4d-df7d-4840-be7b-bc4851b64e91@mailslurp.com", - "password123", - ); - cy.visit(`/users/join-organization`); + cy.login("435f6a4d-df7d-4840-be7b-bc4851b64e91@mailslurp.com"); + cy.get('[name="siret"]').type("21340126800130"); cy.get('[type="submit"]').click(); @@ -23,12 +20,9 @@ describe("join organizations", () => { }); it("join primary school with official contact email", function () { - cy.login( - "435f6a4d-df7d-4840-be7b-bc4851b64e91@mailslurp.com", - "password123", - ); - cy.visit(`/users/join-organization`); + cy.login("435f6a4d-df7d-4840-be7b-bc4851b64e91@mailslurp.com"); + cy.get('[name="siret"]').type("21340126800049"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/reauthenticate_on_admin_page/index.cy.ts b/cypress/e2e/reauthenticate_on_admin_page/index.cy.ts index 47a636b0..1f1c4236 100644 --- a/cypress/e2e/reauthenticate_on_admin_page/index.cy.ts +++ b/cypress/e2e/reauthenticate_on_admin_page/index.cy.ts @@ -1,28 +1,8 @@ -import { generateToken } from "@sunknudsen/totp"; - -const login = (cy) => { - cy.get('[name="login"]').type("unused1@yopmail.com"); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); -}; - -const tfaAuth = (cy) => { - const totp = generateToken("din5ncvbluqpx7xfzqcybmibmtjocnsf", Date.now()); - cy.get("[name=totpToken]").type(totp); - cy.get( - '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', - ).click(); -}; - describe("force recent connexion + 2FA on admin pages", () => { it("should be redirected after long connexion", function () { cy.visit("/"); - login(cy); + cy.login("unused1@yopmail.com"); cy.contains("Votre compte est créé"); @@ -30,7 +10,7 @@ describe("force recent connexion + 2FA on admin pages", () => { cy.contains("merci de valider votre deuxième étape de connexion"); - tfaAuth(cy); + cy.fillTotpFields(); cy.contains("Connexion et compte"); @@ -45,8 +25,7 @@ describe("force recent connexion + 2FA on admin pages", () => { cy.contains("merci de vous identifier à nouveau"); - login(cy); - tfaAuth(cy); + cy.mfaLogin("unused1@yopmail.com"); cy.contains("Connexion et compte"); }); diff --git a/cypress/e2e/redirect_after_session_expiration/index.cy.ts b/cypress/e2e/redirect_after_session_expiration/index.cy.ts index 23312631..86494a24 100644 --- a/cypress/e2e/redirect_after_session_expiration/index.cy.ts +++ b/cypress/e2e/redirect_after_session_expiration/index.cy.ts @@ -1,20 +1,9 @@ -// - -const login = (cy) => { - cy.get('[name="login"]').type("unused1@yopmail.com"); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); -}; - describe("redirect after session expiration", () => { it("should be redirected to organization management page", function () { cy.visit(`/manage-organizations`); - login(cy); + cy.login("unused1@yopmail.com"); + cy.contains("Vos organisations de rattachement"); cy.contains("Commune de lamalou-les-bains - Mairie"); @@ -24,7 +13,8 @@ describe("redirect after session expiration", () => { .contains("Organisations") .click(); - login(cy); + cy.login("unused1@yopmail.com"); + cy.contains("Vos organisations de rattachement"); // Wait for session to expire @@ -33,7 +23,8 @@ describe("redirect after session expiration", () => { // click on delete organization cy.get('[action="/users/quit-organization/1"]').submit(); - login(cy); + cy.login("unused1@yopmail.com"); + // should not redirect on the delete route but on the org management page cy.contains("Vos organisations de rattachement"); // delete action should not have been triggered diff --git a/cypress/e2e/signin_from_legacy_client/index.cy.ts b/cypress/e2e/signin_from_legacy_client/index.cy.ts index 3c3d65b2..f503e33c 100644 --- a/cypress/e2e/signin_from_legacy_client/index.cy.ts +++ b/cypress/e2e/signin_from_legacy_client/index.cy.ts @@ -5,13 +5,7 @@ describe("sign-in from legacy client", () => { cy.visit(`http://localhost:4002`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type("unused1@yopmail.com"); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("unused1@yopmail.com"); cy.contains("moncomptepro-legacy-client"); cy.contains("unused1@yopmail.com"); diff --git a/cypress/e2e/signin_from_standard_client/index.cy.ts b/cypress/e2e/signin_from_standard_client/index.cy.ts index 3dcaa224..5075ec29 100644 --- a/cypress/e2e/signin_from_standard_client/index.cy.ts +++ b/cypress/e2e/signin_from_standard_client/index.cy.ts @@ -5,13 +5,7 @@ describe("sign-in from standard client", () => { cy.visit(`http://localhost:4000`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type("unused1@yopmail.com"); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("unused1@yopmail.com"); cy.contains("moncomptepro-standard-client"); cy.contains("unused1@yopmail.com"); @@ -38,13 +32,7 @@ describe("sign-in from standard client", () => { cy.visit(`http://localhost:4000`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type("unused2@yopmail.com"); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("unused2@yopmail.com"); cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").contains( "Commune de lamalou-les-bains - Mairie", diff --git a/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts b/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts index 8dabea77..22d16484 100644 --- a/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts +++ b/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts @@ -15,15 +15,7 @@ describe("sign-in with email verification renewal", () => { // Visit the signup page cy.visit(`/users/start-sign-in`); - cy.get('[name="login"]').type( - "bad1b70d-e5cb-436c-9ff3-f83d4af5d198@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("bad1b70d-e5cb-436c-9ff3-f83d4af5d198@mailslurp.com"); cy.contains( "pour garantir la sécurité de votre compte, votre adresse email doit être vérifiée régulièrement.", diff --git a/cypress/e2e/signin_with_totp/index.cy.ts b/cypress/e2e/signin_with_totp/index.cy.ts index e34a0965..05ec57d2 100644 --- a/cypress/e2e/signin_with_totp/index.cy.ts +++ b/cypress/e2e/signin_with_totp/index.cy.ts @@ -1,23 +1,9 @@ -import { generateToken } from "@sunknudsen/totp"; - describe("sign-in with TOTP on untrusted browser", () => { it("should sign-in with password and TOTP", function () { cy.visit(`http://localhost:4000`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type("unused1@yopmail.com"); - cy.get('[type="submit"]').click(); - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); - - cy.contains("Valider en deux étapes"); - const totp = generateToken("din5ncvbluqpx7xfzqcybmibmtjocnsf", Date.now()); - cy.get("[name=totpToken]").type(totp); - cy.get( - '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', - ).click(); + cy.mfaLogin("unused1@yopmail.com"); cy.contains('"amr": [\n "pwd",\n "totp",\n "mfa"\n ],'); }); @@ -25,12 +11,8 @@ describe("sign-in with TOTP on untrusted browser", () => { it("should sign-in with password and no TOTP", function () { cy.visit(`http://localhost:4000`); cy.get("button.moncomptepro-button").click(); - cy.get('[name="login"]').type("unused2@yopmail.com"); - cy.get('[type="submit"]').click(); - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + + cy.login("unused2@yopmail.com"); cy.contains("Vérifier votre email"); }); @@ -38,20 +20,8 @@ describe("sign-in with TOTP on untrusted browser", () => { it("should sign-in with password and TOTP when forced by SP", function () { cy.visit(`http://localhost:4000`); cy.get("button#force-2fa").click(); - cy.get('[name="login"]').type("unused2@yopmail.com"); - cy.get('[type="submit"]').click(); - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); - - cy.contains("Valider en deux étapes"); - const totp = generateToken("din5ncvbluqpx7xfzqcybmibmtjocnsf", Date.now()); - cy.get("[name=totpToken]").type(totp); - cy.get( - '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', - ).click(); + cy.mfaLogin("unused2@yopmail.com"); cy.contains('"amr": [\n "pwd",\n "totp",\n "mfa"\n ],'); }); @@ -59,12 +29,7 @@ describe("sign-in with TOTP on untrusted browser", () => { it("should trigger totp rate limiting", function () { cy.visit(`/users/start-sign-in`); - cy.get('[name="login"]').type("unused1@yopmail.com"); - cy.get('[type="submit"]').click(); - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); + cy.login("unused1@yopmail.com"); for (let i = 0; i < 4; i++) { cy.get("[name=totpToken]").type("123456"); diff --git a/cypress/e2e/update_personal_information/index.cy.ts b/cypress/e2e/update_personal_information/index.cy.ts index f642d9c5..0d658a9c 100644 --- a/cypress/e2e/update_personal_information/index.cy.ts +++ b/cypress/e2e/update_personal_information/index.cy.ts @@ -8,10 +8,9 @@ describe("Signup into new entreprise unipersonnelle", () => { }); it("Should send email when user updates personal information", function () { - cy.login( - "9023e9f4-4e54-4ba0-9558-3cb61e7608c6@mailslurp.com", - "password123", - ); + cy.visit("/personal-information"); + + cy.login("9023e9f4-4e54-4ba0-9558-3cb61e7608c6@mailslurp.com"); cy.visit("/personal-information"); @@ -41,13 +40,10 @@ describe("Signup into new entreprise unipersonnelle", () => { }); it("should show an error where putting invalid names or job", () => { - cy.login( - "9023e9f4-4e54-4ba0-9558-3cb61e7608c6@mailslurp.com", - "password123", - ); - cy.visit("/personal-information"); + cy.login("9023e9f4-4e54-4ba0-9558-3cb61e7608c6@mailslurp.com"); + ["given_name", "family_name", "job"].forEach((inputName) => { cy.get(`input[name="${inputName}"]`).clear().type("​"); diff --git a/cypress/e2e/update_totp_application/index.cy.ts b/cypress/e2e/update_totp_application/index.cy.ts index dde2f645..31d6f164 100644 --- a/cypress/e2e/update_totp_application/index.cy.ts +++ b/cypress/e2e/update_totp_application/index.cy.ts @@ -9,32 +9,12 @@ describe("update TOTP application", () => { ); }); it("should update TOTP application, and replace old app with new", function () { - // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit(`/connection-and-account`); - cy.get('[name="login"]').type( - "d2469f84-9547-4190-b989-014876fd54ae@mailslurp.com", - ); - cy.get('[type="submit"]').click(); - - cy.get('[name="password"]').type("password123"); - cy.get('[action="/users/sign-in"] [type="submit"]') - .contains("S’identifier") - .click(); - - // redirect to the TOTP login page - cy.contains("Valider en deux étapes"); - - const totp = generateToken("din5ncvbluqpx7xfzqcybmibmtjocnsf", Date.now()); - cy.get("[name=totpToken]").type(totp); - cy.get( - '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', - ).click(); + cy.mfaLogin("d2469f84-9547-4190-b989-014876fd54ae@mailslurp.com"); cy.contains("Connexion et compte").click(); - cy.contains("Validation en deux étapes"); - cy.contains("Changer d’application d’authentification").click(); cy.contains("Changer d’application d’authentification"); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 7dff52c9..fc3b2410 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,5 +1,6 @@ // +import { generateToken } from "@sunknudsen/totp"; import { checkA11y } from "./a11y/checkA11y"; // @@ -16,11 +17,20 @@ declare global { Cypress.Commands.overwrite("checkA11y", checkA11y); -Cypress.Commands.add("login", (email, password) => { - cy.session([email, password], () => { - // Visit the signup page - cy.visit(`/users/start-sign-in`); +const defaultTotpSecret = "din5ncvbluqpx7xfzqcybmibmtjocnsf"; +const defaultPassword = "password123"; +Cypress.Commands.add("fillTotpFields", (totpSecret = defaultTotpSecret) => { + const totp = generateToken(totpSecret, Date.now()); + cy.get("[name=totpToken]").type(totp); + cy.get( + '[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]', + ).click(); +}); + +Cypress.Commands.add( + "fillLoginFields", + ({ email, password = defaultPassword, totpSecret }) => { // Sign in with the existing inbox cy.get('[name="login"]').type(email); cy.get('[type="submit"]').click(); @@ -29,5 +39,24 @@ Cypress.Commands.add("login", (email, password) => { cy.get('[action="/users/sign-in"] [type="submit"]') .contains("S’identifier") .click(); + + if (totpSecret) { + // redirect to the TOTP login page + cy.contains("Valider en deux étapes"); + + cy.fillTotpFields(totpSecret); + } + }, +); + +Cypress.Commands.add("login", (email) => { + cy.fillLoginFields({ email, password: defaultPassword }); +}); + +Cypress.Commands.add("mfaLogin", (email) => { + cy.fillLoginFields({ + email, + password: defaultPassword, + totpSecret: defaultTotpSecret, }); }); diff --git a/installation.md b/installation.md index 6b051ee4..0598d333 100644 --- a/installation.md +++ b/installation.md @@ -89,7 +89,7 @@ Docker Compose initializes both a PostgreSQL and a Redis database. To connect to these databases, use the following commands: ```bash -docker compose exec db psql postgres://username:password@db:5432/dbname +docker compose exec db psql postgres://moncomptepro:moncomptepro@db:5432/moncomptepro docker compose exec redis redis-cli -h redis -p 6379 ``` diff --git a/src/controllers/main.ts b/src/controllers/main.ts index 56317fd6..814b64c9 100644 --- a/src/controllers/main.ts +++ b/src/controllers/main.ts @@ -216,6 +216,7 @@ export const postDisableForce2faController = async ( updateUserInAuthenticatedSession(req, updatedUser); sendDisable2faMail({ user_id }); + return res.redirect( `/connection-and-account?notification=2fa_successfully_disabled`, ); diff --git a/src/controllers/webauthn.ts b/src/controllers/webauthn.ts index 4dd8676a..0fcaf98f 100644 --- a/src/controllers/webauthn.ts +++ b/src/controllers/webauthn.ts @@ -47,7 +47,9 @@ export const deletePasskeyController = async ( const { email, id: user_id } = getUserFromAuthenticatedSession(req); await deleteUserAuthenticator(email, credential_id); + sendDeleteAccessKeyMail({ user_id }); + return res.redirect( `/connection-and-account?notification=passkey_successfully_deleted`, ); From afd0586d4ead41b7aef81136d5f7542239fbc9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Fri, 13 Sep 2024 17:14:50 +0200 Subject: [PATCH 41/42] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ea7dcdd..3282a8cd 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,10 @@ Ainsi, vous pouvez vous créer n’importe quel compte utilisateur en entrant n À noter que les emails reçus sur les adresses en yopmail.com sont accessibles sur : http://yopmail.com/. -Voici 3 scénarios que vous pouvez tester sur cet environnement : +Voici 2 scénarios que vous pouvez tester sur cet environnement : - entreprise unipersonnelle : créer un compte avec une adresse email jetable, puis utiliser le SIRET d'une organisation unipersonnelle ; - petite mairie : créer un compte avec une adresse email qui n'ait pas comme nom de domaine `yopmail.com`, utiliser le siret d'une collectivité de moins de 50 employés, se connecter en restituant le code reçu à `mairie@yopmail.com` ; -- parrainage : créer un compte avec une adresse email `yopmail.com`, sélectionner l'organisation Direction Interministérielle du Numérique, sélectionner un parrain parmi les utilisateurs proposés ; ## 🖱️ Installer le bouton de connexion MonComptePro sur votre service en ligne From bf0f855dd1e7f0afc6ee2eb5fa32298919e58b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Mon, 16 Sep 2024 15:40:32 +0200 Subject: [PATCH 42/42] refactor: use double quote when no string interpolation --- cypress/e2e/activate_totp/index.cy.ts | 2 +- cypress/e2e/check_email_deliverability/index.cy.ts | 2 +- cypress/e2e/delete_account/index.cy.ts | 2 +- cypress/e2e/delete_totp/index.cy.ts | 8 ++++---- cypress/e2e/join_and_moderation/index.cy.ts | 2 +- .../index.cy.ts | 2 +- cypress/e2e/join_must_confirm/index.cy.ts | 4 ++-- cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts | 2 +- cypress/e2e/join_org_with_verified_domain/index.cy.ts | 4 ++-- .../index.cy.ts | 2 +- .../index.cy.ts | 2 +- cypress/e2e/join_with_official_contact_email/index.cy.ts | 4 ++-- cypress/e2e/redirect_after_session_expiration/index.cy.ts | 2 +- cypress/e2e/reset_password/index.cy.ts | 2 +- .../e2e/set_info_after_account_provisioning/index.cy.ts | 4 ++-- cypress/e2e/signin_from_agentconnect_client/index.cy.ts | 2 +- cypress/e2e/signin_from_legacy_client/index.cy.ts | 4 ++-- cypress/e2e/signin_from_standard_client/index.cy.ts | 4 ++-- .../signin_with_email_verification_renewal/index.cy.ts | 4 ++-- cypress/e2e/signin_with_magic_link/index.cy.ts | 8 ++++---- cypress/e2e/signin_with_totp/index.cy.ts | 8 ++++---- cypress/e2e/signup_entreprise_unipersonnelle/index.cy.ts | 2 +- cypress/e2e/update_totp_application/index.cy.ts | 2 +- 23 files changed, 39 insertions(+), 39 deletions(-) diff --git a/cypress/e2e/activate_totp/index.cy.ts b/cypress/e2e/activate_totp/index.cy.ts index 92163603..72233c89 100644 --- a/cypress/e2e/activate_totp/index.cy.ts +++ b/cypress/e2e/activate_totp/index.cy.ts @@ -10,7 +10,7 @@ describe("add 2fa authentication", () => { }); it("should add 2fa authentication on account user", function () { - cy.visit(`/connection-and-account`); + cy.visit("/connection-and-account"); cy.login("64d9024b-d389-4b9d-948d-a504082c14fa@mailslurp.com"); diff --git a/cypress/e2e/check_email_deliverability/index.cy.ts b/cypress/e2e/check_email_deliverability/index.cy.ts index 0c2ec97b..456e3872 100644 --- a/cypress/e2e/check_email_deliverability/index.cy.ts +++ b/cypress/e2e/check_email_deliverability/index.cy.ts @@ -1,6 +1,6 @@ describe("should suggest valid email address", () => { it("should sign-in", function () { - cy.visit(`http://localhost:4001`); + cy.visit("http://localhost:4001"); cy.get("button.moncomptepro-button").click(); cy.get('[name="login"]').should("have.value", "unused1@yopmail.com"); diff --git a/cypress/e2e/delete_account/index.cy.ts b/cypress/e2e/delete_account/index.cy.ts index aa624eb7..be22c113 100644 --- a/cypress/e2e/delete_account/index.cy.ts +++ b/cypress/e2e/delete_account/index.cy.ts @@ -10,7 +10,7 @@ describe("delete account", () => { }); it("should delete account", function () { - cy.visit(`/connection-and-account`); + cy.visit("/connection-and-account"); cy.login("4cec922b-ecbe-4a46-8511-fc9478c1efd0@mailslurp.com"); diff --git a/cypress/e2e/delete_totp/index.cy.ts b/cypress/e2e/delete_totp/index.cy.ts index 3641481e..6411535e 100644 --- a/cypress/e2e/delete_totp/index.cy.ts +++ b/cypress/e2e/delete_totp/index.cy.ts @@ -13,7 +13,7 @@ describe("delete TOTP connexion", () => { }); it("should delete TOTP application", function () { - cy.visit(`/connection-and-account`); + cy.visit("/connection-and-account"); cy.mfaLogin("eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com"); @@ -41,7 +41,7 @@ describe("delete TOTP connexion", () => { }); it("should not be ask to sign with TOTP", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.login("eab4ab97-875d-4ec7-bdcc-04323948ee63@mailslurp.com"); @@ -49,7 +49,7 @@ describe("delete TOTP connexion", () => { }); it("should disable TOTP", function () { - cy.visit(`/connection-and-account`); + cy.visit("/connection-and-account"); cy.mfaLogin("c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com"); @@ -75,7 +75,7 @@ describe("delete TOTP connexion", () => { }); it("should not be ask to sign with TOTP", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.login("c9fabb94-9274-4ece-a3d0-54b1987c8588@mailslurp.com"); diff --git a/cypress/e2e/join_and_moderation/index.cy.ts b/cypress/e2e/join_and_moderation/index.cy.ts index f61d5462..a2156a52 100644 --- a/cypress/e2e/join_and_moderation/index.cy.ts +++ b/cypress/e2e/join_and_moderation/index.cy.ts @@ -12,7 +12,7 @@ describe("join and moderation", () => { }); it("will be moderated", function () { - cy.visit(`/`); + cy.visit("/"); cy.login("86983334-028f-48b5-881d-8b05d738bec5@mailslurp.net"); diff --git a/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts b/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts index e1dcbf70..9fa9171c 100644 --- a/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts +++ b/cypress/e2e/join_collectivite_territoriale_official_contact_domain/index.cy.ts @@ -10,7 +10,7 @@ describe("join organizations", () => { }); it("join collectivité territoriale with official contact domain", function () { - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.login("76450610-4dcc-4664-b9ab-1cea869b62b1@mailslurp.com"); cy.get('[name="siret"]').type("21740056300011"); diff --git a/cypress/e2e/join_must_confirm/index.cy.ts b/cypress/e2e/join_must_confirm/index.cy.ts index 58c35df5..316bd0d7 100644 --- a/cypress/e2e/join_must_confirm/index.cy.ts +++ b/cypress/e2e/join_must_confirm/index.cy.ts @@ -2,11 +2,11 @@ describe("join organizations", () => { it("join big company with free email provider", function () { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.login("unused1@yopmail.com"); - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.get('[name="siret"]').type("54205118000066"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts b/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts index fcd9cad9..59ae3841 100644 --- a/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts +++ b/cypress/e2e/join_org_with_trackdechets_domain/index.cy.ts @@ -14,7 +14,7 @@ describe("join organizations", () => { }); it("join suggested organisation", function () { - cy.visit(`/`); + cy.visit("/"); cy.login("0c5b976c-b6b0-406e-a7ed-08ddae8d2d81@mailslurp.com"); // The user gets this suggestion because it as mailslurp.com as trackdechets domain diff --git a/cypress/e2e/join_org_with_verified_domain/index.cy.ts b/cypress/e2e/join_org_with_verified_domain/index.cy.ts index d5af0530..31c02314 100644 --- a/cypress/e2e/join_org_with_verified_domain/index.cy.ts +++ b/cypress/e2e/join_org_with_verified_domain/index.cy.ts @@ -14,7 +14,7 @@ describe("join organizations", () => { }); it("join suggested organisation", function () { - cy.visit(`/`); + cy.visit("/"); cy.login("c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp.com"); // The user gets this suggestion because it as mailslurp.com as verified domain @@ -38,7 +38,7 @@ describe("join organizations", () => { }); it("join another organisation", function () { - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.login("c6c64542-5601-43e0-b320-b20da72f6edc@mailslurp.com"); cy.get('[name="siret"]').type("13002526500013"); diff --git a/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts b/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts index b9cac5da..edd2e4d1 100644 --- a/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_code_sent_to_official_contact_email/index.cy.ts @@ -17,7 +17,7 @@ describe("join organizations", () => { }); it("join collectivité territoriale with code send to official contact email", function () { - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.login("c348a2c3-bf54-4f15-bb12-a2d7047c832f@mailslurp.com"); cy.get('[name="siret"]').type("21340126800130"); diff --git a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts index 29855b15..edb653e0 100644 --- a/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_code_sent_to_official_educ_nat_contact_email/index.cy.ts @@ -17,7 +17,7 @@ describe("join organizations", () => { }); it("join collectivité territoriale with code send to official contact email", function () { - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.login("10efdabd-deb0-4d19-a521-6772ca27acf8@mailslurp.com"); cy.get('[name="siret"]').type("19750663700010"); diff --git a/cypress/e2e/join_with_official_contact_email/index.cy.ts b/cypress/e2e/join_with_official_contact_email/index.cy.ts index a69c8830..e42d4092 100644 --- a/cypress/e2e/join_with_official_contact_email/index.cy.ts +++ b/cypress/e2e/join_with_official_contact_email/index.cy.ts @@ -10,7 +10,7 @@ describe("join organizations", () => { }); it("join collectivité territoriale with official contact email", function () { - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.login("435f6a4d-df7d-4840-be7b-bc4851b64e91@mailslurp.com"); cy.get('[name="siret"]').type("21340126800130"); @@ -20,7 +20,7 @@ describe("join organizations", () => { }); it("join primary school with official contact email", function () { - cy.visit(`/users/join-organization`); + cy.visit("/users/join-organization"); cy.login("435f6a4d-df7d-4840-be7b-bc4851b64e91@mailslurp.com"); cy.get('[name="siret"]').type("21340126800049"); diff --git a/cypress/e2e/redirect_after_session_expiration/index.cy.ts b/cypress/e2e/redirect_after_session_expiration/index.cy.ts index 86494a24..d16ba90d 100644 --- a/cypress/e2e/redirect_after_session_expiration/index.cy.ts +++ b/cypress/e2e/redirect_after_session_expiration/index.cy.ts @@ -1,6 +1,6 @@ describe("redirect after session expiration", () => { it("should be redirected to organization management page", function () { - cy.visit(`/manage-organizations`); + cy.visit("/manage-organizations"); cy.login("unused1@yopmail.com"); diff --git a/cypress/e2e/reset_password/index.cy.ts b/cypress/e2e/reset_password/index.cy.ts index d9f3a190..5b5eeb8c 100644 --- a/cypress/e2e/reset_password/index.cy.ts +++ b/cypress/e2e/reset_password/index.cy.ts @@ -11,7 +11,7 @@ describe("sign-in with magic link", () => { it("should reset password then sign-in", function () { // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); // Sign in with the wrong password cy.get('[name="login"]').type( diff --git a/cypress/e2e/set_info_after_account_provisioning/index.cy.ts b/cypress/e2e/set_info_after_account_provisioning/index.cy.ts index 0d901e48..29ac1372 100644 --- a/cypress/e2e/set_info_after_account_provisioning/index.cy.ts +++ b/cypress/e2e/set_info_after_account_provisioning/index.cy.ts @@ -13,7 +13,7 @@ describe("set info after account provisioning", () => { it("should show InclusionConnect welcome page on first visit", function () { // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); // Sign in with the wrong password cy.get('[name="login"]').type( @@ -63,7 +63,7 @@ describe("set info after account provisioning", () => { it("it should not show InclusionConnect welcome page on second visit", function () { // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); // Sign in with the wrong password cy.get('[name="login"]').type( diff --git a/cypress/e2e/signin_from_agentconnect_client/index.cy.ts b/cypress/e2e/signin_from_agentconnect_client/index.cy.ts index e53db705..109987d0 100644 --- a/cypress/e2e/signin_from_agentconnect_client/index.cy.ts +++ b/cypress/e2e/signin_from_agentconnect_client/index.cy.ts @@ -2,7 +2,7 @@ describe("sign-in from agentconnect client", () => { it("should sign-in", function () { - cy.visit(`http://localhost:4001`); + cy.visit("http://localhost:4001"); cy.get("button.moncomptepro-button").click(); cy.get('[name="password"]').type("password123"); diff --git a/cypress/e2e/signin_from_legacy_client/index.cy.ts b/cypress/e2e/signin_from_legacy_client/index.cy.ts index f503e33c..2e13bf6c 100644 --- a/cypress/e2e/signin_from_legacy_client/index.cy.ts +++ b/cypress/e2e/signin_from_legacy_client/index.cy.ts @@ -2,7 +2,7 @@ describe("sign-in from legacy client", () => { it("should sign-in", function () { - cy.visit(`http://localhost:4002`); + cy.visit("http://localhost:4002"); cy.get("button.moncomptepro-button").click(); cy.login("unused1@yopmail.com"); @@ -12,7 +12,7 @@ describe("sign-in from legacy client", () => { cy.contains("Commune de lamalou-les-bains"); // then it should prompt for organization - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.contains("Votre organisation de rattachement"); cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click(); diff --git a/cypress/e2e/signin_from_standard_client/index.cy.ts b/cypress/e2e/signin_from_standard_client/index.cy.ts index 5075ec29..811b6ecd 100644 --- a/cypress/e2e/signin_from_standard_client/index.cy.ts +++ b/cypress/e2e/signin_from_standard_client/index.cy.ts @@ -2,7 +2,7 @@ describe("sign-in from standard client", () => { it("should sign-in without org selection when having only one organization", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.login("unused1@yopmail.com"); @@ -29,7 +29,7 @@ describe("sign-in from standard client", () => { }); it("should sign-in with org selection when having two organization", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.login("unused2@yopmail.com"); diff --git a/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts b/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts index 22d16484..f2791319 100644 --- a/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts +++ b/cypress/e2e/signin_with_email_verification_renewal/index.cy.ts @@ -13,7 +13,7 @@ describe("sign-in with email verification renewal", () => { it("should sign-in with email verification needed", () => { // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.login("bad1b70d-e5cb-436c-9ff3-f83d4af5d198@mailslurp.com"); @@ -40,7 +40,7 @@ describe("sign-in with email verification renewal", () => { }); it("should not show renewal notification for account creation", () => { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.get('[name="login"]').type("unused1@yopmail.com"); cy.get('[type="submit"]').click(); diff --git a/cypress/e2e/signin_with_magic_link/index.cy.ts b/cypress/e2e/signin_with_magic_link/index.cy.ts index ce9822e8..1f46952a 100644 --- a/cypress/e2e/signin_with_magic_link/index.cy.ts +++ b/cypress/e2e/signin_with_magic_link/index.cy.ts @@ -15,7 +15,7 @@ describe("sign-in with magic link", () => { }); it("should sign-up with magic link", function () { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.get('[name="login"]').type( "66ac0a4c-bd2d-490e-a277-1e7c2520100d@mailslurp.com", @@ -47,7 +47,7 @@ describe("sign-in with magic link", () => { }); it("should sign-in with magic link without setting password", function () { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.get('[name="login"]').type( "66ac0a4c-bd2d-490e-a277-1e7c2520100d@mailslurp.com", @@ -83,7 +83,7 @@ describe("sign-in with magic link", () => { }); it("should set a password", function () { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.get('[name="login"]').type( "66ac0a4c-bd2d-490e-a277-1e7c2520100d@mailslurp.com", @@ -113,7 +113,7 @@ describe("sign-in with magic link", () => { }); it("should sign-in with magic link without set password prompt", function () { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.get('[name="login"]').type( "66ac0a4c-bd2d-490e-a277-1e7c2520100d@mailslurp.com", diff --git a/cypress/e2e/signin_with_totp/index.cy.ts b/cypress/e2e/signin_with_totp/index.cy.ts index 05ec57d2..90a93df1 100644 --- a/cypress/e2e/signin_with_totp/index.cy.ts +++ b/cypress/e2e/signin_with_totp/index.cy.ts @@ -1,6 +1,6 @@ describe("sign-in with TOTP on untrusted browser", () => { it("should sign-in with password and TOTP", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.mfaLogin("unused1@yopmail.com"); @@ -9,7 +9,7 @@ describe("sign-in with TOTP on untrusted browser", () => { }); it("should sign-in with password and no TOTP", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button.moncomptepro-button").click(); cy.login("unused2@yopmail.com"); @@ -18,7 +18,7 @@ describe("sign-in with TOTP on untrusted browser", () => { }); it("should sign-in with password and TOTP when forced by SP", function () { - cy.visit(`http://localhost:4000`); + cy.visit("http://localhost:4000"); cy.get("button#force-2fa").click(); cy.mfaLogin("unused2@yopmail.com"); @@ -27,7 +27,7 @@ describe("sign-in with TOTP on untrusted browser", () => { }); it("should trigger totp rate limiting", function () { - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); cy.login("unused1@yopmail.com"); diff --git a/cypress/e2e/signup_entreprise_unipersonnelle/index.cy.ts b/cypress/e2e/signup_entreprise_unipersonnelle/index.cy.ts index 333078d3..11b22545 100644 --- a/cypress/e2e/signup_entreprise_unipersonnelle/index.cy.ts +++ b/cypress/e2e/signup_entreprise_unipersonnelle/index.cy.ts @@ -13,7 +13,7 @@ describe("Signup into new entreprise unipersonnelle", () => { it("creates a user", function () { // Visit the signup page - cy.visit(`/users/start-sign-in`); + cy.visit("/users/start-sign-in"); // Sign up with the previously created inbox cy.get('[name="login"]').type( diff --git a/cypress/e2e/update_totp_application/index.cy.ts b/cypress/e2e/update_totp_application/index.cy.ts index 31d6f164..adae3da5 100644 --- a/cypress/e2e/update_totp_application/index.cy.ts +++ b/cypress/e2e/update_totp_application/index.cy.ts @@ -9,7 +9,7 @@ describe("update TOTP application", () => { ); }); it("should update TOTP application, and replace old app with new", function () { - cy.visit(`/connection-and-account`); + cy.visit("/connection-and-account"); cy.mfaLogin("d2469f84-9547-4190-b989-014876fd54ae@mailslurp.com");