From 361a9378aa61d52e8ab488ccde9f5e24d5566517 Mon Sep 17 00:00:00 2001 From: Mustafa Gezen Date: Sun, 17 Jul 2022 22:16:38 +0200 Subject: [PATCH] Add more customization options for authOptions --- common/frontend_server/index.mjs | 69 ++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/common/frontend_server/index.mjs b/common/frontend_server/index.mjs index 4c098123..5d708984 100644 --- a/common/frontend_server/index.mjs +++ b/common/frontend_server/index.mjs @@ -53,7 +53,7 @@ const { auth } = expressOidc; export default async function(opts) { // Create a new app for health checks. const appZ = express(); - appZ.get('/_/healthz', ((req, res) => { + appZ.get('/healthz', ((req, res) => { res.end(); })); @@ -83,15 +83,19 @@ export default async function(opts) { console.log(`Using clientID: ${opts.clientID}`); console.log(`Using baseURL: ${opts.baseURL}`); - if (opts.issuerBaseURL.endsWith('.localhost') || opts.issuerBaseURL.endsWith('.localhost/')) { - const kong = 'kong-proxy.kong.svc.cluster.local' + if ((opts.issuerBaseURL.endsWith('.localhost') + || opts.issuerBaseURL.endsWith('.localhost/')) + && process.env['BYC_ENV']) { + const kong = 'kong-proxy.kong.svc.cluster.local'; const urlObject = new URL(opts.issuerBaseURL); console.warn(`Forcing ${urlObject.hostname} to resolve to ${kong}`); const lookup = async () => { return new Promise((resolve, reject) => { // noinspection HttpUrlsUsage dns.lookup(kong, { family: 4 }, (err, address, family) => { - if(err) reject(err); + if (err) { + reject(err); + } resolve(address); }); }); @@ -99,11 +103,13 @@ export default async function(opts) { const internalServiceResolve = await lookup(); evilDns.add(urlObject.hostname, internalServiceResolve); // Disable TLS verification for development - process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0 + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0; } const config = { - authRequired: process.env['DISABLE_AUTH_ENFORCE'] ? process.env['DISABLE_AUTH_ENFORCE'] === 'false' : !!!opts.disableAuthEnforce, + authRequired: process.env['DISABLE_AUTH_ENFORCE'] + ? process.env['DISABLE_AUTH_ENFORCE'] === 'false' + : !!!opts.disableAuthEnforce, // Disable telemetry enableTelemetry: false, // Use dev secret is none is present (Prod requires a secret so not a security issue) @@ -115,7 +121,6 @@ export default async function(opts) { clientID: opts.clientID, // The specific application should supply a dev secret while prod secrets should be set as an env variable clientSecret: opts.clientSecret, - // We're currently only using the Rocky issuer issuerBaseURL: opts.issuerBaseURL, idpLogout: true, authorizationParams: { @@ -138,13 +143,50 @@ export default async function(opts) { // Remember, authentication done here is only for simplicity purposes. // The authentication token is then passed on to the API. // Bypassing auth here doesn't accomplish anything. + let middlewares = []; + + + // If requireEmailSuffix is present, let's validate post callback + // that the signed in email ends with a suffix in the allowlist + // Again, a bypass here doesn't accomplish anything. + let requireEmailSuffix = opts.authOptions?.requireEmailSuffix; + if (process.env['AUTH_OPTIONS_REQUIRE_EMAIL_SUFFIX']) { + requireEmailSuffix = process.env['AUTH_OPTIONS_REQUIRE_EMAIL_SUFFIX'].split( + ','); + } + if (requireEmailSuffix) { + middlewares.push((req, res, next) => { + const email = req.oidc?.user?.email; + if (!email) { + return next('No email found in the user object'); + } + const suffixes = requireEmailSuffix; + let isAllowed = false; + for (const suffix of suffixes) { + if (email.endsWith(suffix)) { + isAllowed = true; + break; + } + } + + if (isAllowed) { + next(); + } else { + res.redirect(process.env['AUTH_REJECT_REDIRECT_URL'] + ? process.env['AUTH_REJECT_REDIRECT_URL'] + : (opts.authOptions.authRejectRedirectUrl + || 'https://rockylinux.org')); + } + }); + } + app.use((req, res, next) => { try { auth(config)(req, res, next); } catch (err) { next(err); } - }); + }, [middlewares]); } // Currently in dev, webpack is handling all file serving @@ -200,9 +242,11 @@ export default async function(opts) { // Make it possible to override api url using an env variable. // Example: /api can be set with URL_API // Example 2: /manage/api can be set with URL_MANAGE_API - const prodEnvName = `URL_${x.substr(1).replace('/', '_').toUpperCase()}`; + const prodEnvName = `URL_${x.substr(1).replace('/', + '_').toUpperCase()}`; - const apiUrl = prod ? (process.env[prodEnvName] || opts.apis[x].prodApiUrl) : opts.apis[x].devApiUrl; + const apiUrl = prod ? (process.env[prodEnvName] + || opts.apis[x].prodApiUrl) : opts.apis[x].devApiUrl; createProxyMiddleware({ target: apiUrl, @@ -265,7 +309,7 @@ export default async function(opts) { webpackMildCompile(compiler); const wdm = webpackDevMiddleware(compiler, { - publicPath: opts.webpackConfig.output.publicPath, + publicPath: opts.webpackConfig.output.publicPath }); app.use(history()); @@ -279,7 +323,8 @@ export default async function(opts) { // For SPAs, the only HTML page is the index page if (res.get('content-type').indexOf('text/html') !== -1) { // Run through handlebars compiler with our template parameters - newData = hbs.handlebars.compile(data.toString())(templateParams(req)); + newData = hbs.handlebars.compile(data.toString())( + templateParams(req)); } else { // No new data, just return old data newData = data;