LDAP users management

This commit is contained in:
Sébastien Hut 2015-03-19 16:41:32 +01:00
parent fc894a4c35
commit 1b15f112d8
16 changed files with 885 additions and 15 deletions

View File

@ -9,6 +9,7 @@ var StanzaIO = require('stanza.io');
var AppState = require('./models/state');
var MeModel = require('./models/me');
var LdapUsers = require('./models/ldapUsers');
var MainView = require('./views/main');
var Router = require('./router');
var Storage = require('./storage');
@ -32,8 +33,8 @@ module.exports = {
return;
}
config = JSON.parse(config);
config.useStreamManagement = true;
app.config = JSON.parse(config);
app.config.useStreamManagement = true;
_.extend(this, Backbone.Events);
@ -51,11 +52,11 @@ module.exports = {
app.mucInfos = [];
},
function (cb) {
app.storage.profiles.get(config.jid, function (err, res) {
app.storage.profiles.get(app.config.jid, function (err, res) {
if (res) {
profile = res;
profile.jid = {full: config.jid, bare: config.jid};
config.rosterVer = res.rosterVer;
profile.jid = {full: app.config.jid, bare: app.config.jid};
app.config.rosterVer = res.rosterVer;
}
cb();
});
@ -70,7 +71,7 @@ module.exports = {
}
};
self.api = window.client = StanzaIO.createClient(config);
self.api = window.client = StanzaIO.createClient(app.config);
client.use(pushNotifications);
xmppEventHandlers(self.api, self);
@ -121,6 +122,8 @@ module.exports = {
});
self.view.render();
app.ldapUsers = new LdapUsers();
if (me.contacts.length) {
start();
} else {

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,79 @@
/*global app, me, client, URL*/
"use strict";
var _ = require('underscore');
var HumanModel = require('human-model');
exports.ldapData = function (data) {
if (!data) data = {};
data.uid = app.config.jid.substring(0, app.config.jid.indexOf('@'));
data.password = app.config.credentials.password;
return data;
}
exports.user = HumanModel.define({
initialize: function (attrs) {
},
type: 'ldapUser',
props: {
id: ['string', true, false],
cn: ['string', true, false],
sn: ['string', false, ''],
givenName: ['string', false, ''],
displayName: ['string', false, ''],
mail: ['string', false, ''],
objectClass: ['array', false, []]
},
save: function(userInfos, cb) {
var change = false;
var oldValues = {};
for (var property in userInfos) {
if (userInfos.hasOwnProperty(property) && this[property] !== undefined) {
oldValues[property] = this[property];
if (this[property] != userInfos[property])
change = true;
}
}
if (!change)
return;
var self = this;
$.post('/ldap/user/' + this.id, exports.ldapData(userInfos), function(result) {
result = JSON.parse(result);
if (result) {
for (var property in userInfos) {
if (userInfos.hasOwnProperty(property) && self[property] !== undefined) {
self[property] = userInfos[property];
}
}
}
else {
for (var property in oldValues) {
if (oldValues.hasOwnProperty(property) && self[property] !== undefined) {
self[property] = '';
self[property] = oldValues[property];
}
}
}
cb();
});
},
changePassword: function(newPassword) {
var self = this;
$.post('/ldap/user/' + this.id + '/password', exports.ldapData({newPassword: newPassword}), function(result) {
result = JSON.parse(result);
if (!result) {
app.ldapUsers.fetch();
}
});
},
meIsAdmin: function() {
return me.isAdmin;
}
});

View File

@ -0,0 +1,71 @@
/*global app, client*/
"use strict";
var BaseCollection = require('./baseCollection');
var ldapUser = require('./ldapUser');
module.exports = BaseCollection.extend({
type: 'ldapUsers',
model: ldapUser.user,
comparator: function (model1, model2) {
var name1 = model1.displayName.toLowerCase();
var name2 = model2.displayName.toLowerCase();
if (name1 === name2) {
return 0;
}
if (name1 < name2) {
return -1;
}
return 1;
},
initialize: function (model, options) {
this.bind('change', this.sort, this);
},
fetch: function (cb) {
var self = this;
$.post('/ldap/users', ldapUser.ldapData(), function(users) {
var toRemove = [];
for ( var i = 0; i < self.models.length; i++) {
toRemove.push(self.models[i].id);
}
users = JSON.parse(users);
users.forEach(function(user) {
var existing = self.get(user.id);
if (!existing) {
self.add(user);
}
var index = toRemove.indexOf(user.id);
if (index > -1) {
toRemove.splice(index, 1);
}
});
self.remove(toRemove);
if (cb) cb();
});
},
addUser: function (id) {
var self = this;
$.post('/ldap/users/add', ldapUser.ldapData({newUid: id}), function(result) {
result = JSON.parse(result);
if (result) {
self.fetch();
}
});
},
deleteUser: function (id) {
var self = this;
$.post('/ldap/users/delete', ldapUser.ldapData({removeUid: id}), function(result) {
result = JSON.parse(result);
if (result) {
self.fetch();
}
});
}
});

View File

@ -76,6 +76,12 @@ module.exports = HumanModel.define({
return this.soundEnabled ? "primary" : "secondary";
}
},
isAdmin: {
deps: ['jid'],
fn: function () {
return this.jid.local === SERVER_CONFIG.admin ? 'meIsAdmin' : '';
}
}
},
setActiveContact: function (jid) {
var prev = this.getContact(this._activeContact);
@ -105,6 +111,9 @@ module.exports = HumanModel.define({
self.avatar = avatar.uri;
});
},
hasLdapUsers: function () {
return app.ldapUsers.length > 0 ? 'hasLdapUsers' : '';
},
setSoundNotification: function(enable) {
this.soundEnabled = enable;
},

View File

@ -4,13 +4,15 @@
var crypto = require('crypto');
var BasePage = require('./base');
var templates = require('../templates');
var LDAPUserItem = require('../views/ldapUserItem');
module.exports = BasePage.extend({
template: templates.pages.settings,
classBindings: {
shouldAskForAlertsPermission: '.enableAlerts',
soundEnabledClass: '.soundNotifs'
soundEnabledClass: '.soundNotifs',
hasLdapUsers: '#ldapSettings',
isAdmin: '#newLdapUser'
},
srcBindings: {
avatar: '#avatarChanger img'
@ -24,13 +26,19 @@ module.exports = BasePage.extend({
'click .soundNotifs': 'handleSoundNotifs',
'dragover': 'handleAvatarChangeDragOver',
'drop': 'handleAvatarChange',
'change #uploader': 'handleAvatarChange'
'change #uploader': 'handleAvatarChange',
'keydown #newLdapUser': 'addLdapUser',
},
initialize: function (spec) {
this.render();
this.listenTo(this, 'deleteLdapUser', this.deleteLdapUser);
var self = this;
app.ldapUsers.fetch(function () {
self.render();
});
},
render: function () {
this.renderAndBind();
this.renderCollection(app.ldapUsers, LDAPUserItem, this.$('#ldapUsers'));
return this;
},
enableAlerts: function () {
@ -93,4 +101,14 @@ module.exports = BasePage.extend({
handleSoundNotifs: function (e) {
this.model.setSoundNotification(!this.model.soundEnabled);
},
addLdapUser: function (e) {
if (e.which === 13 && !e.shiftKey) {
var id = e.target.value;
e.target.value = '';
app.ldapUsers.addUser(id);
}
},
deleteLdapUser: function (id) {
app.ldapUsers.deleteUser(id);
}
});

View File

@ -207,6 +207,15 @@ exports.includes.embeds = function anonymous(locals) {
return buf.join("");
};
// ldapUserItem.jade compiled template
exports.includes.ldapUserItem = function anonymous(locals) {
var buf = [];
with (locals || {}) {
buf.push('<li class="ldapUser"><span class="name"></span><span class="delete fa fa-trash"></span><span class="fa fa-plus"></span><div class="wrap"><span class="inputLabel">Display Name</span><input type="text" class="displayName"/><span class="inputLabel">First Name</span><input type="text" class="givenName"/><span class="inputLabel">Last Name</span><input type="text" class="sn"/><span class="inputLabel">Email</span><input type="text" class="mail"/><button class="primary small changePassword">Change Password</button></div></li>');
}
return buf.join("");
};
// message.jade compiled template
exports.includes.message = function anonymous(locals) {
var buf = [];
@ -512,7 +521,7 @@ exports.pages.groupchat = function anonymous(locals) {
exports.pages.settings = function anonymous(locals) {
var buf = [];
with (locals || {}) {
buf.push('<section class="page main"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 30 30" height="30" width="30"><g transform="scale(0.5)"><path d="M37.418,34.3c-2.1-2.721-2.622-6.352-1.292-9.604c0.452-1.107,1.104-2.1,1.902-2.951 c-0.753-0.877-1.573-1.697-2.507-2.387l-2.609,1.408c-1.05-0.629-2.194-1.112-3.414-1.421l-0.845-2.833 c-0.75-0.112-1.512-0.188-2.287-0.188c-0.783,0-1.54,0.075-2.288,0.188l-0.851,2.833c-1.215,0.309-2.355,0.792-3.41,1.421 l-2.614-1.408c-1.229,0.912-2.318,2-3.228,3.231l1.404,2.612c-0.628,1.053-1.11,2.193-1.419,3.411l-2.832,0.849 c-0.114,0.75-0.187,1.508-0.187,2.287c0,0.778,0.073,1.537,0.187,2.286l2.832,0.848c0.309,1.22,0.791,2.36,1.419,3.413l-1.404,2.61 c0.909,1.231,1.999,2.321,3.228,3.231l2.614-1.406c1.055,0.628,2.195,1.11,3.41,1.42l0.851,2.832 c0.748,0.114,1.505,0.188,2.288,0.188c0.775,0,1.537-0.074,2.287-0.188l0.845-2.832c1.224-0.31,2.364-0.792,3.414-1.42l0.062,0.033 l2.045-3.02L37.418,34.3z M26.367,36.776c-2.777,0-5.027-2.253-5.027-5.027c0-2.775,2.25-5.028,5.027-5.028 c2.774,0,5.024,2.253,5.024,5.028C31.391,34.523,29.141,36.776,26.367,36.776z"></path><path d="M51.762,24.505l-1.125-0.459l-1.451,3.55c-0.814,1.993-2.832,3.054-4.505,2.37l-0.355-0.144 c-1.673-0.686-2.37-2.856-1.558-4.849l1.451-3.551l-1.125-0.46c-2.225,0.608-4.153,2.2-5.092,4.501 c-1.225,2.997-0.422,6.312,1.771,8.436l-2.958,6.812l-2.204,3.249l-0.007,2.281l5.275,2.154l1.593-1.633l0.7-3.861l2.901-6.836 c3.049,0.018,5.947-1.785,7.174-4.779C53.186,28.983,52.924,26.499,51.762,24.505z"></path></g></svg><h1 id="title">Settings</h1><div id="avatarChanger"><h4>Change Avatar</h4><div class="uploadRegion"><p>Drag and drop a new avatar here</p><img/><form><input id="uploader" type="file"/></form></div></div><div><h4>Desktop Integration</h4><button class="enableAlerts">Enable alerts</button><button class="primary installFirefox">Install app</button><button class="soundNotifs">sound notifications</button></div><div><button class="logout">Logout</button></div></section>');
buf.push('<section class="page main"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 30 30" height="30" width="30"><g transform="scale(0.5)"><path d="M37.418,34.3c-2.1-2.721-2.622-6.352-1.292-9.604c0.452-1.107,1.104-2.1,1.902-2.951 c-0.753-0.877-1.573-1.697-2.507-2.387l-2.609,1.408c-1.05-0.629-2.194-1.112-3.414-1.421l-0.845-2.833 c-0.75-0.112-1.512-0.188-2.287-0.188c-0.783,0-1.54,0.075-2.288,0.188l-0.851,2.833c-1.215,0.309-2.355,0.792-3.41,1.421 l-2.614-1.408c-1.229,0.912-2.318,2-3.228,3.231l1.404,2.612c-0.628,1.053-1.11,2.193-1.419,3.411l-2.832,0.849 c-0.114,0.75-0.187,1.508-0.187,2.287c0,0.778,0.073,1.537,0.187,2.286l2.832,0.848c0.309,1.22,0.791,2.36,1.419,3.413l-1.404,2.61 c0.909,1.231,1.999,2.321,3.228,3.231l2.614-1.406c1.055,0.628,2.195,1.11,3.41,1.42l0.851,2.832 c0.748,0.114,1.505,0.188,2.288,0.188c0.775,0,1.537-0.074,2.287-0.188l0.845-2.832c1.224-0.31,2.364-0.792,3.414-1.42l0.062,0.033 l2.045-3.02L37.418,34.3z M26.367,36.776c-2.777,0-5.027-2.253-5.027-5.027c0-2.775,2.25-5.028,5.027-5.028 c2.774,0,5.024,2.253,5.024,5.028C31.391,34.523,29.141,36.776,26.367,36.776z"></path><path d="M51.762,24.505l-1.125-0.459l-1.451,3.55c-0.814,1.993-2.832,3.054-4.505,2.37l-0.355-0.144 c-1.673-0.686-2.37-2.856-1.558-4.849l1.451-3.551l-1.125-0.46c-2.225,0.608-4.153,2.2-5.092,4.501 c-1.225,2.997-0.422,6.312,1.771,8.436l-2.958,6.812l-2.204,3.249l-0.007,2.281l5.275,2.154l1.593-1.633l0.7-3.861l2.901-6.836 c3.049,0.018,5.947-1.785,7.174-4.779C53.186,28.983,52.924,26.499,51.762,24.505z"></path></g></svg><h1 id="title">Settings</h1><div id="avatarChanger"><h4>Change Avatar</h4><div class="uploadRegion"><p>Drag and drop a new avatar here</p><img/><form><input id="uploader" type="file"/></form></div></div><div><h4>Desktop Integration</h4><button class="enableAlerts">Enable alerts</button><button class="primary installFirefox">Install app</button><button class="soundNotifs">sound notifications</button></div><div id="ldapSettings"><h4>LDAP settings</h4><ul id="ldapUsers"></ul><input type="text" placeholder="add a ldap user" id="newLdapUser"/></div><div><button class="logout">Logout</button></div></section>');
}
return buf.join("");
};

View File

@ -0,0 +1,14 @@
li.ldapUser
span.name
span.delete.fa.fa-trash
span.fa.fa-plus
.wrap
span.inputLabel Display Name
input(type="text").displayName
span.inputLabel First Name
input(type="text").givenName
span.inputLabel Last Name
input(type="text").sn
span.inputLabel Email
input(type="text").mail
button.primary.small.changePassword Change Password

View File

@ -20,5 +20,10 @@ section.page.main
button.primary.installFirefox Install app
button.soundNotifs sound notifications
div#ldapSettings
h4 LDAP settings
ul#ldapUsers
input(type="text", placeholder="add a ldap user")#newLdapUser
div
button.logout Logout

View File

@ -0,0 +1,140 @@
/*global $, app, me*/
"use strict";
var _ = require('underscore');
var HumanView = require('human-view');
var templates = require('../templates');
module.exports = HumanView.extend({
template: templates.includes.ldapUserItem,
classBindings: {
meIsAdmin: '.delete, .fa-plus, .fa-minus, .wrap'
},
textBindings: {
id: '.name'
},
inputBindings: {
displayName: '.displayName',
givenName: '.givenName',
sn: '.sn',
mail: '.mail'
},
events: {
'click .fa-plus': 'handleDisplayUser',
'click .fa-minus': 'handleDisplayUser',
'click .delete': 'deleteUser',
'blur input': 'saveInfos',
'focus input': 'handleInputFocus',
'select input': 'handleInputSelect',
'click .changePassword': 'changePassword'
},
render: function () {
this.renderAndBind();
return this;
},
handleDisplayUser: function (e) {
var icon = $(e.target);
var wrap = $(e.delegateTarget).find('.wrap');
if (icon.hasClass('fa-plus')) {
wrap.show();
icon.removeClass('fa-plus');
icon.addClass('fa-minus');
} else {
wrap.hide();
icon.removeClass('fa-minus');
icon.addClass('fa-plus');
}
e.preventDefault();
},
handleInputFocus: function (e) {
this.inputWithFocus = e.target;
},
handleInputSelect: function (e) {
this.inputWithSelect = e.target;
},
saveInfos: function (e) {
var input = e.target;
var classes = $(input).attr('class');
var userInfos = {};
userInfos[classes] = input.value;
var ldapUser = this.model;
if (userInfos[classes] == ldapUser[classes])
return;
this.inputWithFocus = null;
this.inputWithSelect = null;
var self = this;
ldapUser.save(userInfos, function () {
if (self.inputWithSelect) {
$(self.inputWithSelect).select();
self.inputWithSelect = null;
self.inputWithFocus = null;
}
else if (self.inputWithFocus) {
$(self.inputWithFocus).focus();
self.inputWithFocus = null;
}
});
},
deleteUser: function (e) {
var self = this;
var ldapUser = this.model;
$.prompt('Are you sure you want to remove ' + ldapUser.id + '?', {
title: 'Remove User',
buttons: { "Yes": true, "Cancel": false },
persistent: true,
submit:function (e, v, m, f) {
if (v) {
self.parent.trigger('deleteLdapUser', ldapUser.id);
}
}
});
e.preventDefault();
},
changePassword: function (e) {
var ldapUser = this.model;
$.prompt({
state0: {
title: 'Change password',
html:'<label>New password for ' + this.model.id + ': <input type="password" name="newPassword1" value=""></label><br />',
buttons: { "Ok": true, "Cancel": false },
focus: "input[name='newPassword1']",
persistent: true,
submit:function (e, v, m, f) {
if (v) {
if (f.newPassword1 != '') {
e.preventDefault();
$.prompt.goToState('state1');
return false;
} else {
$.prompt('Password can not be an empty string.', { title: 'Change password' });
}
}
}
},
state1: {
title: 'Change password',
html:'<label>Confirm password for ' + this.model.id + ': <input type="password" name="newPassword2" value=""></label><br />',
buttons: { "Ok": true, "Cancel": false },
focus: "input[name='newPassword2']",
persistent: true,
submit:function (e, v, m, f) {
if (v) {
if (f.newPassword1 == f.newPassword2) {
ldapUser.changePassword(f.newPassword2);
} else {
$.prompt('the password confirmation must match your password.', { title: 'Change password' });
}
}
}
}
});
}
});

View File

@ -16,5 +16,13 @@
"muc": "chat.localhost",
"startup": "",
"admin": ""
},
"ldap": {
"address": "127.0.0.1",
"user": "cn=admin,dc=example.com",
"password": "password",
"base": "ou=users,dc=example.com",
"filter": "objectClass=person",
"group": "cn=mygroup,ou=groups,dc=example.com"
}
}

View File

@ -11,6 +11,7 @@
"attachmediastream": "1.0.1",
"backbone": "1.0.0",
"bluebird": "^2.3.2",
"body-parser": "1.12.0",
"bows": "0.3.0",
"browserify": "4.x",
"compression": "1.2.2",
@ -23,6 +24,7 @@
"human-view": "1.8.0",
"jade": "1.8.2",
"jxt": "^2.7.0",
"LDAP": "1.2.1",
"moonboots-express": "2.x",
"node-uuid": "^1.4.1",
"notify.js": "0.0.3",

View File

@ -0,0 +1,124 @@
/*! jQuery-Impromptu - v6.0.0 - 2014-12-27
* http://trentrichardson.com/Impromptu
* Copyright (c) 2014 Trent Richardson; Licensed MIT */
.jqifade{
position: absolute;
background-color: #777777;
}
div.jqi{
width: 400px;
max-width:90%;
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
position: absolute;
background-color: #ffffff;
font-size: 11px;
text-align: left;
border: solid 1px #eeeeee;
border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
padding: 7px;
}
div.jqi .jqicontainer{
}
div.jqi .jqiclose{
position: absolute;
top: 4px; right: -2px;
width: 18px;
cursor: default;
color: #bbbbbb;
font-weight: bold;
}
div.jqi .jqistate{
background-color: #fff;
}
div.jqi .jqititle{
padding: 5px 10px;
font-size: 16px;
line-height: 20px;
border-bottom: solid 1px #eeeeee;
}
div.jqi .jqimessage{
padding: 10px;
line-height: 20px;
color: #444444;
overflow: auto;
}
div.jqi .jqibuttons{
text-align: right;
margin: 0 -7px -7px -7px;
border-top: solid 1px #e4e4e4;
background-color: #f4f4f4;
border-radius: 0 0 6px 6px;
-moz-border-radius: 0 0 6px 6px;
-webkit-border-radius: 0 0 6px 6px;
}
div.jqi .jqibuttons button{
margin: 0;
padding: 15px 20px;
background-color: transparent;
font-weight: normal;
border: none;
border-left: solid 1px #e4e4e4;
color: #777;
font-weight: bold;
font-size: 12px;
}
div.jqi .jqibuttons button.jqidefaultbutton{
color: #489afe;
}
div.jqi .jqibuttons button:hover,
div.jqi .jqibuttons button:focus{
color: #287ade;
outline: none;
}
.jqiwarning .jqi .jqibuttons{
background-color: #b95656;
}
/* sub states */
div.jqi .jqiparentstate::after{
background-color: #777;
opacity: 0.6;
filter: alpha(opacity=60);
content: '';
position: absolute;
top:0;left:0;bottom:0;right:0;
border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
}
div.jqi .jqisubstate{
position: absolute;
top:0;
left: 20%;
width: 60%;
padding: 7px;
border: solid 1px #eeeeee;
border-top: none;
border-radius: 0 0 6px 6px;
-moz-border-radius: 0 0 6px 6px;
-webkit-border-radius: 0 0 6px 6px;
}
div.jqi .jqisubstate .jqibuttons button{
padding: 10px 18px;
}
/* arrows for tooltips/tours */
.jqi .jqiarrow{ position: absolute; height: 0; width:0; line-height: 0; font-size: 0; border: solid 10px transparent;}
.jqi .jqiarrowtl{ left: 10px; top: -20px; border-bottom-color: #ffffff; }
.jqi .jqiarrowtc{ left: 50%; top: -20px; border-bottom-color: #ffffff; margin-left: -10px; }
.jqi .jqiarrowtr{ right: 10px; top: -20px; border-bottom-color: #ffffff; }
.jqi .jqiarrowbl{ left: 10px; bottom: -20px; border-top-color: #ffffff; }
.jqi .jqiarrowbc{ left: 50%; bottom: -20px; border-top-color: #ffffff; margin-left: -10px; }
.jqi .jqiarrowbr{ right: 10px; bottom: -20px; border-top-color: #ffffff; }
.jqi .jqiarrowlt{ left: -20px; top: 10px; border-right-color: #ffffff; }
.jqi .jqiarrowlm{ left: -20px; top: 50%; border-right-color: #ffffff; margin-top: -10px; }
.jqi .jqiarrowlb{ left: -20px; bottom: 10px; border-right-color: #ffffff; }
.jqi .jqiarrowrt{ right: -20px; top: 10px; border-left-color: #ffffff; }
.jqi .jqiarrowrm{ right: -20px; top: 50%; border-left-color: #ffffff; margin-top: -10px; }
.jqi .jqiarrowrb{ right: -20px; bottom: 10px; border-left-color: #ffffff; }

View File

@ -1594,6 +1594,12 @@ button.secondary:hover:not(:disabled) {
right: 200px;
z-index: 101;
}
.main #ldapSettings {
display: none;
}
.main #ldapSettings.hasLdapUsers {
display: block;
}
.main > div {
padding: 20px;
border-bottom: 1px solid #e0e0e0;
@ -1620,6 +1626,66 @@ button.secondary:hover:not(:disabled) {
.main > div .soundNotifs.secondary:before {
content: 'Enable ';
}
.main > div #newLdapUser {
display: none;
width: 500px;
height: 20px;
padding: 3px;
font-size: 12px;
}
.main > div #newLdapUser.meIsAdmin {
display: inline-block;
}
.main > div #ldapUsers {
border-radius: 3px;
border: 1px solid #e0e0e0;
background: #f9f9f9;
list-style-type: none;
padding: 5px 5px 5px 8px;
line-height: 20px;
}
.main > div #ldapUsers .fa-plus,
.main > div #ldapUsers .fa-minus,
.main > div #ldapUsers .delete {
display: none;
margin-left: 5px;
cursor: pointer;
color: #000;
}
.main > div #ldapUsers .fa-plus:hover,
.main > div #ldapUsers .fa-minus:hover,
.main > div #ldapUsers .delete:hover {
color: #94b021;
}
.main > div #ldapUsers .fa-plus.meIsAdmin,
.main > div #ldapUsers .fa-minus.meIsAdmin,
.main > div #ldapUsers .delete.meIsAdmin {
display: inline-block;
}
.main > div #ldapUsers .delete:hover {
color: #f00;
}
.main > div #ldapUsers .wrap {
background: #f0f0f0;
border-radius: 3px;
border: 1px solid #e0e0e0;
padding: 5px;
font-size: 12px;
margin-bottom: 5px;
width: 502px;
}
.main > div #ldapUsers .wrap.meIsAdmin {
display: none;
}
.main > div #ldapUsers .wrap input {
width: 500px;
height: 20px;
padding: 3px;
}
.main > div #ldapUsers .wrap .changePassword {
margin-top: 4px;
margin-right: 4px;
}
.uploadRegion {
padding: 15px;
-moz-border-radius: 3px;

View File

@ -23,6 +23,11 @@
right: 200px
z-index: 101
#ldapSettings
display: none
&.hasLdapUsers
display: block
> div
padding: 20px
border-bottom: 1px solid $gray-lighter
@ -49,6 +54,57 @@
&:before
content: 'Enable '
#newLdapUser
display: none
width: 500px
height: 20px
padding: 3px
font-size: 12px
&.meIsAdmin
display: inline-block
#ldapUsers
border-radius: 3px;
border: 1px solid $gray-lighter
background: lighten($gray-lighter, 80%)
list-style-type: none
padding: 5px 5px 5px 8px
line-height: 20px
.fa-plus, .fa-minus, .delete
display: none
margin-left: 5px
cursor: pointer
color: black
&:hover
color: $settingsHoverText
&.meIsAdmin
display: inline-block
.delete
&:hover
color: red
.wrap
background: lighten($gray-lighter, 50%)
border-radius: 3px;
border: 1px solid $gray-lighter
padding: 5px
font-size: 12px
margin-bottom: 5px
width: 502px
&.meIsAdmin
display: none
input
width: 500px
height: 20px
padding: 3px
.changePassword
margin-top: 4px
margin-right: 4px
.uploadRegion
padding: 15px
roundall(3px)

270
server.js
View File

@ -6,11 +6,19 @@ var Moonboots = require('moonboots-express');
var config = require('getconfig');
var templatizer = require('templatizer');
var async = require('async');
var LDAP = require('LDAP');
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
}
var app = express();
var bodyParser = require('body-parser')
var compression = require('compression');
var serveStatic = require('serve-static');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(compression());
app.use(serveStatic(__dirname + '/public'));
if (!config.isDev) {
@ -43,6 +51,258 @@ app.get('/sounds/*', function (req, res) {
res.redirect("./public" + req.baseUrl);
});
function connectLDAP(req, cb) {
if (!config.ldap || !config.ldap.address || !config.ldap.base) {
cb(true);
return;
}
var ldapDN = 'uid=' + req.body.uid + ',' + config.ldap.base;
var ldapPW = req.body.password;
var ldap = new LDAP({ uri: 'ldap://' + config.ldap.address, reconnect: false });
ldap.open(function(err) {
if (err) {
console.log("LDAP: Can not connect to server on ldap://" + config.ldap.address);
cb(true);
return;
}
function closeCb(ldap) {
ldap.close();
console.log("LDAP: Disconnected");
}
ldap.simplebind({ binddn: ldapDN, password: ldapPW }, function(err) {
if (err) {
console.log("LDAP: Can not connect to server with " + ldapDN);
closeCb(ldap);
cb(true);
return;
}
console.log("LDAP: Connected on ldap://" + config.ldap.address + " with " + ldapDN);
if (req.body.uid == config.server.admin && config.ldap.user && config.ldap.password) {
console.log("LDAP: " + ldapDN + " is XMPP admin");
ldap.simplebind({ binddn: config.ldap.user, password: config.ldap.password }, function(err) {
if (err) {
console.log("LDAP: Can not connect to server with " + config.ldap.user);
closeCb(ldap);
cb(true);
return;
}
console.log("LDAP: Connected on ldap://" + config.ldap.address + " with " + config.ldap.user);
cb(false, ldap, closeCb);
});
return;
}
cb(false, ldap, closeCb);
});
});
}
app.post('/ldap/user/:id', function(req, res) {
var dn = 'uid=' + req.params.id.toLowerCase() + ',' + config.ldap.base;
console.log('LDAP: Save user informations (' + dn + ')');
connectLDAP(req, function (err, ldap, closeCb) {
if (err === false) {
var changes = [];
if (req.body.cn != undefined) changes.push({ op: 'replace', attr: 'cn', vals: [ req.body.cn ] });
if (req.body.sn != undefined) changes.push({ op: 'replace', attr: 'sn', vals: [ req.body.sn ] });
if (req.body.givenName != undefined) changes.push({ op: 'replace', attr: 'givenName', vals: [ req.body.givenName ] });
if (req.body.displayName != undefined) changes.push({ op: 'replace', attr: 'displayName', vals: [ req.body.displayName ] });
if (req.body.mail != undefined) changes.push({ op: 'replace', attr: 'mail', vals: [ req.body.mail ] });
ldap.modify(dn, changes, function (err) {
if (err) {
console.log('LDAP: Impossible to change user informations (' + dn + ')');
console.log(err);
res.type('application/javascript');
res.send(false);
closeCb(ldap);
return;
}
console.log('LDAP: User informations saved (' + dn + ')');
res.type('application/javascript');
res.send(true);
closeCb(ldap);
});
}
});
});
app.post('/ldap/user/:id/password', function(req, res) {
var dn = 'uid=' + req.params.id.toLowerCase() + ',' + config.ldap.base;
console.log('LDAP: Change user password (' + dn + ')');
connectLDAP(req, function (err, ldap, closeCb) {
if (err === false) {
var changes = [{ op: 'replace', attr: 'userPassword', vals: [ req.body.newPassword ] }];
ldap.modify(dn, changes, function (err) {
if (err) {
console.log('LDAP: Impossible to change user password (' + dn + ')');
console.log(err);
res.type('application/javascript');
res.send(false);
closeCb(ldap);
return;
}
console.log('LDAP: User password changed (' + dn + ')');
res.type('application/javascript');
res.send(true);
closeCb(ldap);
});
}
});
});
app.post('/ldap/users', function (req, res) {
console.log('LDAP: Get users list');
connectLDAP(req, function (err, ldap, closeCb) {
if (err === false) {
var filter = config.ldap.filter;
if (req.body.uid != config.server.admin) {
var uid = 'uid=' + req.body.uid.toLowerCase();
filter = '(&(' + filter + ')(' + uid + '))';
}
ldap.search({ base: config.ldap.base, filter: filter }, function(err, data) {
var users = new Array();
if (!err) {
data.forEach(function(el) {
var user = {
id: el.uid[0],
cn: el.cn ? el.cn[0] : '',
sn: el.sn ? el.sn[0] : '',
givenName: el.givenName ? el.givenName[0] : '',
displayName: el.displayName ? el.displayName[0] : '',
mail: el.mail ? el.mail[0] : '',
objectClass: el.objectClass
};
users.push(user);
});
}
else {
console.log(err);
}
res.type('application/javascript');
res.send(JSON.stringify(users));
console.log('LDAP: Users list sent');
closeCb(ldap);
});
}
});
});
app.post('/ldap/users/add', function (req, res) {
console.log('LDAP: Add a new user');
connectLDAP(req, function (err, ldap, closeCb) {
if (err === false || !req.body.newUid) {
var dn = 'uid=' + req.body.newUid.toLowerCase() + ',' + config.ldap.base;
var attrs = [
{ attr: 'objectClass', vals: [ 'organizationalPerson', 'person', 'inetOrgPerson'] },
{ attr: 'cn', vals: [ req.body.newUid.capitalize() ] },
{ attr: 'sn', vals: [ req.body.newUid.capitalize() ] },
{ attr: 'givenName', vals: [ req.body.newUid.capitalize() ] },
{ attr: 'displayName', vals: [ req.body.newUid.capitalize() ] },
{ attr: 'userPassword', vals: [ req.body.newUid.toLowerCase() ] }
];
ldap.add(dn, attrs, function (err) {
if (err) {
console.log('LDAP: Impossible to add a new user (' + dn + ')');
console.log(err);
res.type('application/javascript');
res.send(false);
closeCb(ldap);
return;
}
if (config.ldap.group) {
var changes = [
{ op: 'add',
attr: 'member',
vals: [ dn ]
}
];
ldap.modify(config.ldap.group, changes, function (err) {
if (err) console.log(err);
console.log('LDAP: New user added (' + dn + ')');
res.type('application/javascript');
res.send(true);
closeCb(ldap);
});
}
});
}
});
});
app.post('/ldap/users/delete', function (req, res) {
console.log('LDAP: Remove a user');
connectLDAP(req, function (err, ldap, closeCb) {
if (err === false || !req.body.removeUid) {
var dn = 'uid=' + req.body.removeUid.toLowerCase() + ',' + config.ldap.base;
ldap.remove(dn, function (err) {
if (err) {
console.log('LDAP: Impossible to remove this user (' + dn + ')');
console.log(err);
res.type('application/javascript');
res.send(false);
closeCb(ldap);
return;
}
if (config.ldap.group) {
var changes = [
{ op: 'delete',
attr: 'member',
vals: [ dn ]
}
];
ldap.modify(config.ldap.group, changes, function (err) {
if (err) console.log(err);
console.log('LDAP: User removed (' + dn + ')');
res.type('application/javascript');
res.send(true);
closeCb(ldap);
});
}
});
}
});
});
app.get('/oauth/login', function (req, res) {
res.redirect('https://apps.andyet.com/oauth/authorize?client_id=' + config.andyetAuth.id + '&response_type=token');
});
@ -85,14 +345,16 @@ var clientApp = new Moonboots({
__dirname + '/clientapp/libraries/resampler.js',
__dirname + '/clientapp/libraries/IndexedDBShim.min.js',
__dirname + '/clientapp/libraries/sugar-1.2.1-dates.js',
__dirname + '/clientapp/libraries/jquery.oembed.js'
__dirname + '/clientapp/libraries/jquery.oembed.js',
__dirname + '/clientapp/libraries/jquery-impromptu.js'
],
browserify: {
debug: false
},
stylesheets: [
__dirname + '/public/css/otalk.css',
__dirname + '/public/css/jquery.oembed.css'
__dirname + '/public/css/jquery.oembed.css',
__dirname + '/public/css/jquery-impromptu.css'
],
beforeBuildJS: function () {
if (config.isDev) {
@ -138,5 +400,5 @@ clientApp.on('ready', function () {
//}, app).listen(config.http.port);
app.listen(config.http.port, function () {
console.log('demo.stanza.io running at: ' + config.http.baseUrl);
})
console.log('Otalk running at: ' + config.http.baseUrl);
});