parent
3c7c6c2e5a
commit
4da99981af
@ -0,0 +1,45 @@ |
||||
// Pass in username, password as normal
|
||||
// customLdapOptions should be passed in if you want to override LDAP_DEFAULTS
|
||||
// on any particular call (if you have multiple ldap servers you'd like to connect to)
|
||||
// You'll likely want to set the dn value here {dn: "..."}
|
||||
Meteor.loginWithLDAP = function(username, password, customLdapOptions, callback) { |
||||
// Retrieve arguments as array
|
||||
var args = []; |
||||
for (var i = 0; i < arguments.length; i++) { |
||||
args.push(arguments[i]); |
||||
} |
||||
// Pull username and password
|
||||
username = args.shift(); |
||||
password = args.shift(); |
||||
|
||||
// Check if last argument is a function
|
||||
// if it is, pop it off and set callback to it
|
||||
if (typeof args[args.length-1] == 'function') callback = args.pop(); else callback = null; |
||||
|
||||
// if args still holds options item, grab it
|
||||
if (args.length > 0) customLdapOptions = args.shift(); else customLdapOptions = {}; |
||||
|
||||
// Set up loginRequest object
|
||||
var loginRequest = { |
||||
username: username, |
||||
ldapPass: password, |
||||
ldapOptions: customLdapOptions |
||||
}; |
||||
|
||||
Accounts.callLoginMethod({ |
||||
// Call login method with ldap = true
|
||||
// This will hook into our login handler for ldap
|
||||
methodArguments: [loginRequest], |
||||
userCallback: function(error, result) { |
||||
if (error) { |
||||
if (callback) { |
||||
callback(error); |
||||
} |
||||
} else { |
||||
if (callback) { |
||||
callback(); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
@ -0,0 +1,172 @@ |
||||
var slug = function (text) { |
||||
text = slugify(text, '.'); |
||||
return text.replace(/[^0-9a-z-_.]/g, ''); |
||||
}; |
||||
|
||||
function fallbackDefaultAccountSystem(bind, username, password) { |
||||
if (typeof username === 'string') |
||||
if (username.indexOf('@') === -1) |
||||
username = {username: username}; |
||||
else |
||||
username = {email: username}; |
||||
|
||||
loginRequest = { |
||||
user: username, |
||||
password: { |
||||
digest: SHA256(password), |
||||
algorithm: "sha-256" |
||||
} |
||||
}; |
||||
|
||||
return Accounts._runLoginHandlers(bind, loginRequest); |
||||
} |
||||
|
||||
function getDataToSyncUserData(ldapUser) { |
||||
const syncUserData = RocketChat.settings.get('LDAP_Sync_User_Data'); |
||||
const syncUserDataFieldMap = RocketChat.settings.get('LDAP_Sync_User_Data_FieldMap').trim(); |
||||
|
||||
if (syncUserData && syncUserDataFieldMap) { |
||||
const fieldMap = JSON.parse(syncUserDataFieldMap); |
||||
let userData = {}; |
||||
|
||||
let emailList = []; |
||||
_.map(fieldMap, function(userField, ldapField) { |
||||
if (!ldapUser.hasOwnProperty(ldapField)) { |
||||
return; |
||||
} |
||||
|
||||
switch (userField) { |
||||
case 'email': |
||||
if (_.isObject(ldapUser[ldapField] === 'object')) { |
||||
_.map(ldapUser[ldapField], function (item) { |
||||
emailList.push({ address: item, verified: true }); |
||||
}); |
||||
} else { |
||||
emailList.push({ address: ldapUser[ldapField], verified: true }); |
||||
} |
||||
break; |
||||
|
||||
case 'name': |
||||
userData.name = ldapUser[ldapField]; |
||||
break; |
||||
} |
||||
}); |
||||
|
||||
if (emailList.length > 0) { |
||||
userData.emails = emailList; |
||||
} |
||||
|
||||
if (_.size(userData)) { |
||||
return userData; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function syncUserData(userId, ldapUser) { |
||||
const userData = getDataToSyncUserData(ldapUser); |
||||
if (userId && userData) { |
||||
Meteor.users.update(userId, { $set: userData }); |
||||
} |
||||
} |
||||
|
||||
Accounts.registerLoginHandler("ldap", function(loginRequest) { |
||||
const self = this; |
||||
|
||||
if (!loginRequest.ldapOptions) { |
||||
return undefined; |
||||
} |
||||
|
||||
if (RocketChat.settings.get('LDAP_Enable') !== true) { |
||||
return fallbackDefaultAccountSystem(self, loginRequest.username, loginRequest.ldapPass); |
||||
} |
||||
|
||||
const ldap = new LDAP(); |
||||
let ldapUser; |
||||
|
||||
try { |
||||
ldap.connectSync(); |
||||
users = ldap.searchUsersSync(loginRequest.username); |
||||
|
||||
if (users.length !== 1) { |
||||
console.log('LDAP: Search returned', users.length, 'record(s)'); |
||||
throw new Error('User not Found'); |
||||
} |
||||
|
||||
if (ldap.authSync(users[0].dn, loginRequest.ldapPass) === true) { |
||||
ldapUser = users[0]; |
||||
} else { |
||||
console.log('wrong password'); |
||||
} |
||||
} catch(error) { |
||||
console.log(error); |
||||
} |
||||
|
||||
ldap.disconnect(); |
||||
|
||||
if (ldapUser === undefined) { |
||||
console.log('[LDAP] Falling back to standard account base'); |
||||
return fallbackDefaultAccountSystem(self, loginRequest.username, loginRequest.ldapPass); |
||||
} |
||||
|
||||
const username = slug(loginRequest.username); |
||||
|
||||
// Look to see if user already exists
|
||||
const user = Meteor.users.findOne({ |
||||
username: username |
||||
}); |
||||
|
||||
// Login user if they exist
|
||||
if (user) { |
||||
if (user.ldap !== true) { |
||||
throw new Meteor.Error("LDAP-login-error", "LDAP Authentication succeded, but there's already an existing user with provided username ["+username+"] in Mongo."); |
||||
} |
||||
|
||||
const stampedToken = Accounts._generateStampedLoginToken(); |
||||
const hashStampedToken = |
||||
Meteor.users.update(user._id, { |
||||
$push: { |
||||
'services.resume.loginTokens': Accounts._hashStampedToken(stampedToken) |
||||
} |
||||
}); |
||||
|
||||
syncUserData(user._id, ldapUser); |
||||
return { |
||||
userId: user._id, |
||||
token: stampedToken.token |
||||
}; |
||||
} |
||||
|
||||
// Create new user
|
||||
var userObject = { |
||||
username: username |
||||
}; |
||||
|
||||
let userData = getDataToSyncUserData(ldapUser); |
||||
|
||||
if (userData && userData.emails) { |
||||
userObject.email = userData.emails[0].address; |
||||
} else if (ldapUser.mail && ldapUser.mail.indexOf('@') > -1) { |
||||
userObject.email = ldapUser.mail; |
||||
} else if (RocketChat.settings.get('LDAP_Default_Domain') !== '') { |
||||
userObject.email = username + '@' + RocketChat.settings.get('LDAP_Default_Domain'); |
||||
} else { |
||||
throw new Meteor.Error("LDAP-login-error", "LDAP Authentication succeded, there is no email to create an account."); |
||||
} |
||||
|
||||
let userId = Accounts.createUser(userObject); |
||||
|
||||
syncUserData(userId, ldapUser); |
||||
Meteor.users.update(userId, { |
||||
$set: { |
||||
ldap: true |
||||
} |
||||
}); |
||||
|
||||
Meteor.runAsUser(userId, function() { |
||||
Meteor.call('joinDefaultChannels'); |
||||
}); |
||||
|
||||
return { |
||||
userId: userId |
||||
}; |
||||
}); |
||||
@ -0,0 +1,38 @@ |
||||
Meteor.startup -> |
||||
RocketChat.settings.addGroup 'LDAP', -> |
||||
enableQuery = {_id: 'LDAP_Enable', value: true} |
||||
enableTLSQuery = [ |
||||
{_id: 'LDAP_Enable', value: true} |
||||
{_id: 'LDAP_Encryption', value: {$in: ['tls', 'ssl']}} |
||||
] |
||||
customBindSearchEnabledQuery = [ |
||||
{_id: 'LDAP_Enable', value: true} |
||||
{_id: 'LDAP_Use_Custom_Domain_Search', value: true} |
||||
] |
||||
customBindSearchDisabledQuery = [ |
||||
{_id: 'LDAP_Enable', value: true} |
||||
{_id: 'LDAP_Use_Custom_Domain_Search', value: false} |
||||
] |
||||
syncDataQuery = [ |
||||
{_id: 'LDAP_Enable', value: true} |
||||
{_id: 'LDAP_Sync_User_Data', value: true} |
||||
] |
||||
|
||||
@add 'LDAP_Enable', false, { type: 'boolean', public: true } |
||||
@add 'LDAP_Host', '', { type: 'string', enableQuery: enableQuery } |
||||
@add 'LDAP_Port', '389', { type: 'string', enableQuery: enableQuery } |
||||
@add 'LDAP_Encryption', 'plain', { type: 'select', values: [ { key: 'plain', i18nLabel: 'No_Encryption' }, { key: 'tls', i18nLabel: 'StartTLS' }, { key: 'ssl', i18nLabel: 'SSL/LDAPS' } ], enableQuery: enableQuery } |
||||
@add 'LDAP_CA_Cert', '', { type: 'string', multiline: true, enableQuery: enableTLSQuery } |
||||
@add 'LDAP_Reject_Unauthorized', true, { type: 'boolean', enableQuery: enableTLSQuery } |
||||
@add 'LDAP_Domain_Base', '', { type: 'string', enableQuery: enableQuery } |
||||
@add 'LDAP_Use_Custom_Domain_Search', false, { type: 'boolean' , enableQuery: enableQuery } |
||||
@add 'LDAP_Custom_Domain_Search', '', { type: 'string' , enableQuery: customBindSearchEnabledQuery } |
||||
@add 'LDAP_Domain_Search_User', '', { type: 'string', enableQuery: customBindSearchDisabledQuery } |
||||
@add 'LDAP_Domain_Search_Password', '', { type: 'string', enableQuery: customBindSearchDisabledQuery } |
||||
@add 'LDAP_Restricted_User_Groups', '', { type: 'string', enableQuery: customBindSearchDisabledQuery } |
||||
@add 'LDAP_Domain_Search_User_ID', 'sAMAccountName', { type: 'string', enableQuery: customBindSearchDisabledQuery } |
||||
@add 'LDAP_Domain_Search_Object_Class', 'user', { type: 'string', enableQuery: customBindSearchDisabledQuery } |
||||
@add 'LDAP_Domain_Search_Object_Category', 'person', { type: 'string', enableQuery: customBindSearchDisabledQuery } |
||||
@add 'LDAP_Sync_User_Data', false, { type: 'boolean' , enableQuery: enableQuery } |
||||
@add 'LDAP_Sync_User_Data_FieldMap', '{"cn":"name", "mail":"email"}', { type: 'string', enableQuery: syncDataQuery } |
||||
@add 'LDAP_Default_Domain', '', { type: 'string' , enableQuery: enableQuery } |
||||
Loading…
Reference in new issue