You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
10 KiB
344 lines
10 KiB
'use strict' |
|
const express = require('express') |
|
const app = express() |
|
const mongoose = require('mongoose') |
|
const Schema = mongoose.Schema |
|
const database = 'mongodb://' + process.env.mongousr + ':' + process.env.mongopwd + '@mongo:27017/test' |
|
const today = new Date() |
|
var counter = 0 |
|
const Prometheus = require('prom-client') |
|
const fs = require('file-system') |
|
const marked = require('marked') |
|
const jwt = require('jsonwebtoken') |
|
const bodyParser= require('body-parser') |
|
const secret = 'wowmuchsecretveryhiddenwow' |
|
const successUrl = 'yay.html' |
|
const failureUrl = 'nay.html' |
|
const cookieParser = require('cookie-parser') |
|
const os = require('os') |
|
|
|
app.use(cookieParser()) |
|
|
|
app.use(express.static('html')) |
|
|
|
// const morgan = require('morgan') |
|
// // use morgan to log requests to the console |
|
// app.use(morgan('dev')) |
|
|
|
// global controller |
|
// app.get('/*',function(req,res,next){ |
|
// res.header.token = 'sample-token' |
|
// next() // http://expressjs.com/guide.html#passing-route control |
|
// }) |
|
|
|
// a middleware with no mount path gets executed for every request to the app |
|
// app.use(function(req, res, next) { |
|
// res.setHeader('charset', 'utf-8') |
|
// next() |
|
// }) |
|
|
|
// setting CORS headers |
|
app.all('/', function(req, res, next) { |
|
res.header("Access-Control-Allow-Origin", "*") |
|
res.header("Access-Control-Allow-Headers", "X-Requested-With") |
|
next() |
|
}) |
|
|
|
// database connection (with retries) |
|
const options = { |
|
autoIndex: false, // Don't build indexes |
|
reconnectTries:30, // Retry up to 30 times |
|
reconnectInterval: 500, // Reconnect every 500ms |
|
poolSize: 10, // Maintain up to 10 socket connections |
|
// If not connected, return errors immediately rather than waiting for reconnect |
|
bufferMaxEntries: 0, |
|
useNewUrlParser: true |
|
} |
|
|
|
const connectWithRetry = () => { |
|
console.log('MongoDB connection with retry') |
|
mongoose.connect(database, options).then(()=>{ |
|
console.log('MongoDB is connected') |
|
}).catch(err=>{ |
|
console.log('MongoDB connection unsuccessful, retry after 5 seconds.') |
|
setTimeout(connectWithRetry, 5000) |
|
}) |
|
} |
|
|
|
const libCounter = new Prometheus.Counter({ |
|
name: 'lib_invocation_count', |
|
help: 'A simple counter for app access during runtime created with prometheus nodejs library' |
|
}) |
|
|
|
const libUptime = new Prometheus.Counter({ |
|
name: 'lib_upTime', |
|
help: 'uptime A counter of the application\'s uptime in seconds created with prometheus nodejs library.' |
|
}) |
|
|
|
console.log('mongousr: ', process.env.mongousr) |
|
console.log('mongopwd: ', process.env.mongopwd) |
|
|
|
// Prometheus Default Metrics collector |
|
const collectDefaultMetrics = Prometheus.collectDefaultMetrics |
|
// Probe every 5th second. |
|
collectDefaultMetrics({ timeout: 5000 }) |
|
|
|
// new schema model object based on the structure of what I want to put on MongoDB collection |
|
var testSchema = new Schema({ |
|
thingies: { |
|
type: String |
|
} |
|
},{ |
|
collection: 'test' |
|
}) |
|
|
|
// new object that will hold the data using model structure made above |
|
var thingies = mongoose.model('thingieName', testSchema) |
|
|
|
// Default message for testing |
|
app.get('/', (req, res, next)=>{ |
|
res.json([{message:'yes, your nodejs app is really running'}]) |
|
// res.send('Oh hay' + '\n') |
|
counter++ // for prometheus invocation_count metric |
|
libCounter.inc() // for prometheus lib_invocation_count metric |
|
console.log('Hello, I\'m inside endpoint \'/\'') |
|
console.log('HTTP headers below:') |
|
console.log(req.headers) |
|
console.log('Cookies: ', req.cookies) |
|
console.log('Cookies: ', res.cookies) |
|
console.log('\x1b[35m', 'HOSTNAME below:') |
|
console.log(os.hostname()) |
|
next() |
|
}) |
|
|
|
// cookie experiments endpoint |
|
app.get('/cookie', function(req, res, next) { |
|
// res.cookie('cookiename', 'cookievalue') |
|
// res.setHeader('Set-Cookie', 'cookiename=cookievalue HttpOnly') |
|
// res.cookie('foo3', 'bar3', { maxAge: 900000, httpOnly: true }) |
|
res.json({message: 'I am inside endpoint /cookie'}) |
|
res.json(JSON.stringify(req.headers)) |
|
res.end() |
|
console.log('\x1b[35m', 'Cookies: ', req.cookies) |
|
console.log('\x1b[35m', 'req.cookies.token below:') |
|
console.log(req.cookies.token) |
|
}) |
|
|
|
// Clear cookies |
|
app.get('/clear', function(req, res) { |
|
res.clearCookie('token') |
|
res.clearCookie('cookiename') |
|
res.clearCookie('Authorization') |
|
res.clearCookie('foo3') |
|
console.log(req.cookies) |
|
res.status(200).send('Cookies cleared') |
|
}) |
|
|
|
// Test endpoint for md files rendering |
|
app.get('/test', function(req, res) { |
|
var path = '/app/README.md' |
|
var file = fs.readFileSync(path, 'utf8') |
|
res.send(marked(file.toString())) |
|
}) |
|
|
|
// Prometheus metrics endpoint - Library |
|
app.get('/metrics', function(req, res){ |
|
libUptime.inc(Math.floor(process.uptime())) |
|
res.set('Content-Type', Prometheus.register.contentType) |
|
res.end(Prometheus.register.metrics()) |
|
libUptime.reset() |
|
}) |
|
|
|
// Prometheus metrics endpoint - Handmade |
|
app.get('/metrics2', function(req, res){ |
|
var now = new Date() |
|
var passedTime = now - today |
|
res.writeHead(200, {'Content-Type':'text/plain'}) |
|
res.write('# HELP uptime A counter of the application\'s uptime in millisenconds.' + '\n') |
|
res.write('# TYPE uptime counter' + '\n') |
|
res.write('uptime ' + passedTime + '\n') |
|
res.write('# HELP invocation_count A simple counter for app access during runtime' + '\n') |
|
res.write('# TYPE invocation_count counter'+ '\n') |
|
res.write('invocation_count ' + counter + '\n') |
|
res.end() |
|
}) |
|
|
|
// JWT generation |
|
app.use(bodyParser.urlencoded({ extended: false })) |
|
|
|
app.post('/token', function(req, res) { |
|
|
|
const claims_power = { |
|
username: req.body.username, |
|
password: req.body.password, |
|
subject: 'power#1234', |
|
issuer: 'http://youcantrustme.io', |
|
scope: 'admin' |
|
} |
|
|
|
const claims_user = { |
|
username: req.body.username, |
|
password: req.body.password, |
|
subject: 'normal_user', |
|
issuer: 'http://youcantrustme.io', |
|
scope: 'user' |
|
} |
|
|
|
switch(req.body.username) { |
|
case 'user1': |
|
if (req.body.password === 'pass1') { |
|
let token = jwt.sign(claims_user, secret) |
|
res.cookie('token', token) |
|
// res.setHeader('Set-Cookie', 'token=' + token + ' HttpOnly') |
|
// res.setHeader('Set-Cookie', 'Authorization=Bearer ' + token + ' HttpOnly') |
|
console.log('JWT Token: ' + token) |
|
console.log(jwt.decode(token)) |
|
res.redirect(successUrl) |
|
} else { |
|
res.redirect(failureUrl) |
|
} |
|
break |
|
case 'power': |
|
if (req.body.password === 'weak') { |
|
let token = jwt.sign(claims_power, secret) |
|
res.cookie('token', token) |
|
// res.setHeader('Set-Cookie', 'token=' + token + ' HttpOnly') |
|
console.log('JWT Token: ' + token) |
|
console.log(jwt.decode(token)) |
|
res.redirect(successUrl) |
|
} else { |
|
res.redirect(failureUrl) |
|
} |
|
break |
|
default: |
|
res.status(500).send('User not found') |
|
} |
|
console.log('http headers below:') |
|
console.log(req.headers) |
|
}) |
|
|
|
|
|
// Restricted route root |
|
const restrictedRoutes = express.Router() |
|
app.use('/restricted', restrictedRoutes) |
|
|
|
// Setting thingies so thingies work on restrictedRoutes |
|
restrictedRoutes.all('/', function(req, res, next) { |
|
res.header("Access-Control-Allow-Origin", "*") |
|
res.header("Access-Control-Allow-Headers", "X-Requested-With") |
|
let hostname = os.hostname() |
|
res.setHeader('HOSTNAME', hostname) |
|
console.log(hostname) |
|
next() |
|
}) |
|
|
|
restrictedRoutes.use(function (req, res, next) { |
|
if (req.cookies.token) { |
|
jwt.verify(req.cookies.token, secret, function (err, decoded) { |
|
if (err) { |
|
res.json({ message: 'invalid token' }) |
|
} else { |
|
req.decoded = decoded |
|
console.log(decoded) |
|
console.log(req.decoded['scope']) |
|
switch(req.decoded['scope']) { |
|
case 'user': |
|
res.status(200).json([{message: 'Need ADMIN scope to access the good stuff'}]) |
|
break |
|
case 'admin': |
|
next() |
|
break |
|
default: |
|
res.status(401).json([{message: 'Not authorized'}]) |
|
} |
|
} |
|
}) |
|
} else { |
|
res.status(500).json({ message: 'no token found' }) |
|
} |
|
}) |
|
|
|
// Restricted endpoint |
|
restrictedRoutes.get('/', (req, res) => { |
|
res.status(200).json([{secret:'You have access to restricted contents!'}]) |
|
console.log(JSON.stringify({secret:'You have access to restricted contents!'})) |
|
}) |
|
|
|
restrictedRoutes.get('/hostname', (req, res) => { |
|
let hostname = {hostname: os.hostname()} |
|
res.status(200).json(hostname) |
|
console.log(JSON.stringify(hostname)) |
|
}) |
|
|
|
// // Restricted route root stupid |
|
// const restrictedRoutes = express.Router() |
|
// app.use('/restricted', restrictedRoutes) |
|
|
|
// restrictedRoutes.use( function(req, res, next){ |
|
// let sentToken = req.headers['token'] |
|
// console.log('hello there, do not mind me.') |
|
// console.log('next line will show the JWT token:') |
|
// console.log(sentToken) |
|
// console.log('okay, so, next line will show the decoded JWT token:') |
|
// let decodedToken = jwt.decode(sentToken) |
|
// console.log(decodedToken) |
|
// console.log('yay, so now I am going to verify it and show again the decoded token if successful.') |
|
// console.log('if NOT successful I will not let you see the secret message') |
|
// console.log('here we go...') |
|
// let verifiedToken = jwt.verify(sentToken, secret) |
|
// console.log(verifiedToken) |
|
// if (verifiedToken) { |
|
// console.log('success! secret message will be shown.') |
|
// next() |
|
// } else { |
|
// res.status(401).json({message: 'nope, you are not authorized'}) |
|
// } |
|
// }) |
|
|
|
// Restricted route root test (KISS) |
|
const router = express.Router() |
|
app.use('/api', router) |
|
|
|
router.use( function(req, res, next){ |
|
console.log('yo, this should always be called whenever /api or anything inside is called') |
|
next() |
|
}) |
|
|
|
// Restricted route endpoint test (KISS) |
|
router.get('/inside', (req, res) => { |
|
console.log('I am inside /api, hopefully') |
|
res.status(200).json({message: 'it worked'}) |
|
}) |
|
|
|
// JWT decode test |
|
app.get('/decode', function(req, res){ |
|
let sentToken = req.headers['token'] |
|
if (sentToken) { |
|
var decode = jwt.verify(sentToken, secret) |
|
console.log(decode) |
|
res.status(200).send('success') |
|
} |
|
}) |
|
|
|
// Mongo query |
|
app.get('/info', function(req, res){ |
|
thingies.find({}).then(function (thingies) { |
|
res.json(thingies) |
|
}) |
|
}) |
|
|
|
|
|
// Mongo insert |
|
app.post('/info/add/:name', function(req, res){ |
|
var item = {thingies: req.params.name} |
|
var data = new thingies(item) |
|
data.save() |
|
res.send('thingie ' + req.params.name + ' added to database' + '\n') |
|
}) |
|
|
|
connectWithRetry() |
|
|
|
app.listen(3001, () => { |
|
console.log('Server running on port 3001') |
|
console.log('process.env.PORT: ' + process.env.PORT) |
|
}) |