Skip to content

Commit

Permalink
Importe site vitrine depuis projet OOTS-France
Browse files Browse the repository at this point in the history
Commit de référence : numerique-gouv/oots-france@e4e8a81

Co-authored-by: Fabien Lamarque <[email protected]>
  • Loading branch information
egaillot and Fabinout committed May 23, 2024
1 parent 1d3ec4f commit c67a965
Show file tree
Hide file tree
Showing 43 changed files with 8,168 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .env.site.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
AVEC_CONNEXION_FC_PLUS= # autorise connexion à FC Plus
AVEC_ENVOI_COOKIE_SUR_HTTP= # autorise envoi du cookie de session par HTTP avec valeur true
SECRET_JETON_SESSION= # secret utilisé pour chiffrer et déchiffrer le jeton stocké dans le cookie de session

AVEC_AUTHENTIFICATION_EIDAS= # si renseigné à `true`, passe par le « bridge eIDAS » pour l'authentification
CLE_PRIVEE_JWK_EN_BASE64= # Cle privée utilisée pour déchiffrer les infos utilisateur provenant de eIDAS (données au format JWK, chiffrées en base64)
IDENTIFIANT_CLIENT_FCPLUS= # identifiant d'accès au serveur FC+
SECRET_CLIENT_FCPLUS= # secret d'accès au serveur FC+
URL_CONFIGURATION_OPEN_ID_FCPLUS= # URL accès aux informations de configuration Open ID de FranceConnect+
URL_REDIRECTION_CONNEXION= # URL redirection après authentification FranceConnect+
URL_REDIRECTION_DECONNEXION= # URL redirection après destruction session FranceConnect+
3 changes: 3 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PORT_MOCK_FCPLUS= # port d'accès au mock FC plus
PORT_SITE_VITRINE= # port sur lequel le serveur écoute
URL_BASE= # URL du serveur sur lequel les conteneurs sont instanciés (ex. https://example.com)
28 changes: 28 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = {
env: {
browser: true,
es2021: true,
jest: true,
},
extends: 'airbnb-base',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
overrides: [{
files: ['src/erreurs.js'],
rules: { 'max-classes-per-file': ['off'] },
}, {
files: ['test*/**/*.*js'],
rules: { 'no-new': ['off'] },
}],
plugins: ['no-only-tests'],
rules: {
'no-param-reassign': ['error', {
props: true,
ignorePropertyModificationsFor: ['requete'],
}],
'no-only-tests/no-only-tests': 'error',
'class-methods-use-this': ['error', { enforceForClassFields: false }],
},
};
77 changes: 77 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '38 6 * * 2'

jobs:
analyze:
name: Analyze
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality


# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
31 changes: 31 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs

name: Node.js CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.env*
!.env*.template
/nginx/
node_modules/
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node:18

RUN npm install -g npm

WORKDIR /usr/src/app
COPY package.json package-lock.json /usr/src/app/
RUN npm install

COPY . /usr/src/app
EXPOSE 3000
CMD ["npm", "start"]
58 changes: 58 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
version: '3'

x-site-vitrine: &configuration-base
build:
context: .
env_file:
- ./.env.site
volumes:
- .:/usr/src/app
- node_modules:/usr/src/app/node_modules/

services:
test:
<<: *configuration-base
command: "npm run test:watch"

certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./nginx/certbot/conf:/etc/letsencrypt
- ./nginx/certbot/www:/var/www/certbot
depends_on:
- nginx
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

nginx:
image: nginx:mainline-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/certbot/conf:/etc/letsencrypt
- ./nginx/certbot/www:/var/www/certbot
depends_on:
- web
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

web:
<<: *configuration-base
command: "npx nodemon server.js"
ports:
- "${PORT_SITE_VITRINE}:3000"
depends_on:
- mock_fcplus

mock_fcplus:
<<: *configuration-base
command: "npx nodemon mockFCPlus.js"
ports:
- "${PORT_MOCK_FCPLUS}:4000"
environment:
- URL_BASE_MOCK_FCPLUS=${URL_BASE}:${PORT_MOCK_FCPLUS}

volumes:
node_modules:
129 changes: 129 additions & 0 deletions mockFCPlus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
const express = require('express');
const jose = require('jose');

const adaptateurChiffrement = require('./src/adaptateurs/adaptateurChiffrement');

const JETON_CAS_SIGNATURE_INVALIDE = 'jetonCasSignatureInvalide';

const jwkValide = {
kty: 'RSA',
n: 'whYOFK2Ocbbpb_zVypi9SeKiNUqKQH0zTKN1-6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4HHHsrYCf2-FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS_Yv4hRvWfQPcc2Gc3-_fQOOW57zVy-rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj-KgDPjymkMGoJlO3aKppsjfbt_AH6GGdRghYRLOUwQU-h-ofWHR3lbYiKtXPn5dN24kiHy61e3VAQ9_YAZlwXC_99GGtw_NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZASw',
e: 'AQAB',
d: 'VuVE_KEP6323WjpbBdAIv7HGahGrgGANvbxZsIhm34lsVOPK0XDegZkhAybMZHjRhp-gwVxX5ChC-J3cUpOBH5FNxElgW6HizD2Jcq6t6LoLYgPSrfEHm71iHg8JsgrqfUnGYFzMJmv88C6WdCtpgG_qJV1K00_Ly1G1QKoBffEs-v4fAMJrCbUdCz1qWto-PU-HLMEo-krfEpGgcmtZeRlDADh8cETMQlgQfQX2VWq_aAP4a1SXmo-j0cvRU4W5Fj0RVwNesIpetX2ZFz4p_JmB5sWFEj_fC7h5z2lq-6Bme2T3BHtXkIxoBW0_pYVnASC8P2puO5FnVxDmWuHDYQ',
p: '07rgXd_tLUhVRF_g1OaqRZh5uZ8hiLWUSU0vu9coOaQcatSqjQlIwLW8UdKv_38GrmpIfgcEVQjzq6rFBowUm9zWBO9Eq6enpasYJBOeD8EMeDK-nsST57HjPVOCvoVC5ZX-cozPXna3iRNZ1TVYBY3smn0IaxysIK-zxESf4pM',
q: '6qrE9TPhCS5iNR7QrKThunLu6t4H_8CkYRPLbvOIt2MgZyPLiZCsvdkTVSOX76QQEXt7Y0nTNua69q3K3Jhf-YOkPSJsWTxgrfOnjoDvRKzbW3OExIMm7D99fVBODuNWinjYgUwGSqGAsb_3TKhtI-Gr5ls3fn6B6oEjVL0dpmk',
dp: 'mHqjrFdgelT2OyiFRS3dAAPf3cLxJoAGC4gP0UoQyPocEP-Y17sQ7t-ygIanguubBy65iDFLeGXa_g0cmSt2iAzRAHrDzI8P1-pQl2KdWSEg9ssspjBRh_F_AiJLLSPRWn_b3-jySkhawtfxwO8Kte1QsK1My765Y0zFvJnjPws',
dq: 'KmjaV4YcsVAUp4z-IXVa5htHWmLuByaFjpXJOjABEUN0467wZdgjn9vPRp-8Ia8AyGgMkJES_uUL_PDDrMJM9gb4c6P4-NeUkVtreLGMjFjA-_IQmIMrUZ7XywHsWXx0c2oLlrJqoKo3W-hZhR0bPFTYgDUT_mRWjk7wV6wl46E',
qi: 'iYltkV_4PmQDfZfGFpzn2UtYEKyhy-9t3Vy8Mw2VHLAADKGwJvVK5ficQAr2atIF1-agXY2bd6KV-w52zR8rmZfTr0gobzYIyqHczOm13t7uXJv2WygY7QEC2OGjdxa2Fr9RnvS99ozMa5nomZBqTqT7z5QV33czjPRCjvg6FcE',
};

const jwkInvalide = {
kty: 'RSA',
n: 'y6CltudeoaMikeSwChFGWePEDUJevbb-pb2o0aDREQ_7jqfwUR6uCGU_eXmEfIor-f3afasfBmbpiKIHSPosUFSbpImkBoCwd5W8miSBwhWIxYIgUnsjGu4nfaM9i2NtHc4EGG8SM5yQd6i6Eb1q2KTbjwIzsPAMTwnrGmYmeFVZPK6wdtDYKuXzrmt6CcBZu3duG4Y0uhz-DhssVgZQZIMyn6mEnep-XsvKvRjNU7Gl26woWnHueIlcG0e4WaoPxbc5Xd1ZbT7Bu6M8N3FjHHS6FrXIqeYtrzJOtwDe37IOsuR4d87n_cNKf311fXK6iYYimoSOQwsSAG6WgalfNw',
e: 'AQAB',
d: 'W8BlKwcR0s9JEmfzEnY6NuK0Qi03t1AvacsNuHc3_PIwrVTqqgKi9FF6ymeA1QUFT72cp6dlcWMJs3Eeyzk-omudPgRvDicKXLfxpZrxhNxjJmu92Kx3YvkQfkIBxz7judxMaB4UG4FebyxtuvSYokmWTNf3JrDjOvIDJ4ADsZAlb3QXzJwRzKMzYMN6PAt5CW9CEPqR_nwu9p_zlUFtC4qDXitfmSpLO5quZTpDJrBYWsnHdj77pe3knqcjw8lTGLCojSDmCydbG7hkz3Xq31-eY0YFOPJgWcbQMkLiyF0y5GsF1CjRKFNywgfxljzRzEiLCs4Ihof0phkX-vqT4Q',
p: '8coKPaGjRE9q-xjVRLfAWpJ9aW6D6xUVGeC26jJu9D2nPyG3mpORzn72EtP_VxsHK7KQ4DnNgvT000-mSeiKkjufxTss7N2-TJUupkc0IJpr5QjDXP3fJcplJGyF6BgBVaUuJJWytRrrBrdQBtCKv5dNkhdDNZFhIB7TYPGs5ak',
q: '15htNxigCp5aq23f5507wKFamM_sOZSKYldTNod8XJOtnYXT1ZOY7AWUwHWIs3glU8xlw9GX1VJcWn9YGqe6rYgd_yxVglq2PR7vVhUuT7Dym0r5Z1Q605DAqh3tyVfFkegxYB0kNwmSw3LHvxsxCyXeaUOzeRO5oDM4SyXjad8',
dp: 'Eeh69bGhHBAdxldChIJvlsW-0C5FSwYWuAHyyknN-f0PBBgFN0eyxu6UXzSgdt0jnNLu9AyT8h0efQArOtIkYUxVOxB09V4_GAD8oYgojjmhwCb0AVE0U-I3t4jqKhSNFMDVOBR2Vf-WZLrzDG4puKMGNcnPSopn_S8LTOTZf3E',
dq: 'vuFNkQJUcBJT5IObQc2MIbi6JaGxXCmPfBIkspqyGKUHiff63ZWYRx-J2_wz0_ID2nWVhBIFg_Evo1AsCS2HsixZopr1-jumLec9r9GA9z2LDsMKndmNW9NFQVjONv1nBw-054vljHUFY9Yz05eXjG8yw7AVLpWwO44dwSsCdbE',
qi: 'tA3h9_ecg1pu9zenGhdkW9lY7HqU3a41ROPQxYVX8rVybgwiO7MlqBQmy2zgezB5NQfzIgeWMtgThemN5RiQ3bunGRBOHL9PFjMo_aGO3hZkj93EdoxZBiIlLwJX11yxmMBC05jojVR-2XjX9Eijs5xmFdrjkngmwlAhbYyz6M0',
};

const ootsJWK = JSON.parse(atob(process.env.CLE_PRIVEE_JWK_EN_BASE64));

const enJWE = (cleSignature, infos) => {
const headerJWT = {
alg: 'RS256',
};

const headerJWE = {
alg: 'RSA-OAEP',
enc: 'A256GCM',
cty: 'JWT',
kid: adaptateurChiffrement.cleHachage(ootsJWK.n),
iss: 'http://oots',
aud: process.env.IDENTIFIANT_CLIENT_FCPLUS,
};

return jose.importJWK(cleSignature)
.then((clePrivee) => new jose.SignJWT(infos)
.setProtectedHeader(headerJWT)
.setIssuedAt()
.setIssuer('http://mock_fcplus')
.setAudience(process.env.IDENTIFIANT_CLIENT_FCPLUS)
.setExpirationTime('1h')
.sign(clePrivee))
.then((jwt) => jose.importJWK(ootsJWK)
.then((clePrivee) => new jose.CompactEncrypt(new TextEncoder().encode(jwt))
.setProtectedHeader(headerJWE)
.encrypt(clePrivee)));
};

const port = 4000;
const app = express();

app.use(express.urlencoded({ extended: true }));

app.get('/', (_requete, reponse) => {
reponse.json({
authorization_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/debut_session`,
end_session_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/fin_session`,
jwks_uri: `${process.env.URL_BASE_MOCK_FCPLUS}/jwks`,
token_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/jeton`,
userinfo_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/userinfo`,
});
});

app.get('/debut_session', (requete, reponse) => {
const etat = requete.params.state;
reponse.redirect(`${process.env.URL_REDIRECTION_CONNEXION}?state=${etat}&code=abcdef`);
});

app.get('/fin_session', (_requete, reponse) => {
reponse.redirect(process.env.URL_REDIRECTION_DECONNEXION);
});

app.post('/jeton', (requete, reponse) => {
const { code } = requete.body;
const jeton = (code === 'XXX') ? JETON_CAS_SIGNATURE_INVALIDE : 'unJeton';

enJWE(jwkValide, {})
.then((jwe) => reponse.json({ access_token: jeton, id_token: jwe }));
});

app.get('/jwks', (_requete, reponse) => {
const { kty, n, e } = jwkValide;

reponse.json({
keys: [{
use: 'sig',
kty,
n,
e,
}],
});
});

app.get('/userinfo', (requete, reponse) => {
const jeton = requete.headers.authorization.match(/Bearer (.*)/)[1];
const cleSignature = (jeton === JETON_CAS_SIGNATURE_INVALIDE) ? jwkInvalide : jwkValide;

const envoieInfos = (infos) => enJWE(cleSignature, infos)
.then((jwe) => reponse.send(jwe));

envoieInfos({
given_name: 'Anne-Juliette',
family_name: 'HAUDEBERT',
birthdate: '1962-08-24',
gender: 'female',
sub: '1234567890abcdef',
});
});

app.listen(port, () => {
/* eslint-disable no-console */

console.log(`Mock FCPlus est démarré et écoute sur le port ${port} !…`);

/* eslint-enable no-console */
});
31 changes: 31 additions & 0 deletions nginx.template/conf/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
server {
listen 80;
server_name example.com;
server_tokens off;

location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

location / {
return 301 https://$host$request_uri;
}
}

server {
listen 443 ssl;
server_name example.com;
server_tokens off;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

location / {
proxy_pass http://web:3000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Loading

0 comments on commit c67a965

Please sign in to comment.