mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +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/
|
||||
build/
|
||||
.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 express from 'express'
|
||||
import { renderFile } from 'ejs'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { Routes } from './routes'
|
||||
import { passportMiddleware } from './middleware'
|
||||
import { getAuthMechanisms } from './utils'
|
||||
import { AuthMechanism } from './types'
|
||||
import session from 'express-session'
|
||||
|
||||
dotenv.config()
|
||||
const authMechanisms = getAuthMechanisms()
|
||||
|
||||
const app = express()
|
||||
|
||||
@@ -16,14 +22,44 @@ app.set('views', path.join(__dirname, './views'))
|
||||
app.use(express.json({ limit: '50mb' }))
|
||||
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())
|
||||
|
||||
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('/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
|
||||
|
||||
@@ -1,37 +1,21 @@
|
||||
import path from 'path'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import dotenv from 'dotenv'
|
||||
import express from 'express'
|
||||
import session from 'express-session'
|
||||
import passport from 'passport'
|
||||
import { Strategy } from 'passport-local'
|
||||
const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2')
|
||||
import { ensureLoggedIn } from 'connect-ensure-login'
|
||||
import { readFile } from '@sasjs/utils'
|
||||
|
||||
import { AuthMechanism } from './types'
|
||||
import { getAuthMechanisms } from './utils'
|
||||
import indexRouter, { Routes } from './routes'
|
||||
|
||||
export const passportMiddleware = (): express.Express => {
|
||||
dotenv.config()
|
||||
const authMechanisms = getAuthMechanisms()
|
||||
|
||||
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)
|
||||
|
||||
middleware.use(passport.initialize())
|
||||
@@ -49,10 +33,10 @@ export const passportMiddleware = (): express.Express => {
|
||||
authMechanisms[0] === AuthMechanism.NoSecurity
|
||||
) {
|
||||
console.log('Using No Security')
|
||||
middleware.get('/', indexRouter)
|
||||
middleware.all('/*', indexRouter)
|
||||
} else {
|
||||
middleware.get(
|
||||
'/',
|
||||
middleware.all(
|
||||
'/*',
|
||||
ensureLoggedIn({ redirectTo: '/SASjsLogon' }),
|
||||
indexRouter
|
||||
)
|
||||
@@ -65,15 +49,18 @@ const setupPassportStrategies = (authMechanisms: string[]) => {
|
||||
if (authMechanisms.includes(AuthMechanism.Local)) {
|
||||
console.log('Using Local Authentication')
|
||||
passport.use(
|
||||
new Strategy((username: string, password: string, cb: Function) => {
|
||||
console.log('username', username)
|
||||
if (username !== 'SaadJutt')
|
||||
return cb(null, false, { message: 'Incorrect Username' })
|
||||
new Strategy(async (username: string, code: string, cb: Function) => {
|
||||
const content = await readFile(
|
||||
path.join(__dirname, '..', 'security.json')
|
||||
)
|
||||
const { code: securityCode } = JSON.parse(content)
|
||||
if (securityCode !== code)
|
||||
return cb(null, false, { message: 'Incorrect Security Code' })
|
||||
|
||||
const user = {
|
||||
id: 'SOMEID',
|
||||
username: username,
|
||||
displayName: 'displayName'
|
||||
displayName: username
|
||||
}
|
||||
return cb(null, user)
|
||||
})
|
||||
@@ -95,11 +82,20 @@ const setupPassportStrategies = (authMechanisms: string[]) => {
|
||||
profile: any,
|
||||
done: any
|
||||
) {
|
||||
// currently we can't find a way to exchange access token by user info (see userProfile implementation), so
|
||||
// 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)
|
||||
const decoded = 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(
|
||||
Routes.AzureSignInRedirect,
|
||||
passport.authenticate('azure_ad_oauth2', {
|
||||
successRedirect: '/',
|
||||
failureRedirect: Routes.Login,
|
||||
failureMessage: true
|
||||
})
|
||||
}),
|
||||
(req, res) => {
|
||||
const session: any = req.session
|
||||
const returnTo = session.returnTo ?? '/'
|
||||
session.returnTo = undefined
|
||||
res.redirect(returnTo)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (authMechanisms.includes(AuthMechanism.Local)) {
|
||||
app.post(
|
||||
Routes.LocalSignIn,
|
||||
passport.authenticate(['local'], {
|
||||
successRedirect: '/',
|
||||
passport.authenticate('local', {
|
||||
failureRedirect: Routes.Login,
|
||||
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 { 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 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) => {
|
||||
const query = req.query
|
||||
|
||||
if (!isRequestQuery(query)) {
|
||||
res.send('Welcome to @sasjs/server API')
|
||||
res.send(`${header(req.user)}Welcome to @sasjs/server API`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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:</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) => {
|
||||
|
||||
@@ -27,7 +27,11 @@
|
||||
|
||||
<form action="/signin-with-local" method="post">
|
||||
<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>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user