From 7b0bc24112d5fe3c5e42b18a58d2a76b20348e1e Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Mon, 15 Jun 2020 11:52:27 -0400 Subject: [PATCH 1/8] update for authentication --- controllers/authentication.js | 113 ++++++++++++++++++++++++++++++++++ package.json | 1 + requests.rest | 29 +++++++++ server.js | 4 +- 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 controllers/authentication.js create mode 100644 requests.rest diff --git a/controllers/authentication.js b/controllers/authentication.js new file mode 100644 index 0000000..7fb174a --- /dev/null +++ b/controllers/authentication.js @@ -0,0 +1,113 @@ +/*========================== + * Subscriptions + * + * @description: Managing the client subscription to be notified about a given topic + * @author: Government of Canada; @duboisp + * @version: 1.0 + ===========================*/ + + const axios = require('axios'); + + const NotifyClient = require('notifications-node-client').NotifyClient; // https://docs.notifications.service.gov.uk/node.html#node-js-client-documentation + + const entities = require("entities"); + + const dbConn = module.parent.exports.dbConn; + const ObjectId = require('mongodb').ObjectId; + + var options = { + apiVersion: 'v1', // default + endpoint: 'http://127.0.0.1:8200' // default + //token: '1234' // optional client token; can be fetched after valid initialization of the server + }; + + // get new instance of the client + const vault = require("node-vault")(options); + + + + const processEnv = process.env, + _devLog = !!!processEnv.prodNoLog, + _keySalt = processEnv.keySalt || "salt", + _validHosts = JSON.parse(processEnv.validHosts || '["localhost:8080"]'), + _errorPage = processEnv.errorPage || "https://canada.ca", + _successJSO = processEnv.successJSO || { statusCode: 200, ok: 1 }, + _cErrorsJSO = processEnv.cErrorsJSO || { statusCode: 400, bad: 1, msg: "Bad request" }, + _sErrorsJSO = processEnv.sErrorsJSO || { statusCode: 500, err: 1 }, + _notifyEndPoint = processEnv.notifyEndPoint || "https://api.notification.alpha.canada.ca", + _confirmBaseURL = processEnv.confirmBaseURL || "https://apps.canada.ca/x-notify/subs/confirm/", + _nbMinutesBF = processEnv.notSendBefore || 25, // Default of 25 minutes. + _bypassSubscode = processEnv.subscode, + _topicCacheLimit = processEnv.topicCacheLimit || 50, + _notifyCacheLimit = processEnv.notifyCacheLimit || 40, + _flushAccessCode = processEnv.flushAccessCode, + _flushAccessCode2 = processEnv.flushAccessCode2, + _notifyUsTimeLimit = processEnv.notifyUsTimeLimit || 180000, + _subsLinkSuffix = processEnv.subsLinkSuffix || "853e0212b92a127"; + + let notifyCached = [], + notifyCachedIndexes = [], + topicCached = [], + topicCachedIndexes = [], + fakeSubsIncrement = 0, + _notifyUsNotBeforeTimeLimit = 0; + + + + + // init vault server + vault.init({ secret_shares: 1, secret_threshold: 1 }) + .then( (result) => { + var keys = result.keys; + // set token for all following requests + vault.token = result.root_token; + // unseal vault server + console.log("result.root_token : " + result.root_token); + return vault.unseal({ secret_shares: 1, key: keys[0] }) + }) + .catch(console.error); + + + vault.write('secret/hello', { value: 'world', lease: '1s' }) + .then( () => vault.read('secret/hello')) + .then( () => vault.delete('secret/hello')) + .catch(console.error); + + // + // Get key + // + // @return; a JSON containing valid key + // + exports.getKey = ( req, res, next ) => { + + generateAuthenticationKey().then(data => { + res.json({data}) + }) + }; + + + + // + // Get Authentication Token key from Vault + // + // @return; a JSON containing valid key + // + generateAuthenticationKey = () => { + + /*return axios.get("https://dog.ceo/api/breeds/list/all", + { headers: {'Content-Type': 'application/json'}} + )*/ + + return axios.get("http://0.0.0.0:8200/v1/auth/token/lookup-self", + { headers: {'X-Vault-Token': 's.FYNWqTDJQMTuCnNDscJEszFs'}} + ) + .then((response) => { + console.log(response.data); + //console.log(response.status); + return response.data + }, (error) => { + console.log(error); + }); + } + + diff --git a/package.json b/package.json index 689a021..ce72b1f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "mongodb": "^3.5.5", "morgan": "^1.9.1", "mustache": "^4.0.1", + "node-vault": "^0.9.18", "nodemailer": "^6.4.6", "notifications-node-client": "^4.7.2", "passport": "^0.4.1", diff --git a/requests.rest b/requests.rest new file mode 100644 index 0000000..807abb3 --- /dev/null +++ b/requests.rest @@ -0,0 +1,29 @@ +GET http://localhost:8080/api/v0.1/subs/getAuthenticationKey + + +### + +DELETE http://localhost:8080/logout +Content-Type: application/json + +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNTY4NzU5OTIyfQ.RT6wszuCeFLwC_6ksmNMIELxiC5s-uRivfRxyZof5ag" +} + +### + +POST http://localhost:8080/token +Content-Type: application/json + +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNTY4NzU5OTIyfQ.RT6wszuCeFLwC_6ksmNMIELxiC5s-uRivfRxyZof5ag" +} + +### + +POST http://localhost:8080/login +Content-Type: application/json + +{ + "username": "Jim" +} \ No newline at end of file diff --git a/server.js b/server.js index 5cd6359..453b166 100644 --- a/server.js +++ b/server.js @@ -71,6 +71,8 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). const managersController = require('./controllers/managers'); const smtpController = require('./controllers/sendsmtp'); + const authenticationController = require('./controllers/authentication'); + /** @@ -91,7 +93,7 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). /** * Subscriber routes. */ - app.get('/api/v0.1/subs/postkey', subsController.getKey); + app.get('/api/v0.1/subs/getAuthenticationKey', authenticationController.getKey); app.post('/api/v0.1/subs/email/add', // Need to do more testing // passport.authenticate('basic', { session: false }), From 9998ca8cc0c41542673c7c0f93d28baf22aff60f Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Tue, 23 Jun 2020 17:15:24 -0400 Subject: [PATCH 2/8] add public aws trail service for vault --- controllers/authentication.js | 31 ++++++++---- docker-compose.yml | 2 +- package.json | 1 + requests.rest | 12 +++++ startupNotify.txt | 92 +++++++++++++++++++++++++++++++++++ vault/config/vault.json | 6 +++ 6 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 startupNotify.txt create mode 100644 vault/config/vault.json diff --git a/controllers/authentication.js b/controllers/authentication.js index 7fb174a..8f31df6 100644 --- a/controllers/authentication.js +++ b/controllers/authentication.js @@ -22,9 +22,7 @@ }; // get new instance of the client - const vault = require("node-vault")(options); - - + //const vault = require("node-vault"); const processEnv = process.env, _devLog = !!!processEnv.prodNoLog, @@ -54,7 +52,7 @@ - + /* // init vault server vault.init({ secret_shares: 1, secret_threshold: 1 }) .then( (result) => { @@ -72,6 +70,7 @@ .then( () => vault.read('secret/hello')) .then( () => vault.delete('secret/hello')) .catch(console.error); + */ // // Get key @@ -94,12 +93,10 @@ // generateAuthenticationKey = () => { - /*return axios.get("https://dog.ceo/api/breeds/list/all", - { headers: {'Content-Type': 'application/json'}} - )*/ + - return axios.get("http://0.0.0.0:8200/v1/auth/token/lookup-self", - { headers: {'X-Vault-Token': 's.FYNWqTDJQMTuCnNDscJEszFs'}} + /*return axios.get("http://127.0.0.1:8200/v1/secret?help=1", + { headers: {'X-Vault-Token': 'root'}} ) .then((response) => { console.log(response.data); @@ -108,6 +105,22 @@ }, (error) => { console.log(error); }); +*/ + /// http://localhost:8200/v1/sys/seal-status + // https://dog.ceo/api/breeds/list/all + // http://172.18.0.1:8200/v1/sys/seal-status + return axios.get("http://ec2-100-26-121-207.compute-1.amazonaws.com:8200/v1/sys/seal-status", + { headers: {'Content-Type': 'application/json'}} + ) + .then((response) => { + console.log(response.data); + //console.log(response.status); + return response.data + }, (error) => { + console.log(error); + }); + + } diff --git a/docker-compose.yml b/docker-compose.yml index 1bca26d..dd173d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,4 +28,4 @@ services: - x-notify-net networks: x-notify-net: - driver: bridge + diff --git a/package.json b/package.json index ce72b1f..a5da590 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "morgan": "^1.9.1", "mustache": "^4.0.1", "node-vault": "^0.9.18", + "node-vault-client": "^0.5.6", "nodemailer": "^6.4.6", "notifications-node-client": "^4.7.2", "passport": "^0.4.1", diff --git a/requests.rest b/requests.rest index 807abb3..dd35178 100644 --- a/requests.rest +++ b/requests.rest @@ -1,5 +1,17 @@ GET http://localhost:8080/api/v0.1/subs/getAuthenticationKey +### +GET https://dog.ceo/api/breeds/list/all + +### + +#GET http://127.0.0.1:8200/v1/auth/token/lookup-self +GET http://127.0.0.1:8200/v1/secret?help=1 +Content-Type: application/json + +{ + "X-Vault-Token": "root" +} ### diff --git a/startupNotify.txt b/startupNotify.txt new file mode 100644 index 0000000..bacf41e --- /dev/null +++ b/startupNotify.txt @@ -0,0 +1,92 @@ +Step 1/9 : FROM node:12-slim + ---> 396c358d2c97 +Step 2/9 : ARG NODE_ENV=development + ---> Using cache + ---> ec22cd84ed28 +Step 3/9 : ENV NODE_ENV=${NODE_ENV} + ---> Using cache + ---> a0271bcb4eb4 +Step 4/9 : WORKDIR ./ + ---> Using cache + ---> 55ddc83e254b +Step 5/9 : COPY package*.json ./ + ---> Using cache + ---> 6a097baab7ca +Step 6/9 : RUN npm install -g nodemon + ---> Using cache + ---> 39f66d28de92 +Step 7/9 : RUN npm install + ---> Using cache + ---> 694037fbd2fb +Step 8/9 : COPY . . + ---> ebfd24661b02 +Step 9/9 : CMD [ "npm", "start" ] + ---> Running in 5fce3a565b5c +Removing intermediate container 5fce3a565b5c + ---> 5b3d136b188a + +Successfully built 5b3d136b188a +Successfully tagged x-notify_x-notify:latest +Attaching to 7d6425291c36_myvault, x-notify-mongo, x-notify +x-notify-mongo | 2020-06-18T20:37:41.677+0000 I CONTROL [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none' +x-notify-mongo | 2020-06-18T20:37:41.679+0000 W ASIO [main] No TransportLayer configured during NetworkInterface startup +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=049c2b5cf7e7 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] db version v4.2.6 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] git version: 20364840b8f1af16917e4c23c1b5f5efd8b352f8 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.1.1 11 Sep 2018 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] allocator: tcmalloc +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] modules: none +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] build environment: +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] distmod: ubuntu1804 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] distarch: x86_64 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] target_arch: x86_64 +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I CONTROL [initandlisten] options: { net: { bindIp: "*" } } +7d6425291c36_myvault | error loading configuration from /vault/config/vault.json: stat /vault/config/vault.json: no such file or directory +x-notify-mongo | 2020-06-18T20:37:41.680+0000 W STORAGE [initandlisten] Detected unclean shutdown - /data/db/mongod.lock is not empty. +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I STORAGE [initandlisten] Detected data files in /data/db created by the 'wiredTiger' storage engine, so setting the active storage engine to 'wiredTiger'. +x-notify-mongo | 2020-06-18T20:37:41.680+0000 W STORAGE [initandlisten] Recovering data from the last clean checkpoint. +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I STORAGE [initandlisten] +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem +x-notify-mongo | 2020-06-18T20:37:41.680+0000 I STORAGE [initandlisten] wiredtiger_open config: create,cache_size=483M,cache_overflow=(file_max=0M),session_max=33000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000,close_scan_interval=10,close_handle_minimum=250),statistics_log=(wait=0),verbose=[recovery_progress,checkpoint_progress], +7d6425291c36_myvault exited with code 1 +x-notify-mongo | 2020-06-18T20:37:42.184+0000 I STORAGE [initandlisten] WiredTiger message [1592512662:184002][1:0x7ff742105b00], txn-recover: Recovering log 53 through 54 +x-notify-mongo | 2020-06-18T20:37:42.225+0000 I STORAGE [initandlisten] WiredTiger message [1592512662:225435][1:0x7ff742105b00], txn-recover: Recovering log 54 through 54 +x-notify-mongo | 2020-06-18T20:37:42.298+0000 I STORAGE [initandlisten] WiredTiger message [1592512662:298325][1:0x7ff742105b00], txn-recover: Main recovery loop: starting at 53/3200 to 54/256 +x-notify-mongo | 2020-06-18T20:37:42.298+0000 I STORAGE [initandlisten] WiredTiger message [1592512662:298852][1:0x7ff742105b00], txn-recover: Recovering log 53 through 54 +x-notify-mongo | 2020-06-18T20:37:42.357+0000 I STORAGE [initandlisten] WiredTiger message [1592512662:357637][1:0x7ff742105b00], file:sizeStorer.wt, txn-recover: Recovering log 54 through 54 +x-notify-mongo | 2020-06-18T20:37:42.397+0000 I STORAGE [initandlisten] WiredTiger message [1592512662:397326][1:0x7ff742105b00], file:sizeStorer.wt, txn-recover: Set global recovery timestamp: (0, 0) +x-notify | +x-notify | > x-notify@1.0.0 start / +x-notify | > nodemon -L server.js +x-notify | +x-notify-mongo | 2020-06-18T20:37:42.476+0000 I RECOVERY [initandlisten] WiredTiger recoveryTimestamp. Ts: Timestamp(0, 0) +x-notify-mongo | 2020-06-18T20:37:42.482+0000 I STORAGE [initandlisten] Timestamp monitor starting +x-notify-mongo | 2020-06-18T20:37:42.490+0000 I CONTROL [initandlisten] +x-notify-mongo | 2020-06-18T20:37:42.490+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. +x-notify-mongo | 2020-06-18T20:37:42.490+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. +x-notify-mongo | 2020-06-18T20:37:42.490+0000 I CONTROL [initandlisten] +x-notify-mongo | 2020-06-18T20:37:42.498+0000 I SHARDING [initandlisten] Marking collection local.system.replset as collection version: +x-notify-mongo | 2020-06-18T20:37:42.500+0000 I STORAGE [initandlisten] Flow Control is enabled on this deployment. +x-notify-mongo | 2020-06-18T20:37:42.501+0000 I SHARDING [initandlisten] Marking collection admin.system.roles as collection version: +x-notify-mongo | 2020-06-18T20:37:42.501+0000 I SHARDING [initandlisten] Marking collection admin.system.version as collection version: +x-notify-mongo | 2020-06-18T20:37:42.504+0000 I SHARDING [initandlisten] Marking collection local.startup_log as collection version: +x-notify-mongo | 2020-06-18T20:37:42.507+0000 I FTDC [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data' +x-notify-mongo | 2020-06-18T20:37:42.510+0000 I SHARDING [LogicalSessionCacheRefresh] Marking collection config.system.sessions as collection version: +x-notify-mongo | 2020-06-18T20:37:42.511+0000 I SHARDING [LogicalSessionCacheReap] Marking collection config.transactions as collection version: +x-notify-mongo | 2020-06-18T20:37:42.511+0000 I NETWORK [listener] Listening on /tmp/mongodb-27017.sock +x-notify-mongo | 2020-06-18T20:37:42.511+0000 I NETWORK [listener] Listening on 0.0.0.0 +x-notify-mongo | 2020-06-18T20:37:42.512+0000 I NETWORK [listener] waiting for connections on port 27017 +x-notify | [nodemon] 2.0.4 +x-notify | [nodemon] to restart at any time, enter `rs` +x-notify | [nodemon] watching path(s): *.* +x-notify | [nodemon] watching extensions: js,mjs,json +x-notify | [nodemon] starting `node server.js` +x-notify | event-loop-stats not found, ignoring event loop metrics... +x-notify-mongo | 2020-06-18T20:37:43.003+0000 I FTDC [ftdc] Unclean full-time diagnostic data capture shutdown detected, found interim file, some metrics may have been lost. OK +x-notify-mongo | 2020-06-18T20:37:43.010+0000 I SHARDING [ftdc] Marking collection local.oplog.rs as collection version: +x-notify-mongo | 2020-06-18T20:37:43.050+0000 I NETWORK [listener] connection accepted from 172.18.0.3:54218 #1 (1 connection now open) +x-notify-mongo | 2020-06-18T20:37:43.055+0000 I NETWORK [conn1] received client metadata from 172.18.0.3:54218 conn1: { driver: { name: "nodejs", version: "3.5.7" }, os: { type: "Linux", name: "linux", architecture: "x64", version: "4.19.76-linuxkit" }, platform: "'Node.js v12.16.3, LE (unified)" } +x-notify | ? App is running at http://localhost:8080 in development mode +x-notify | Press CTRL-C to stop +x-notify | diff --git a/vault/config/vault.json b/vault/config/vault.json new file mode 100644 index 0000000..38db4bc --- /dev/null +++ b/vault/config/vault.json @@ -0,0 +1,6 @@ +{ + "backend": {"file": {"path": "/vault/file"}}, + "listener": {"tcp": {"address": "0.0.0.0:8200", "tls_disable": 1}}, + "default_lease_ttl": "168h", + "max_lease_ttl": "0h" +} \ No newline at end of file From 8e63a6d92e9c4dac6aced2bbc922b9f3fd915b1a Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Thu, 16 Jul 2020 10:16:20 -0400 Subject: [PATCH 3/8] separate the user handler in different files --- controllers/users.js | 289 +++++++++++++++++++++++++++++++++++++++++++ server.js | 9 ++ 2 files changed, 298 insertions(+) create mode 100644 controllers/users.js diff --git a/controllers/users.js b/controllers/users.js new file mode 100644 index 0000000..9fe6937 --- /dev/null +++ b/controllers/users.js @@ -0,0 +1,289 @@ +/** + * Module dependencies. + */ + + +const express = require('express'); // HTTP server +const compression = require('compression'); // gzip for the HTTP body +const cors = require('cors'); // CORS + +const logger = require('morgan'); // HTTP request logger +const bcrypt = require('bcryptjs'); +const expressStatusMonitor = require('express-status-monitor'); // Monitor of the service (CPU/Mem,....) +const errorHandler = require('errorhandler'); +const dotenv = require('dotenv'); // Application configuration +const path = require('path'); +const chalk = require('chalk'); // To color message in console log +const jwt = require('jsonwebtoken') +const passport = require('passport'); // Authentication + + +const bodyParser = require('body-parser'); +//const { generateKeyPair } = require('crypto'); +const crypto = require('crypto'); +const util = require('util'); + +const MongoClient = require('mongodb').MongoClient; + +const processEnv = process.env; + +/** + * Create Express server. + */ +const app = express(); + + +/** + * Load environment variables from .env file, where API keys and passwords are configured. + */ +dotenv.config({ + path: '.env' +}); + +const _corsSettings = JSON.parse(processEnv.cors || '{"optionSucessStatus":200}'); // Parse CORS settings + + +/** + * Connect to MongoDB. + */ + +MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ).then( ( mongoInstance ) => { + + var dbConn = mongoInstance.db( processEnv.MONGODB_NAME || 'sandbox' ); + var userNameSecretKeyCollection = dbConn.collection("userNameSecretKey"); + var userNamePasswordCollection = dbConn.collection("userNamePassword"); + userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); + //userNamePasswordCollection.createIndex( { "userName": 1 }, { unique: true } ); +// + + + +/** + * Express configuration. + */ +app.set('host', processEnv.Host || '0.0.0.0'); +app.set('port', processEnv.Port || 8080); + +//app.use(compression()); // Compression not recommended +app.use(logger( processEnv.LOG_FORMAT || 'dev')); + +app.use(bodyParser.json()); // for parsing application/json + +app.disable('x-powered-by'); + + + + + +/** + * Middleware to enable cors + */ +app.use( cors( { "origin": "*" } ) ); + + + + // List mailing for the user +app.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), + verifyToken, ( req, res ) => { + const user = req.user; + res.json( { + id: "uid-33", + created: "2020-06-16", + updated: "2020-06-16", + title: "Mailing Title", + user + } ); + +}); + +// Generate the secret key +let keyMap = new Map() +const NO_USER = "noUser"; +app.post('/test/getSecretKey', (req, res) => { + const secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); + console.log(secretKey); + // first loading to get secret key, there is no way to get to know the user info + keyMap.set(NO_USER, secretKey); + + + userNameSecretKeyCollection.replaceOne( + { userName: NO_USER }, + { userName: NO_USER, secretKey: secretKey }, + { upsert : true} + ).then( () => { + console.log("1 document inserted on api /test/getSecretKey "); + }).catch( ( e ) => { + console.log( "err while generate secretKey on api /test/getSecretKey" ); + console.log( e ); + }); + + res.json({ secretKey: secretKey }) + }) + + + // Get all the username Password +app.get('/test/getAllUserNamePassword', (req, res) => { + + userNamePasswordCollection.find({}).toArray(function(err, result) { + if (err) throw err; + console.log(result); + res.sendStatus(200); + }); + } + ); + + + + // Register +app.post('/test/register', (req, res) => { + var { username, password } = req.body; + console.log(username + " as username and password " + password); + let errors = []; + + userNamePasswordCollection.findOne({ username: username }).then(user => { + if (user) { + errors.push({ msg: 'UserName already exists' }); + console.log("UserName already exists"); + res.status(200).send("UserName already exists"); + } else { + bcrypt.genSalt(10, (err, salt) => { + bcrypt.hash(password, salt, (err, hash) => { + if (err) throw err; + password = hash; + userNamePasswordCollection.insertOne({username: username, password: password}) + .then(user => { + console.log("You are now registered and can log in"); + //res.redirect('/users/login'); + res.sendStatus(200); + }) + .catch(err => { + console.log(err); + res.sendStatus(500); + }); + }); + }); + } + }); + } + ); + + + + + +// Generate the key and persist in hashmap +app.post('/test/login', verifyToken, (req, res) => { + // Authenticate User + //res.status(500).send('The email is not registered'); + //console.log( req.headers ); + //console.log( req.body ); + + const username = req.body.username; + const password = req.body.password; + console.log("username is " + username + " and password is " + password); + var secretKey; + + // Match user + userNamePasswordCollection.findOne({ + username: username + }).then(user => { + if (!user) { + console.log("That email is not registered"); + res.status(500).send('The email is not registered'); + } else { + // Match password + bcrypt.compare(password, user.password, (err, isMatch) => { + if (err) throw err; + if (isMatch) { + console.log("Password is matched and user can login"); + secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); + console.log(secretKey); + keyMap.set(username, secretKey); + + userNameSecretKeyCollection.replaceOne( + { userName: username }, + { userName: username, secretKey: secretKey }, + { upsert: true } + ).then( () => { + console.log("1 document inserted on api /test/login"); + res.json({ secretKey: secretKey }); + }).catch( ( e ) => { + console.log( "err while generate secretKey on api /test/login" ); + console.log( e ); + }); + } else { + console.log("Password incorrect"); + } + }); + } + }); + + + }) + + + +// Authenticate the JWT and verify that if it is tampered or not +// FORMATE OF TOKEN +// Authorization : Bearer +// Verify Token +function verifyToken(req, res, next) { + // check if the secretKey is generated by server + // check if the request include jws in http header authroization + const authHeader = req.headers['authorization'] + const token = authHeader && authHeader.split(' ')[1] + if (token == null) return res.sendStatus(401) + console.log("incoming token payload : " + token); + + let secretKey =''; + if (req.body.secretKey){ + secretKey = req.body.secretKey; + jwt.verify(token, secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + } else { + let payload = token.split('.')[1]; + let buff = new Buffer(payload, 'base64'); + let payLoadJson = JSON.parse(buff.toString('ascii')); + let userNameFromPayload = payLoadJson.name; + secretKey = keyMap.get(userNameFromPayload); + + + userNameSecretKeyCollection.find({}).toArray(function(err, result) { + if (err) throw err; + console.log(result); + }); + + userNameSecretKeyCollection.findOne( + { userName: userNameFromPayload } + ).then((documentRecord) => { + console.log("userName in payload in verify : " + documentRecord.userName); + console.log("secretKey in mongoDb : " + documentRecord.secretKey); + jwt.verify(token, documentRecord.secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + }).catch( (e) => { + console.log( "look up document by useName in verify" ); + console.log( e ); + }); + } + + } + + + + +module.exports = app; + diff --git a/server.js b/server.js index 453b166..9661e98 100644 --- a/server.js +++ b/server.js @@ -73,6 +73,8 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). const authenticationController = require('./controllers/authentication'); + const users = require('./controllers/users'); + /** @@ -164,6 +166,13 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). bodyParser.urlencoded({extended:false, limit: '10kb'}), smtpController.sendMailPOST); + /** + * Users related handlers such as register, login and verification + */ + + + + /** * Error Handler. */ From 1683bbd74f2f86ebf4e19a8715acaa65988bf6f0 Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Thu, 16 Jul 2020 11:02:30 -0400 Subject: [PATCH 4/8] add users router --- controllers/users.js | 94 ++++---------------------------------------- package.json | 1 + server.js | 15 +++++-- 3 files changed, 21 insertions(+), 89 deletions(-) diff --git a/controllers/users.js b/controllers/users.js index 9fe6937..88c21ac 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -1,89 +1,18 @@ /** * Module dependencies. */ - - const express = require('express'); // HTTP server -const compression = require('compression'); // gzip for the HTTP body -const cors = require('cors'); // CORS - -const logger = require('morgan'); // HTTP request logger const bcrypt = require('bcryptjs'); -const expressStatusMonitor = require('express-status-monitor'); // Monitor of the service (CPU/Mem,....) -const errorHandler = require('errorhandler'); -const dotenv = require('dotenv'); // Application configuration -const path = require('path'); -const chalk = require('chalk'); // To color message in console log const jwt = require('jsonwebtoken') -const passport = require('passport'); // Authentication - - -const bodyParser = require('body-parser'); -//const { generateKeyPair } = require('crypto'); const crypto = require('crypto'); -const util = require('util'); - -const MongoClient = require('mongodb').MongoClient; - -const processEnv = process.env; /** * Create Express server. */ -const app = express(); - - -/** - * Load environment variables from .env file, where API keys and passwords are configured. - */ -dotenv.config({ - path: '.env' -}); - -const _corsSettings = JSON.parse(processEnv.cors || '{"optionSucessStatus":200}'); // Parse CORS settings - - -/** - * Connect to MongoDB. - */ +const usersRouter = express(); -MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ).then( ( mongoInstance ) => { - - var dbConn = mongoInstance.db( processEnv.MONGODB_NAME || 'sandbox' ); - var userNameSecretKeyCollection = dbConn.collection("userNameSecretKey"); - var userNamePasswordCollection = dbConn.collection("userNamePassword"); - userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); - //userNamePasswordCollection.createIndex( { "userName": 1 }, { unique: true } ); -// - - - -/** - * Express configuration. - */ -app.set('host', processEnv.Host || '0.0.0.0'); -app.set('port', processEnv.Port || 8080); - -//app.use(compression()); // Compression not recommended -app.use(logger( processEnv.LOG_FORMAT || 'dev')); - -app.use(bodyParser.json()); // for parsing application/json - -app.disable('x-powered-by'); - - - - - -/** - * Middleware to enable cors - */ -app.use( cors( { "origin": "*" } ) ); - - - - // List mailing for the user -app.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), +// List mailing for the user +usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), verifyToken, ( req, res ) => { const user = req.user; res.json( { @@ -99,7 +28,7 @@ app.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), // Generate the secret key let keyMap = new Map() const NO_USER = "noUser"; -app.post('/test/getSecretKey', (req, res) => { +usersRouter.post('/getSecretKey', (req, res) => { const secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); console.log(secretKey); // first loading to get secret key, there is no way to get to know the user info @@ -122,7 +51,7 @@ app.post('/test/getSecretKey', (req, res) => { // Get all the username Password -app.get('/test/getAllUserNamePassword', (req, res) => { +usersRouter.get('/getAllUserNamePassword', (req, res) => { userNamePasswordCollection.find({}).toArray(function(err, result) { if (err) throw err; @@ -133,9 +62,8 @@ app.get('/test/getAllUserNamePassword', (req, res) => { ); - // Register -app.post('/test/register', (req, res) => { +usersRouter.post('/register', (req, res) => { var { username, password } = req.body; console.log(username + " as username and password " + password); let errors = []; @@ -168,11 +96,8 @@ app.post('/test/register', (req, res) => { ); - - - // Generate the key and persist in hashmap -app.post('/test/login', verifyToken, (req, res) => { +usersRouter.post('/login', verifyToken, (req, res) => { // Authenticate User //res.status(500).send('The email is not registered'); //console.log( req.headers ); @@ -282,8 +207,5 @@ function verifyToken(req, res, next) { } - - - -module.exports = app; +module.exports = usersRouter; diff --git a/package.json b/package.json index a5da590..765172f 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "aws-sdk": "^2.659.0", + "bcryptjs": "^2.4.3", "chalk": "^3.0.0", "compression": "^1.7.4", "cors": "^2.8.5", diff --git a/server.js b/server.js index 9661e98..8a3fb27 100644 --- a/server.js +++ b/server.js @@ -64,6 +64,12 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). //app.emit('ready'); + var dbConn = mongoInstance.db( processEnv.MONGODB_NAME || 'sandbox' ); + var userNameSecretKeyCollection = dbConn.collection("userNameSecretKey"); + var userNamePasswordCollection = dbConn.collection("userNamePassword"); + userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); + + /** * Controllers (route handlers). */ @@ -86,11 +92,14 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). app.use(expressStatusMonitor( { path: processEnv.ServerStatusPath || "/admin/sys-status" } )); app.use(compression()); app.use(logger( processEnv.LOG_FORMAT || 'dev')); - app.use(bodyParser.json()); // for parsing application/json - app.disable('x-powered-by'); + + /** + * Middleware to enable cors + */ + app.use( cors( { "origin": "*" } ) ); /** * Subscriber routes. @@ -170,9 +179,9 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). * Users related handlers such as register, login and verification */ + app.use('/api/v0.1/users', require('./routes/users.js')); - /** * Error Handler. */ From 3142e3bbc84db24bca467d12d7fff41b1def6d5e Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Thu, 16 Jul 2020 16:47:20 -0400 Subject: [PATCH 5/8] add users related routers --- controllers/users.js | 38 ++++++++++++++++++++++---------------- server.js | 20 +++++++++----------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/controllers/users.js b/controllers/users.js index 88c21ac..de0b155 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -3,27 +3,20 @@ */ const express = require('express'); // HTTP server const bcrypt = require('bcryptjs'); -const jwt = require('jsonwebtoken') +const jwt = require('jsonwebtoken'); const crypto = require('crypto'); +const userNameSecretKeyCollection = module.parent.exports.userNameSecretKeyCollection; + +const userNamePasswordCollection = module.parent.exports.userNamePasswordCollection; + +userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); + /** * Create Express server. */ const usersRouter = express(); -// List mailing for the user -usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), - verifyToken, ( req, res ) => { - const user = req.user; - res.json( { - id: "uid-33", - created: "2020-06-16", - updated: "2020-06-16", - title: "Mailing Title", - user - } ); - -}); // Generate the secret key let keyMap = new Map() @@ -50,7 +43,7 @@ usersRouter.post('/getSecretKey', (req, res) => { }) - // Get all the username Password +// Get all the username Password usersRouter.get('/getAllUserNamePassword', (req, res) => { userNamePasswordCollection.find({}).toArray(function(err, result) { @@ -62,7 +55,7 @@ usersRouter.get('/getAllUserNamePassword', (req, res) => { ); - // Register +// Register usersRouter.post('/register', (req, res) => { var { username, password } = req.body; console.log(username + " as username and password " + password); @@ -147,6 +140,19 @@ usersRouter.post('/login', verifyToken, (req, res) => { }) +// List mailing for the user +usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), + verifyToken, ( req, res ) => { + const user = req.user; + res.json( { + id: "uid-33", + created: "2020-06-16", + updated: "2020-06-16", + title: "Mailing Title", + user + } ); + +}); // Authenticate the JWT and verify that if it is tampered or not // FORMATE OF TOKEN diff --git a/server.js b/server.js index 8a3fb27..b553b25 100644 --- a/server.js +++ b/server.js @@ -19,7 +19,8 @@ const passport = require('passport'); // Authentication const BasicStrategy = require('passport-http').BasicStrategy; const bodyParser = require('body-parser'); -//const crypto = require('crypto'); // To encrypt Notify keys + +//const bcrypt = require('bcryptjs'); const MongoClient = require('mongodb').MongoClient; @@ -59,15 +60,12 @@ const app = express(); */ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ).then( ( mongoInstance ) => { + var dbConnInstance = mongoInstance.db( processEnv.MONGODB_NAME || 'subs' );; - module.exports.dbConn = mongoInstance.db( processEnv.MONGODB_NAME || 'subs' ); - //app.emit('ready'); - - - var dbConn = mongoInstance.db( processEnv.MONGODB_NAME || 'sandbox' ); - var userNameSecretKeyCollection = dbConn.collection("userNameSecretKey"); - var userNamePasswordCollection = dbConn.collection("userNamePassword"); - userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); + module.exports.dbConn = dbConnInstance; + module.exports.userNameSecretKeyCollection = dbConnInstance.collection("userNameSecretKey"); + module.exports.userNamePasswordCollection = dbConnInstance.collection("userNamePassword"); + /** @@ -79,7 +77,7 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). const authenticationController = require('./controllers/authentication'); - const users = require('./controllers/users'); + //const users = require('./controllers/users'); @@ -179,7 +177,7 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). * Users related handlers such as register, login and verification */ - app.use('/api/v0.1/users', require('./routes/users.js')); + app.use('/api/v0.1/users', require('./controllers/users.js')); /** From 7378ed786d99e32fdffce282291d92b4f285a2a1 Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Thu, 16 Jul 2020 17:55:28 -0400 Subject: [PATCH 6/8] export endpoints --- controllers/users.js | 107 ++++-------------- controllers/usersBackup.js | 218 +++++++++++++++++++++++++++++++++++++ server.js | 72 +++++++++++- 3 files changed, 306 insertions(+), 91 deletions(-) create mode 100644 controllers/usersBackup.js diff --git a/controllers/users.js b/controllers/users.js index de0b155..397271a 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -12,16 +12,12 @@ const userNamePasswordCollection = module.parent.exports.userNamePasswordCollect userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); -/** - * Create Express server. - */ -const usersRouter = express(); - - -// Generate the secret key let keyMap = new Map() const NO_USER = "noUser"; -usersRouter.post('/getSecretKey', (req, res) => { + + +exports.getSecretKey = ( req, res, next ) => { + const secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); console.log(secretKey); // first loading to get secret key, there is no way to get to know the user info @@ -40,23 +36,23 @@ usersRouter.post('/getSecretKey', (req, res) => { }); res.json({ secretKey: secretKey }) - }) +}; // Get all the username Password -usersRouter.get('/getAllUserNamePassword', (req, res) => { - +exports.getAllUserNamePassword = ( req, res, next ) => { + userNamePasswordCollection.find({}).toArray(function(err, result) { if (err) throw err; console.log(result); res.sendStatus(200); }); - } - ); - +}; // Register -usersRouter.post('/register', (req, res) => { +exports.register = ( req, res, next ) => { + + var { username, password } = req.body; console.log(username + " as username and password " + password); let errors = []; @@ -85,12 +81,12 @@ usersRouter.post('/register', (req, res) => { }); } }); - } - ); - +}; // Generate the key and persist in hashmap -usersRouter.post('/login', verifyToken, (req, res) => { +exports.login = ( req, res, next ) => { + + // Authenticate User //res.status(500).send('The email is not registered'); //console.log( req.headers ); @@ -135,14 +131,11 @@ usersRouter.post('/login', verifyToken, (req, res) => { }); } }); - - - }) - +}; // List mailing for the user -usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), - verifyToken, ( req, res ) => { +exports.getMailingByTopicId = ( req, res, next ) => { + const user = req.user; res.json( { id: "uid-33", @@ -151,67 +144,5 @@ usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), title: "Mailing Title", user } ); - -}); - -// Authenticate the JWT and verify that if it is tampered or not -// FORMATE OF TOKEN -// Authorization : Bearer -// Verify Token -function verifyToken(req, res, next) { - // check if the secretKey is generated by server - // check if the request include jws in http header authroization - const authHeader = req.headers['authorization'] - const token = authHeader && authHeader.split(' ')[1] - if (token == null) return res.sendStatus(401) - console.log("incoming token payload : " + token); - - let secretKey =''; - if (req.body.secretKey){ - secretKey = req.body.secretKey; - jwt.verify(token, secretKey, (err, decoded) => { - console.log(err) - if (err) return res.sendStatus(403) - console.log("decoded payload : " + decoded.name); - console.log("decoded payload : " + decoded.sub); - console.log("decoded payload : " + decoded.iat); - req.user = decoded - next() - }) - } else { - let payload = token.split('.')[1]; - let buff = new Buffer(payload, 'base64'); - let payLoadJson = JSON.parse(buff.toString('ascii')); - let userNameFromPayload = payLoadJson.name; - secretKey = keyMap.get(userNameFromPayload); - - - userNameSecretKeyCollection.find({}).toArray(function(err, result) { - if (err) throw err; - console.log(result); - }); - - userNameSecretKeyCollection.findOne( - { userName: userNameFromPayload } - ).then((documentRecord) => { - console.log("userName in payload in verify : " + documentRecord.userName); - console.log("secretKey in mongoDb : " + documentRecord.secretKey); - jwt.verify(token, documentRecord.secretKey, (err, decoded) => { - console.log(err) - if (err) return res.sendStatus(403) - console.log("decoded payload : " + decoded.name); - console.log("decoded payload : " + decoded.sub); - console.log("decoded payload : " + decoded.iat); - req.user = decoded - next() - }) - }).catch( (e) => { - console.log( "look up document by useName in verify" ); - console.log( e ); - }); - } - - } - -module.exports = usersRouter; +}; diff --git a/controllers/usersBackup.js b/controllers/usersBackup.js new file mode 100644 index 0000000..8e791e1 --- /dev/null +++ b/controllers/usersBackup.js @@ -0,0 +1,218 @@ +/** + * Module dependencies. + */ +const express = require('express'); // HTTP server +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const crypto = require('crypto'); + +const userNameSecretKeyCollection = module.parent.exports.userNameSecretKeyCollection; + +const userNamePasswordCollection = module.parent.exports.userNamePasswordCollection; + +userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); + +/** + * Create Express server. + */ +const usersRouter = express(); + + +// Generate the secret key +let keyMap = new Map() +const NO_USER = "noUser"; +usersRouter.post('/getSecretKey', (req, res) => { + const secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); + console.log(secretKey); + // first loading to get secret key, there is no way to get to know the user info + keyMap.set(NO_USER, secretKey); + + + userNameSecretKeyCollection.replaceOne( + { userName: NO_USER }, + { userName: NO_USER, secretKey: secretKey }, + { upsert : true} + ).then( () => { + console.log("1 document inserted on api /test/getSecretKey "); + }).catch( ( e ) => { + console.log( "err while generate secretKey on api /test/getSecretKey" ); + console.log( e ); + }); + + res.json({ secretKey: secretKey }) + }) + + + +// Get all the username Password +usersRouter.get('/getAllUserNamePassword', (req, res) => { + + userNamePasswordCollection.find({}).toArray(function(err, result) { + if (err) throw err; + console.log(result); + res.sendStatus(200); + }); + } + ); + + +// Register +usersRouter.post('/register', (req, res) => { + var { username, password } = req.body; + console.log(username + " as username and password " + password); + let errors = []; + + userNamePasswordCollection.findOne({ username: username }).then(user => { + if (user) { + errors.push({ msg: 'UserName already exists' }); + console.log("UserName already exists"); + res.status(200).send("UserName already exists"); + } else { + bcrypt.genSalt(10, (err, salt) => { + bcrypt.hash(password, salt, (err, hash) => { + if (err) throw err; + password = hash; + userNamePasswordCollection.insertOne({username: username, password: password}) + .then(user => { + console.log("You are now registered and can log in"); + //res.redirect('/users/login'); + res.sendStatus(200); + }) + .catch(err => { + console.log(err); + res.sendStatus(500); + }); + }); + }); + } + }); + } + ); + + +// Generate the key and persist in hashmap +usersRouter.post('/login', verifyToken, (req, res) => { + // Authenticate User + //res.status(500).send('The email is not registered'); + //console.log( req.headers ); + //console.log( req.body ); + + const username = req.body.username; + const password = req.body.password; + console.log("username is " + username + " and password is " + password); + var secretKey; + + // Match user + userNamePasswordCollection.findOne({ + username: username + }).then(user => { + if (!user) { + console.log("That email is not registered"); + res.status(500).send('The email is not registered'); + } else { + // Match password + bcrypt.compare(password, user.password, (err, isMatch) => { + if (err) throw err; + if (isMatch) { + console.log("Password is matched and user can login"); + secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); + console.log(secretKey); + keyMap.set(username, secretKey); + + userNameSecretKeyCollection.replaceOne( + { userName: username }, + { userName: username, secretKey: secretKey }, + { upsert: true } + ).then( () => { + console.log("1 document inserted on api /test/login"); + res.json({ secretKey: secretKey }); + }).catch( ( e ) => { + console.log( "err while generate secretKey on api /test/login" ); + console.log( e ); + }); + } else { + console.log("Password incorrect"); + } + }); + } + }); + + + }) + + +// List mailing for the user +usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), + verifyToken, ( req, res ) => { + const user = req.user; + res.json( { + id: "uid-33", + created: "2020-06-16", + updated: "2020-06-16", + title: "Mailing Title", + user + } ); + +}); + +// Authenticate the JWT and verify that if it is tampered or not +// FORMATE OF TOKEN +// Authorization : Bearer +// Verify Token +function verifyToken(req, res, next) { + // check if the secretKey is generated by server + // check if the request include jws in http header authroization + const authHeader = req.headers['authorization'] + const token = authHeader && authHeader.split(' ')[1] + if (token == null) return res.sendStatus(401) + console.log("incoming token payload : " + token); + + let secretKey =''; + if (req.body.secretKey){ + secretKey = req.body.secretKey; + jwt.verify(token, secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + } else { + let payload = token.split('.')[1]; + let buff = new Buffer(payload, 'base64'); + let payLoadJson = JSON.parse(buff.toString('ascii')); + let userNameFromPayload = payLoadJson.name; + secretKey = keyMap.get(userNameFromPayload); + + + userNameSecretKeyCollection.find({}).toArray(function(err, result) { + if (err) throw err; + console.log(result); + }); + + userNameSecretKeyCollection.findOne( + { userName: userNameFromPayload } + ).then((documentRecord) => { + console.log("userName in payload in verify : " + documentRecord.userName); + console.log("secretKey in mongoDb : " + documentRecord.secretKey); + jwt.verify(token, documentRecord.secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + }).catch( (e) => { + console.log( "look up document by useName in verify" ); + console.log( e ); + }); + } + + } + +module.exports = usersRouter; + diff --git a/server.js b/server.js index b553b25..55c5924 100644 --- a/server.js +++ b/server.js @@ -20,7 +20,7 @@ const BasicStrategy = require('passport-http').BasicStrategy; const bodyParser = require('body-parser'); -//const bcrypt = require('bcryptjs'); +const bcrypt = require('bcryptjs'); const MongoClient = require('mongodb').MongoClient; @@ -77,7 +77,7 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). const authenticationController = require('./controllers/authentication'); - //const users = require('./controllers/users'); + const usersController = require('./controllers/users'); @@ -177,8 +177,74 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). * Users related handlers such as register, login and verification */ - app.use('/api/v0.1/users', require('./controllers/users.js')); + //app.use('/api/v0.1/users', require('./controllers/users')); + app.get('/api/v0.1/users/getSecretKey', usersController.getSecretKey); + app.get('/api/v0.1/users/getAllUserNamePassword', usersController.getAllUserNamePassword); + app.post('/api/v0.1/users/register', usersController.register); + app.post('/api/v0.1/users/login', verifyToken, usersController.login); + app.get('/api/v0.1/users/mailing/create/:topicId', cors( { "origin": "*" } ), verifyToken, usersController.getMailingByTopicId); + + + +// Authenticate the JWT and verify that if it is tampered or not +// FORMATE OF TOKEN +// Authorization : Bearer +// Verify Token +function verifyToken(req, res, next) { + // check if the secretKey is generated by server + // check if the request include jws in http header authroization + const authHeader = req.headers['authorization'] + const token = authHeader && authHeader.split(' ')[1] + if (token == null) return res.sendStatus(401) + console.log("incoming token payload : " + token); + + let secretKey =''; + if (req.body.secretKey){ + secretKey = req.body.secretKey; + jwt.verify(token, secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + } else { + let payload = token.split('.')[1]; + let buff = new Buffer(payload, 'base64'); + let payLoadJson = JSON.parse(buff.toString('ascii')); + let userNameFromPayload = payLoadJson.name; + secretKey = keyMap.get(userNameFromPayload); + + + userNameSecretKeyCollection.find({}).toArray(function(err, result) { + if (err) throw err; + console.log(result); + }); + + userNameSecretKeyCollection.findOne( + { userName: userNameFromPayload } + ).then((documentRecord) => { + console.log("userName in payload in verify : " + documentRecord.userName); + console.log("secretKey in mongoDb : " + documentRecord.secretKey); + jwt.verify(token, documentRecord.secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + }).catch( (e) => { + console.log( "look up document by useName in verify" ); + console.log( e ); + }); + } + + } /** * Error Handler. From 3ca90304f6cf7e7bd0efac674fc2e0c9590c8663 Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Fri, 17 Jul 2020 11:16:08 -0400 Subject: [PATCH 7/8] implement logout function to delete secreteKey from collection --- controllers/users.js | 129 +++++++++++++++++++++++++++++++------ controllers/usersBackup.js | 108 ++++++------------------------- package.json | 1 + server.js | 70 ++------------------ 4 files changed, 134 insertions(+), 174 deletions(-) diff --git a/controllers/users.js b/controllers/users.js index 397271a..e0ce56e 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -4,6 +4,7 @@ const express = require('express'); // HTTP server const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); +const cors = require('cors'); // CORS const crypto = require('crypto'); const userNameSecretKeyCollection = module.parent.exports.userNameSecretKeyCollection; @@ -12,12 +13,18 @@ const userNamePasswordCollection = module.parent.exports.userNamePasswordCollect userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); +/** + * Create Express server. + */ +const usersRouter = express(); + + +// Generate the secret key let keyMap = new Map() const NO_USER = "noUser"; +var username; - -exports.getSecretKey = ( req, res, next ) => { - +usersRouter.get('/getSecretKey', (req, res) => { const secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); console.log(secretKey); // first loading to get secret key, there is no way to get to know the user info @@ -36,23 +43,24 @@ exports.getSecretKey = ( req, res, next ) => { }); res.json({ secretKey: secretKey }) -}; + }) + // Get all the username Password -exports.getAllUserNamePassword = ( req, res, next ) => { - +usersRouter.get('/getAllUserNamePassword', (req, res) => { + userNamePasswordCollection.find({}).toArray(function(err, result) { if (err) throw err; console.log(result); res.sendStatus(200); }); -}; + } + ); + // Register -exports.register = ( req, res, next ) => { - - +usersRouter.post('/register', (req, res) => { var { username, password } = req.body; console.log(username + " as username and password " + password); let errors = []; @@ -81,18 +89,18 @@ exports.register = ( req, res, next ) => { }); } }); -}; + } + ); + // Generate the key and persist in hashmap -exports.login = ( req, res, next ) => { - - +usersRouter.post('/login', verifyToken, (req, res) => { // Authenticate User //res.status(500).send('The email is not registered'); //console.log( req.headers ); //console.log( req.body ); - const username = req.body.username; + username = req.body.username; const password = req.body.password; console.log("username is " + username + " and password is " + password); var secretKey; @@ -131,11 +139,14 @@ exports.login = ( req, res, next ) => { }); } }); -}; -// List mailing for the user -exports.getMailingByTopicId = ( req, res, next ) => { + }) + + +// List mailing for the user +usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), + verifyToken, ( req, res ) => { const user = req.user; res.json( { id: "uid-33", @@ -144,5 +155,85 @@ exports.getMailingByTopicId = ( req, res, next ) => { title: "Mailing Title", user } ); -}; + +}); + +// Logout +usersRouter.get('/logout', (req, res) => { + req.logout(); + // delete the user related document in the collection + console.log("logout username : " + username) + var myquery = { username: username }; + userNameSecretKeyCollection.deleteOne(myquery, function(err, obj) { + if (err) throw err; + console.log("1 document deleted" + obj); + }); + + res.sendStatus(200); + + + //req.flash('success_msg', 'You are logged out'); + //res.redirect('/users/login'); + }); + +// Authenticate the JWT and verify that if it is tampered or not +// FORMATE OF TOKEN +// Authorization : Bearer +// Verify Token +function verifyToken(req, res, next) { + // check if the secretKey is generated by server + // check if the request include jws in http header authroization + const authHeader = req.headers['authorization'] + const token = authHeader && authHeader.split(' ')[1] + if (token == null) return res.sendStatus(401) + console.log("incoming token payload : " + token); + + let secretKey =''; + if (req.body.secretKey){ + secretKey = req.body.secretKey; + jwt.verify(token, secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + } else { + let payload = token.split('.')[1]; + let buff = new Buffer(payload, 'base64'); + let payLoadJson = JSON.parse(buff.toString('ascii')); + let userNameFromPayload = payLoadJson.name; + secretKey = keyMap.get(userNameFromPayload); + + + userNameSecretKeyCollection.find({}).toArray(function(err, result) { + if (err) throw err; + console.log(result); + }); + + userNameSecretKeyCollection.findOne( + { userName: userNameFromPayload } + ).then((documentRecord) => { + console.log("userName in payload in verify : " + documentRecord.userName); + console.log("secretKey in mongoDb : " + documentRecord.secretKey); + jwt.verify(token, documentRecord.secretKey, (err, decoded) => { + console.log(err) + if (err) return res.sendStatus(403) + console.log("decoded payload : " + decoded.name); + console.log("decoded payload : " + decoded.sub); + console.log("decoded payload : " + decoded.iat); + req.user = decoded + next() + }) + }).catch( (e) => { + console.log( "look up document by useName in verify" ); + console.log( e ); + }); + } + + } + +module.exports = usersRouter; diff --git a/controllers/usersBackup.js b/controllers/usersBackup.js index 8e791e1..397271a 100644 --- a/controllers/usersBackup.js +++ b/controllers/usersBackup.js @@ -12,16 +12,12 @@ const userNamePasswordCollection = module.parent.exports.userNamePasswordCollect userNameSecretKeyCollection.createIndex( { "userName": 1 }, { unique: true } ); -/** - * Create Express server. - */ -const usersRouter = express(); - - -// Generate the secret key let keyMap = new Map() const NO_USER = "noUser"; -usersRouter.post('/getSecretKey', (req, res) => { + + +exports.getSecretKey = ( req, res, next ) => { + const secretKey = crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); console.log(secretKey); // first loading to get secret key, there is no way to get to know the user info @@ -40,24 +36,23 @@ usersRouter.post('/getSecretKey', (req, res) => { }); res.json({ secretKey: secretKey }) - }) +}; - // Get all the username Password -usersRouter.get('/getAllUserNamePassword', (req, res) => { - +exports.getAllUserNamePassword = ( req, res, next ) => { + userNamePasswordCollection.find({}).toArray(function(err, result) { if (err) throw err; console.log(result); res.sendStatus(200); }); - } - ); - +}; // Register -usersRouter.post('/register', (req, res) => { +exports.register = ( req, res, next ) => { + + var { username, password } = req.body; console.log(username + " as username and password " + password); let errors = []; @@ -86,12 +81,12 @@ usersRouter.post('/register', (req, res) => { }); } }); - } - ); - +}; // Generate the key and persist in hashmap -usersRouter.post('/login', verifyToken, (req, res) => { +exports.login = ( req, res, next ) => { + + // Authenticate User //res.status(500).send('The email is not registered'); //console.log( req.headers ); @@ -136,14 +131,11 @@ usersRouter.post('/login', verifyToken, (req, res) => { }); } }); - - - }) - +}; // List mailing for the user -usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), - verifyToken, ( req, res ) => { +exports.getMailingByTopicId = ( req, res, next ) => { + const user = req.user; res.json( { id: "uid-33", @@ -152,67 +144,5 @@ usersRouter.get( '/mailing/create/:topicId', cors( { "origin": "*" } ), title: "Mailing Title", user } ); - -}); - -// Authenticate the JWT and verify that if it is tampered or not -// FORMATE OF TOKEN -// Authorization : Bearer -// Verify Token -function verifyToken(req, res, next) { - // check if the secretKey is generated by server - // check if the request include jws in http header authroization - const authHeader = req.headers['authorization'] - const token = authHeader && authHeader.split(' ')[1] - if (token == null) return res.sendStatus(401) - console.log("incoming token payload : " + token); - - let secretKey =''; - if (req.body.secretKey){ - secretKey = req.body.secretKey; - jwt.verify(token, secretKey, (err, decoded) => { - console.log(err) - if (err) return res.sendStatus(403) - console.log("decoded payload : " + decoded.name); - console.log("decoded payload : " + decoded.sub); - console.log("decoded payload : " + decoded.iat); - req.user = decoded - next() - }) - } else { - let payload = token.split('.')[1]; - let buff = new Buffer(payload, 'base64'); - let payLoadJson = JSON.parse(buff.toString('ascii')); - let userNameFromPayload = payLoadJson.name; - secretKey = keyMap.get(userNameFromPayload); - - - userNameSecretKeyCollection.find({}).toArray(function(err, result) { - if (err) throw err; - console.log(result); - }); - - userNameSecretKeyCollection.findOne( - { userName: userNameFromPayload } - ).then((documentRecord) => { - console.log("userName in payload in verify : " + documentRecord.userName); - console.log("secretKey in mongoDb : " + documentRecord.secretKey); - jwt.verify(token, documentRecord.secretKey, (err, decoded) => { - console.log(err) - if (err) return res.sendStatus(403) - console.log("decoded payload : " + decoded.name); - console.log("decoded payload : " + decoded.sub); - console.log("decoded payload : " + decoded.iat); - req.user = decoded - next() - }) - }).catch( (e) => { - console.log( "look up document by useName in verify" ); - console.log( e ); - }); - } - - } - -module.exports = usersRouter; +}; diff --git a/package.json b/package.json index 765172f..71cd12a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "errorhandler": "^1.5.1", "express": "^4.17.1", "express-status-monitor": "^1.2.8", + "jsonwebtoken": "^8.5.1", "mongodb": "^3.5.5", "morgan": "^1.9.1", "mustache": "^4.0.1", diff --git a/server.js b/server.js index 55c5924..ca8b03a 100644 --- a/server.js +++ b/server.js @@ -20,8 +20,6 @@ const BasicStrategy = require('passport-http').BasicStrategy; const bodyParser = require('body-parser'); -const bcrypt = require('bcryptjs'); - const MongoClient = require('mongodb').MongoClient; const processEnv = process.env; @@ -54,7 +52,6 @@ passport.use(new BasicStrategy({ qop: 'auth' }, const app = express(); - /** * Connect to MongoDB. */ @@ -77,7 +74,7 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). const authenticationController = require('./controllers/authentication'); - const usersController = require('./controllers/users'); + //const usersController = require('./controllers/users'); @@ -177,75 +174,16 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {useUnifiedTopology: true} ). * Users related handlers such as register, login and verification */ - //app.use('/api/v0.1/users', require('./controllers/users')); + app.use('/api/v0.1/users', require('./controllers/users')); - app.get('/api/v0.1/users/getSecretKey', usersController.getSecretKey); + /*app.get('/api/v0.1/users/getSecretKey', usersController.getSecretKey); app.get('/api/v0.1/users/getAllUserNamePassword', usersController.getAllUserNamePassword); app.post('/api/v0.1/users/register', usersController.register); app.post('/api/v0.1/users/login', verifyToken, usersController.login); app.get('/api/v0.1/users/mailing/create/:topicId', cors( { "origin": "*" } ), verifyToken, usersController.getMailingByTopicId); + */ - -// Authenticate the JWT and verify that if it is tampered or not -// FORMATE OF TOKEN -// Authorization : Bearer -// Verify Token -function verifyToken(req, res, next) { - // check if the secretKey is generated by server - // check if the request include jws in http header authroization - const authHeader = req.headers['authorization'] - const token = authHeader && authHeader.split(' ')[1] - if (token == null) return res.sendStatus(401) - console.log("incoming token payload : " + token); - - let secretKey =''; - if (req.body.secretKey){ - secretKey = req.body.secretKey; - jwt.verify(token, secretKey, (err, decoded) => { - console.log(err) - if (err) return res.sendStatus(403) - console.log("decoded payload : " + decoded.name); - console.log("decoded payload : " + decoded.sub); - console.log("decoded payload : " + decoded.iat); - req.user = decoded - next() - }) - } else { - let payload = token.split('.')[1]; - let buff = new Buffer(payload, 'base64'); - let payLoadJson = JSON.parse(buff.toString('ascii')); - let userNameFromPayload = payLoadJson.name; - secretKey = keyMap.get(userNameFromPayload); - - - userNameSecretKeyCollection.find({}).toArray(function(err, result) { - if (err) throw err; - console.log(result); - }); - - userNameSecretKeyCollection.findOne( - { userName: userNameFromPayload } - ).then((documentRecord) => { - console.log("userName in payload in verify : " + documentRecord.userName); - console.log("secretKey in mongoDb : " + documentRecord.secretKey); - jwt.verify(token, documentRecord.secretKey, (err, decoded) => { - console.log(err) - if (err) return res.sendStatus(403) - console.log("decoded payload : " + decoded.name); - console.log("decoded payload : " + decoded.sub); - console.log("decoded payload : " + decoded.iat); - req.user = decoded - next() - }) - }).catch( (e) => { - console.log( "look up document by useName in verify" ); - console.log( e ); - }); - } - - } - /** * Error Handler. */ From 503add47e9e1b797295187ff885c960f89877c6c Mon Sep 17 00:00:00 2001 From: Weiquan Yuan Date: Fri, 17 Jul 2020 11:28:17 -0400 Subject: [PATCH 8/8] clean up --- controllers/users.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/controllers/users.js b/controllers/users.js index e0ce56e..0c956db 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -171,9 +171,6 @@ usersRouter.get('/logout', (req, res) => { res.sendStatus(200); - - //req.flash('success_msg', 'You are logged out'); - //res.redirect('/users/login'); }); // Authenticate the JWT and verify that if it is tampered or not