Browse Source

Merge remote-tracking branch 'origin/developer' into developer

master
Edwina Barbalan 4 years ago
parent
commit
83d1d4dea8

+ 0
- 15
message.model.js View File

@@ -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);

+ 0
- 3
mongodb.config.js.example View File

@@ -1,3 +0,0 @@
module.exports = {
url: 'mongodb://localhost:27017/mydb'
}

+ 156
- 9
public/index.html View File

@@ -58,8 +58,8 @@

<!-- NOTE: Load JQuery, Vue.js, VueRouter, Vuetify -->
<script src="lib/jquery-3.3.1.min.js"></script>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script src="lib/vue-2.6.10.js"></script>
<script src="lib/vue-router-3.0.1.js"></script>

<!-- Buefy/Vuetify -->
<script src="lib/buefy-0.7.5.js"></script>
@@ -84,6 +84,7 @@
<script src="routes/profil.js"></script>
<script src="routes/msgCard.js"></script>
<script src="routes/messageData.js"></script>
<script src="routes/tagData.js"></script>
<script src="routes/profilCard.js"></script>
<script src="routes/profilData.js"></script>
<script src="routes/login.js"></script>
@@ -97,12 +98,36 @@
<div class="om-header-container">
<div class="om-header">
<a class="logo-img" href="index.html"><img src="img/app_icon.png" width=45px height=45px></a>
<form class="om-searchbar">
<form class="om-searchbar" @submit.prevent="search()">
<b-field>
<b-input placeholder="Suche..."
type="search"
icon="magnify">
</b-input>
<b-autocomplete
rounded
v-model="searchtext"
@keydown.native.enter="search"
:data="filteredDataArray"
placeholder="suche..."
icon="magnify"
@select="option => selected = option">
</b-autocomplete>

<!--<b-input
type="search"
v-model="searchtext"
icon="magnify"
placeholder="Suche...">
</b-input>-->
<!--<b-taginput
id="search-text"
type="search"
v-model="selected"
:data=taglist
autocomplete
allow-new:true
icon="magnify"
placeholder="Suche.."
@typing="getFilteredTags"
@input="saveTagsToArray">
</b-taginput>-->
</b-field>
</form>
</div>
@@ -161,6 +186,12 @@
</div>

<script>
var tagArray = ["lorem"];
const dat=[
'th',
'efi',
'wichtig',
];

const routes = [
{ path: "/", component: HomeRouter },
@@ -179,11 +210,127 @@
var app = new Vue({
router,
el: '#api',
data: {
data: function() {
return{
searchtext: "",
selected: [],
taglist: dat,
};
},
computed:{
filteredDataArray() {
return this.taglist.filter((option) => {
return option
.toString()
.toLowerCase()
.indexOf(this.searchtext.toLowerCase()) >= 0
})
}
},
methods: {
}
search: function() {
console.log("Searchtext: " +this.searchtext.replace(/#/g,''));
$.ajax({
url: "api/msg/search/" + this.searchtext.replace(/#/g, ''),
method: "GET"
}).done(jd => {
// NICHT SO wg. Vue: _messagelist = jd;
_messagelist.splice(0, _messagelist.length);
_messagelist.push.apply(_messagelist, jd);
//console.log("jd: "+jd);
for (var e in jd) {
if (!_messages[jd[e]]) {
get_insert_message(jd[e]);
}
}
}).fail(function(e, f, g) {
console.log("searching: err: " + e + f + g);
})
},
getFilteredTags(text) {
this.taglist = dat.filter((option) => {
return option
.toString()
.toLowerCase()
.indexOf(text.toLowerCase()) >= 0
})
this.search();
},
saveTagsToArray: function() {
tagArray = this.selected;
console.info(tagArray);
},
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.search();
this.list_tags();
},
});
function get_insert_tag(id){
$.ajax({ url: "api/tag/"+id, method: "GET" }).done(function (tag) {
dat.push("#"+tag.name);
console.log("it worked!");
}).fail(function (e, f, g) {
console.log("cannot load " + id + ".json: " + e + f + g);
})
}



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();
},
};
*/

</script>

<!-- CDN_Vue.js minified lib -->

public/lib/vue.js → public/lib/vue-2.6.10.js View File


public/lib/vue-router.js → public/lib/vue-router-3.0.1.js View File


+ 2
- 2
public/main.js View File

@@ -4,7 +4,7 @@

// Wait until page loaded
window.addEventListener('load', () => {
if ('serviceWorker' in navigator) {
/*if ('serviceWorker' in navigator) {
// NOTE: ServiceWorker Registration
return navigator.serviceWorker.register('serviceWorker.js', {
scope: '/'
@@ -16,7 +16,7 @@ window.addEventListener('load', () => {
} else {
console.log('[ServiceWorker] are not supported.');
return;
}
}*/
});

// NOTE: Set Bootstrap materialdesign

+ 54
- 22
public/routes/createMessage.js View File

@@ -1,7 +1,8 @@
var tagArray = [];
const data=[
'#th',
'#efi',
'#wichtig',
'th',
'efi',
'wichtig',
];
const CreateMsgRouter = {
template: `
@@ -18,13 +19,15 @@ const CreateMsgRouter = {

<b-field label="Tags">
<b-taginput
id="tag"
v-model="selected"
:data=items
:data=taglist
autocomplete
allow-new:false
icon="label"
placeholder="#"
@typing="getFilteredTags" id="tag">
@typing="getFilteredTags"
@input="saveTagsToArray">
</b-taginput>
</b-field>

@@ -33,38 +36,41 @@ const CreateMsgRouter = {
</b-field>

<b-button @click="$router.go(-1)">ABBRECHEN</b-button>
<b-button type="is-primary" @click="$router.push('/home')">SENDEN</b-button>


<b-button type="is-primary" @click="createMsg">SENDEN</b-button>

</div>
`,
data: function () {
return {
selected: [],
items: data
taglist: data,
};
},
methods: {
saveTagsToArray: function() {
tagArray = this.selected;
console.info(tagArray);
},
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 = tagArray;
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,16 +79,42 @@ const CreateMsgRouter = {
}
},
getFilteredTags(text) {
this.items = data.filter((option) => {
this.taglist = data.filter((option) => {
return option
.toString()
.toLowerCase()
.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("Array:"+this.data);
}).fail(function (e, f, g) {
console.log("cannot load " + id + ".json: " + e + f + g);
})
}

+ 4
- 4
public/routes/home.js View File

@@ -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);

+ 2
- 1
public/routes/msgCard.js View File

@@ -5,7 +5,8 @@ Vue.component('MsgCard', {
<img src="favicon.ico" width=20px height=20px>
</h6>
{{ msg.message }}<br><br>
<a href="#">{{ msg.tag }}</a></p>
<a v-for="tag in msg.tag" href="#">#{{ tag }} </a>
</p>
<div class="om-card-footer"> <div class="om-user-line">
<i class="material-icons">account_circle</i>
Erstellt von {{ msg.user }}</div>

+ 2
- 0
public/routes/tagData.js View File

@@ -0,0 +1,2 @@
_taglist = [];
_tags = [];

+ 151
- 207
server.js View File

@@ -1,230 +1,176 @@
// 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'),
dbs = require('./server/dbs');
/*
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
//serveFavicon = require ('serve-favicon'),
bodyParser = require ('body-parser');
//MongoStore = require ('connect-mongo')(session); // uss mongodb as session storage
var Message = require('./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();

var http_port=8013;
https_port=8889;

/*
* Init
*/
/*ll
common .init ();
authorize.init (common);
common .init();
authorize .init(common);
dbs .init (common);
files .init (common);
*/
//files .init (common);
mong.Promise = global.Promise;

// 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');


/*
* 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
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
}));

// Args
app.use (bodyParser.json());
app.use (bodyParser.urlencoded({extended: true}));

// API
//var api_routes = express.Router(); // express app-object routing
//app.use ('/api', api_routes);

app.use (function (req, res, done) {
console.log(req.url);
done();
});

//global.__basedir = __dirname;

// Static Files
app.use(express.static(__dirname + '/public')); // Allow server access to 'public' folder

//app.use(express.static('resources'));

// Configuring the database
var dbConfig = require('./mongodb.config.js');
var mongoose = require('mongoose');

mongoose.Promise = global.Promise;

// Connecting to the database
//mongoose.connect(`mongodb://${server}/${dbConfig.url}`)
mongoose.connect(dbConfig.url, {useNewUrlParser: true}).then(() => {
console.log("Successfully connected to MongoDB.");
}).catch(err => {
console.log('Could not connect to MongoDB.');
process.exit();
});


//require('./app/routes/message.route.js')(app);

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);
});
});
// TODO Favicon for Desktop
//app.use (serveFavicon (__dirname + '/public/favicon.ico'));

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);
}
});
// Minimal Logging
//app.use (morgan ('dev'));
// Advanced Logging
morgan.token('user', function (req, res) {
return (req.session && req.session.user) || '-';
});

/*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);
}
});
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';
});
*/
/*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!!"});
}
});
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
}));

// Other stuff is NOT authorized unless logged in
//app.use (authorize.genCheckAuthorized ('user'));
// API
var api_routes = express.Router(); // express app-object routing
app.use('/api', api_routes);

// Uploaded files
//app.use ('/uploads', expr ess.static(__dirname + '/uploads'));
// 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'));

// 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

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);

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);
}
//api_routes.use(authorize.genCheckAuthorized('user'));
api_routes.post('/logout', authorize.logout);

// 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);
}
}

addRoutes (dbs);
addRoutes (files);
*/
addRoutes(dbs);

/*
* 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,39 +184,37 @@ 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);
process.on("uncaughtException", function (err) {
console.error("*** Uncaught Exception:");
console.error(err.stack);
});



+ 118
- 0
server/authorization.js View File

@@ -0,0 +1,118 @@
// Original file created by Prof.Dr. Matthias Hopf

/*
* Authorization
*/
var common, User;
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 };
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, 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) {
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;

+ 80
- 0
server/common.js View File

@@ -0,0 +1,80 @@
// Original file created by Prof.Dr. Matthias Hopf

/*
* 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;

+ 31
- 0
server/crypto.js View File

@@ -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;

+ 237
- 0
server/dbs.js View File

@@ -0,0 +1,237 @@
/*
* 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);
}
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, result) {
if (err) {
console.log (err);
res.status(404).json(err);
} else {
//console.log(JSON.stringify(result));
res.json(result);
}
});
},
},
"msg/search": {
params: ":phrase",
/* GET /api/msg/id/[massage-id]
* -> Message schema
* Get a particular message
*/
get: function(req, res) {
model.Messages.find({$text: {$search: req.params.phrase}})
.exec().then(results => {
//selects id from message:
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);
});
},
},
"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,
tag: 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": {
/* 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
* - 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: "" },
tag: { type: [String],
_comment: "" },
//createtime: { type: Date, default: Date.now },
});
messageSchema.index({ "$**":'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/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);
}
});
});
*/
module.exports = dbs;

+ 106
- 0
server/ldap_ohm.js View File

@@ -0,0 +1,106 @@
// Original file created by Prof.Dr. Matthias Hopf

/*
* 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;

+ 35
- 0
server/ldap_test.js View File

@@ -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));
});
});

+ 9
- 0
server_config.json View File

@@ -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
}

+ 0
- 8
tag.model.js View File

@@ -1,8 +0,0 @@
const mongoose = require('mongoose');


const TagSchema = mongoose.Schema({
name: { type: String, required: true }, //unique
});

module.exports = mongoose.model('Tag', TagSchema);

+ 0
- 14
user.model.js View File

@@ -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);

Loading…
Cancel
Save