From b8f632e6441f3827822ddba0634ce2e66cc1e74f Mon Sep 17 00:00:00 2001 From: William Mantly Date: Fri, 15 May 2020 15:17:57 -0400 Subject: [PATCH] user edit --- nodejs/models/group_ldap.js | 18 +-- nodejs/models/user_ldap.js | 162 +++++++++++++------- nodejs/public/js/app.js | 15 +- nodejs/public/js/val.js | 11 +- nodejs/routes/auth.js | 2 +- nodejs/routes/group.js | 6 +- nodejs/routes/index.js | 4 + nodejs/routes/user.js | 28 +++- nodejs/views/groups.ejs | 9 +- nodejs/views/home.ejs | 255 ++++++++++++++++++++++++-------- nodejs/views/invite.ejs | 2 +- nodejs/views/invite_email.ejs | 3 +- nodejs/views/login.ejs | 5 +- nodejs/views/reset_password.ejs | 4 +- nodejs/views/user_form.ejs | 13 +- nodejs/views/users.ejs | 37 +++-- 16 files changed, 411 insertions(+), 163 deletions(-) diff --git a/nodejs/models/group_ldap.js b/nodejs/models/group_ldap.js index 69c47ee..7231a92 100644 --- a/nodejs/models/group_ldap.js +++ b/nodejs/models/group_ldap.js @@ -7,11 +7,14 @@ const client = new Client({ url: conf.url, }); -async function getGroups(client){ +async function getGroups(client, member){ try{ + + let memberFilter = member ? `(member=${member})`: '' + let groups = (await client.search(conf.groupBase, { scope: 'sub', - filter: '(&(objectClass=groupOfNames))', + filter: `(&(objectClass=groupOfNames)${memberFilter})`, attributes: ['*', 'createTimestamp', 'modifyTimestamp'], })).searchEntries; @@ -81,11 +84,11 @@ async function removeMember(client, group, user){ var Group = {}; -Group.list = async function(){ +Group.list = async function(member){ try{ await client.bind(conf.bindDN, conf.bindPassword); - let groups = await getGroups(client) + let groups = await getGroups(client, member) await client.unbind(); @@ -95,11 +98,11 @@ Group.list = async function(){ } } -Group.listDetail = async function(){ +Group.listDetail = async function(member){ try{ await client.bind(conf.bindDN, conf.bindPassword); - let groups = await getGroups(client) + let groups = await getGroups(client, member) await client.unbind(); @@ -207,7 +210,4 @@ Group.remove = async function(){ } } - - - module.exports = {Group}; diff --git a/nodejs/models/user_ldap.js b/nodejs/models/user_ldap.js index 1d46adc..8a3d166 100644 --- a/nodejs/models/user_ldap.js +++ b/nodejs/models/user_ldap.js @@ -12,75 +12,84 @@ const client = new Client({ }); async function addPosixGroup(client, data){ + try{ - const groups = (await client.search(conf.groupBase, { - scope: 'sub', - filter: '(&(objectClass=posixGroup))', - })).searchEntries; + const groups = (await client.search(conf.groupBase, { + scope: 'sub', + filter: '(&(objectClass=posixGroup))', + })).searchEntries; - data.gidNumber = (Math.max(...groups.map(i => i.gidNumber))+1)+''; + data.gidNumber = (Math.max(...groups.map(i => i.gidNumber))+1)+''; - await client.add(`cn=${data.cn},${conf.groupBase}`, { - cn: data.cn, - gidNumber: data.gidNumber, - objectclass: [ 'posixGroup', 'top' ] - }); + await client.add(`cn=${data.cn},${conf.groupBase}`, { + cn: data.cn, + gidNumber: data.gidNumber, + objectclass: [ 'posixGroup', 'top' ] + }); - return data; + return data; }catch(error){ - throw error; + throw error; } } async function addPosixAccount(client, data){ try{ - const people = (await client.search(conf.userBase, { - scope: 'sub', - filter: conf.userFilter, - })).searchEntries; + const people = (await client.search(conf.userBase, { + scope: 'sub', + filter: conf.userFilter, + })).searchEntries; - data.uidNumber = (Math.max(...people.map(i => i.uidNumber))+1)+''; + data.uidNumber = (Math.max(...people.map(i => i.uidNumber))+1)+''; - await client.add(`cn=${data.cn},${conf.userBase}`, { - cn: data.cn, - sn: data.sn, - uid: data.uid, - uidNumber: data.uidNumber, - gidNumber: data.gidNumber, - givenName: data.givenName, - mail: data.mail, - mobile: data.mobile, - loginShell: data.loginShell, - homeDirectory: data.homeDirectory, - userPassword: data.userPassword, - objectclass: [ 'inetOrgPerson', 'posixAccount', 'top' ] - }); + await client.add(`cn=${data.cn},${conf.userBase}`, { + cn: data.cn, + sn: data.sn, + uid: data.uid, + uidNumber: data.uidNumber, + gidNumber: data.gidNumber, + givenName: data.givenName, + mail: data.mail, + mobile: data.mobile, + loginShell: data.loginShell, + homeDirectory: data.homeDirectory, + userPassword: data.userPassword, + description: data.description || ' ', + sudoHost: 'ALL', + sudoCommand: 'ALL', + sudoUser: data.uid, + sshPublicKey: data.sshPublicKey, + objectclass: ['inetOrgPerson', 'sudoRole', 'ldapPublicKey', 'posixAccount', 'top' ] + }); - return data + return data }catch(error){ - throw error; + throw error; } } async function addLdapUser(client, data){ + var group; + try{ - data.uid = `${data.givenName[0]}${data.sn}`; - data.cn = data.uid; - data.loginShell = '/bin/bash'; - data.homeDirectory= `/home/${data.uid}`; - data.userPassword = '{MD5}'+crypto.createHash('md5').update(data.userPassword, "binary").digest('base64'); + data.uid = `${data.givenName[0]}${data.sn}`; + data.cn = data.uid; + data.loginShell = '/bin/bash'; + data.homeDirectory= `/home/${data.uid}`; + data.userPassword = '{MD5}'+crypto.createHash('md5').update(data.userPassword, "binary").digest('base64'); - data = await addPosixGroup(client, data); - data = await addPosixAccount(client, data); + group = await addPosixGroup(client, data); + data = await addPosixAccount(client, group); - return data; + return data; }catch(error){ - throw error; + await deleteLdapDN(client, `cn=${data.uid},${conf.groupBase}`, true); + throw error; } } @@ -93,9 +102,19 @@ async function deleteLdapUser(client, data){ } } +async function deleteLdapDN(client, dn, ignoreError){ + try{ + client.del(dn) + }catch(error){ + if(!ignoreError) throw error; + console.error('ERROR: deleteLdapDN', error) + } +} + const user_parse = function(data){ if(data[conf.userNameAttribute]){ data.username = data[conf.userNameAttribute] + data.userPassword = undefined; } return data; @@ -152,23 +171,22 @@ User.listDetail = async function(){ } }; -User.get = async function(data, value){ +User.get = async function(data, key){ try{ if(typeof data !== 'object'){ let uid = data; data = {}; data.uid = uid; } - + + await client.bind(conf.bindDN, conf.bindPassword); - data.searchKey = data.searchKey || conf.userNameAttribute; + data.searchKey = data.searchKey || key || conf.userNameAttribute; data.searchValue = data.searchValue || data.uid; let filter = `(&${conf.userFilter}(${data.searchKey}=${data.searchValue}))`; - console.log('get filter', filter) - const res = await client.search(conf.userBase, { scope: 'sub', filter: filter, @@ -196,10 +214,10 @@ User.get = async function(data, value){ } }; -User.exists = async function(data){ +User.exists = async function(data, key){ // Return true or false if the requested entry exists ignoring error's. try{ - await this.get(data); + await this.get(data, key); return true }catch(error){ @@ -241,6 +259,35 @@ User.add = async function(data) { } }; +User.update = async function(data){ + try{ + let editableFeilds = ['mobile', 'sshPublicKey', 'description']; + + await client.bind(conf.bindDN, conf.bindPassword); + + for(let field of editableFeilds){ + if(data[field]){ + await client.modify(this.dn, [ + new Change({ + operation: 'replace', + modification: new Attribute({ + type: field, + values: [data[field]] + }) + }), + ]); + } + } + + await client.unbind() + + return this; + + }catch(error){ + throw error; + } +}; + User.addByInvite = async function(data){ try{ let token = await InviteToken.get(data.token); @@ -270,6 +317,11 @@ User.addByInvite = async function(data){ User.verifyEmail = async function(data){ try{ + + let exists = await this.exists(data.mail, 'mail'); + + if(exists) throw new Error('EmailInUse'); + let token = await InviteToken.get(data.token); await token.update({mail: data.mail}) await Mail.sendTemplate( @@ -294,8 +346,6 @@ User.passwordReset = async function(url, mail){ searchValue: mail }); - console.log('user', user) - let token = await PasswordResetToken.add(user); await Mail.sendTemplate( @@ -338,11 +388,11 @@ User.setPassword = async function(data){ await client.modify(this.dn, [ new Change({ - operation: 'replace', - modification: new Attribute({ - type: 'userPassword', - values: ['{MD5}'+crypto.createHash('md5').update(data.userPassword, "binary").digest('base64')] - })}), + operation: 'replace', + modification: new Attribute({ + type: 'userPassword', + values: ['{MD5}'+crypto.createHash('md5').update(data.userPassword, "binary").digest('base64')] + })}), ]); await client.unbind(); diff --git a/nodejs/public/js/app.js b/nodejs/public/js/app.js index 4d7e0b4..ab0afef 100755 --- a/nodejs/public/js/app.js +++ b/nodejs/public/js/app.js @@ -86,6 +86,7 @@ app.api = (function(app){ })(app) app.auth = (function(app) { + var user = {} function setToken(token){ localStorage.setItem('APIToken', token); } @@ -97,6 +98,7 @@ app.auth = (function(app) { function isLoggedIn(callack){ if(getToken()){ return app.api.get('user/me', function(error, data){ + if(!error) app.auth.user = data; return callack(error, data); }); }else{ @@ -153,6 +155,7 @@ app.user = (function(app){ } function remove(args, callack){ + if(!confirm('Delete '+ args.uid+ 'user?')) return false; app.api.delete('user/'+ args.uid, function(error, data){ callack(error, data); }); @@ -298,8 +301,6 @@ app.util = (function(app){ $.holdReady( true ); if(!location.pathname.includes('/login')){ app.auth.isLoggedIn(function(error, isLoggedIn){ - console.log('here', error, isLoggedIn) - if(error || !isLoggedIn){ app.auth.logOut(function(){}) location.replace('/login/?redirect='+location.pathname); @@ -335,17 +336,17 @@ function formAJAX( btn, del ) { var formData = $form.find( '[name]' ).serializeObject(); // builds query formDataing var method = $form.attr('method') || 'post'; + if( !$form.validate()) { + app.util.actionMessage('Please fix the form errors.', $form, 'danger') + return false; + } + app.util.actionMessage( '
Loading...
', $form, 'info' ); - if( !$form.validate()) { - app.util.actionMessage('Please fix the form errors.', $form, 'danger') - return false; - } - app.api[method]($form.attr('action'), formData, function(error, data){ app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table if(!error){ diff --git a/nodejs/public/js/val.js b/nodejs/public/js/val.js index 331295a..5372bf4 100755 --- a/nodejs/public/js/val.js +++ b/nodejs/public/js/val.js @@ -142,6 +142,15 @@ $.validateSettings({ if ( reg.test( value ) === false ) { return "Weak password, Try again"; } - } + }, + email: function( value ){ + + //validated email address + //more testing + var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i); + if( !pattern.test( value ) ){ + return 'Invalid'; + } + }, } }); \ No newline at end of file diff --git a/nodejs/routes/auth.js b/nodejs/routes/auth.js index 12b1c26..8cdaa9b 100755 --- a/nodejs/routes/auth.js +++ b/nodejs/routes/auth.js @@ -35,7 +35,7 @@ router.post('/resetpassword', async function(req, res, next){ try{ let sent = await User.passwordReset(`${req.protocol}://${req.hostname}`, req.body.mail); - console.info('resetpassword for', req.body.mail, sent) + console.info('resetpassword for', req.body.mail, 'sent') return res.json({ message: 'If the emaill address is in our system, you will receive a message.' diff --git a/nodejs/routes/group.js b/nodejs/routes/group.js index 18bbee7..685b8b5 100644 --- a/nodejs/routes/group.js +++ b/nodejs/routes/group.js @@ -6,8 +6,12 @@ const {Group} = require('../models/group_ldap'); router.get('/', async function(req, res, next){ try{ + let member = req.query.member ? await User.get(req.query.member) : {} + + console.log('member', member) + return res.json({ - results: await Group[req.query.detail ? "listDetail" : "list"]() + results: await Group[req.query.detail ? "listDetail" : "list"](member.dn) }); }catch(error){ next(error); diff --git a/nodejs/routes/index.js b/nodejs/routes/index.js index 461505e..981d405 100755 --- a/nodejs/routes/index.js +++ b/nodejs/routes/index.js @@ -16,6 +16,10 @@ router.get('/users', function(req, res, next) { res.render('users', { title: 'Express' }); }); +router.get('/users/:uid', function(req, res, next) { + res.render('home', { title: 'Express' }); +}); + router.get('/groups', function(req, res, next) { res.render('groups', { title: 'Express' }); }); diff --git a/nodejs/routes/user.js b/nodejs/routes/user.js index 8531718..cbe3a1a 100755 --- a/nodejs/routes/user.js +++ b/nodejs/routes/user.js @@ -27,14 +27,28 @@ router.delete('/:uid', async function(req, res, next){ try{ let user = await User.get(req.params.uid); - console.log('delete user', user); - return res.json({uid: req.params.uid, results: await user.remove()}) }catch(error){ next(error); } }); +router.put('/:uid', async function(req, res, next){ + try{ + let user = await User.get(req.params.uid); + + // console.log('update user', user); + + return res.json({ + results: await user.update(req.body), + message: `Updated ${req.params.uid} user` + + }); + }catch(error){ + next(error); + } +}); + router.get('/me', async function(req, res, next){ try{ @@ -91,4 +105,14 @@ router.post('/key', async function(req, res, next){ }); +router.get('/:uid', async function(req, res, next){ + try{ + return res.json({ + results: await User.get(req.params.uid), + }); + }catch(error){ + next(error); + } +}); + module.exports = router; diff --git a/nodejs/views/groups.ejs b/nodejs/views/groups.ejs index 847b80c..f7a47cc 100644 --- a/nodejs/views/groups.ejs +++ b/nodejs/views/groups.ejs @@ -1,7 +1,7 @@ <%- include('top') %>