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); |
module.exports = { | |||||
url: 'mongodb://localhost:27017/mydb' | |||||
} |
<!-- NOTE: Load JQuery, Vue.js, VueRouter, Vuetify --> | <!-- NOTE: Load JQuery, Vue.js, VueRouter, Vuetify --> | ||||
<script src="lib/jquery-3.3.1.min.js"></script> | <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 --> | <!-- Buefy/Vuetify --> | ||||
<script src="lib/buefy-0.7.5.js"></script> | <script src="lib/buefy-0.7.5.js"></script> | ||||
<script src="routes/profil.js"></script> | <script src="routes/profil.js"></script> | ||||
<script src="routes/msgCard.js"></script> | <script src="routes/msgCard.js"></script> | ||||
<script src="routes/messageData.js"></script> | <script src="routes/messageData.js"></script> | ||||
<script src="routes/tagData.js"></script> | |||||
<script src="routes/profilCard.js"></script> | <script src="routes/profilCard.js"></script> | ||||
<script src="routes/profilData.js"></script> | <script src="routes/profilData.js"></script> | ||||
<script src="routes/login.js"></script> | <script src="routes/login.js"></script> | ||||
<div class="om-header-container"> | <div class="om-header-container"> | ||||
<div class="om-header"> | <div class="om-header"> | ||||
<a class="logo-img" href="index.html"><img src="img/app_icon.png" width=45px height=45px></a> | <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-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> | </b-field> | ||||
</form> | </form> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<script> | <script> | ||||
var tagArray = ["lorem"]; | |||||
const dat=[ | |||||
'th', | |||||
'efi', | |||||
'wichtig', | |||||
]; | |||||
const routes = [ | const routes = [ | ||||
{ path: "/", component: HomeRouter }, | { path: "/", component: HomeRouter }, | ||||
var app = new Vue({ | var app = new Vue({ | ||||
router, | router, | ||||
el: '#api', | 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: { | 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> | </script> | ||||
<!-- CDN_Vue.js minified lib --> | <!-- CDN_Vue.js minified lib --> |
// Wait until page loaded | // Wait until page loaded | ||||
window.addEventListener('load', () => { | window.addEventListener('load', () => { | ||||
if ('serviceWorker' in navigator) { | |||||
/*if ('serviceWorker' in navigator) { | |||||
// NOTE: ServiceWorker Registration | // NOTE: ServiceWorker Registration | ||||
return navigator.serviceWorker.register('serviceWorker.js', { | return navigator.serviceWorker.register('serviceWorker.js', { | ||||
scope: '/' | scope: '/' | ||||
} else { | } else { | ||||
console.log('[ServiceWorker] are not supported.'); | console.log('[ServiceWorker] are not supported.'); | ||||
return; | return; | ||||
} | |||||
}*/ | |||||
}); | }); | ||||
// NOTE: Set Bootstrap materialdesign | // NOTE: Set Bootstrap materialdesign |
var tagArray = []; | |||||
const data=[ | const data=[ | ||||
'#th', | |||||
'#efi', | |||||
'#wichtig', | |||||
'th', | |||||
'efi', | |||||
'wichtig', | |||||
]; | ]; | ||||
const CreateMsgRouter = { | const CreateMsgRouter = { | ||||
template: ` | template: ` | ||||
<b-field label="Tags"> | <b-field label="Tags"> | ||||
<b-taginput | <b-taginput | ||||
id="tag" | |||||
v-model="selected" | v-model="selected" | ||||
:data=items | |||||
:data=taglist | |||||
autocomplete | autocomplete | ||||
allow-new:false | allow-new:false | ||||
icon="label" | icon="label" | ||||
placeholder="#" | placeholder="#" | ||||
@typing="getFilteredTags" id="tag"> | |||||
@typing="getFilteredTags" | |||||
@input="saveTagsToArray"> | |||||
</b-taginput> | </b-taginput> | ||||
</b-field> | </b-field> | ||||
</b-field> | </b-field> | ||||
<b-button @click="$router.go(-1)">ABBRECHEN</b-button> | <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> | </div> | ||||
`, | `, | ||||
data: function () { | data: function () { | ||||
return { | return { | ||||
selected: [], | selected: [], | ||||
items: data | |||||
taglist: data, | |||||
}; | }; | ||||
}, | }, | ||||
methods: { | methods: { | ||||
saveTagsToArray: function() { | |||||
tagArray = this.selected; | |||||
console.info(tagArray); | |||||
}, | |||||
createMsg: function () { | 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({ | $.ajax({ | ||||
url: "api/createMsg", | |||||
url: "api/msg", | |||||
data: { | data: { | ||||
sub: subject, | |||||
mess: message, | |||||
use: user, | |||||
ta: tag | |||||
subject: _subject, | |||||
message: _message, | |||||
user: _user, | |||||
tag: _tag | |||||
}, | }, | ||||
method: "POST" | method: "POST" | ||||
}).done(have_result).fail(have_error); | }).done(have_result).fail(have_error); | ||||
function have_result(res) { | function have_result(res) { | ||||
console.log(res); | |||||
//console.log(res); | |||||
router.push('/home') | |||||
} | } | ||||
function have_error(err) { | function have_error(err) { | ||||
} | } | ||||
}, | }, | ||||
getFilteredTags(text) { | getFilteredTags(text) { | ||||
this.items = data.filter((option) => { | |||||
this.taglist = data.filter((option) => { | |||||
return option | return option | ||||
.toString() | .toString() | ||||
.toLowerCase() | .toLowerCase() | ||||
.indexOf(text.toLowerCase()) >= 0 | .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 () { | mounted: function () { | ||||
this.list_tags(); | |||||
if ($(this).bootstrapMaterialDesign) | if ($(this).bootstrapMaterialDesign) | ||||
$(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); | |||||
}) | |||||
} |
}); | }); | ||||
},*/ | },*/ | ||||
list_messages: function () { | list_messages: function () { | ||||
$.ajax({url: "api/ids", method: "GET"}) | |||||
$.ajax({url: "api/msg/ids", method: "GET"}) | |||||
.done(jd => { | .done(jd => { | ||||
// NICHT SO wg. Vue: _messagelist = jd; | // NICHT SO wg. Vue: _messagelist = jd; | ||||
_messagelist.splice(0, _messagelist.length); | _messagelist.splice(0, _messagelist.length); | ||||
_messagelist.push.apply(_messagelist, jd); | _messagelist.push.apply(_messagelist, jd); | ||||
console.log("jd: "+jd); | |||||
//console.log("jd: "+jd); | |||||
for (var e in jd) { | for (var e in jd) { | ||||
if (!_messages[jd[e]]) { | if (!_messages[jd[e]]) { | ||||
get_insert_message(jd[e]); | get_insert_message(jd[e]); | ||||
} | } | ||||
}*/ | }*/ | ||||
}).fail(function (e, f, g) { | }).fail(function (e, f, g) { | ||||
console.log("err: " + e + f + g); | |||||
console.log("list_msg: err: " + e + f + g); | |||||
}); | }); | ||||
} | } | ||||
}, | }, | ||||
}; | }; | ||||
function get_insert_message(id) { | 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); | Vue.set(_messages, id, msg); | ||||
}).fail(function (e, f, g) { | }).fail(function (e, f, g) { | ||||
console.log("cannot load " + id + ".json: " + e + f + g); | console.log("cannot load " + id + ".json: " + e + f + g); |
<img src="favicon.ico" width=20px height=20px> | <img src="favicon.ico" width=20px height=20px> | ||||
</h6> | </h6> | ||||
{{ msg.message }}<br><br> | {{ 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"> | <div class="om-card-footer"> <div class="om-user-line"> | ||||
<i class="material-icons">account_circle</i> | <i class="material-icons">account_circle</i> | ||||
Erstellt von {{ msg.user }}</div> | Erstellt von {{ msg.user }}</div> |
_taglist = []; | |||||
_tags = []; |
// Original file created by Prof.Dr. Matthias Hopf | |||||
/** | /** | ||||
* Express based http & https server | * Express based http & https server | ||||
* | * | ||||
* Requires express >= 4 | * 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'), | dbs = require ('./server/dbs'), | ||||
files = require ('./server/files'); | 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 app = express(); | ||||
var http_port=8013; | |||||
https_port=8889; | |||||
/* | /* | ||||
* Init | * Init | ||||
*/ | */ | ||||
/*ll | |||||
common .init (); | |||||
authorize.init (common); | |||||
common .init(); | |||||
authorize .init(common); | |||||
dbs .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 | * 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 | // 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 | // Other stuff is NOT authorized unless logged in | ||||
//app.use (authorize.genCheckAuthorized ('user')); | //app.use (authorize.genCheckAuthorized ('user')); | ||||
// Uploaded files | |||||
//app.use ('/uploads', express.static(__dirname + '/uploads')); | |||||
// Errors | |||||
// No error so far? Then it's a 404! | // 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 | ||||
*/ | */ | ||||
/* | |||||
// API allowed for all | // API allowed for all | ||||
api_routes.post ('/login', authorize.login); // /api/login | |||||
api_routes.post('/login', authorize.login); | |||||
// Validate all other API calls | // 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 | * 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); | |||||
}); | }); | ||||
/* | /* | ||||
* openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem | * openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem | ||||
* rm certrequest.csr | * 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 | * 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); | |||||
}); | }); | ||||
// 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; |
// 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; |
// 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; |
/* | |||||
* 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; |
// 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; |
// 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)); | |||||
}); | |||||
}); |
{ | |||||
"dbConn": "mongodb://127.0.0.1:27017/om", | |||||
"dbLocalConn": "mongodb://localhost:27017/omdb", | |||||
"dbUser": "om", | |||||
"dbPwd": "aeg1phuKeDaixese", | |||||
"initialReservedTime": 120, | |||||
"httpPort": 8013, | |||||
"httpsPort": 8889 | |||||
} |
const mongoose = require('mongoose'); | |||||
const TagSchema = mongoose.Schema({ | |||||
name: { type: String, required: true }, //unique | |||||
}); | |||||
module.exports = mongoose.model('Tag', TagSchema); |
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); |