From 68eaefa0690e43699eca633d46ff1eda2ebb6058 Mon Sep 17 00:00:00 2001 From: Maxime Golfier <25312957+maxgfr@users.noreply.github.com> Date: Thu, 28 Sep 2023 12:32:52 +0200 Subject: [PATCH] fix(env): use config file instead of env for public variable (#1039) --- .../env/dev/templates/www.configmap.yaml | 6 ---- .../env/preprod/templates/www.configmap.yaml | 6 ---- .../env/prod/templates/www.configmap.yaml | 6 ---- targets/frontend/.env.sample | 35 ------------------- targets/frontend/src/config.ts | 10 ++++++ targets/frontend/src/hoc/CustomUrqlClient.js | 5 ++- targets/frontend/src/lib/auth/jwt.js | 4 ++- targets/frontend/src/lib/auth/setJwtCookie.js | 3 +- targets/frontend/src/lib/auth/token.js | 4 +-- .../src/lib/emails/activateAccount.ts | 4 +-- .../frontend/src/lib/emails/lostPassword.js | 4 +-- targets/frontend/src/pages/api/graphql.js | 3 +- targets/frontend/src/pages/api/login.js | 4 ++- .../frontend/src/pages/api/refresh_token.js | 9 ++--- .../frontend/src/pages/api/reset_password.js | 5 ++- .../frontend/src/pages/api/storage/[path].js | 3 +- .../frontend/src/pages/api/storage/index.js | 3 +- targets/frontend/src/pages/fichiers.js | 2 +- targets/frontend/src/pages/user/new.js | 3 +- 19 files changed, 37 insertions(+), 82 deletions(-) delete mode 100644 targets/frontend/.env.sample create mode 100644 targets/frontend/src/config.ts diff --git a/.kontinuous/env/dev/templates/www.configmap.yaml b/.kontinuous/env/dev/templates/www.configmap.yaml index f042ab654..88ee879bb 100644 --- a/.kontinuous/env/dev/templates/www.configmap.yaml +++ b/.kontinuous/env/dev/templates/www.configmap.yaml @@ -3,15 +3,9 @@ apiVersion: v1 metadata: name: www data: - ACCOUNT_MAIL_SENDER: "contact@fabrique.social.gouv.fr" - GITLAB_PROJECT_ID: "270" - GITLAB_URL: https://gitlab.factory.social.gouv.fr/api/v4 HASURA_GRAPHQL_ENDPOINT: "http://hasura/v1/graphql" - JWT_TOKEN_EXPIRES: "15" # 15 min MATOMO_URL: "https://matomo.fabrique.social.gouv.fr/" - NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES: "10080" NODE_ENV: "production" - REFRESH_TOKEN_EXPIRES: "43200" SENTRY_DSN: "https://a5e2451c6b5840c0afa28d5e94060427@sentry.fabrique.social.gouv.fr/42" SMTP_URL: "smtp.tipimail.com" STORAGE_CONTAINER: "cdtn-dev-source" diff --git a/.kontinuous/env/preprod/templates/www.configmap.yaml b/.kontinuous/env/preprod/templates/www.configmap.yaml index 6cd1be079..3112bc4c2 100644 --- a/.kontinuous/env/preprod/templates/www.configmap.yaml +++ b/.kontinuous/env/preprod/templates/www.configmap.yaml @@ -3,15 +3,9 @@ apiVersion: v1 metadata: name: www data: - ACCOUNT_MAIL_SENDER: "contact@fabrique.social.gouv.fr" - GITLAB_PROJECT_ID: "270" - GITLAB_URL: https://gitlab.factory.social.gouv.fr/api/v4 HASURA_GRAPHQL_ENDPOINT: "http://hasura/v1/graphql" - JWT_TOKEN_EXPIRES: "15" # 15 min MATOMO_URL: "https://matomo.fabrique.social.gouv.fr/" - NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES: "10080" NODE_ENV: "production" - REFRESH_TOKEN_EXPIRES: "43200" SENTRY_DSN: "https://a5e2451c6b5840c0afa28d5e94060427@sentry.fabrique.social.gouv.fr/42" SMTP_URL: "smtp.tipimail.com" STORAGE_CONTAINER: "cdtn-preprod-source" diff --git a/.kontinuous/env/prod/templates/www.configmap.yaml b/.kontinuous/env/prod/templates/www.configmap.yaml index a1b48b6e8..a8e384dd1 100644 --- a/.kontinuous/env/prod/templates/www.configmap.yaml +++ b/.kontinuous/env/prod/templates/www.configmap.yaml @@ -3,17 +3,11 @@ apiVersion: v1 metadata: name: www data: - ACCOUNT_MAIL_SENDER: "contact@fabrique.social.gouv.fr" - GITLAB_PROJECT_ID: "270" - GITLAB_URL: https://gitlab.factory.social.gouv.fr/api/v4 HASURA_GRAPHQL_ENDPOINT: "http://hasura/v1/graphql" - JWT_TOKEN_EXPIRES: "15" # 15 min MATOMO_SITE_ID: "27" MATOMO_URL: "https://matomo.fabrique.social.gouv.fr/" - NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES: "10080" NODE_ENV: "production" PRODUCTION: "true" - REFRESH_TOKEN_EXPIRES: "43200" SENTRY_DSN: "https://a5e2451c6b5840c0afa28d5e94060427@sentry.fabrique.social.gouv.fr/42" SMTP_URL: "smtp.tipimail.com" STORAGE_CONTAINER: "cdtn" diff --git a/targets/frontend/.env.sample b/targets/frontend/.env.sample deleted file mode 100644 index 731822989..000000000 --- a/targets/frontend/.env.sample +++ /dev/null @@ -1,35 +0,0 @@ -## - -## frontend config - -## - -## - -## Config needed by nextjs at build time - -## - -# Mail - -ACCOUNT_MAIL_SENDER=contact@fabrique.social.gouv.fr - -# JWT lifetime (15min) - -JWT_TOKEN_EXPIRES=15 - -# Refresh token lifetime (30 days in minutes) - -REFRESH_TOKEN_EXPIRES=43200 - -# Activation token lifetime (7 days in minutes) - -NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES=10080 - -FRONTEND_URL=http://localhost:3000 -HASURA_GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql -HASURA_GRAPHQL_JWT_SECRET={"type": "HS256", "key":"a_pretty_long_secret_key_that_should_be_at_least_32_char"} -PORT=3000 - -GITLAB_URL=https://gitlab.factory.social.gouv.fr/api/v4 -GITLAB_PROJECT_ID=270 diff --git a/targets/frontend/src/config.ts b/targets/frontend/src/config.ts new file mode 100644 index 000000000..e26dffe0d --- /dev/null +++ b/targets/frontend/src/config.ts @@ -0,0 +1,10 @@ +export const ACCOUNT_MAIL_SENDER = "contact@fabrique.social.gouv.fr"; +export const JWT_TOKEN_EXPIRES = 15; // 15 min +export const REFRESH_TOKEN_EXPIRES = 43200; // 30 days in minutes +export const ACTIVATION_TOKEN_EXPIRES = 10080; // 7 days in minutes +export const HASURA_GRAPHQL_JWT_SECRET = process.env + .HASURA_GRAPHQL_JWT_SECRET ?? { + type: "HS256", + key: "a_pretty_long_secret_key_that_should_be_at_least_32_char", +}; +export const BASE_URL = process.env.FRONTEND_HOST || `http://localhost:3000`; diff --git a/targets/frontend/src/hoc/CustomUrqlClient.js b/targets/frontend/src/hoc/CustomUrqlClient.js index 1a9bc6db3..96e387219 100644 --- a/targets/frontend/src/hoc/CustomUrqlClient.js +++ b/targets/frontend/src/hoc/CustomUrqlClient.js @@ -1,4 +1,5 @@ import { withUrqlClient } from "next-urql"; +import { BASE_URL } from "src/config"; import { customAuthExchange, customErrorExchange, @@ -8,9 +9,7 @@ import { cacheExchange, dedupExchange, fetchExchange } from "urql"; export const withCustomUrqlClient = (Component) => withUrqlClient( (ssrExchange, ctx) => { - const url = ctx?.req - ? `${process.env.FRONTEND_URL}/api/graphql` - : `/api/graphql`; + const url = ctx?.req ? `${BASE_URL}/api/graphql` : `/api/graphql`; console.log( "[ withUrqlClient ]", ctx ? (ctx?.req ? "server" : "client") : "no ctx", diff --git a/targets/frontend/src/lib/auth/jwt.js b/targets/frontend/src/lib/auth/jwt.js index 89b80ae4d..949b5c410 100644 --- a/targets/frontend/src/lib/auth/jwt.js +++ b/targets/frontend/src/lib/auth/jwt.js @@ -1,6 +1,8 @@ import jwt, { verify } from "jsonwebtoken"; -const { HASURA_GRAPHQL_JWT_SECRET, JWT_TOKEN_EXPIRES } = process.env; +import { HASURA_GRAPHQL_JWT_SECRET } from "../../config"; + +import { JWT_TOKEN_EXPIRES } from "../../config"; let jwtSecret; try { diff --git a/targets/frontend/src/lib/auth/setJwtCookie.js b/targets/frontend/src/lib/auth/setJwtCookie.js index dc0faa86c..4a4a4ccdf 100644 --- a/targets/frontend/src/lib/auth/setJwtCookie.js +++ b/targets/frontend/src/lib/auth/setJwtCookie.js @@ -1,10 +1,11 @@ import cookie from "cookie"; +import { REFRESH_TOKEN_EXPIRES } from "../../config"; export function setJwtCookie(res, refresh_token, jwt_token) { const cookies = [ cookie.serialize("refresh_token", refresh_token, { httpOnly: true, - maxAge: parseInt(process.env.REFRESH_TOKEN_EXPIRES, 10) * 60, // maxAge in second + maxAge: parseInt(REFRESH_TOKEN_EXPIRES, 10) * 60, // maxAge in second path: "/", sameSite: "Strict", secure: process.env.NODE_ENV === "production", diff --git a/targets/frontend/src/lib/auth/token.js b/targets/frontend/src/lib/auth/token.js index 0544ed077..d848029e0 100644 --- a/targets/frontend/src/lib/auth/token.js +++ b/targets/frontend/src/lib/auth/token.js @@ -39,9 +39,7 @@ export async function auth(ctx) { try { console.log("[auth] refresh token"); const tokenData = await request( - ctx?.req - ? `${process.env.FRONTEND_URL}/api/refresh_token` - : "/api/refresh_token", + ctx?.req ? `${BASE_URL}/api/refresh_token` : "/api/refresh_token", { body: {}, credentials: "include", diff --git a/targets/frontend/src/lib/emails/activateAccount.ts b/targets/frontend/src/lib/emails/activateAccount.ts index 76b56040a..5dc7e370e 100644 --- a/targets/frontend/src/lib/emails/activateAccount.ts +++ b/targets/frontend/src/lib/emails/activateAccount.ts @@ -1,8 +1,6 @@ +import { BASE_URL } from "src/config"; import sendmail from "./sendmail"; -const BASE_URL = - process.env.FRONTEND_HOST || `http://localhost:${process.env.PORT}`; - export function sendActivateAccountEmail(email: string, secret_token: string) { const subject = "Activation de votre compte"; const activateUrl = `${BASE_URL}/change_password?token=${secret_token}&activate=1`; // todo: dynamic hostname diff --git a/targets/frontend/src/lib/emails/lostPassword.js b/targets/frontend/src/lib/emails/lostPassword.js index f76b1b177..35d6bae69 100644 --- a/targets/frontend/src/lib/emails/lostPassword.js +++ b/targets/frontend/src/lib/emails/lostPassword.js @@ -1,8 +1,6 @@ +import { BASE_URL } from "../../config"; import sendmail from "./sendmail"; -const BASE_URL = - process.env.FRONTEND_HOST || `http://localhost:${process.env.PORT}`; - export function sendLostPasswordEmail(email, secret_token) { const activateUrl = `${BASE_URL}/change_password?token=${secret_token}`; // todo: dynamic hostname const subject = "RĂ©initialisation de votre mot de passe"; diff --git a/targets/frontend/src/pages/api/graphql.js b/targets/frontend/src/pages/api/graphql.js index e8c907512..8261842c8 100644 --- a/targets/frontend/src/pages/api/graphql.js +++ b/targets/frontend/src/pages/api/graphql.js @@ -20,7 +20,8 @@ const proxy = createProxyMiddleware({ }, pathRewrite: { "^/api/graphql": "/v1/graphql" }, prependPath: false, - target: process.env.HASURA_GRAPHQL_ENDPOINT, + target: + process.env.HASURA_GRAPHQL_ENDPOINT ?? "http://localhost:8080/v1/graphql", ws: true, xfwd: true, // proxy websockets }); diff --git a/targets/frontend/src/pages/api/login.js b/targets/frontend/src/pages/api/login.js index 81490541a..c30d8982a 100644 --- a/targets/frontend/src/pages/api/login.js +++ b/targets/frontend/src/pages/api/login.js @@ -9,7 +9,9 @@ import { getExpiryDate } from "src/lib/duration"; import { loginQuery, refreshTokenMutation } from "./login.gql"; -const { REFRESH_TOKEN_EXPIRES = "", JWT_TOKEN_EXPIRES = "" } = process.env; +const { REFRESH_TOKEN_EXPIRES = "" } = process.env; + +import { JWT_TOKEN_EXPIRES } from "../../config"; export default async function login(req, res) { const apiError = createErrorFor(res); diff --git a/targets/frontend/src/pages/api/refresh_token.js b/targets/frontend/src/pages/api/refresh_token.js index 02e61bfc0..5c574cbe5 100644 --- a/targets/frontend/src/pages/api/refresh_token.js +++ b/targets/frontend/src/pages/api/refresh_token.js @@ -6,6 +6,7 @@ import { generateJwtToken } from "src/lib/auth/jwt"; import { setJwtCookie } from "src/lib/auth/setJwtCookie"; import { getExpiryDate } from "src/lib/duration"; import { v4 as uuidv4 } from "uuid"; +import { REFRESH_TOKEN_EXPIRES, JWT_TOKEN_EXPIRES } from "../../config"; import { deletePreviousRefreshTokenMutation, @@ -66,9 +67,7 @@ export default async function refreshToken(req, res) { result = await client .mutation(deletePreviousRefreshTokenMutation, { new_refresh_token_data: { - expires_at: getExpiryDate( - parseInt(process.env.REFRESH_TOKEN_EXPIRES, 10) - ), + expires_at: getExpiryDate(parseInt(REFRESH_TOKEN_EXPIRES, 10)), refresh_token: new_refresh_token, user_id: user.id, }, @@ -87,9 +86,7 @@ export default async function refreshToken(req, res) { res.json({ jwt_token, - jwt_token_expiry: getExpiryDate( - parseInt(process.env.JWT_TOKEN_EXPIRES, 10) || 15 - ), + jwt_token_expiry: getExpiryDate(parseInt(JWT_TOKEN_EXPIRES, 10) || 15), refresh_token: new_refresh_token, }); } diff --git a/targets/frontend/src/pages/api/reset_password.js b/targets/frontend/src/pages/api/reset_password.js index d887aeb41..89d6676a2 100644 --- a/targets/frontend/src/pages/api/reset_password.js +++ b/targets/frontend/src/pages/api/reset_password.js @@ -4,6 +4,7 @@ import { client } from "@shared/graphql-client"; import { createErrorFor } from "src/lib/apiError"; import { getExpiryDate } from "src/lib/duration"; import { v4 as uuidv4 } from "uuid"; +import { ACTIVATION_TOKEN_EXPIRES } from "../../config"; export default async function reset_password(req, res) { const apiError = createErrorFor(res); @@ -28,9 +29,7 @@ export default async function reset_password(req, res) { const result = await client .mutation(udpateSecretTokenMutation, { email, - expires: getExpiryDate( - parseInt(process.env.NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES, 10) - ), + expires: getExpiryDate(parseInt(ACTIVATION_TOKEN_EXPIRES, 10)), secret_token, }) .toPromise(); diff --git a/targets/frontend/src/pages/api/storage/[path].js b/targets/frontend/src/pages/api/storage/[path].js index a148f0504..5bfd18c83 100644 --- a/targets/frontend/src/pages/api/storage/[path].js +++ b/targets/frontend/src/pages/api/storage/[path].js @@ -2,9 +2,10 @@ import Boom from "@hapi/boom"; import { verify } from "jsonwebtoken"; import { createErrorFor } from "src/lib/apiError"; import { deleteBlob } from "src/lib/azure"; +import { HASURA_GRAPHQL_JWT_SECRET } from "../../../config"; const container = process.env.STORAGE_CONTAINER; -const jwtSecret = JSON.parse(process.env.HASURA_GRAPHQL_JWT_SECRET); +const jwtSecret = JSON.parse(HASURA_GRAPHQL_JWT_SECRET); export default async function deleteFiles(req, res) { const apiError = createErrorFor(res); diff --git a/targets/frontend/src/pages/api/storage/index.js b/targets/frontend/src/pages/api/storage/index.js index 49c399d02..9d114442b 100644 --- a/targets/frontend/src/pages/api/storage/index.js +++ b/targets/frontend/src/pages/api/storage/index.js @@ -5,9 +5,10 @@ import { createErrorFor } from "src/lib/apiError"; import { getContainerBlobs, uploadBlob } from "src/lib/azure"; import { isUploadFileSafe } from "src/lib/secu"; import * as stream from "stream"; +import { HASURA_GRAPHQL_JWT_SECRET } from "../../../config"; const container = process.env.STORAGE_CONTAINER; -const jwtSecret = JSON.parse(process.env.HASURA_GRAPHQL_JWT_SECRET); +const jwtSecret = JSON.parse(HASURA_GRAPHQL_JWT_SECRET); async function endPoint(req, res) { const apiError = createErrorFor(res); diff --git a/targets/frontend/src/pages/fichiers.js b/targets/frontend/src/pages/fichiers.js index 66a0b7ca2..1387d8a4f 100644 --- a/targets/frontend/src/pages/fichiers.js +++ b/targets/frontend/src/pages/fichiers.js @@ -30,7 +30,7 @@ import { } from "@mui/material"; const listFiles = () => - request(`${process.env.FRONTEND_URL || ""}/api/storage`, { + request(`/api/storage`, { headers: { token: getToken()?.jwt_token || "" }, }); diff --git a/targets/frontend/src/pages/user/new.js b/targets/frontend/src/pages/user/new.js index 68dc81e65..10edc24d2 100644 --- a/targets/frontend/src/pages/user/new.js +++ b/targets/frontend/src/pages/user/new.js @@ -7,6 +7,7 @@ import { withUserProvider } from "src/hoc/UserProvider"; import { getExpiryDate } from "src/lib/duration"; import { useMutation } from "urql"; import { Alert } from "@mui/material"; +import { ACTIVATION_TOKEN_EXPIRES } from "../../config"; const registerUserMutation = ` mutation registerUser($user: auth_users_insert_input! ) { @@ -29,7 +30,7 @@ function prepareMutationData(input) { user: { ...input, secret_token_expires_at: getExpiryDate( - parseInt(process.env.NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES, 10) + parseInt(ACTIVATION_TOKEN_EXPIRES, 10) ), user_roles: { data: { role: input.default_role } }, },