From 37e7d757c3bd00a3fdc1a73b91f80826617eb246 Mon Sep 17 00:00:00 2001 From: Erik Roemmelt Date: Tue, 18 Jun 2019 15:47:42 +0200 Subject: [PATCH 01/16] moved db-model-schemata, adjust user-model --- message.model.js => database/message.model.js | 0 tag.model.js => database/tag.model.js | 0 database/user.model.js | 21 +++++++++++++++++++ user.model.js | 14 ------------- 4 files changed, 21 insertions(+), 14 deletions(-) rename message.model.js => database/message.model.js (100%) rename tag.model.js => database/tag.model.js (100%) create mode 100644 database/user.model.js delete mode 100644 user.model.js diff --git a/message.model.js b/database/message.model.js similarity index 100% rename from message.model.js rename to database/message.model.js diff --git a/tag.model.js b/database/tag.model.js similarity index 100% rename from tag.model.js rename to database/tag.model.js diff --git a/database/user.model.js b/database/user.model.js new file mode 100644 index 0000000..632ccf0 --- /dev/null +++ b/database/user.model.js @@ -0,0 +1,21 @@ +const mongoose = require('mongoose'); + + +const UserSchema = mongoose.Schema({ +// _id: { type: String }, + name: { type: String, required: true }, + pwd: { type: String }, +// hash: { type: String }, +// salt: { type: String }, +// type: { type: String }, + roles: { type: [String], required: true }, + tags: { type: [String] }, +// deactivated: { type: Boolean }, +// participating: { type: [String] }, +// host: { type: Boolean }, + bookmarks: { type: [String] }, +}); +//tags as index: +//UserSchema.index({tags:'text'}); + +module.exports = mongoose.model('User', UserSchema); diff --git a/user.model.js b/user.model.js deleted file mode 100644 index c5115e7..0000000 --- a/user.model.js +++ /dev/null @@ -1,14 +0,0 @@ -const mongoose = require('mongoose'); - - -const UserSchema = mongoose.Schema({ - name: { type: String, required: true }, - password: {type: String}, password: {type: String}, - - role: {type: String, required: true}, - tags: [{type: String }], -}); -//tags as index: -//UserSchema.index({tags:'text'}); - -module.exports = mongoose.model('User', UserSchema); From eb89e2c3bf36192b28215f1b6719ad1b7aa009f0 Mon Sep 17 00:00:00 2001 From: Erik Roemmelt Date: Tue, 18 Jun 2019 15:52:03 +0200 Subject: [PATCH 02/16] renaming library (adding version nr.) --- public/lib/{vue.js => vue-2.6.10.js} | 0 public/lib/{vue-router.js => vue-router-3.0.1.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename public/lib/{vue.js => vue-2.6.10.js} (100%) rename public/lib/{vue-router.js => vue-router-3.0.1.js} (100%) diff --git a/public/lib/vue.js b/public/lib/vue-2.6.10.js similarity index 100% rename from public/lib/vue.js rename to public/lib/vue-2.6.10.js diff --git a/public/lib/vue-router.js b/public/lib/vue-router-3.0.1.js similarity index 100% rename from public/lib/vue-router.js rename to public/lib/vue-router-3.0.1.js From 4fe1bbd0e5f9c7b24feadc0d70e388ea9c46ecc7 Mon Sep 17 00:00:00 2001 From: Erik Roemmelt Date: Tue, 18 Jun 2019 15:52:34 +0200 Subject: [PATCH 03/16] renaming library includes --- public/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index 3dc8dd6..8861930 100644 --- a/public/index.html +++ b/public/index.html @@ -58,8 +58,8 @@ - - + + From 5c26a11023e0b75235d2b21a9546374eeb2f5d98 Mon Sep 17 00:00:00 2001 From: Erik Roemmelt Date: Tue, 18 Jun 2019 15:54:07 +0200 Subject: [PATCH 04/16] restructure server.js, adding ldap access, part 1/2 role authorization --- server.js | 268 +++++++++++++++++++++------------------- server/authorization.js | 119 ++++++++++++++++++ server/common.js | 78 ++++++++++++ server/ldap_ohm.js | 104 ++++++++++++++++ server/ldap_test.js | 35 ++++++ server_config.json | 9 ++ 6 files changed, 485 insertions(+), 128 deletions(-) create mode 100644 server/authorization.js create mode 100644 server/common.js create mode 100644 server/ldap_ohm.js create mode 100644 server/ldap_test.js create mode 100644 server_config.json diff --git a/server.js b/server.js index e90218b..c424653 100644 --- a/server.js +++ b/server.js @@ -3,105 +3,162 @@ * * Requires express >= 4 */ - +var common = require ('./server/common'), + authorize = require ('./server/authorization'); /* -var common = require ('./server/common'), - authorize = require ('./server/authorization'), dbs = require ('./server/dbs'), files = require ('./server/files'); */ -var fs = require ('fs'), - http = require ('http'), - https = require ('https'), - express = require ('express'), - session = require ('express-session'), // session management - morgan = require ('morgan'), // logger +const fs = common.fs, // file sync, read certificates + http = common.http, // http handler + https = require ('https'), // https handler + express = require ('express'), // node server framework + session = require ('express-session'), // session management (security) + morgan = require ('morgan'), // logger //serveFavicon = require ('serve-favicon'), - bodyParser = require ('body-parser'); - //MongoStore = require ('connect-mongo')(session); // uss mongodb as session storage -var Message = require('./message.model.js'); + bodyParser = require ('body-parser'), // post request bodyparser + MongoStore = require ('connect-mongo')(session), // use mongodb as session storage + Message = require('./database/message.model.js'); var app = express(); -var http_port=8013; - https_port=8889; - /* * Init */ -/*ll common .init (); authorize.init (common); -dbs .init (common); -files .init (common); -*/ +//dbs .init (common); +//files .init (common); -// Security -app.disable ('x-powered-by'); // TODO: Disable Header information: Powerd by Express -> Information disclosure + +/* + * Security + * + * TODO: Install helmet + * https://expressjs.com/de/advanced/best-practice-security.html + * + * (Disable Header information: Powerd by Express) + * -> Information disclosure + */ +app.disable ('x-powered-by'); + +// Session Management +app.set('trust proxy', 1) // trust first proxy, neccessary for cookie secure: true flag +app.use (session({ + secret: 'ahhgylhuvh', // caesar(3) 2 letter surname + resave: false, + saveUninitialized: false, + cookie: { + maxAge: 30*24*3600*1000, // TODO: ttl for session as well (Store) + secure: true, // true for https only (since our app works only with https) + }, + name: 'om.sid', + store: new MongoStore ({mongooseConnection: common.mongoose.connection, ttl: 30*24*3600}), // mongoose + connect-mongo + //store: new MemoryStore ({checkPeriod: 24*3600*1000}), // memorystore +})); /* * Route Control */ -// Logger -app.use (morgan ('dev')); -//app.use(express.logger ( { format: 'default', stream: output_stream } )); - // Fastpaths //app.use (serveFavicon (__dirname + '/public/favicon.ico')); -// Session Management -app.use (session({ - secret: 'adluhohks', - resave: false, - saveUninitialized: false, - cookie: { - maxAge: 30*24*3600*1000, // TODO: ttl for session as well (Store) - secure: false, // true for https only - }, - name: 'om.sid', - //store: new MongoStore ({mongooseConnection: dbs.mongoose.connection, ttl: 30*24*3600}), // mongoose + connect-mongo - //store: new MemoryStore ({checkPeriod: 24*3600*1000}), // memorystore -})); -// Args +// Minimal Logging +//app.use (morgan ('dev')); +// Advanced Logging +morgan.token ('user', function (req, res) { return (req.session && req.session.user) || '-'; }); +morgan.token ('userColored', function (req, res) { + var color = 0; + if (req.session && req.session.roles) + color = req.session.roles.admin ? 31 // red + : req.session.roles.user ? 34 // blue + : 0; // no color + return '\x1b[' + color + 'm' + ((req.session && req.session.user) || '-') + '\x1b[0m'; +}); +morgan.token ('statusColored', function (req, res) { + var color = res.statusCode >= 500 ? 31 // red + : res.statusCode >= 400 ? 33 // yellow + : res.statusCode >= 300 ? 36 // cyan + : res.statusCode >= 200 ? 32 // green + : 0; // no color + return '\x1b[' + color + 'm' + (res.headersSent ? res.statusCode : '-') + '\x1b[0m'; +}); +app.use (morgan (':date[iso] :statusColored :method :url :userColored :response-time ms :res[content-length]')); + +// BodyParser +// Returns middleware that only parses json bodies. +// (https://www.npmjs.com/package/body-parser#bodyparserjsonoptions) app.use (bodyParser.json()); +// Returns middleware that only parses urlencoded bodies +// with qs library (https://www.npmjs.com/package/qs#readme) app.use (bodyParser.urlencoded({extended: true})); // API -//var api_routes = express.Router(); // express app-object routing -//app.use ('/api', api_routes); +var api_routes = express.Router(); // express app-object routing -app.use (function (req, res, done) { - console.log(req.url); - done(); -}); - -//global.__basedir = __dirname; +app.use ('/api', api_routes); // Static Files -app.use(express.static(__dirname + '/public')); // Allow server access to 'public' folder +// Allow server access to 'public' folder +app.use(express.static(__dirname + '/public')); -//app.use(express.static('resources')); +// Other stuff is NOT authorized unless logged in +//app.use (authorize.genCheckAuthorized ('user')); + +// Uploaded files +//app.use ('/uploads', express.static(__dirname + '/uploads')); // Configuring the database -var dbConfig = require('./mongodb.config.js'); -var mongoose = require('mongoose'); +//var dbConfig = require('./mongodb.config.js'); -mongoose.Promise = global.Promise; +common.mongoose.Promise = global.Promise; // Connecting to the database -//mongoose.connect(`mongodb://${server}/${dbConfig.url}`) -mongoose.connect(dbConfig.url, {useNewUrlParser: true}).then(() => { +// Local db: common.config.dbLocalConn +// Efi db: common.config.dbConn +common.mongoose.connect (common.config.dbLocalConn, {useNewUrlParser: true}) .then( () => { console.log("Successfully connected to MongoDB."); -}).catch(err => { +}).catch( err => { console.log('Could not connect to MongoDB.'); process.exit(); }); +// No error so far? Then it's a 404! +//app.use (function (req, res, next) { next (common.genError (404, req.url)); }); +//app.use (routes.errorHandler (true)); /* true: show stack traces */ -//require('./app/routes/message.route.js')(app); + +/* + * API + */ +/* +// API allowed for all +api_routes.post ('/login', authorize.login); // /api/login + +// Validate all other API calls +api_routes.use (authorize.genCheckAuthorized ('user')); +api_routes.post ('/logout', authorize.logout); + +function addRoutes (r) { + for (var e in r.routes) { + var params = r.routes[e].params ? "/" + r.routes[e].params : ""; + console.log ("Adding routes for /" + e + params + ":" + + (r.routes[e].get ? " get":" ") + (r.routes[e].post ? " post":" ") + + (r.routes[e].put ? " put":" ") + (r.routes[e].delete ? " delete":" ")); + if (r.routes[e].get) + api_routes.get ('/' + e + params, r.routes[e].get); + if (r.routes[e].post) + api_routes.post ('/' + e + params, r.routes[e].post); + if (r.routes[e].put) + api_routes.put ('/' + e + params, r.routes[e].put); + if (r.routes[e].delete) + api_routes.delete ('/' + e + params, r.routes[e].delete); + } +} +*/ app.get ('/api/ids', function (req, res) { Message.find({},{id: true}) .exec () .then(results => { @@ -169,62 +226,17 @@ app.post("/api/createMsg", function(req, res){ }); }); -// Other stuff is NOT authorized unless logged in -//app.use (authorize.genCheckAuthorized ('user')); - -// Uploaded files -//app.use ('/uploads', expr ess.static(__dirname + '/uploads')); - -// Other stuff is NOT authorized unless logged in -//app.use (authorize.genCheckAuthorized ('user')); - -// Uploaded files -//app.use ('/uploads', express.static(__dirname + '/uploads')); - -// Errors -// No error so far? Then it's a 404! -//app.use (function (req, res, next) { next (common.genError (404, req.url)); }); -//app.use (routes.errorHandler (true)); /* true: show stack traces */ // TODO: Error Handler - - /* - * API - */ -/* -// API allowed for all -api_routes.post ('/login', authorize.login); // /api/login - -// Validate all other API calls -api_routes.use (authorize.genCheckAuthorized ('user')); -api_routes.post ('/logout', authorize.logout); - -function addRoutes (r) { - for (var e in r.routes) { - var params = r.routes[e].params ? "/" + r.routes[e].params : ""; - console.log ("Adding routes for /" + e + params + ":" + - (r.routes[e].get ? " get":" ") + (r.routes[e].post ? " post":" ") + - (r.routes[e].put ? " put":" ") + (r.routes[e].delete ? " delete":" ")); - if (r.routes[e].get) - api_routes.get ('/' + e + params, r.routes[e].get); - if (r.routes[e].post) - api_routes.post ('/' + e + params, r.routes[e].post); - if (r.routes[e].put) - api_routes.put ('/' + e + params, r.routes[e].put); - if (r.routes[e].delete) - api_routes.delete ('/' + e + params, r.routes[e].delete); - } -} - addRoutes (dbs); +addRoutes (admin); addRoutes (files); */ /* * Servers */ - -http.createServer (app) .listen (http_port, function () { - console.log ("Express http server listening on port " + http_port); +http.createServer (app) .listen (common.config.httpPort, function () { + console.log ("Express http server listening on port " + common.config.httpPort); }); /* @@ -238,36 +250,36 @@ http.createServer (app) .listen (http_port, function () { * openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem * rm certrequest.csr */ - -var options; -try { - try { - // In case it's a real certificate: add CA chain cersts (TODO: use array if required) - var ca = fs.readFileSync ('keys/ca_cert.pem'); - } catch (e) { - ca = undefined; - console.log ("Note: Can't read CA bundle: "+e); - } - if (ca != null) { - - options = { - key: fs.readFileSync ('keys/omkey.pem'), - cert: fs.readFileSync ('keys/certificate.pem'), - ca: ca - }; - https.createServer (options, app) .listen (https_port, function () { - console.log ("Express https server listening on port " + https_port); - }); +if (common.config.httpsPort) { + var options; + try { + try { + // In case it's a real certificate: add CA chain cersts (TODO: use array if required) + var ca = fs.readFileSync ('keys/ca_cert.pem'); + } catch (e) { + ca = undefined; + console.log ("Note: Can't read CA bundle: "+e); + } + if (ca != undefined) { + options = { + key: fs.readFileSync ('keys/omkey.pem'), + cert: fs.readFileSync ('keys/certificate.pem'), + ca: ca + }; + https.createServer (options, app) .listen (common.config.httpsPort, function () { + console.log ("Express https server listening on port " + common.config.httpsPort); + }); + } + } catch (e) { + console.log ("Note: Can't read SSL keys/certs: "+e+"\nDisabling https server"); } -} catch (e) { - console.log ("Note: Can't read SSL keys/certs: "+e+"\nDisabling https server"); +} else { + console.log("Note: https server disabled by config"); } - /* * Uncaught Exceptions */ - process.on ("uncaughtException", function (err) { console.error ("*** Uncaught Exception:"); console.error (err.stack); diff --git a/server/authorization.js b/server/authorization.js new file mode 100644 index 0000000..ebd0e5f --- /dev/null +++ b/server/authorization.js @@ -0,0 +1,119 @@ +/* + * Authorization + */ + +var common, User; +const ldap = require ('./ldap_ohm'); +//const crypto = require ("./crypto"); + +// deactivated is not used yet +const serverVisibleSession = { user: true, name: true, type: true, mail: true, roles: true, deactivated: true, host: true }; +const clientVisibleSession = { user: true, name: true, type: true, mail: true, roles: true }; + + +// Fill in session object +function fillSession (req, user, roles, cb) { + if (req.session === undefined) + next (common.genError (500, "Error")); + req.session.regenerate (function (err) { + if (user !== undefined && ! err) { + common.shallowCopy (user, serverVisibleSession, {roles: true}, req.session); + if (user._id) { + req.session.user = user._id; + } + req.session.roles = roles; + } + return cb (err); + }); +} + +const authorization = { + // Generate Error object suitible for throwing or next()ing + genCheckAuthorized: function (group) { + return function (req, res, next) { + if (req.session === undefined || req.session.user === undefined || + req.session.roles === undefined) + return next (common.genError (403, "Unauthorized")); + if (req.session.roles[group] === undefined) + return next (common.genError (403, "Unauthorized")); + next (); + } + }, + + // Login route: requires .user and .pwd params + login: function (req, res, next) { + var user = req.body.user || ''; + var pwd = req.body.pwd || ''; + + // Helper: Return valid session Object + function returnSession () { + // Only export client visible parts of session object + var copy = common.shallowCopy (req.session, clientVisibleSession); + return res.json (copy); + } + // Helper: Return error + function returnError () { + fillSession (req, undefined, undefined, function (err) { + next (common.genError (401, "Unauthorized")); + }); + } + + // TODO Auth: validate session ID + // Check whether to just validate current session ID + if (user === '' && pwd === '') { + console.log ("auth revalidate: " + req.session.user); + if (req.session.user === undefined) + return returnError(); + return returnSession (); + } + + // check local database + User.findById (req.body.user) .exec (function (err, entry) { + // If there is a local user AND it has a password associated, test against this, and only this + /* + if (entry != null && entry.pwd) { + if (crypto.checkLocalAuth (entry, req.body.pwd)) { + return fillSession (req, entry, common.arrayToHash(entry.roles), returnSession); + } + return returnError (); + } + */ + + // check ldap + ldap.authorize (user.toLowerCase(), pwd, function (found) { + console.log ("ldap authorize " + user + " returns " + JSON.stringify (found)); + // No ldap entry either -> unauthorized + if (found == null) { + return returnError (); + } + // If there is an entry w/o password, use it for roles etc. + if (entry) { + if (! entry.name || entry.name === "") + entry.name = found.name; + if (! entry.mail || entry.mail === "") + entry.mail = found.mail; + if (! entry.type || entry.type === "") + entry.type = found.type; + if (! entry.orclgender || entry.orclgender === "") + entry.orclgender = found.orclgender; + return fillSession (req, entry, entry.roles.length > 0 ? common.arrayToHash(entry.roles) : {user:true}, returnSession); + } + // Otherwise create standard user entry + return fillSession (req, found, {user:true}, returnSession); + }); + }); + }, + logout: function (req, res, next) { + fillSession (req, undefined, undefined, function (err) { + return res.json ({}); + }); + }, + init: function (_common) { + common = _common; + ldap.init (_common); + User = require('../database/user.model.js');; + }, +}; + + +module.exports = authorization; diff --git a/server/common.js b/server/common.js new file mode 100644 index 0000000..dd2e716 --- /dev/null +++ b/server/common.js @@ -0,0 +1,78 @@ +/* + * Common functions and imports + */ + +var common = { + fs: require('fs'), // file sync + http: require('http'), + mongoose: require('mongoose'), // Needed for db connection. + //util: require('util'), // Why is it needed here? + //fork: require('child_process') .fork, // What does that? + + // Generate Error object suitible for throwing or next()ing + // For a better exception handling + genError: function (code, message) { + var err = new Error (common.http.STATUS_CODES[code] + (message != undefined && message != "" ? ": "+message : "")); + err.status = code; + // to generally disable stack traces for these manually created error Objects: + delete err.stack; + return err; + }, + + // Generate deep copy + // Only include properties incl (all if undefined), strip properties excl (associative arrays) + deepCopy: function (inp, incl, excl) { + // For now, JSON is considered fastest / easiest + var obj = JSON.parse (JSON.stringify (inp)); + if (incl) { + for (var k in obj) { + if (incl[k] === undefined) + delete obj[k]; + } + } + if (excl) { + for (var k in excl) { + delete obj[k]; + } + } + return obj; + }, + + // Create shallow (1 level) copy of object, use obj if already present (merge) + // Only include properties incl (all if undefined), strip properties excl (associative arrays) + shallowCopy: function (inp, incl, excl, obj) { + var keys = inp; + if (obj === undefined) + obj = {}; + if (typeof inp == "array") + obj = []; + if (incl !== undefined) + keys = incl; + for (var k in keys) { + if (inp[k] !== undefined && (excl === undefined || ! excl[k])) + obj[k] = inp[k]; + } + return obj; + }, + + // Create hash of 'true' entries for array/mongoose object + arrayToHash: function (array) { + var hash = {}; + for (var e=0; e < array.length; e++) { + hash[array[e]] = true; + } + return hash; + }, + + // Log output session cookie + debug: function (req) { + console.log ("- " + req.headers.cookie + "\n+ " + req.session.id + "\n " + JSON.stringify (req.session)); + }, + + // Init config data + init: function () { + this.config = JSON.parse (this.fs.readFileSync ("server_config.json")); + }, +}; + +module.exports = common; diff --git a/server/ldap_ohm.js b/server/ldap_ohm.js new file mode 100644 index 0000000..99e0977 --- /dev/null +++ b/server/ldap_ohm.js @@ -0,0 +1,104 @@ +/* + * Valdiate ohm logins with ldap service + */ +const ldap = require('ldapjs'); +const ldap_escape = require('ldap-escape'); + + +// TODO: Where do I get the URL from?? A: Is given. +var ldap_client = ldap.createClient({ + //url: 'ldap://gso2.ads1.fh-nuernberg.de/', + url: 'ldap://sso.cs.ohm-hochschule.de:389/', + //url: 'ldaps://sso.cs.ohm-hochschule.de:636/', + reconnect: true, + // timeouts don't work reliably +}); + +// TODO: Where do I get the 'bindpath' parameters info from? A: Is given. +const ldap_config = { + bindpath: 'cn=Users,dc=ohm-hochschule,dc=de', + timeout: 2000 +}; + +const ldap_ohm = { + init: function () { + }, + + // Authorize user with password + // Calls callback with null if unauthorized + // Calls callback with object describing user if successful: + authorize: function (user, pwd, cb) { + if (typeof user != 'string' || typeof pwd != 'string') + return cb (null); + // Empty passwords *may* bind successfully anonymously + if (user.length < 1 || pwd.length < 1) + return cb (null); + + /* Same function, different writing style */ + /* Escape ldap login input */ + //escaped = ldap_escape.dn`cn=${user},`+ldap_config.bindpath; + escaped = ldap_escape.dn (['cn=',','+ldap_config.bindpath], user); + + // Timeout handler: call callback, + // make sure later ldap returns don't do anything weird + var return_object = {}; + var timeoutHandle = setTimeout (function () { + console.log('ldap timeout'); + return_object = null; + cb (null); + }, ldap_config.timeout); + + // Bind ldap to user (authorize) + ldap_client.bind (escaped, pwd, function (err, res) { + if (return_object === null) + return; // Timeout, cb has already been called + if (err !== null) { + console.log ("ldap bind: failed for user " + user + ": " + err); + clearTimeout (timeoutHandle); + return cb (null); + } + + // Search for user entry of just bound user + // There should be only one... + ldap_client.search (escaped, { sizeLimit: 1 }, function (err, res) { + if (return_object === null) + return; // Timeout, cb has already been called + if (err !== null) { + console.log ("ldap search: search after bind didn't work for user " + + user + ": " + err); + clearTimeout (timeoutHandle); + return cb (null); + } + // Populate return with search results + res.on('searchEntry', function(entry) { + if (return_object === null) + return; // Timeout, cb has already been called + return_object.user = user; + return_object.name = entry.object.displayname; + return_object.type = entry.object.employeetype; + return_object.mail = entry.object.mail; + return_object.gender = entry.object.orclgender; + + // Calling cb here, not in 'end', because of potential bugs with + // concurrency failures, and we have our single(!) entry + // https://github.com/joyent/node-ldapjs/pull/424 + clearTimeout (timeoutHandle); + if (typeof return_object.mail != 'string' || return_object.mail.length < 1) { + console.log("ldap search error after bind for user " + user); + return cb (null); + } + return cb (return_object); + }); + res.on('error', function(err) { + console.log('ldap error: ' + err.message); + }); + res.on('end', function(result) { + // TODO: Did we forget something? + // TODO: analyze result.status? + }); + }); + }); + } +}; + +module.exports = ldap_ohm; diff --git a/server/ldap_test.js b/server/ldap_test.js new file mode 100644 index 0000000..a899f74 --- /dev/null +++ b/server/ldap_test.js @@ -0,0 +1,35 @@ +// Terminal call: node server/ldap_test.js - needs VPN or eduroam +const inquirer = require('inquirer'), + ldap = require('./ldap_ohm.js'); + +inquirer.prompt([ + { + name: 'username', + type: 'input', + message: 'Enter your VirtuOhm username:', + validate: function( value ) { + if (value.length) { + return true; + } else { + return 'Please enter your username.'; + } + } + }, + { + name: 'password', + type: 'password', + message: 'Enter your password:', + validate: function(value) { + if (value.length) { + return true; + } else { + return 'Please enter your password.'; + } + } + }]) + .then(answers => { + ldap.init(null); + ldap.authorize(answers.username,answers.password,function(user) { + console.log(JSON.stringify(user)); + }); + }); diff --git a/server_config.json b/server_config.json new file mode 100644 index 0000000..bd0f6aa --- /dev/null +++ b/server_config.json @@ -0,0 +1,9 @@ +{ + "dbConn": "mongodb://127.0.0.1:27017/om", + "dbLocalConn": "mongodb://localhost:27017/omdb", + "dbUser": "om", + "dbPwd": "aeg1phuKeDaixese", + "initialReservedTime": 120, + "httpPort": 8013, + "httpsPort": 8889 +} From 82f8e4bbdea5785c56c923b779132e4af78721ad Mon Sep 17 00:00:00 2001 From: Erik Roemmelt Date: Thu, 20 Jun 2019 17:17:45 +0200 Subject: [PATCH 05/16] outsource db routes, schemata and logic into dbs.js --- database/message.model.js | 15 -- database/tag.model.js | 8 - database/user.model.js | 21 --- mongodb.config.js.example | 3 - public/index.html | 50 +++++- public/routes/createMessage.js | 33 ++-- public/routes/home.js | 8 +- server.js | 296 +++++++++++++-------------------- server/authorization.js | 17 +- server/common.js | 2 + server/crypto.js | 31 ++++ server/dbs.js | 222 +++++++++++++++++++++++++ server/ldap_ohm.js | 2 + 13 files changed, 448 insertions(+), 260 deletions(-) delete mode 100644 database/message.model.js delete mode 100644 database/tag.model.js delete mode 100644 database/user.model.js delete mode 100644 mongodb.config.js.example create mode 100644 server/crypto.js create mode 100644 server/dbs.js diff --git a/database/message.model.js b/database/message.model.js deleted file mode 100644 index f914fb7..0000000 --- a/database/message.model.js +++ /dev/null @@ -1,15 +0,0 @@ -const mongoose = require('mongoose'); - -//const Schema = mongoose.Schema, -//ObjectId = Schema.ObjectId; - -const MessageSchema = mongoose.Schema({ - subject: { type: String, required: true }, - message: { type: String, required: true }, - user: { type: String, required: true }, - tag: [{type: String }], - //createtime: { type: Date, default: Date.now }, -}); -MessageSchema.index({tag:'text'}); - -module.exports = mongoose.model('Message', MessageSchema); diff --git a/database/tag.model.js b/database/tag.model.js deleted file mode 100644 index b3e3ad7..0000000 --- a/database/tag.model.js +++ /dev/null @@ -1,8 +0,0 @@ -const mongoose = require('mongoose'); - - -const TagSchema = mongoose.Schema({ - name: { type: String, required: true }, //unique -}); - -module.exports = mongoose.model('Tag', TagSchema); diff --git a/database/user.model.js b/database/user.model.js deleted file mode 100644 index 632ccf0..0000000 --- a/database/user.model.js +++ /dev/null @@ -1,21 +0,0 @@ -const mongoose = require('mongoose'); - - -const UserSchema = mongoose.Schema({ -// _id: { type: String }, - name: { type: String, required: true }, - pwd: { type: String }, -// hash: { type: String }, -// salt: { type: String }, -// type: { type: String }, - roles: { type: [String], required: true }, - tags: { type: [String] }, -// deactivated: { type: Boolean }, -// participating: { type: [String] }, -// host: { type: Boolean }, - bookmarks: { type: [String] }, -}); -//tags as index: -//UserSchema.index({tags:'text'}); - -module.exports = mongoose.model('User', UserSchema); diff --git a/mongodb.config.js.example b/mongodb.config.js.example deleted file mode 100644 index 36a4a1a..0000000 --- a/mongodb.config.js.example +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - url: 'mongodb://localhost:27017/mydb' -} diff --git a/public/index.html b/public/index.html index 8861930..92b3ca8 100644 --- a/public/index.html +++ b/public/index.html @@ -98,10 +98,19 @@ @@ -178,6 +187,43 @@ methods: { } }); + const search_data = []; +/* + export.default { + data: function () { + return { + selected: [], + taglist: data, + }; + }, + computed: { + filteredTagArray() { + this.taglist = data.filter((option) => { + return option + .toString() + .toLowerCase() + .indexOf(this.name.toLowerCase()) >= 0 + }) + }, + + }, + methods: { + evaluate(text) { + this.taglist = data.filter((option) => { + return option + .toString() + .toLowerCase() + .indexOf(text.toLowerCase()) >= 0 + }) + }, + }, + mounted: function () { + if ($(this).bootstrapMaterialDesign) + $(this).bootstrapMaterialDesign(); + }, + }; +*/ + diff --git a/public/routes/createMessage.js b/public/routes/createMessage.js index b59b7f5..3746d3b 100644 --- a/public/routes/createMessage.js +++ b/public/routes/createMessage.js @@ -19,7 +19,7 @@ const CreateMsgRouter = { ABBRECHEN - SENDEN + SENDEN @@ -42,29 +42,30 @@ const CreateMsgRouter = { data: function () { return { selected: [], - items: data + taglist: data, }; }, methods: { createMsg: function () { - var subject = $("#subject").val(); - var message = $("#message").val(); - var tag = $("#tag").val(); - var user = $("#user").val(); - console.log("Message Created: " + tag + " " + message + " " + user); + var _subject = $("#subject").val(); + var _message = $("#message").val(); + var _tag = $("#tag").val(); + var _user = $("#user").val(); + //console.log("Message Created: " + _tag + " " + _message + " " + _user); $.ajax({ - url: "api/createMsg", + url: "api/msg", data: { - sub: subject, - mess: message, - use: user, - ta: tag + subject: _subject, + message: _message, + user: _user, + tag: _tag }, method: "POST" }).done(have_result).fail(have_error); function have_result(res) { - console.log(res); + //console.log(res); + router.push('/home') } function have_error(err) { @@ -73,13 +74,13 @@ const CreateMsgRouter = { } }, getFilteredTags(text) { - this.items = data.filter((option) => { + this.taglist = data.filter((option) => { return option .toString() .toLowerCase() .indexOf(text.toLowerCase()) >= 0 }) - }, + }, }, mounted: function () { if ($(this).bootstrapMaterialDesign) diff --git a/public/routes/home.js b/public/routes/home.js index 630eeb6..d120b9f 100644 --- a/public/routes/home.js +++ b/public/routes/home.js @@ -24,12 +24,12 @@ const HomeRouter = { }); },*/ list_messages: function () { - $.ajax({url: "api/ids", method: "GET"}) + $.ajax({url: "api/msg/ids", method: "GET"}) .done(jd => { // NICHT SO wg. Vue: _messagelist = jd; _messagelist.splice(0, _messagelist.length); _messagelist.push.apply(_messagelist, jd); - console.log("jd: "+jd); + //console.log("jd: "+jd); for (var e in jd) { if (!_messages[jd[e]]) { get_insert_message(jd[e]); @@ -49,7 +49,7 @@ const HomeRouter = { } }*/ }).fail(function (e, f, g) { - console.log("err: " + e + f + g); + console.log("list_msg: err: " + e + f + g); }); } }, @@ -61,7 +61,7 @@ const HomeRouter = { }; function get_insert_message(id) { - $.ajax({ url: "api/msg/"+id, method: "GET" }).done(function (msg) { + $.ajax({ url: "api/msg/id/"+id, method: "GET" }).done(function (msg) { Vue.set(_messages, id, msg); }).fail(function (e, f, g) { console.log("cannot load " + id + ".json: " + e + f + g); diff --git a/server.js b/server.js index c424653..d5ac4f7 100644 --- a/server.js +++ b/server.js @@ -1,35 +1,38 @@ +// Original file created by Prof.Dr. Matthias Hopf + /** * Express based http & https server * * Requires express >= 4 */ -var common = require ('./server/common'), - authorize = require ('./server/authorization'); +var common = require('./server/common'), + authorize = require('./server/authorization'), + dbs = require('./server/dbs'); /* dbs = require ('./server/dbs'), files = require ('./server/files'); */ -const fs = common.fs, // file sync, read certificates - http = common.http, // http handler - https = require ('https'), // https handler - express = require ('express'), // node server framework - session = require ('express-session'), // session management (security) - morgan = require ('morgan'), // logger - //serveFavicon = require ('serve-favicon'), - bodyParser = require ('body-parser'), // post request bodyparser - MongoStore = require ('connect-mongo')(session), // use mongodb as session storage - Message = require('./database/message.model.js'); +const fs = common.fs, // file sync, read certificates + http = common.http, // http handler + https = require('https'), // https handler + express = require('express'), // node server framework + session = require('express-session'), // session management (security) + morgan = require('morgan'), // logger + mong = common.mongoose, // mongoose +// serveFavicon = require('serve-favicon'), // provide favicon + bodyParser = require('body-parser'), // post request bodyparser + MongoStore = require('connect-mongo')(session); // use mongodb as session storage var app = express(); /* * Init */ -common .init (); -authorize.init (common); -//dbs .init (common); +common .init(); +authorize .init(common); +dbs .init (common); //files .init (common); - +mong.Promise = global.Promise; /* * Security @@ -40,203 +43,134 @@ authorize.init (common); * (Disable Header information: Powerd by Express) * -> Information disclosure */ -app.disable ('x-powered-by'); - -// Session Management -app.set('trust proxy', 1) // trust first proxy, neccessary for cookie secure: true flag -app.use (session({ - secret: 'ahhgylhuvh', // caesar(3) 2 letter surname - resave: false, - saveUninitialized: false, - cookie: { - maxAge: 30*24*3600*1000, // TODO: ttl for session as well (Store) - secure: true, // true for https only (since our app works only with https) - }, - name: 'om.sid', - store: new MongoStore ({mongooseConnection: common.mongoose.connection, ttl: 30*24*3600}), // mongoose + connect-mongo - //store: new MemoryStore ({checkPeriod: 24*3600*1000}), // memorystore -})); +app.disable('x-powered-by'); /* * Route Control */ +// Session Management +app.set('trust proxy', 1) // trust first proxy, neccessary for cookie secure: true flag +app.use(session({ + secret: 'ahhgylhuvh', // caesar(3) 2 letter surname + resave: false, + saveUninitialized: false, + cookie: { + maxAge: 30 * 24 * 3600 * 1000, // TODO: ttl for session as well (Store) + secure: true, // true for https only (since our app works only with https) + }, + name: 'om.sid', + store: new MongoStore({ + mongooseConnection: mong.connection, + ttl: 30 * 24 * 3600 + }), // mongoose + connect-mongo + //store: new MemoryStore ({checkPeriod: 24*3600*1000}), // memorystore +})); -// Fastpaths +// TODO Favicon for Desktop //app.use (serveFavicon (__dirname + '/public/favicon.ico')); - // Minimal Logging //app.use (morgan ('dev')); // Advanced Logging -morgan.token ('user', function (req, res) { return (req.session && req.session.user) || '-'; }); -morgan.token ('userColored', function (req, res) { - var color = 0; - if (req.session && req.session.roles) - color = req.session.roles.admin ? 31 // red - : req.session.roles.user ? 34 // blue - : 0; // no color - return '\x1b[' + color + 'm' + ((req.session && req.session.user) || '-') + '\x1b[0m'; +morgan.token('user', function (req, res) { + return (req.session && req.session.user) || '-'; }); -morgan.token ('statusColored', function (req, res) { - var color = res.statusCode >= 500 ? 31 // red - : res.statusCode >= 400 ? 33 // yellow - : res.statusCode >= 300 ? 36 // cyan - : res.statusCode >= 200 ? 32 // green - : 0; // no color - return '\x1b[' + color + 'm' + (res.headersSent ? res.statusCode : '-') + '\x1b[0m'; +morgan.token('userColored', function (req, res) { + var color = 0; + if (req.session && req.session.roles) + color = req.session.roles.admin ? 31 // red + : req.session.roles.user ? 34 // blue + : 0; // no color + return '\x1b[' + color + 'm' + ((req.session && req.session.user) || '-') + '\x1b[0m'; }); -app.use (morgan (':date[iso] :statusColored :method :url :userColored :response-time ms :res[content-length]')); +morgan.token('statusColored', function (req, res) { + var color = res.statusCode >= 500 ? 31 // red + : res.statusCode >= 400 ? 33 // yellow + : res.statusCode >= 300 ? 36 // cyan + : res.statusCode >= 200 ? 32 // green + : 0; // no color + return '\x1b[' + color + 'm' + (res.headersSent ? res.statusCode : '-') + '\x1b[0m'; +}); +app.use(morgan(':date[iso] :statusColored :method :url :userColored :response-time ms :res[content-length]')); // BodyParser // Returns middleware that only parses json bodies. // (https://www.npmjs.com/package/body-parser#bodyparserjsonoptions) -app.use (bodyParser.json()); +app.use(bodyParser.json()); // Returns middleware that only parses urlencoded bodies // with qs library (https://www.npmjs.com/package/qs#readme) -app.use (bodyParser.urlencoded({extended: true})); +app.use(bodyParser.urlencoded({ + extended: true +})); // API var api_routes = express.Router(); // express app-object routing +app.use('/api', api_routes); -app.use ('/api', api_routes); - -// Static Files -// Allow server access to 'public' folder +// Static Files - Allow access to 'public' folder app.use(express.static(__dirname + '/public')); // Other stuff is NOT authorized unless logged in //app.use (authorize.genCheckAuthorized ('user')); -// Uploaded files -//app.use ('/uploads', express.static(__dirname + '/uploads')); - -// Configuring the database -//var dbConfig = require('./mongodb.config.js'); - -common.mongoose.Promise = global.Promise; - -// Connecting to the database -// Local db: common.config.dbLocalConn -// Efi db: common.config.dbConn -common.mongoose.connect (common.config.dbLocalConn, {useNewUrlParser: true}) .then( () => { - console.log("Successfully connected to MongoDB."); -}).catch( err => { - console.log('Could not connect to MongoDB.'); - process.exit(); -}); - // No error so far? Then it's a 404! -//app.use (function (req, res, next) { next (common.genError (404, req.url)); }); +app.use(function (req, res, next) { + next(common.genError(404, req.url)); +}); //app.use (routes.errorHandler (true)); /* true: show stack traces */ - /* * API */ -/* // API allowed for all -api_routes.post ('/login', authorize.login); // /api/login +api_routes.post('/login', authorize.login); // Validate all other API calls -api_routes.use (authorize.genCheckAuthorized ('user')); -api_routes.post ('/logout', authorize.logout); +//api_routes.use(authorize.genCheckAuthorized('user')); +api_routes.post('/logout', authorize.logout); -function addRoutes (r) { - for (var e in r.routes) { - var params = r.routes[e].params ? "/" + r.routes[e].params : ""; - console.log ("Adding routes for /" + e + params + ":" + - (r.routes[e].get ? " get":" ") + (r.routes[e].post ? " post":" ") + - (r.routes[e].put ? " put":" ") + (r.routes[e].delete ? " delete":" ")); - if (r.routes[e].get) - api_routes.get ('/' + e + params, r.routes[e].get); - if (r.routes[e].post) - api_routes.post ('/' + e + params, r.routes[e].post); - if (r.routes[e].put) - api_routes.put ('/' + e + params, r.routes[e].put); - if (r.routes[e].delete) - api_routes.delete ('/' + e + params, r.routes[e].delete); - } +// Add API routes +function addRoutes(r) { + for (var e in r.routes) { + var route = '/' + e + (r.routes[e].params ? "/" + r.routes[e].params : ""); + var log = "Adding routes for " + route + ":"; + /* + var auth = r.routes[e].auth || r.auth; + if (auth) { + log += " [auth]"; + api_routes.use (route, function (req, res, next) { + if (! auth(req)) + return next (common.genError (403, "Unauthorized")); + next (); + }); + } + */ + /* + var role = r.routes[e].role || r.role; + if (role) { + log += " [role:"+role+"]"; + api_routes.use (route, authorize.genCheckAuthorized (role)); + } + */ + const methods = ["get", "post", "put", "delete"]; + for (var m in methods) { + if (r.routes[e][methods[m]]) { + log += " " + methods[m]; + api_routes[methods[m]](route, r.routes[e][methods[m]]); + } + } + console.log(log); + } } -*/ -app.get ('/api/ids', function (req, res) { - Message.find({},{id: true}) .exec () .then(results => { - //selects id from message: - var parsed = []; - for (var i in results) { - parsed.push (results[i].id); - } - //var parsed = results.map (x => x._id); - res.send(parsed); - } ) - .catch(err => { - console.log (err); - res .status(500) .json (err); - }); -}); - -app.get ("/api/msg/:id", function (req, res) { - Message.findOne ({_id: req.params.id}) .exec (function (err, results){ - if (err) { - console.log (err); - res .status(404) .json (err); - } else { - console.log(JSON.stringify(results)); - res.json(results); - } - }); -}); - -/*app.get ("/api/msg/search/:phrase", function (req, res) { - Message.find ({$text: {$search: req.params.phrase}) .then (function (err, results){ - if (err) { - console.log (err); - res .status(404) .json (err); - } else { - console.log(JSON.stringify(results)); - res.json(results); - } - }); -}); -*/ -/*function makeid() { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for (var i = 0; i < 5; i++) - text += possible.charAt(Math.floor(Math.random() * possible.length)); - - return text; -}*/ - -app.post("/api/createMsg", function(req, res){ - //x = mongoose.Types.ObjectId(); - //y = x.toString(); - //var z = makeid(); - console.log("SUbject: "+JSON.stringify(req.body)); - var message = new Message( {subject: req.body.sub, message: req.body.mess, user: req.body.use, tag: req.body.ta } ); - - message.save(function(err,result){ - if(err){ - return res .status(401) .send(err.message); - }else{ - res.json({message: "Message created!!"}); - } - }); -}); - -/* -addRoutes (dbs); -addRoutes (admin); -addRoutes (files); -*/ +addRoutes(dbs); /* * Servers */ -http.createServer (app) .listen (common.config.httpPort, function () { - console.log ("Express http server listening on port " + common.config.httpPort); +http.createServer(app).listen(common.config.httpPort, function () { + console.log("Express http server listening on port " + common.config.httpPort); }); /* @@ -255,23 +189,23 @@ if (common.config.httpsPort) { try { try { // In case it's a real certificate: add CA chain cersts (TODO: use array if required) - var ca = fs.readFileSync ('keys/ca_cert.pem'); + var ca = fs.readFileSync('keys/ca_cert.pem'); } catch (e) { ca = undefined; - console.log ("Note: Can't read CA bundle: "+e); + console.log("Note: Can't read CA bundle: " + e); } if (ca != undefined) { options = { - key: fs.readFileSync ('keys/omkey.pem'), - cert: fs.readFileSync ('keys/certificate.pem'), + key: fs.readFileSync('keys/omkey.pem'), + cert: fs.readFileSync('keys/certificate.pem'), ca: ca - }; - https.createServer (options, app) .listen (common.config.httpsPort, function () { - console.log ("Express https server listening on port " + common.config.httpsPort); + }; + https.createServer(options, app).listen(common.config.httpsPort, function () { + console.log("Express https server listening on port " + common.config.httpsPort); }); } } catch (e) { - console.log ("Note: Can't read SSL keys/certs: "+e+"\nDisabling https server"); + console.log("Note: Can't read SSL keys/certs: " + e + "\nDisabling https server"); } } else { console.log("Note: https server disabled by config"); @@ -280,9 +214,7 @@ if (common.config.httpsPort) { /* * Uncaught Exceptions */ -process.on ("uncaughtException", function (err) { - console.error ("*** Uncaught Exception:"); - console.error (err.stack); +process.on("uncaughtException", function (err) { + console.error("*** Uncaught Exception:"); + console.error(err.stack); }); - - diff --git a/server/authorization.js b/server/authorization.js index ebd0e5f..7717ccb 100644 --- a/server/authorization.js +++ b/server/authorization.js @@ -1,10 +1,11 @@ +// Original file created by Prof.Dr. Matthias Hopf + /* * Authorization */ - var common, User; -const ldap = require ('./ldap_ohm'); -//const crypto = require ("./crypto"); +const ldap = require ('./ldap_ohm'), + crypto = require ("../server/crypto"); // deactivated is not used yet const serverVisibleSession = { user: true, name: true, type: true, mail: true, roles: true, deactivated: true, host: true }; @@ -66,18 +67,16 @@ const authorization = { return returnError(); return returnSession (); } - - // check local database +/* + // check local database, then ldap User.findById (req.body.user) .exec (function (err, entry) { // If there is a local user AND it has a password associated, test against this, and only this - /* if (entry != null && entry.pwd) { if (crypto.checkLocalAuth (entry, req.body.pwd)) { return fillSession (req, entry, common.arrayToHash(entry.roles), returnSession); } return returnError (); } - */ // check ldap ldap.authorize (user.toLowerCase(), pwd, function (found) { @@ -101,7 +100,7 @@ const authorization = { // Otherwise create standard user entry return fillSession (req, found, {user:true}, returnSession); }); - }); + });*/ }, logout: function (req, res, next) { fillSession (req, undefined, undefined, function (err) { @@ -111,7 +110,7 @@ const authorization = { init: function (_common) { common = _common; ldap.init (_common); - User = require('../database/user.model.js');; + //User = require('../database/user.model.js');; }, }; diff --git a/server/common.js b/server/common.js index dd2e716..e71297d 100644 --- a/server/common.js +++ b/server/common.js @@ -1,3 +1,5 @@ +// Original file created by Prof.Dr. Matthias Hopf + /* * Common functions and imports */ diff --git a/server/crypto.js b/server/crypto.js new file mode 100644 index 0000000..0159428 --- /dev/null +++ b/server/crypto.js @@ -0,0 +1,31 @@ +// Original file created by Prof.Dr. Matthias Hopf + +/* + * Crypto routines for Authorization + */ + +const crypto = require ("crypto"); + +const defaultHash = "sha256"; +const defaultSaltLen = 16; // More (e.g. 256) for extra paranoia + +const mod = { + encodePwd: function (entry, pwd) { + return crypto.createHash (entry.hash) .update (entry.salt + ":" + pwd, 'utf8') .digest ('base64'); + }, + checkLocalAuth: function (entry, pwd) { + if (!entry || !entry._id || !entry.hash || !entry.salt || !entry.hash || !entry.pwd || + !pwd || pwd === '') + return false; + return mod.encodePwd (entry, pwd) === entry.pwd; + }, + fillLocalAuth: function (entry, pwd) { + if (!entry.hash) + entry.hash = defaultHash; + entry.salt = crypto.randomBytes (defaultSaltLen) .toString('base64'); + entry.pwd = mod.encodePwd (entry, pwd); + }, +} + + +module.exports = mod; diff --git a/server/dbs.js b/server/dbs.js new file mode 100644 index 0000000..004962f --- /dev/null +++ b/server/dbs.js @@ -0,0 +1,222 @@ +/* + * Main database access functions + */ +var common, + model = {}; + +const dbs = { + /* Method API route + * <- to server + * -> to client + * Description + */ + routes: { + "msg/ids": { + /* GET /api/msg/ids [no args] + * -> Array of message schema object ids + * Get ALL known message ids + */ + get: function(req, res) { + model.Messages.find({}, {_id: true}).exec() + .then(results => { + //selects id from message: + var parsed = []; + for (var i in results) { + parsed.push (results[i]._id); + } + //var parsed = results.map (x => x._id); + res.send(parsed); + } ) + .catch(err => { + console.log (err); + res.status(500).json(err); + }); + }, + }, + "msg/id": { + params: ":id", + /* GET /api/msg/id/[massage-id] + * -> Message schema + * Get a particular message + */ + get: function(req, res) { + model.Messages.findById(req.params.id) .exec(function(err, results) { + if (err) { + console.log (err); + res.status(404).json(err); + } else { + //console.log(JSON.stringify(results)); + res.json(results); + } + }); + }, + }, + "msg/search": {}, + "msg": { + /* POST /api/msg + * <- Message schema + * -> Message schema + * Create a new message + */ + post: function(req, res) { + /* + if ( !(req.body.tags instanceof Array) ) { + return res.status(400).json({ error: "bad request" }); + }*/ + console.log("Subject: "+JSON.stringify(req.body)); + model.Messages.create({ + subject: req.body.subject, + message: req.body.message, + user: req.body.user, + tags: req.body.tag + }, function(err, result) { + if (err) { + return res.status(401).json(err.message); + } else { + res.json({message: "Message created!!"}); + } + if (result == null) { + return res.status(500).json("Can not create message.") + } + }); + }, + /* PUT /api/msg + * <- + * -> + * Update a message + */ + //put: function(req, res) {}, + }, + "tag/ids": {}, + "tag": {}, + }, + /* Initialize requirements + * - DB connection + * - DB schemata + */ + init: function (_common) { + common = _common; + + /* DB Connection + * Local db: common.config.dbLocalConn + * TH db: common.config.dbConn + */ + common.mongoose.connect (common.config.dbLocalConn, { + useNewUrlParser: true + }).then(() => { + console.log("Database connected successfully."); + }).catch(err => { + console.log('Database connection error.'); + process.exit(); + }); + + /* DB Schemata + * Privat fields: + * - per model: _list: Elements that are included in list fetch + * - per entry: _comment: Comment for Admin UI - TODO: not working yet + */ + var messageSchema = common.mongoose.Schema({ + subject: { type: String, required: true, + _comment: "" }, + message: { type: String, required: true, + _comment: "" }, + user: { type: String, required: true, + _comment: "" }, + tags: { type: [String], + _comment: "" }, + //createtime: { type: Date, default: Date.now }, + }); + messageSchema.index({ tag:'text' }); + model.Messages = common.mongoose.model('messages', messageSchema); + model.Messages._list = [ "" ]; + + var tagSchema = common.mongoose.Schema({ + name: { type: String, required: true, + _comment: "" }, //unique + }); + model.Tags = common.mongoose.model('tags', messageSchema); + model.Tags._list = [ "" ]; + + var userSchema = common.mongoose.Schema({ + //_id: { type: String }, + name: { type: String, required: true, + _comment: "" }, + pwd: { type: String, + _comment: "" }, + //hash: { type: String }, + //salt: { type: String }, + //type: { type: String }, + roles: { type: [String], required: true, + _comment: "" }, + tags: { type: [String], + _comment: "" }, + //deactivated: { type: Boolean }, + //participating: { type: [String] }, + //host: { type: Boolean }, + bookmarks: { type: [String], + _comment: "" }, + }); + model.Users = common.mongoose.model('users', userSchema); + model.Users._list = [ "" ]; + }, + models: model, +}; + + + +/* +app.get ('/api/ids', function (req, res) { + Message.find({},{id: true}) .exec () .then(results => { + //selects id from message: + var parsed = []; + for (var i in results) { + parsed.push (results[i].id); + } + //var parsed = results.map (x => x._id); + res.send(parsed); + } ) + .catch(err => { + console.log (err); + res .status(500) .json (err); + }); +}); + +app.get ("/api/msg/:id", function (req, res) { + Message.findOne ({_id: req.params.id}) .exec (function (err, results){ + if (err) { + console.log (err); + res .status(404) .json (err); + } else { + console.log(JSON.stringify(results)); + res.json(results); + } + }); +}); + +/*app.get ("/api/msg/search/:phrase", function (req, res) { + Message.find ({$text: {$search: req.params.phrase}) .then (function (err, results){ + if (err) { + console.log (err); + res .status(404) .json (err); + } else { + console.log(JSON.stringify(results)); + res.json(results); + } + }); +}); +*/ +/* +app.post("/api/createMsg", function(req, res){ + console.log("Subject: "+JSON.stringify(req.body)); + var message = new Message( {subject: req.body.sub, message: req.body.mess, user: req.body.use, tag: req.body.ta } ); + + message.save(function(err,result){ + if(err){ + return res .status(401) .send(err.message); + }else{ + res.json({message: "Message created!!"}); + } + }); +}); +*/ +module.exports = dbs; diff --git a/server/ldap_ohm.js b/server/ldap_ohm.js index 99e0977..5ae2f06 100644 --- a/server/ldap_ohm.js +++ b/server/ldap_ohm.js @@ -1,3 +1,5 @@ +// Original file created by Prof.Dr. Matthias Hopf + /* * Valdiate ohm logins with ldap service */ From cad0230c4704b4438d81fa7a7083b7696a68c15d Mon Sep 17 00:00:00 2001 From: Erik Roemmelt Date: Thu, 20 Jun 2019 23:30:51 +0200 Subject: [PATCH 06/16] Fix: tags were not visible on "home" page -> solved --- public/routes/createMessage.js | 11 ++++-- server/dbs.js | 61 +++++----------------------------- 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/public/routes/createMessage.js b/public/routes/createMessage.js index 3746d3b..2ed475c 100644 --- a/public/routes/createMessage.js +++ b/public/routes/createMessage.js @@ -1,3 +1,4 @@ +var tagArray = []; const data=[ '#th', '#efi', @@ -18,13 +19,15 @@ const CreateMsgRouter = { + @typing="getFilteredTags" + @input="saveTagsToArray"> @@ -46,10 +49,14 @@ const CreateMsgRouter = { }; }, methods: { + saveTagsToArray: function() { + tagArray = this.selected; + console.info(tagArray); + }, createMsg: function () { var _subject = $("#subject").val(); var _message = $("#message").val(); - var _tag = $("#tag").val(); + var _tag = tagArray; var _user = $("#user").val(); //console.log("Message Created: " + _tag + " " + _message + " " + _user); $.ajax({ diff --git a/server/dbs.js b/server/dbs.js index 004962f..9be9ebc 100644 --- a/server/dbs.js +++ b/server/dbs.js @@ -17,14 +17,13 @@ const dbs = { * Get ALL known message ids */ get: function(req, res) { - model.Messages.find({}, {_id: true}).exec() + model.Messages.find({},{_id: true}).exec() .then(results => { //selects id from message: var parsed = []; for (var i in results) { parsed.push (results[i]._id); } - //var parsed = results.map (x => x._id); res.send(parsed); } ) .catch(err => { @@ -40,13 +39,13 @@ const dbs = { * Get a particular message */ get: function(req, res) { - model.Messages.findById(req.params.id) .exec(function(err, results) { + model.Messages.findById(req.params.id) .exec(function(err, result) { if (err) { console.log (err); res.status(404).json(err); } else { - //console.log(JSON.stringify(results)); - res.json(results); + //console.log(JSON.stringify(result)); + res.json(result); } }); }, @@ -63,12 +62,13 @@ const dbs = { if ( !(req.body.tags instanceof Array) ) { return res.status(400).json({ error: "bad request" }); }*/ - console.log("Subject: "+JSON.stringify(req.body)); + //console.log("Subject: "+JSON.stringify(req.body)); + model.Messages.create({ subject: req.body.subject, message: req.body.message, user: req.body.user, - tags: req.body.tag + tag: req.body.tag }, function(err, result) { if (err) { return res.status(401).json(err.message); @@ -122,7 +122,7 @@ const dbs = { _comment: "" }, user: { type: String, required: true, _comment: "" }, - tags: { type: [String], + tag: { type: [String], _comment: "" }, //createtime: { type: Date, default: Date.now }, }); @@ -163,37 +163,8 @@ const dbs = { }; - /* -app.get ('/api/ids', function (req, res) { - Message.find({},{id: true}) .exec () .then(results => { - //selects id from message: - var parsed = []; - for (var i in results) { - parsed.push (results[i].id); - } - //var parsed = results.map (x => x._id); - res.send(parsed); - } ) - .catch(err => { - console.log (err); - res .status(500) .json (err); - }); -}); - -app.get ("/api/msg/:id", function (req, res) { - Message.findOne ({_id: req.params.id}) .exec (function (err, results){ - if (err) { - console.log (err); - res .status(404) .json (err); - } else { - console.log(JSON.stringify(results)); - res.json(results); - } - }); -}); - -/*app.get ("/api/msg/search/:phrase", function (req, res) { +app.get ("/api/msg/search/:phrase", function (req, res) { Message.find ({$text: {$search: req.params.phrase}) .then (function (err, results){ if (err) { console.log (err); @@ -205,18 +176,4 @@ app.get ("/api/msg/:id", function (req, res) { }); }); */ -/* -app.post("/api/createMsg", function(req, res){ - console.log("Subject: "+JSON.stringify(req.body)); - var message = new Message( {subject: req.body.sub, message: req.body.mess, user: req.body.use, tag: req.body.ta } ); - - message.save(function(err,result){ - if(err){ - return res .status(401) .send(err.message); - }else{ - res.json({message: "Message created!!"}); - } - }); -}); -*/ module.exports = dbs; From e93526c639746902de4f9f81680ab4d30d95a359 Mon Sep 17 00:00:00 2001 From: senta_mandutz Date: Tue, 25 Jun 2019 14:43:33 +0200 Subject: [PATCH 07/16] tags from database implemented --- public/index.html | 1 + public/routes/createMessage.js | 26 +++++++++++++++++++++ public/routes/tagData.js | 2 ++ server/dbs.js | 41 ++++++++++++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 public/routes/tagData.js diff --git a/public/index.html b/public/index.html index 92b3ca8..0c82b3e 100644 --- a/public/index.html +++ b/public/index.html @@ -84,6 +84,7 @@ + diff --git a/public/routes/createMessage.js b/public/routes/createMessage.js index 2ed475c..9f37a4b 100644 --- a/public/routes/createMessage.js +++ b/public/routes/createMessage.js @@ -88,9 +88,35 @@ const CreateMsgRouter = { .indexOf(text.toLowerCase()) >= 0 }) }, + list_tags: function () { + $.ajax({url: "api/tag/ids",method: "GET"}) + .done(jd => { + // NICHT SO wg. Vue: _messagelist = jd; + _taglist.splice(0, _taglist.length); + _taglist.push.apply(_taglist, jd); + console.log("tag: jd: " + jd); + for (var e in jd) { + if (!_tags[jd[e]]) { + get_insert_tag(jd[e]); + } + } + }).fail(function (e, f, g) { + console.log("err: " + e + f + g); + }); + } }, mounted: function () { + this.list_tags(); if ($(this).bootstrapMaterialDesign) $(this).bootstrapMaterialDesign(); }, }; + +function get_insert_tag(id){ + $.ajax({ url: "api/tag/"+id, method: "GET" }).done(function (tag) { + data.push(tag.name); + console.log("it worked!"); + }).fail(function (e, f, g) { + console.log("cannot load " + id + ".json: " + e + f + g); + }) +} diff --git a/public/routes/tagData.js b/public/routes/tagData.js new file mode 100644 index 0000000..3ef34ad --- /dev/null +++ b/public/routes/tagData.js @@ -0,0 +1,2 @@ +_taglist = []; +_tags = []; diff --git a/server/dbs.js b/server/dbs.js index 9be9ebc..b882189 100644 --- a/server/dbs.js +++ b/server/dbs.js @@ -87,8 +87,45 @@ const dbs = { */ //put: function(req, res) {}, }, - "tag/ids": {}, - "tag": {}, + "tag/ids": { + /* GET /api/tag/ids [no args] + * -> Array of tag schema object ids + * Get ALL known tag ids + */ + get: function(req, res) { + model.Tags.find({},{_id: true}).exec() + .then(results => { + //selects id from tag: + var parsed = []; + for (var i in results) { + parsed.push (results[i]._id); + } + res.send(parsed); + } ) + .catch(err => { + console.log (err); + res.status(500).json(err); + }); + }, + }, + "tag": { + params: ":id", + /* GET /api/tag/[tag-id] + * -> Tag schema + * Get a particular tag + */ + get: function(req, res) { + model.Tags.findById(req.params.id) .exec(function(err, result) { + if (err) { + console.log (err); + res.status(404).json(err); + } else { + //console.log(JSON.stringify(result)); + res.json(result); + } + }); + }, + }, }, /* Initialize requirements * - DB connection From 156ec36d1ede858abb31482a657bd643a326ea05 Mon Sep 17 00:00:00 2001 From: senta_mandutz Date: Thu, 27 Jun 2019 10:28:48 +0200 Subject: [PATCH 08/16] easy search implemented --- public/index.html | 35 ++++++++++++++++++++++++++++++++--- public/main.js | 4 ++-- server/dbs.js | 25 +++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/public/index.html b/public/index.html index 0c82b3e..9b8358b 100644 --- a/public/index.html +++ b/public/index.html @@ -97,10 +97,11 @@
-
`, data: function () { @@ -114,7 +112,7 @@ const CreateMsgRouter = { function get_insert_tag(id){ $.ajax({ url: "api/tag/"+id, method: "GET" }).done(function (tag) { - data.push(tag.name); + data.push("#"+tag.name); console.log("it worked!"); }).fail(function (e, f, g) { console.log("cannot load " + id + ".json: " + e + f + g); From 8c0d3dc669bc52ca1eec85bb93f201a8f2105cfd Mon Sep 17 00:00:00 2001 From: senta_mandutz Date: Thu, 27 Jun 2019 11:13:40 +0200 Subject: [PATCH 10/16] # in front of tags and changed index of messages --- public/routes/createMessage.js | 4 ++-- server/dbs.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/routes/createMessage.js b/public/routes/createMessage.js index 9f37a4b..0afbf03 100644 --- a/public/routes/createMessage.js +++ b/public/routes/createMessage.js @@ -114,8 +114,8 @@ const CreateMsgRouter = { function get_insert_tag(id){ $.ajax({ url: "api/tag/"+id, method: "GET" }).done(function (tag) { - data.push(tag.name); - console.log("it worked!"); + data.push("#" + tag.name); + console.log("Array:"+this.data); }).fail(function (e, f, g) { console.log("cannot load " + id + ".json: " + e + f + g); }) diff --git a/server/dbs.js b/server/dbs.js index 8b68f47..acb8fa7 100644 --- a/server/dbs.js +++ b/server/dbs.js @@ -184,7 +184,7 @@ const dbs = { _comment: "" }, //createtime: { type: Date, default: Date.now }, }); - messageSchema.index({ tag:'text', subject:'text', message:'text', user:'text' }); + messageSchema.index({ "$**":'text' }); model.Messages = common.mongoose.model('messages', messageSchema); model.Messages._list = [ "" ]; From 5280c47477aad87983e7359e7868faa5b3f02d4f Mon Sep 17 00:00:00 2001 From: Xenia Date: Thu, 27 Jun 2019 11:14:37 +0200 Subject: [PATCH 11/16] first Try to autocomplete the search --- public/index.html | 56 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/public/index.html b/public/index.html index 9b8358b..1719686 100644 --- a/public/index.html +++ b/public/index.html @@ -99,20 +99,18 @@ - - - + +
@@ -169,6 +167,12 @@