Compare commits
6 Commits
dcbe7509d2
...
d48aca05bc
Author | SHA1 | Date |
---|---|---|
Ruben Meyer | d48aca05bc | |
Ruben Meyer | 7b02cdbaf8 | |
Ruben Meyer | 71f6b18b85 | |
Ruben Meyer | 50cc5c93a9 | |
Ruben Meyer | 8ddd2e9013 | |
Ruben Meyer | 2420bd2e80 |
1
app.js
1
app.js
|
@ -8,7 +8,6 @@
|
|||
// GDS: Global Data System
|
||||
global['gds'] = {
|
||||
debug: (process.env.NODE_ENV === 'debug') ? true : false,
|
||||
cache: {},
|
||||
cfg: require(__dirname+'/bin/config')
|
||||
};
|
||||
global['__dirname'] = __dirname;
|
||||
|
|
|
@ -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)
|
||||
});
|
||||
|
@ -51,6 +56,15 @@ models.group = new Schema({
|
|||
roles: {type: String, default: ""} // roles; separated by commas "a,b,a.b,c.*,d.z.*"
|
||||
});
|
||||
|
||||
// pathRules for access management
|
||||
models.pathRules = new Schema({
|
||||
group: Schema.Types.ObjectId, // reference to group
|
||||
expression: String, // path expression; e.g.: "(/blocks/.*)"
|
||||
rule: String, // e.g.: block
|
||||
type: String, // e.g: "404", "missing_permission", "login",
|
||||
options: {type: Object, default: {}} // more options...
|
||||
});
|
||||
|
||||
// application | service
|
||||
models.application = new Schema({
|
||||
name: String, // recognizable application name; ex. "passRXBN - Password Manager"
|
||||
|
@ -81,6 +95,7 @@ module.exports = (con) => {
|
|||
// initialize models
|
||||
mdls.user = con.model('User', models.user);
|
||||
mdls.group = con.model('Group', models.group);
|
||||
mdls.pathRules = con.model('PathRules', models.pathRules);
|
||||
mdls.application = con.model('Application', models.application);
|
||||
mdls.activity = con.model('Activity', models.activity);
|
||||
mdls.authCode = con.model('AuthCode', models.authCode);
|
||||
|
|
|
@ -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,10 +150,10 @@ 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)};
|
||||
if(typeof haystack !== 'string' && typeof haystack !== 'object') return {err: new TypeError('haystack is not a string|object::database.getUser('+haystack+')', module.filename)};
|
||||
|
||||
let userModel = models.user;
|
||||
|
||||
|
@ -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,17 +197,17 @@ 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)};
|
||||
if(!(typeof id === 'string' || id instanceof mongoose.Types.ObjectId)) return {err: new TypeError('id is not a string::database.updateUser('+id+','+JSON.stringify(obj)+')', module.filename)};
|
||||
if(typeof obj !== 'object') return {err: new TypeError('obj is not an object::database.updateUser('+id+','+JSON.stringify(obj)+')', module.filename)};
|
||||
|
||||
let userModel = models.user;
|
||||
|
||||
try {
|
||||
data = await userModel.findByIdAndUpdate(id, obj).exec();
|
||||
return {data: data};
|
||||
return {reply: data};
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
|
@ -219,10 +222,10 @@ 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)};
|
||||
if(!(typeof id === 'string' || id instanceof mongoose.Types.ObjectId)) return {err: new TypeError('id is not a string::database.updateNewAction('+id+','+JSON.stringify(options)+')', module.filename)};
|
||||
if(typeof options !== 'object' && options !== null) return {err: new TypeError('obj is not an object::database.updateUserProfile('+id+','+JSON.stringify(obj)+')', module.filename)};
|
||||
|
||||
let date = new Date().toISOString();
|
||||
|
@ -260,6 +263,204 @@ 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('roles 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' || id instanceof mongoose.Types.ObjectId)) 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 {reply: 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};
|
||||
}
|
||||
};
|
||||
|
||||
// //////// //////// ////////// // // ////////
|
||||
// // // // // // // // //
|
||||
// //////// // // // // // //
|
||||
// // //////// // //////// ////////
|
||||
// // // // // // // //
|
||||
// // // // // // // ////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* get pathrules
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @return {Object} async(rules, err)
|
||||
*/
|
||||
methods.getPathRules = async () => {
|
||||
var PathRules = models.pathRules;
|
||||
try {
|
||||
rules = await PathRules.find({}).exec();
|
||||
return {reply: rules};
|
||||
} catch(err) {
|
||||
return {err: err};
|
||||
}
|
||||
};
|
||||
|
||||
// //////// //////// //////// //////
|
||||
// // // // // // // //
|
||||
|
@ -269,11 +470,12 @@ methods.addActivity = async (id, data) => {
|
|||
//
|
||||
//////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* get Applications
|
||||
* @author Ruben Meyer
|
||||
* @async
|
||||
* @return {Array} async(apps, err)
|
||||
* @return {Object} async(apps, err)
|
||||
*/
|
||||
methods.getApps = async () => {
|
||||
var Application = models.application;
|
||||
|
@ -289,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)};
|
||||
|
@ -322,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)};
|
||||
|
@ -344,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();
|
||||
|
@ -373,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 {};
|
||||
|
@ -395,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;
|
||||
|
|
|
@ -45,35 +45,21 @@ methods.start = () => {
|
|||
|
||||
//static files
|
||||
app.use('/res', (req, res, next) => {
|
||||
if(typeof global['gds'].cache.web == 'undefined') global['gds'].cache.web = {};
|
||||
|
||||
let dir = global['__dirname'] + '/res/web';
|
||||
let joined_path = path.join(dir, /^[^?]+/.exec(req.url)[0]);
|
||||
|
||||
// path already cached; not exist
|
||||
if(global['gds'].cache.web[joined_path] == false) {
|
||||
res.status(404).end();
|
||||
global['logs'].info("[web] (404) path not found: "+joined_path);
|
||||
// path already cached; exist
|
||||
} else if(global['gds'].cache.web[joined_path] == true){
|
||||
let contentType = mime.contentType(path.extname(joined_path));
|
||||
res.setHeader('Content-Type', contentType);
|
||||
fs.createReadStream(joined_path).pipe(res);
|
||||
// check path
|
||||
} else {
|
||||
fs.exists(joined_path, (exists) => {
|
||||
global['gds'].cache.web[joined_path] = exists;
|
||||
if(exists) {
|
||||
let contentType = mime.contentType(path.extname(joined_path));
|
||||
res.setHeader('Content-Type', contentType);
|
||||
fs.exists(joined_path, (exists) => {
|
||||
if(exists) {
|
||||
let contentType = mime.contentType(path.extname(joined_path));
|
||||
res.setHeader('Content-Type', contentType);
|
||||
|
||||
fs.createReadStream(joined_path).pipe(res);
|
||||
} else {
|
||||
res.status(404).end();
|
||||
global['logs'].info("[web] (404) path not found: "+joined_path);
|
||||
}
|
||||
});
|
||||
}
|
||||
fs.createReadStream(joined_path).pipe(res);
|
||||
} else {
|
||||
res.status(404).end();
|
||||
global['logs'].info("[web] (404) path not found: "+joined_path);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// BodyParser & CookieParser
|
||||
|
|
|
@ -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({
|
||||
|
@ -233,16 +233,6 @@ getRoutes = async () => {
|
|||
}
|
||||
});
|
||||
|
||||
if(global['gds'].debug) {
|
||||
// DEBUG info
|
||||
route.get('/info', (req, res) => {
|
||||
let obj = {};
|
||||
if(req.session) obj.session = req.session;
|
||||
if(req.cookies) obj.cookie = req.cookies;
|
||||
res.type('json').end(JSON.stringify(obj));
|
||||
});
|
||||
}
|
||||
|
||||
return route;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,11 +7,6 @@
|
|||
/**
|
||||
* EXPLANATIONS:
|
||||
*
|
||||
* groups: ["anon", "user", "admin"]
|
||||
* - anon: not logged in; no cookies
|
||||
* - user: logged in; non-special group
|
||||
* - admin: logged in; admin group 999 or equivalent
|
||||
*
|
||||
* expressions: RegExp tested on req.path
|
||||
* - ex.:
|
||||
* - req.path = "/profile/456";
|
||||
|
@ -25,44 +20,8 @@
|
|||
* - 404: File not found
|
||||
* - missing_permission: Missing Permission page
|
||||
* - login: login page
|
||||
*
|
||||
* NOW ADDED TO DATABASE
|
||||
*/
|
||||
let rules = [
|
||||
{
|
||||
group: "anon",
|
||||
expression: "(/blocks/.*)",
|
||||
rule: "block",
|
||||
type: "404"
|
||||
},
|
||||
{
|
||||
group: "anon",
|
||||
expression: "(/error/.*)",
|
||||
rule: "block",
|
||||
type: "404"
|
||||
},
|
||||
{
|
||||
group: "anon",
|
||||
expression: "(/admin/.*)",
|
||||
rule: "block",
|
||||
type: "login"
|
||||
},
|
||||
{
|
||||
group: "user",
|
||||
expression: "(/blocks/.*)",
|
||||
rule: "block",
|
||||
type: "404"
|
||||
},
|
||||
{
|
||||
group: "user",
|
||||
expression: "(/error/.*)",
|
||||
rule: "block",
|
||||
type: "404"
|
||||
},
|
||||
{
|
||||
group: "user",
|
||||
expression: "(/admin/.*)",
|
||||
rule: "block",
|
||||
type: "missing_permission"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = rules;
|
||||
module.exports = [];
|
||||
|
|
|
@ -13,26 +13,6 @@ path = require('path');
|
|||
|
||||
var cfg = require(global['__dirname']+'/bin/config');
|
||||
|
||||
// reduce IO file checks - save file state in cache
|
||||
var fileCheck = (file) => {
|
||||
if(typeof global['gds'].cache.web == 'undefined') global['gds'].cache.web = {};
|
||||
let dir = global['__dirname'] + '/bin/web/views';
|
||||
let path_j = path.join(dir, file.toLowerCase());
|
||||
if(typeof global['gds'].cache.web[path_j] == 'undefined') {
|
||||
if(fs.existsSync(path_j+'.pug')) {
|
||||
global['gds'].cache.web[path_j] = true;
|
||||
} else {
|
||||
global['gds'].cache.web[path_j] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(global['gds'].cache.web[path_j] === true) {
|
||||
return path_j;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let getRoutes = async () => {
|
||||
let db = global['requireModule']('database');
|
||||
await db.connect();
|
||||
|
@ -40,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) => {
|
||||
|
@ -75,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)
|
||||
|
@ -102,7 +91,10 @@ let getRoutes = async () => {
|
|||
|
||||
}
|
||||
|
||||
res.render('login', view_obj);
|
||||
return res.render('login', view_obj);
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -110,30 +102,36 @@ let getRoutes = async () => {
|
|||
* all other routes
|
||||
* @url /*
|
||||
* @method all
|
||||
* @TODO comments
|
||||
*/
|
||||
route.all('/*', asyncer(async (req, res, next) => {
|
||||
route.get('/*', asyncer(async (req, res, next) => {
|
||||
// passthrough to next route
|
||||
if(req.path.startsWith('/api'))
|
||||
return next();
|
||||
|
||||
if(req.path == "/request") return res.render('error/404');
|
||||
|
||||
let pathRules = require("./rules");
|
||||
let pathRules = await db.getPathRules();
|
||||
|
||||
let group = "anon";
|
||||
// retrieve guest group - set as default
|
||||
let groups = await db.getGroups();
|
||||
guestId = null;
|
||||
groups.reply.forEach((group) => {
|
||||
if(group.name == "Guest") guestId = group._id;
|
||||
});
|
||||
let group = guestId;
|
||||
|
||||
// set user group
|
||||
if(req.session && req.session.user) {
|
||||
group = "user";
|
||||
if(req.session.user.group == 999) group = "admin";
|
||||
group = req.session.user.group;
|
||||
}
|
||||
|
||||
pathRules.forEach((rule) => {
|
||||
for(i = 0; i < pathRules.reply.length; i++) {
|
||||
rule = pathRules.reply[i];
|
||||
if(rule.rule == "block") {
|
||||
if(group == rule.group) {
|
||||
if(group == String(rule.group)) {
|
||||
let regex = new RegExp(rule.expression, "g");
|
||||
if(regex.test(req.path)) {
|
||||
if(rule.type == "404") {
|
||||
global['logs'].info("[web] (404) path not found: "+req.path);
|
||||
return res.status(404).render('error/404', {
|
||||
error_code: 404,
|
||||
error_msg: 'msg.request.file.not_found',
|
||||
|
@ -146,7 +144,7 @@ let getRoutes = async () => {
|
|||
session: req.session,
|
||||
cfg: cfg
|
||||
});
|
||||
} else if(rule.type == "login") {
|
||||
} else if(rule.type == "login" && (!req.session || !req.session.user)) {
|
||||
return res.status(401).render('error/login', {
|
||||
error_code: 401,
|
||||
session: req.session,
|
||||
|
@ -162,15 +160,13 @@ let getRoutes = async () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(fileCheck(req.path)) {
|
||||
// query apps
|
||||
apps = await db.getApps();
|
||||
};
|
||||
|
||||
let dir = global['__dirname'] + '/bin/web/views';
|
||||
let path_j = path.join(dir, req.path.toLowerCase());
|
||||
if(fs.existsSync(path_j+'.pug')) {
|
||||
return res.render(req.path.replace(/^\//, ''), {
|
||||
session: req.session,
|
||||
apps: apps.reply,
|
||||
cfg: cfg
|
||||
});
|
||||
} else {
|
||||
|
@ -182,10 +178,6 @@ let getRoutes = async () => {
|
|||
cfg: cfg
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: try to login
|
||||
// TODO: role-based authorization
|
||||
// TODO: show login page or page
|
||||
}));
|
||||
|
||||
return route;
|
||||
|
|
|
@ -7,7 +7,10 @@ append var
|
|||
div(class="uk-width-auto uk-width-1-4@s")
|
||||
.uk-flex.uk-flex-auto.uk-flex-column.uk-flex-center.uk-margin-left.uk-margin-right
|
||||
h1 Please login
|
||||
form.uk-form-horizontal
|
||||
#login_msg.uk-alert(data-uk-alert).uk-hidden
|
||||
a.uk-close-alt.uk-alert-close(href="#")
|
||||
p
|
||||
form.uk-form-horizontal(onsubmit="return loginEvent();")
|
||||
.uk-margin
|
||||
label.uk-form-label(for="login_user") Username / Email
|
||||
.uk-form-controls
|
||||
|
@ -16,5 +19,6 @@ append var
|
|||
label.uk-form-label(for="login_pass") Password
|
||||
.uk-form-controls
|
||||
input.uk-input#login_pass(type="password")
|
||||
a(href="/login").uk-button.uk-button-default Login
|
||||
input(hidden,type="submit")
|
||||
button(onclick="login()").uk-button.uk-button-default Login
|
||||
div(class="uk-width-auto uk-width-1-4@s")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
function loginEvent() {
|
||||
login();
|
||||
return false;
|
||||
}
|
||||
|
||||
// '/login', login user
|
||||
function loginEvent() { login(); return false; } // btnEvent handling
|
||||
function login() {
|
||||
let user = document.getElementById("login_user").value;
|
||||
let pass = document.getElementById("login_pass").value;
|
||||
|
@ -42,6 +41,7 @@ function login() {
|
|||
};
|
||||
};
|
||||
|
||||
// '/logout', logout user
|
||||
function logout() {
|
||||
let ajax = new XMLHttpRequest();
|
||||
ajax.open("GET", "/api/logout", true);
|
||||
|
@ -58,6 +58,7 @@ setTimeout(function () {
|
|||
}
|
||||
}, 100);
|
||||
|
||||
// '/authenticate', cancels Authentication
|
||||
function cancelRequest() {
|
||||
let ajax = new XMLHttpRequest();
|
||||
ajax.open("GET", "/api/cancel", true);
|
||||
|
|
Loading…
Reference in New Issue