Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importe site vitrine depuis projet OOTS-France #1

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading