1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-05 05:40:06 +00:00

fix: read security code from file + redirect

This commit is contained in:
Saad Jutt
2021-10-14 18:51:32 +05:00
parent 8fb7129f86
commit 4912915a6e
6 changed files with 113 additions and 46 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ sas/
tmp/ tmp/
build/ build/
.env .env
security.json

3
security.json.example Normal file
View File

@@ -0,0 +1,3 @@
{
"code": "place security code here"
}

View File

@@ -1,10 +1,16 @@
import path from 'path' import path from 'path'
import express from 'express' import express from 'express'
import { renderFile } from 'ejs' import { renderFile } from 'ejs'
import dotenv from 'dotenv'
import { Routes } from './routes' import { Routes } from './routes'
import { passportMiddleware } from './middleware' import { passportMiddleware } from './middleware'
import { getAuthMechanisms } from './utils' import { getAuthMechanisms } from './utils'
import { AuthMechanism } from './types'
import session from 'express-session'
dotenv.config()
const authMechanisms = getAuthMechanisms()
const app = express() const app = express()
@@ -16,14 +22,44 @@ app.set('views', path.join(__dirname, './views'))
app.use(express.json({ limit: '50mb' })) app.use(express.json({ limit: '50mb' }))
app.use(express.static(path.join(__dirname, '..', 'public'))) app.use(express.static(path.join(__dirname, '..', 'public')))
const sessionConfig = {
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
cookie: {
secure: false // set this to true on production
}
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1) // trust first proxy
sessionConfig.cookie.secure = true // serve secure cookies
}
app.use(session(sessionConfig))
app.get(Routes.Login, (req, res) => {
if (
authMechanisms.length === 1 &&
authMechanisms[0] === AuthMechanism.NoSecurity
) {
res.redirect('/')
} else {
const session: any = req.session
const isAuthenticated = !!session?.passport?.user
if (isAuthenticated) {
res.redirect('/')
} else {
res.render('sasjslogon.html', { authMechanisms })
}
}
})
app.use(passportMiddleware()) app.use(passportMiddleware())
const authMechanisms = getAuthMechanisms()
app.get(Routes.Login, (req, res) => {
res.render('sasjslogon.html', { authMechanisms })
})
app.get('/error', (req, res) => res.redirect('/500.html')) app.get('/error', (req, res) => res.redirect('/500.html'))
app.get('/unauthorized', (req, res) => res.redirect('/401.html')) app.get('/unauthorized', (req, res) => res.redirect('/401.html'))
app.get('*', (req, res) => res.status(404).redirect('/404.html')) // app.get('*', (req, res) => res.status(404).redirect('/404.html'))
export default app export default app

View File

@@ -1,37 +1,21 @@
import path from 'path'
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import dotenv from 'dotenv'
import express from 'express' import express from 'express'
import session from 'express-session'
import passport from 'passport' import passport from 'passport'
import { Strategy } from 'passport-local' import { Strategy } from 'passport-local'
const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2') const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2')
import { ensureLoggedIn } from 'connect-ensure-login' import { ensureLoggedIn } from 'connect-ensure-login'
import { readFile } from '@sasjs/utils'
import { AuthMechanism } from './types' import { AuthMechanism } from './types'
import { getAuthMechanisms } from './utils' import { getAuthMechanisms } from './utils'
import indexRouter, { Routes } from './routes' import indexRouter, { Routes } from './routes'
export const passportMiddleware = (): express.Express => { export const passportMiddleware = (): express.Express => {
dotenv.config()
const authMechanisms = getAuthMechanisms() const authMechanisms = getAuthMechanisms()
const middleware = express() const middleware = express()
const sessionConfig = {
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
cookie: {
secure: false // set this to true on production
}
}
if (middleware.get('env') === 'production') {
middleware.set('trust proxy', 1) // trust first proxy
sessionConfig.cookie.secure = true // serve secure cookies
}
middleware.use(session(sessionConfig))
setupPassportStrategies(authMechanisms) setupPassportStrategies(authMechanisms)
middleware.use(passport.initialize()) middleware.use(passport.initialize())
@@ -49,10 +33,10 @@ export const passportMiddleware = (): express.Express => {
authMechanisms[0] === AuthMechanism.NoSecurity authMechanisms[0] === AuthMechanism.NoSecurity
) { ) {
console.log('Using No Security') console.log('Using No Security')
middleware.get('/', indexRouter) middleware.all('/*', indexRouter)
} else { } else {
middleware.get( middleware.all(
'/', '/*',
ensureLoggedIn({ redirectTo: '/SASjsLogon' }), ensureLoggedIn({ redirectTo: '/SASjsLogon' }),
indexRouter indexRouter
) )
@@ -65,15 +49,18 @@ const setupPassportStrategies = (authMechanisms: string[]) => {
if (authMechanisms.includes(AuthMechanism.Local)) { if (authMechanisms.includes(AuthMechanism.Local)) {
console.log('Using Local Authentication') console.log('Using Local Authentication')
passport.use( passport.use(
new Strategy((username: string, password: string, cb: Function) => { new Strategy(async (username: string, code: string, cb: Function) => {
console.log('username', username) const content = await readFile(
if (username !== 'SaadJutt') path.join(__dirname, '..', 'security.json')
return cb(null, false, { message: 'Incorrect Username' }) )
const { code: securityCode } = JSON.parse(content)
if (securityCode !== code)
return cb(null, false, { message: 'Incorrect Security Code' })
const user = { const user = {
id: 'SOMEID', id: 'SOMEID',
username: username, username: username,
displayName: 'displayName' displayName: username
} }
return cb(null, user) return cb(null, user)
}) })
@@ -95,11 +82,20 @@ const setupPassportStrategies = (authMechanisms: string[]) => {
profile: any, profile: any,
done: any done: any
) { ) {
// currently we can't find a way to exchange access token by user info (see userProfile implementation), so const decoded = jwt.decode(params.id_token)
// you will need a jwt-package like https://github.com/auth0/node-jsonwebtoken to decode id_token and get waad profile
var waadProfile = profile || jwt.decode(params.id_token)
done(null, { id: waadProfile.upn }) const user = {
id: 'ID',
username: 'username',
displayName: 'display name'
}
if (decoded && typeof decoded === 'object') {
user.id = decoded.oid
user.username = decoded.unique_name
user.displayName = decoded.name
}
done(null, user)
} }
) )
) )
@@ -127,20 +123,30 @@ const setupPassportRoutes = (
app.get( app.get(
Routes.AzureSignInRedirect, Routes.AzureSignInRedirect,
passport.authenticate('azure_ad_oauth2', { passport.authenticate('azure_ad_oauth2', {
successRedirect: '/',
failureRedirect: Routes.Login, failureRedirect: Routes.Login,
failureMessage: true failureMessage: true
}) }),
(req, res) => {
const session: any = req.session
const returnTo = session.returnTo ?? '/'
session.returnTo = undefined
res.redirect(returnTo)
}
) )
} }
if (authMechanisms.includes(AuthMechanism.Local)) { if (authMechanisms.includes(AuthMechanism.Local)) {
app.post( app.post(
Routes.LocalSignIn, Routes.LocalSignIn,
passport.authenticate(['local'], { passport.authenticate('local', {
successRedirect: '/',
failureRedirect: Routes.Login, failureRedirect: Routes.Login,
failureMessage: true failureMessage: true
}) }),
(req, res) => {
const session: any = req.session
const returnTo = session.returnTo ?? '/'
session.returnTo = undefined
res.redirect(returnTo)
}
) )
} }
} }

View File

@@ -1,23 +1,40 @@
import express from 'express' import express from 'express'
import { processSas, createFileTree, getTreeExample } from '../controllers' import { processSas, createFileTree, getTreeExample } from '../controllers'
import { ExecutionResult, isRequestQuery, isFileTree } from '../types' import {
ExecutionResult,
isRequestQuery,
isFileTree,
AuthMechanism
} from '../types'
import { getAuthMechanisms } from '../utils'
const router = express.Router() const router = express.Router()
const header = (user: any) => {
const authMechanisms = getAuthMechanisms()
if (
authMechanisms.length === 1 &&
authMechanisms[0] === AuthMechanism.NoSecurity
)
return '<div><p>No Security applied</p></div>'
return `<div><p>Logged in as ${user.username} <a href="/signout" role="button">Logout</a></p></div>`
}
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
const query = req.query const query = req.query
if (!isRequestQuery(query)) { if (!isRequestQuery(query)) {
res.send('Welcome to @sasjs/server API') res.send(`${header(req.user)}Welcome to @sasjs/server API`)
return return
} }
const result: ExecutionResult = await processSas(query) const result: ExecutionResult = await processSas(query)
res.send(`<b>Executed!</b><br> res.send(`${header(req.user)}<b>Executed!</b><br>
<p>Log is located:</p> ${result.logPath}<br> <p>Log is located:</p> ${result.logPath}<br>
<p>Log:</p> <textarea style="width: 100%; height: 100%">${result.log}</textarea>`) <p>Log:</p> <textarea style="width: 100%; height: 100%">${
result.log
}</textarea>`)
}) })
router.post('/deploy', async (req, res) => { router.post('/deploy', async (req, res) => {

View File

@@ -27,7 +27,11 @@
<form action="/signin-with-local" method="post"> <form action="/signin-with-local" method="post">
<input type="text" name="username" placeholder="Username" /> <input type="text" name="username" placeholder="Username" />
<input type="password" name="password" placeholder="Password" /> <input
type="text"
name="password"
placeholder="Security Code"
/>
<button type="submit" class="contrast">Sign in</button> <button type="submit" class="contrast">Sign in</button>
</form> </form>
</div> </div>