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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ sas/
|
|||||||
tmp/
|
tmp/
|
||||||
build/
|
build/
|
||||||
.env
|
.env
|
||||||
|
security.json
|
||||||
3
security.json.example
Normal file
3
security.json.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"code": "place security code here"
|
||||||
|
}
|
||||||
46
src/app.ts
46
src/app.ts
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user