web,db - user groups using object ids now
This commit is contained in:
parent
8ddd2e9013
commit
50cc5c93a9
@ -28,7 +28,8 @@ module.exports = {
|
||||
app: {
|
||||
locale: 'de-DE', // default locale (de-DE & en-EN should be available)
|
||||
name: 'authRXBN',
|
||||
passhashDelimiter: '|'
|
||||
passhashDelimiter: '|',
|
||||
adminGroupname: "Administration"
|
||||
},
|
||||
mongoose: {
|
||||
uri: process.env.DB_URL,
|
||||
|
@ -34,12 +34,17 @@ models.user = new Schema({
|
||||
},
|
||||
mfa: { // multi factor authentication
|
||||
active: {type: Boolean, default: false},
|
||||
type: {type: String, default: ""},
|
||||
data: {type: String, default: ""} // tel number or secret token
|
||||
data: {type: Array, default: [ // add each mfa type
|
||||
//{
|
||||
// no: 0,
|
||||
// type: "TOTP"||"HOTP"||"WebAuthn",
|
||||
// data: "32CharHex"||"32CharHex"||"UserPublicKey"
|
||||
//}, ...
|
||||
]}
|
||||
},
|
||||
settings: {type: Object, default: {}}, // custom settings (theme etc. pp.)
|
||||
roles: {type: String, default: ""}, // user-defined roles and permissions
|
||||
group: {type: Number, default: 0}, // group-id for group-defined roles and permissions
|
||||
group: Schema.Types.ObjectId, // reference to group
|
||||
reg_date: {type: Date, default: Date.now}, // registration date
|
||||
last_action: {type: Date, default: Date.now}, // last action (activity date)
|
||||
});
|
||||
|
@ -73,8 +73,8 @@ methods.getConnection = () => {
|
||||
* @param {String} nick nickname
|
||||
* @param {String} email email
|
||||
* @param {String} passhash hashed password
|
||||
* @param {Number} group Group id (normally 0 -> user)
|
||||
* @return {Array} async (reply, err)
|
||||
* @param {Number} group Group ObjectId
|
||||
* @return {Object} async (reply, err)
|
||||
*/
|
||||
methods.addUser = async (nick, email, passhash, group) => {
|
||||
if(typeof nick !== 'string') return {err: new TypeError('nick is not a string::database.addUser('+nick+','+email+','+passhash+','+group+')', module.filename)};
|
||||
@ -105,7 +105,7 @@ methods.addUser = async (nick, email, passhash, group) => {
|
||||
* @async
|
||||
* @TODO add functionality
|
||||
* @param {String} haystack email or nick
|
||||
* @return {Array} async(reply, err)
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.delUser = async (haystack) => {
|
||||
if(typeof haystack !== 'string') return {err: new TypeError('haystack is not a string::database.delUser('+haystack+')', module.filename)};
|
||||
@ -129,7 +129,7 @@ methods.delUser = async (haystack) => {
|
||||
* get all users
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @return {Array} async(reply, err)
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.getUsers = async () => {
|
||||
let userModel = models.user;
|
||||
@ -150,7 +150,7 @@ methods.getUsers = async () => {
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @param {String|String[]} haystack email or nick
|
||||
* @return async(reply, err)
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.getUser = async (haystack) => {
|
||||
if(typeof haystack !== 'string' && typeof haystack !== 'object') return {err: new TypeError('email or nickname is not a string|object::database.getUser('+haystack+')', module.filename)};
|
||||
@ -161,14 +161,17 @@ methods.getUser = async (haystack) => {
|
||||
haystack = sanitize(haystack);
|
||||
|
||||
let or = [];
|
||||
if(typeof haystack === 'string') {
|
||||
if(haystack instanceof mongoose.Types.ObjectId) {
|
||||
or.push({_id: haystack});
|
||||
}
|
||||
else if(typeof haystack === 'string') {
|
||||
or = [{nickname: haystack}, {email: haystack}, {token: haystack}];
|
||||
if(haystack.match(/^[0-9a-fA-F]{24}$/)) or.push({_id: haystack});
|
||||
if(haystack.match(/^[0-9a-fA-F]{24}$/)) or.push({_id: mongoose.Types.ObjectId(haystack)});
|
||||
}
|
||||
else {
|
||||
or = [];
|
||||
for(let i = 0; i < haystack.length; i++) {
|
||||
if(haystack[i].match(/^[0-9a-fA-F]{24}$/)) or.push({_id: haystack[i]});
|
||||
if(haystack[i].match(/^[0-9a-fA-F]{24}$/)) or.push({_id: mongoose.Types.ObjectId(haystack[i])});
|
||||
or.push({nickname: haystack[i]});
|
||||
or.push({email: haystack[i]});
|
||||
or.push({token: haystack[i]});
|
||||
@ -178,8 +181,8 @@ methods.getUser = async (haystack) => {
|
||||
try {
|
||||
users = await userModel.find().or(or).exec();
|
||||
|
||||
if(users.length > 0)
|
||||
return {reply: users};
|
||||
if(users.length == 1)
|
||||
return {reply: users[0]};
|
||||
else
|
||||
return {reply: false};
|
||||
} catch(err) {
|
||||
@ -194,7 +197,7 @@ methods.getUser = async (haystack) => {
|
||||
* @async
|
||||
* @param {Number} id User ID
|
||||
* @param {Object} obj data
|
||||
* @return {Array} async(reply, err)
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.updateUser = async (id, obj) => {
|
||||
if(typeof id !== 'string') return {err: new TypeError('id is not a string::database.updateUser('+id+','+JSON.stringify(obj)+')', module.filename)};
|
||||
@ -219,7 +222,7 @@ methods.updateUser = async (id, obj) => {
|
||||
* @TODO UPDATE METHOD; PROBABLY OUTDATED
|
||||
* @param {Number} id User ID
|
||||
* @param {Object} data data JSON -> remember
|
||||
* @return {Array} async({date => 'Login Date', token => 'RememberMe Cookie Token'}, err)
|
||||
* @return {Object} async({date => 'Login Date', token => 'RememberMe Cookie Token'}, err)
|
||||
*/
|
||||
methods.addActivity = async (id, data) => {
|
||||
if(typeof id !== 'string') return {err: new TypeError('id is not a string::database.updateNewAction('+id+','+JSON.stringify(options)+')', module.filename)};
|
||||
@ -260,6 +263,179 @@ methods.addActivity = async (id, data) => {
|
||||
}
|
||||
};
|
||||
|
||||
// //////// /////// //////// // // /////// //////
|
||||
// // // // // // // // // // //
|
||||
// //////// /////// // // // // /////// //////
|
||||
// // // // // // // // // // //
|
||||
// //////// // // //////// //////// // //////
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* get all groups
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.getGroups = async () => {
|
||||
let groupModel = models.group;
|
||||
|
||||
try {
|
||||
groups = await groupModel.find({}).exec();
|
||||
if(groups.length > 0)
|
||||
return {reply: groups};
|
||||
else
|
||||
return {reply: false};
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* query groups by UUID or name
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @param {String|String[]} haystack UUID or name
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.getGroup = async (haystack) => {
|
||||
if(typeof haystack !== 'string' && typeof haystack !== 'object') return {err: new TypeError('haystack is not a string|object::database.getGroup('+haystack+')', module.filename)};
|
||||
|
||||
let groupModel = models.group;
|
||||
|
||||
// sanitize input
|
||||
haystack = sanitize(haystack);
|
||||
|
||||
let or = [];
|
||||
if(haystack instanceof mongoose.Types.ObjectId) {
|
||||
or.push({_id: haystack});
|
||||
}
|
||||
else if(typeof haystack === 'string') {
|
||||
or = [{name: haystack}];
|
||||
if(haystack.match(/^[0-9a-fA-F]{24}$/)) or.push({_id: mongoose.Types.ObjectId(haystack)});
|
||||
}
|
||||
else {
|
||||
or = [];
|
||||
for(let i = 0; i < haystack.length; i++) {
|
||||
if(haystack[i].match(/^[0-9a-fA-F]{24}$/)) or.push({_id: mongoose.Types.ObjectId(haystack[i])});
|
||||
or.push({name: haystack[i]});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
groups = await groupModel.find().or(or).exec();
|
||||
|
||||
if(groups.length == 1)
|
||||
return {reply: groups[0]};
|
||||
else
|
||||
return {reply: false};
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds Group to Database
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @param {String} name name
|
||||
* @param {String} roles roles
|
||||
* @return {Object} async (reply, err)
|
||||
*/
|
||||
methods.addGroup = async (name, roles) => {
|
||||
if(typeof name !== 'string') return {err: new TypeError('name is not a string::database.addGroup('+name+','+roles+')', module.filename)};
|
||||
if(typeof roles !== 'string') return {err: new TypeError('name is not a string::database.addGroup('+name+','+roles+')', module.filename)};
|
||||
|
||||
let groupModel = models.group;
|
||||
|
||||
let group = new groupModel();
|
||||
|
||||
// sanitize input
|
||||
group.name = sanitize(nick);
|
||||
group.roles = sanitize(email);
|
||||
|
||||
try {
|
||||
reply = await group.save();
|
||||
return {reply: 1};
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* updates obj keys in group entry
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @param {Number} id Group ID
|
||||
* @param {Object} obj data
|
||||
* @return {Object} async(reply, err)
|
||||
*/
|
||||
methods.updateGroup = async (id, obj) => {
|
||||
if(typeof id !== 'string') return {err: new TypeError('id is not a string::database.updateGroup('+id+','+JSON.stringify(obj)+')', module.filename)};
|
||||
if(typeof obj !== 'object') return {err: new TypeError('obj is not an object::database.updateGroup('+id+','+JSON.stringify(obj)+')', module.filename)};
|
||||
|
||||
let groupModel = models.group;
|
||||
|
||||
try {
|
||||
data = await groupModel.findByIdAndUpdate(id, obj).exec();
|
||||
return {data: data};
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* delete group and set fallback group for users
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @param {String} id group id
|
||||
* @param {String} fallbackId fallback group id
|
||||
* @return {Boolean}
|
||||
*/
|
||||
methods.delGroup = async (id, fallbackId) => {
|
||||
if(typeof id !== 'string') return {err: new TypeError('id is not a string::database.delGroup('+id+','+ fallbackId +')', module.filename)};
|
||||
if(typeof fallbackId !== 'string') return {err: new TypeError('fallbackId is not a string::database.delGroup('+id+','+ fallbackId +')', module.filename)};
|
||||
|
||||
let groupModel = models.group;
|
||||
let userModel = models.user;
|
||||
let pathModel = models.pathRules;
|
||||
|
||||
// sanitize input
|
||||
id = sanitize(id);
|
||||
fallbackId = sanitize(fallbackId);
|
||||
|
||||
try {
|
||||
|
||||
try {
|
||||
// find users
|
||||
users = await userModel.find({group: id}).exec();
|
||||
|
||||
// set fallback group for each user
|
||||
users.forEach(async (user) => {
|
||||
await userModel.findByIdAndUpdate(user._id, {group: fallbackId}).exec();
|
||||
});
|
||||
|
||||
// find rules
|
||||
rules = await pathModel.find({group: id}).exec();
|
||||
|
||||
// set fallback group for each rule
|
||||
rules.forEach(async (rule) => {
|
||||
await pathModel.findByIdAndUpdate(rule._id, {group: fallbackId}).exec();
|
||||
});
|
||||
|
||||
// remove group
|
||||
reply = await groupModel.findByIdAndRemove(id).exec();
|
||||
return {reply: 1};
|
||||
} catch (e) {
|
||||
return {err: e};
|
||||
}
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
};
|
||||
|
||||
// //////// //////// ////////// // // ////////
|
||||
// // // // // // // // //
|
||||
// //////// // // // // // //
|
||||
@ -294,11 +470,12 @@ methods.getPathRules = async () => {
|
||||
//
|
||||
//////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* get Applications
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @return {Array} async(apps, err)
|
||||
* @return {Object} async(apps, err)
|
||||
*/
|
||||
methods.getApps = async () => {
|
||||
var Application = models.application;
|
||||
@ -314,8 +491,9 @@ methods.getApps = async () => {
|
||||
* return auth obj
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @TODO
|
||||
* @param {Object} obj data obj (aId, uId)
|
||||
* @return {Array} async({timestamp, token}, err)
|
||||
* @return {Object} async({timestamp, token}, err)
|
||||
*/
|
||||
methods.setAuthCode = async (obj) => {
|
||||
if(typeof obj !== 'object') return {err: new TypeError('obj is not an object::database.setAuthCode('+JSON.stringify(obj)+')', module.filename)};
|
||||
@ -347,8 +525,9 @@ methods.setAuthCode = async (obj) => {
|
||||
* return auth obj
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @TODO
|
||||
* @param {Object} obj data obj (aId, aSecret, uId, token)
|
||||
* @return {Array} async(bool, err)
|
||||
* @return {Object} async(bool, err)
|
||||
*/
|
||||
methods.getAuth = async (obj) => {
|
||||
if(typeof obj !== 'object') return {err: new TypeError('obj is not an object::database.getAuthCode('+JSON.stringify(obj)+')', module.filename)};
|
||||
@ -369,9 +548,11 @@ methods.getAuth = async (obj) => {
|
||||
var Application = models.application;
|
||||
|
||||
try {
|
||||
if(!(obj.aId instanceof mongoose.Types.ObjectId)) obj.aId = mongoose.Types.ObjectId(obj.aId);
|
||||
|
||||
data1 = await Application.findOne({
|
||||
$and: [
|
||||
{_id: mongoose.Types.ObjectId(obj.aId)},
|
||||
{_id: obj.aId},
|
||||
{secret: obj.aSecret}
|
||||
]
|
||||
}).exec();
|
||||
@ -398,11 +579,12 @@ methods.getAuth = async (obj) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* return if app is permitted to do access call
|
||||
* return app permission
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @TODO
|
||||
* @param {Object} obj data obj (aId, redirectUrl)
|
||||
* @return {Array} async(bool, err)
|
||||
* @return {Object} async(bool, err)
|
||||
*/
|
||||
methods.verifyAppCall = async (obj) => {
|
||||
return {};
|
||||
@ -420,7 +602,7 @@ methods.verifyAppCall = async (obj) => {
|
||||
* returns user count
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @return {Array} async(int, err)
|
||||
* @return {Object} async(int, err)
|
||||
*/
|
||||
methods.userCount = async () => {
|
||||
let userModel = models.user;
|
||||
|
@ -76,7 +76,7 @@ getRoutes = async () => {
|
||||
}
|
||||
|
||||
// no reply (user does not exist) or password is wrong
|
||||
if(!user.reply || user.reply === null || user.reply.length == 0 || user.reply.length > 1 || !global['requireModule']('auth').validateHash(user.reply[0].passhash, pass)) {
|
||||
if(!user.reply || user.reply === null || user.reply.length == 0 || user.reply.length > 1 || !global['requireModule']('auth').validateHash(user.reply.passhash, pass)) {
|
||||
return res.type('json').status(401).end(JSON.stringify({
|
||||
status: 401,
|
||||
message: 'msg.auth.login.failed'
|
||||
@ -88,8 +88,8 @@ getRoutes = async () => {
|
||||
|
||||
// add session data
|
||||
req.session.user = {
|
||||
'id': user.reply[0]._id,
|
||||
'group': user.reply[0].group
|
||||
'id': user.reply._id,
|
||||
'group': user.reply.group
|
||||
};
|
||||
|
||||
return res.type('json').end(JSON.stringify({
|
||||
|
@ -20,22 +20,27 @@ let getRoutes = async () => {
|
||||
/**
|
||||
* main page
|
||||
* @url /
|
||||
* @method all
|
||||
* @method GET
|
||||
*/
|
||||
route.all('/', asyncer(async (req, res, next) => {
|
||||
// TODO: show login page or dashboard
|
||||
// res.end('login or dashboard');
|
||||
apps = await db.getApps();
|
||||
res.render('index', {
|
||||
route.get('/', asyncer(async (req, res, next) => {
|
||||
obj = {
|
||||
session: req.session,
|
||||
apps: apps.reply,
|
||||
cfg: cfg
|
||||
});
|
||||
};
|
||||
|
||||
// if user is logged in
|
||||
if(req.session && req.session.user) {
|
||||
obj.user = (await db.getUser(req.session.user.id)).reply;
|
||||
obj.group = (await db.getGroup(obj.user.group)).reply;
|
||||
apps = await db.getApps();
|
||||
obj.apps = apps.reply;
|
||||
}
|
||||
res.render('index', obj);
|
||||
}));
|
||||
|
||||
/**
|
||||
* login page or apprequest page
|
||||
* @url /
|
||||
* @url /authenticate
|
||||
* @method GET
|
||||
*/
|
||||
route.get('/authenticate', asyncer(async (req, res) => {
|
||||
@ -55,25 +60,29 @@ let getRoutes = async () => {
|
||||
// req.query.appId
|
||||
// verify appId (if in rep)
|
||||
req.session.appRequest.appId = req.query.appId;
|
||||
|
||||
// TODO: on accept, setAuthCode and redirect with token
|
||||
// on cancel, redirect to dashboard
|
||||
}
|
||||
} else {
|
||||
return res.redirect('/');
|
||||
}
|
||||
|
||||
// if user is logged in, show request page
|
||||
if(req.session && req.session.user) {
|
||||
res.render('request', {
|
||||
user = await db.getUser(req.session.user.id);
|
||||
group = await db.getGroup(user.reply.group);
|
||||
|
||||
return res.render('request', {
|
||||
session: req.session,
|
||||
appRequest: req.session.appRequest,
|
||||
apps: apps.reply,
|
||||
cfg: cfg
|
||||
cfg: cfg,
|
||||
user: user.reply,
|
||||
group: group.reply
|
||||
});
|
||||
// if user isnt logged in, show login page
|
||||
} else {
|
||||
if(!req.query.appId) req.session.appRequest = {};
|
||||
|
||||
let view_obj = { session: req.session };
|
||||
let view_obj = { session: req.session, cfg: cfg };
|
||||
if(req.query.appId) {
|
||||
apps.reply.forEach((app) => {
|
||||
if(app._id == req.query.appId)
|
||||
@ -82,7 +91,10 @@ let getRoutes = async () => {
|
||||
|
||||
}
|
||||
|
||||
res.render('login', view_obj);
|
||||
return res.render('login', view_obj);
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
mixin navItem(name, id, symbol, href)
|
||||
li(title=name, id=id)
|
||||
a(href=href)
|
||||
i.fa-fw(class=symbol)
|
||||
if(symbol !== "")
|
||||
i.fa-fw(class=symbol)
|
||||
span= name
|
||||
|
||||
// Navigation
|
||||
@ -14,6 +15,8 @@ nav(uk-navbar).uk-navbar-container
|
||||
.uk-navbar-right.uk-margin-right
|
||||
ul.uk-navbar-nav
|
||||
if(session && session.user)
|
||||
if(group && group.name == cfg.app.adminGroupname)
|
||||
+navItem("Administration", "admin", "", "/admin")
|
||||
+navItem("Apps", "apps", "fas fa-tachometer-alt", "/")
|
||||
+navItem("Settings", "settings", "fas fa-wrench", "/settings")
|
||||
+navItem("Logout", "logout", "fas fa-sign-out-alt", "/logout")
|
||||
|
Loading…
x
Reference in New Issue
Block a user