'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 CORS headers restrictedRoutes.all('/', function(req, res, next) { res.header("Access-Control-Allow-Origin", "*") res.header("Access-Control-Allow-Headers", "X-Requested-With") next() }) restrictedRoutes.use(function (req, res, next) { res.setHeader('HOSTNAME', os.hostname()) 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!'})) }) // // 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) })