[FIX] Vairous LDAP issues & add pagination

pull/8372/head
Rodrigo Nascimento 8 years ago
parent 3468d7827d
commit a2f42e4d6d
No known key found for this signature in database
GPG Key ID: CFCE33B7B01AC335
  1. 54
      packages/rocketchat-i18n/i18n/en.i18n.json
  2. 10
      packages/rocketchat-ldap/package.js
  3. 5
      packages/rocketchat-ldap/server/index.js
  4. 314
      packages/rocketchat-ldap/server/ldap.js
  5. 30
      packages/rocketchat-ldap/server/loginHandler.js
  6. 114
      packages/rocketchat-ldap/server/settings.js
  7. 211
      packages/rocketchat-ldap/server/sync.js
  8. 18
      packages/rocketchat-ldap/server/syncUsers.js
  9. 3
      packages/rocketchat-ldap/server/testConnection.js
  10. 14
      packages/rocketchat-markdown/markdown.js
  11. 1
      packages/rocketchat-markdown/parser/marked/marked.js
  12. 2
      packages/rocketchat-ui-admin/client/admin.html
  13. 136
      server/startup/migrations/v102.js

@ -987,24 +987,20 @@
"LDAP": "LDAP",
"LDAP_CA_Cert": "CA Cert",
"LDAP_Connect_Timeout": "Connection Timeout (ms)",
"LDAP_Custom_Domain_Search": "Custom Domain Search",
"LDAP_Custom_Domain_Search_Description": "A piece of JSON that governs bind and connection info and is of the form:<br/> <code>{\"filter\": \"(&(objectCategory=person)(objectclass=user)(memberOf=CN=ROCKET_ACCESS,CN=Users,DC=domain,DC=com)(sAMAccountName=#{username}))\", \"scope\": \"sub\", \"userDN\": \"rocket.service@domain.com\", \"password\": \"urpass\"}</code>",
"LDAP_Default_Domain": "Default Domain",
"LDAP_Default_Domain_Description": "If provided the Default Domain will be used to create an unique email for users where email was not imported from LDAP. The email will be mounted as `username@default_domain` or `unique_id@default_domain`.<br/>Example: `rocket.chat`",
"LDAP_Description": "LDAP is a hierarchical database that many companies use to provide single sign on - a facility for sharing one password between multiple sites and services. For advanced configuration information and examples, please consult our wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/.",
"LDAP_Domain_Base": "Domain Base",
"LDAP_Domain_Base_Description": "The fully qualified Distinguished Name (DN) of an LDAP subtree you want to search for users and groups. You can add as many as you like; however, each group must be defined in the same domain base as the users that belong to it. If you specify restricted user groups, only users that belong to those groups will be in scope. We recommend that you specify the top level of your LDAP directory tree as your domain base and use search filter to control access.",
"LDAP_Domain_Search_Filter": "Domain Search Filter",
"LDAP_Domain_Search_Filter_Description": "If specified, only users that match this filter will be allowed to log in. If no filter is specified, all users within the scope of the specified domain base will be able to sign in.<br/>E.g. for Active Directory `memberOf=cn=ROCKET_CHAT,ou=General Groups`.<br/>E.g. for OpenLDAP (extensible match search) `ou:dn:=ROCKET_CHAT`.",
"LDAP_Domain_Search_Object_Category": "Domain Search Object Category",
"LDAP_Domain_Search_Object_Category_Description": "The *objectCategory* that identify your users.<br/>Leave empty for *OpenLDAP*.<br/>E.g. `person`, etc.",
"LDAP_Domain_Search_Object_Class": "Domain Search Object Class",
"LDAP_Domain_Search_Object_Class_Description": "The *objectclass* that identify your users. <br/>E.g. `organizationalPerson`, `user`, `inetOrgPerson`, etc.",
"LDAP_Domain_Search_Password": "Domain Search Password",
"LDAP_Domain_Search_Password_Description": "The password for the domain search user.",
"LDAP_Domain_Search_User": "Domain Search User",
"LDAP_Domain_Search_User_Description": "The LDAP user that performs user lookups to authenticate other users when they sign in. <br/>This is typically a service account created specifically for third-party integrations. Use a fully qualified name, such as `cn=Administrator,cn=Users,dc=Example,dc=com`.",
"LDAP_Domain_Search_User_ID": "Domain Search User ID",
"LDAP_Domain_Search_User_ID_Description": "The LDAP attribute that identifies the LDAP user who attempts authentication. This field should be `sAMAccountName` for most Active Directory installations, but it may be `uid` for other LDAP solutions, such as OpenLDAP. You can use `mail` to identify users by email or whatever attribute you want.<br>You can use multiple values separated by comma to allow users to login using multiple identifiers like username or email.",
"LDAP_BaseDN": "Base DN",
"LDAP_BaseDN_Description": "The fully qualified Distinguished Name (DN) of an LDAP subtree you want to search for users and groups. You can add as many as you like; however, each group must be defined in the same domain base as the users that belong to it. If you specify restricted user groups, only users that belong to those groups will be in scope. We recommend that you specify the top level of your LDAP directory tree as your domain base and use search filter to control access.",
"LDAP_User_Search_Field": "Search Field",
"LDAP_User_Search_Field_Description": "The LDAP attribute that identifies the LDAP user who attempts authentication. This field should be `sAMAccountName` for most Active Directory installations, but it may be `uid` for other LDAP solutions, such as OpenLDAP. You can use `mail` to identify users by email or whatever attribute you want.<br>You can use multiple values separated by comma to allow users to login using multiple identifiers like username or email.",
"LDAP_User_Search_Filter": "Filter",
"LDAP_User_Search_Filter_Description": "If specified, only users that match this filter will be allowed to log in. If no filter is specified, all users within the scope of the specified domain base will be able to sign in.<br/>E.g. for Active Directory `memberOf=cn=ROCKET_CHAT,ou=General Groups`.<br/>E.g. for OpenLDAP (extensible match search) `ou:dn:=ROCKET_CHAT`.",
"LDAP_User_Search_Scope": "Scope",
"LDAP_Authentication": "Enable",
"LDAP_Authentication_Password": "Password",
"LDAP_Authentication_UserDN": "User DN",
"LDAP_Authentication_UserDN_Description": "The LDAP user that performs user lookups to authenticate other users when they sign in. <br/>This is typically a service account created specifically for third-party integrations. Use a fully qualified name, such as `cn=Administrator,cn=Users,dc=Example,dc=com`.",
"LDAP_Enable": "Enable",
"LDAP_Enable_Description": "Attempt to utilize LDAP for authentication.",
"LDAP_Encryption": "Encryption",
@ -1024,7 +1020,7 @@
"LDAP_Host": "Host",
"LDAP_Host_Description": "The LDAP host, e.g. `ldap.example.com` or `10.0.0.30`.",
"LDAP_Idle_Timeout": "Idle Timeout (ms)",
"LDAP_Import_Users": "Import LDAP users",
"LDAP_Idle_Timeout_Description": "How many mileseconds wait after the latest LDAP opration until close the connection. (Each opreation will open a new connection)",
"LDAP_Import_Users_Description": "It True sync process will be import all LDAP users <br/> *Caution!* Specify search filter to not import excess users.",
"LDAP_Login_Fallback": "Login Fallback",
"LDAP_Login_Fallback_Description": "If the login on LDAP is not successful try to login in default/local account system. Helps when the LDAP is down for some reason.",
@ -1033,19 +1029,28 @@
"LDAP_Port": "Port",
"LDAP_Port_Description": "Port to access LDAP. eg: `389` or `636` for LDAPS",
"LDAP_Reject_Unauthorized": "Reject Unauthorized",
"LDAP_Reject_Unauthorized_Description": "Disable this option to allow certificates that can not be verified. Usually Self Signed Certificates will require this option disabled to work",
"LDAP_Sync_User_Avatar": "Sync User Avatar",
"LDAP_Sync_User_Data": "Sync Data",
"LDAP_Sync_User_Data_Description": "Keep user data in sync with server on login (eg: name, email).",
"LDAP_Sync_Now": "Background Sync Now",
"LDAP_Sync_Now_Description": "Will execute the **Background Sync** now rather than wait the **Sync Interval** even if **Background Sync** is False.<br/>This Action is asynchronous, please see the logs for more information about the process",
"LDAP_Background_Sync": "Background Sync",
"LDAP_Background_Sync_Interval": "Background Sync Interval",
"LDAP_Background_Sync_Interval_Description": "The interval between synchronizations. Example `every 24 hours` or `on the first day of the week`, more examples at [Cron Text Parser](http://bunkat.github.io/later/parsers.html#text)",
"LDAP_Background_Sync_Import_New_Users": "Background Sync Import New Users",
"LDAP_Background_Sync_Import_New_Users_Description": "Will import all users (based on your filter criteria) that exists in LDAP and does not exists in Rocket.Chat",
"LDAP_Background_Sync_Keep_Existant_Users_Updated": "Background Sync Update Existing Users",
"LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Will sync the avatar, fields, username, etc (based on your configuration) of all users already imported from LDAP on every **Sync Interval**",
"LDAP_Sync_User_Data": "Sync User Data",
"LDAP_Sync_User_Data_Description": "Keep user data in sync with server on **login** or on **background sync** (eg: name, email).",
"LDAP_Sync_User_Data_FieldMap": "User Data Field Map",
"LDAP_Sync_User_Data_FieldMap_Description": "Configure how user account fields (like email) are populated from a record in LDAP (once found). <br/>As an example, `{\"cn\":\"name\", \"mail\":\"email\"}` will choose a person's human readable name from the cn attribute, and their email from the mail attribute. Additionally it is possible to use variables, for example: `{ \"#{givenName} #{sn}\": \"name\", \"mail\": \"email\" }` uses a combination of the user's first name and last name for the rocket chat `name` field.<br/>Available fields in Rocket.Chat: `name`, `email` and `customFields`.",
"LDAP_Sync_Users": "Sync Users",
"LDAP_Test_Connection": "Test Connection",
"LDAP_Sync_User_Data_FieldMap_Description": "Configure how user account fields (like email) are populated from a record in LDAP (once found). <br/>As an example, `{\"cn\":\"name\", \"mail\":\"email\"}` will choose a person's human readable name from the cn attribute, and their email from the mail attribute. Additionally it is possible to use variables, for example: `{ \"#{givenName} #{sn}\": \"name\", \"mail\": \"email\" }` uses a combination of the user's first name and last name for the rocket chat `name` field.<br/>Available fields in Rocket.Chat: `name`, `email` and `customFields`.", "LDAP_Test_Connection": "Test Connection",
"LDAP_Timeout": "Timeout (ms)",
"LDAP_Timeout_Description": "How many mileseconds wait for a search result before return an error",
"LDAP_Unique_Identifier_Field": "Unique Identifier Field",
"LDAP_Unique_Identifier_Field_Description": "Which field will be used to link the LDAP user and the Rocket.Chat user. You can inform multiple values separated by comma to try to get the value from LDAP record.<br/>Default value is `objectGUID,ibm-entryUUID,GUID,dominoUNID,nsuniqueId,uidNumber`",
"LDAP_Use_Custom_Domain_Search": "Use Custom Domain Search",
"LDAP_Use_Custom_Domain_Search_Description": "Write your own filter to search users in the LDAP server.",
"LDAP_Username_Field": "Username Field",
"LDAP_Username_Field_Description": "Which field will be used as *username* for new users. Leave empty to use the username informed on login page.<br/>You can use template tags too, like `#{givenName}.#{sn}`.<br/>Default value is `sAMAccountName`.",
"Execute_Synchronization_Now": "Execute Synchronization Now",
"Least_Amount": "Least Amount",
"Leave_Group_Warning": "Are you sure you want to leave the group \"%s\"?",
"Leave_Livechat_Warning": "Are you sure you want to leave the livechat with \"%s\"?",
@ -1702,6 +1707,7 @@
"Survey_instructions": "Rate each question according to your satisfaction, 1 meaning you are completely unsatisfied and 5 meaning you are completely satisfied.",
"Symbols": "Symbols",
"Sync_success": "Sync success",
"Sync_in_progress": "Syncronization in progress",
"Sync_Users": "Sync Users",
"System_messages": "System Messages",
"Tag": "Tag",

@ -11,6 +11,7 @@ Package.onUse(function(api) {
api.use('rocketchat:lib');
api.use('yasaricli:slugify');
api.use('ecmascript');
api.use('underscore');
api.use('sha');
api.use('templating', 'client');
@ -20,12 +21,5 @@ Package.onUse(function(api) {
api.addFiles('client/loginHelper.js', 'client');
api.addFiles('server/ldap.js', 'server');
api.addFiles('server/sync.js', 'server');
api.addFiles('server/loginHandler.js', 'server');
api.addFiles('server/settings.js', 'server');
api.addFiles('server/testConnection.js', 'server');
api.addFiles('server/syncUsers.js', 'server');
api.export('LDAP', 'server');
api.mainModule('server/index.js', 'server');
});

@ -0,0 +1,5 @@
import './loginHandler';
import './settings';
import './testConnection';
import './syncUsers';
import './sync';

@ -1,5 +1,4 @@
/* globals LDAP:true, LDAPJS */
/* exported LDAP */
/* globals LDAPJS */
const ldapjs = LDAPJS;
@ -12,31 +11,28 @@ const logger = new Logger('LDAP', {
}
});
LDAP = class LDAP {
export default class LDAP {
constructor() {
const self = this;
this.ldapjs = ldapjs;
self.ldapjs = ldapjs;
this.connected = false;
self.connected = false;
self.options = {
this.options = {
host: RocketChat.settings.get('LDAP_Host'),
port: RocketChat.settings.get('LDAP_Port'),
timeout: RocketChat.settings.get('LDAP_Timeout'),
connect_timeout: RocketChat.settings.get('LDAP_Connect_Timeout'),
idle_timeout: RocketChat.settings.get('LDAP_Idle_Timeout'),
encryption: RocketChat.settings.get('LDAP_Encryption'),
ca_cert: RocketChat.settings.get('LDAP_CA_Cert'),
reject_unauthorized: RocketChat.settings.get('LDAP_Reject_Unauthorized') || false,
domain_base: RocketChat.settings.get('LDAP_Domain_Base'),
use_custom_domain_search: RocketChat.settings.get('LDAP_Use_Custom_Domain_Search'),
custom_domain_search: RocketChat.settings.get('LDAP_Custom_Domain_Search'),
domain_search_user: RocketChat.settings.get('LDAP_Domain_Search_User'),
domain_search_password: RocketChat.settings.get('LDAP_Domain_Search_Password'),
domain_search_filter: RocketChat.settings.get('LDAP_Domain_Search_Filter'),
domain_search_user_id: RocketChat.settings.get('LDAP_Domain_Search_User_ID'),
domain_search_object_class: RocketChat.settings.get('LDAP_Domain_Search_Object_Class'),
domain_search_object_category: RocketChat.settings.get('LDAP_Domain_Search_Object_Category'),
Authentication: RocketChat.settings.get('LDAP_Authentication'),
Authentication_UserDN: RocketChat.settings.get('LDAP_Authentication_UserDN'),
Authentication_Password: RocketChat.settings.get('LDAP_Authentication_Password'),
BaseDN: RocketChat.settings.get('LDAP_BaseDN'),
User_Search_Filter: RocketChat.settings.get('LDAP_User_Search_Filter'),
User_Search_Scope: RocketChat.settings.get('LDAP_User_Search_Scope'),
User_Search_Field: RocketChat.settings.get('LDAP_User_Search_Field'),
group_filter_enabled: RocketChat.settings.get('LDAP_Group_Filter_Enable'),
group_filter_object_class: RocketChat.settings.get('LDAP_Group_Filter_ObjectClass'),
group_filter_group_id_attribute: RocketChat.settings.get('LDAP_Group_Filter_Group_Id_Attribute'),
@ -44,36 +40,45 @@ LDAP = class LDAP {
group_filter_group_member_format: RocketChat.settings.get('LDAP_Group_Filter_Group_Member_Format'),
group_filter_group_name: RocketChat.settings.get('LDAP_Group_Filter_Group_Name')
};
}
self.connectSync = Meteor.wrapAsync(self.connectAsync, self);
self.searchAllSync = Meteor.wrapAsync(self.searchAllAsync, self);
connectSync(...args) {
if (!this._connectSync) {
this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
}
return this._connectSync(...args);
}
connectAsync(callback) {
const self = this;
searchAllSync(...args) {
if (!this._searchAllSync) {
this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
}
return this._searchAllSync(...args);
}
connectAsync(callback) {
logger.connection.info('Init setup');
let replied = false;
const connectionOptions = {
url: `${ self.options.host }:${ self.options.port }`,
timeout: 1000 * 60 * 10,
connectTimeout: self.options.connect_timeout,
idleTimeout: self.options.idle_timeout,
reconnect: false
url: `${ this.options.host }:${ this.options.port }`,
timeout: this.options.timeout,
connectTimeout: this.options.connect_timeout,
idleTimeout: this.options.idle_timeout,
reconnect: true
};
const tlsOptions = {
rejectUnauthorized: self.options.reject_unauthorized
rejectUnauthorized: this.options.reject_unauthorized
};
if (self.options.ca_cert && self.options.ca_cert !== '') {
if (this.options.ca_cert && this.options.ca_cert !== '') {
// Split CA cert into array of strings
const chainLines = RocketChat.settings.get('LDAP_CA_Cert').split('\n');
let cert = [];
const ca = [];
chainLines.forEach(function(line) {
chainLines.forEach((line) => {
cert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(cert.join('\n'));
@ -83,7 +88,7 @@ LDAP = class LDAP {
tlsOptions.ca = ca;
}
if (self.options.encryption === 'ssl') {
if (this.options.encryption === 'ssl') {
connectionOptions.url = `ldaps://${ connectionOptions.url }`;
connectionOptions.tlsOptions = tlsOptions;
} else {
@ -93,11 +98,11 @@ LDAP = class LDAP {
logger.connection.info('Connecting', connectionOptions.url);
logger.connection.debug('connectionOptions', connectionOptions);
self.client = ldapjs.createClient(connectionOptions);
this.client = ldapjs.createClient(connectionOptions);
self.bindSync = Meteor.wrapAsync(self.client.bind, self.client);
this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
self.client.on('error', function(error) {
this.client.on('error', (error) => {
logger.connection.error('connection', error);
if (replied === false) {
replied = true;
@ -105,17 +110,25 @@ LDAP = class LDAP {
}
});
if (self.options.encryption === 'tls') {
this.client.on('idle', () => {
logger.search.info('Idle');
this.disconnect();
});
this.client.on('close', () => {
logger.search.info('Closed');
});
if (this.options.encryption === 'tls') {
// Set host parameter for tls.connect which is used by ldapjs starttls. This shouldn't be needed in newer nodejs versions (e.g v5.6.0).
// https://github.com/RocketChat/Rocket.Chat/issues/2035
// https://github.com/mcavage/node-ldapjs/issues/349
tlsOptions.host = self.options.host;
tlsOptions.host = this.options.host;
logger.connection.info('Starting TLS');
logger.connection.debug('tlsOptions', tlsOptions);
self.client.starttls(tlsOptions, null, function(error, response) {
this.client.starttls(tlsOptions, null, (error, response) => {
if (error) {
logger.connection.error('TLS connection', error);
if (replied === false) {
@ -126,16 +139,16 @@ LDAP = class LDAP {
}
logger.connection.info('TLS connected');
self.connected = true;
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
} else {
self.client.on('connect', function(response) {
this.client.on('connect', (response) => {
logger.connection.info('LDAP connected');
self.connected = true;
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
@ -143,126 +156,98 @@ LDAP = class LDAP {
});
}
setTimeout(function() {
setTimeout(() => {
if (replied === false) {
logger.connection.error('connection time out', connectionOptions.timeout);
logger.connection.error('connection time out', connectionOptions.connectTimeout);
replied = true;
callback(new Error('Timeout'));
}
}, connectionOptions.timeout);
}, connectionOptions.connectTimeout);
}
getDomainBindSearch() {
const self = this;
getUserFilter(username) {
const filter = [];
if (self.options.use_custom_domain_search === true) {
let custom_domain_search;
try {
custom_domain_search = JSON.parse(self.options.custom_domain_search);
} catch (error) {
throw new Error('Invalid Custom Domain Search JSON');
if (this.options.User_Search_Filter !== '') {
if (this.options.User_Search_Filter[0] === '(') {
filter.push(`${ this.options.User_Search_Filter }`);
} else {
filter.push(`(${ this.options.User_Search_Filter })`);
}
return {
filter: custom_domain_search.filter,
domain_search_user: custom_domain_search.userDN || '',
domain_search_password: custom_domain_search.password || ''
};
}
const filter = ['(&'];
if (self.options.domain_search_object_category !== '') {
filter.push(`(objectCategory=${ self.options.domain_search_object_category })`);
}
if (self.options.domain_search_object_class !== '') {
filter.push(`(objectclass=${ self.options.domain_search_object_class })`);
}
const usernameFilter = this.options.User_Search_Field.split(',').map(item => `(${ item }=${ username })`);
if (self.options.domain_search_filter !== '') {
filter.push(`(${ self.options.domain_search_filter })`);
}
const domain_search_user_id = self.options.domain_search_user_id.split(',');
if (domain_search_user_id.length === 1) {
filter.push(`(${ domain_search_user_id[0] }=#{username})`);
if (usernameFilter.length === 0) {
logger.error('LDAP_LDAP_User_Search_Field not defined');
} else if (usernameFilter.length === 1) {
filter.push(`${ usernameFilter[0] }`);
} else {
filter.push('(|');
domain_search_user_id.forEach((item) => {
filter.push(`(${ item }=#{username})`);
});
filter.push(')');
filter.push(`(|${ usernameFilter.join('') })`);
}
filter.push(')');
return {
filter: filter.join(''),
domain_search_user: self.options.domain_search_user || '',
domain_search_password: self.options.domain_search_password || ''
};
return `(&${ filter.join('') })`;
}
bindIfNecessary() {
const self = this;
if (self.domainBinded === true) {
if (this.domainBinded === true) {
return;
}
const domain_search = self.getDomainBindSearch();
if (domain_search.domain_search_user !== '' && domain_search.domain_search_password !== '') {
logger.bind.info('Binding admin user', domain_search.domain_search_user);
self.bindSync(domain_search.domain_search_user, domain_search.domain_search_password);
self.domainBinded = true;
if (this.options.Authentication !== true) {
return;
}
}
searchUsersSync(username) {
const self = this;
self.bindIfNecessary();
logger.bind.info('Binding UserDN', this.options.Authentication_UserDN);
this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password);
this.domainBinded = true;
}
const domain_search = self.getDomainBindSearch();
searchUsersSync(username, page) {
this.bindIfNecessary();
const searchOptions = {
filter: domain_search.filter.replace(/#{username}/g, username),
scope: 'sub'
filter: this.getUserFilter(username),
scope: this.options.User_Search_Scope || 'sub',
paged: {
pageSize: 250,
pagePause: !!page
}
};
logger.search.info('Searching user', username);
logger.search.debug('searchOptions', searchOptions);
logger.search.debug('domain_base', self.options.domain_base);
logger.search.debug('BaseDN', this.options.BaseDN);
return self.searchAllSync(self.options.domain_base, searchOptions);
if (page) {
return this.searchAllPaged(this.options.BaseDN, searchOptions, page);
}
return this.searchAllSync(this.options.BaseDN, searchOptions);
}
getUserByIdSync(id, attribute) {
const self = this;
self.bindIfNecessary();
this.bindIfNecessary();
const Unique_Identifier_Field = RocketChat.settings.get('LDAP_Unique_Identifier_Field').split(',');
let filter;
if (attribute) {
filter = new self.ldapjs.filters.EqualityFilter({
filter = new this.ldapjs.filters.EqualityFilter({
attribute,
value: new Buffer(id, 'hex')
});
} else {
const filters = [];
Unique_Identifier_Field.forEach(function(item) {
filters.push(new self.ldapjs.filters.EqualityFilter({
Unique_Identifier_Field.forEach((item) => {
filters.push(new this.ldapjs.filters.EqualityFilter({
attribute: item,
value: new Buffer(id, 'hex')
}));
});
filter = new self.ldapjs.filters.OrFilter({filters});
filter = new this.ldapjs.filters.OrFilter({filters});
}
const searchOptions = {
@ -272,9 +257,9 @@ LDAP = class LDAP {
logger.search.info('Searching by id', id);
logger.search.debug('search filter', searchOptions.filter.toString());
logger.search.debug('domain_base', self.options.domain_base);
logger.search.debug('BaseDN', this.options.BaseDN);
const result = self.searchAllSync(self.options.domain_base, searchOptions);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
@ -288,22 +273,18 @@ LDAP = class LDAP {
}
getUserByUsernameSync(username) {
const self = this;
self.bindIfNecessary();
const domain_search = self.getDomainBindSearch();
this.bindIfNecessary();
const searchOptions = {
filter: domain_search.filter.replace(/#{username}/g, username),
scope: 'sub'
filter: this.getUserFilter(username),
scope: this.options.User_Search_Scope || 'sub'
};
logger.search.info('Searching user', username);
logger.search.debug('searchOptions', searchOptions);
logger.search.debug('domain_base', self.options.domain_base);
logger.search.debug('BaseDN', this.options.BaseDN);
const result = self.searchAllSync(self.options.domain_base, searchOptions);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
@ -317,24 +298,22 @@ LDAP = class LDAP {
}
isUserInGroup(username) {
const self = this;
if (!self.options.group_filter_enabled) {
if (!this.options.group_filter_enabled) {
return true;
}
const filter = ['(&'];
if (self.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${ self.options.group_filter_object_class })`);
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${ this.options.group_filter_object_class })`);
}
if (self.options.group_filter_group_member_attribute !== '') {
filter.push(`(${ self.options.group_filter_group_member_attribute }=${ self.options.group_filter_group_member_format })`);
if (this.options.group_filter_group_member_attribute !== '') {
filter.push(`(${ this.options.group_filter_group_member_attribute }=${ this.options.group_filter_group_member_format })`);
}
if (self.options.group_filter_group_id_attribute !== '') {
filter.push(`(${ self.options.group_filter_group_id_attribute }=${ self.options.group_filter_group_name })`);
if (this.options.group_filter_group_id_attribute !== '') {
filter.push(`(${ this.options.group_filter_group_id_attribute }=${ this.options.group_filter_group_name })`);
}
filter.push(')');
@ -345,7 +324,7 @@ LDAP = class LDAP {
logger.search.debug('Group filter LDAP:', searchOptions.filter);
const result = self.searchAllSync(self.options.domain_base, searchOptions);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return false;
@ -353,18 +332,64 @@ LDAP = class LDAP {
return true;
}
searchAllPaged(BaseDN, options, page) {
this.bindIfNecessary();
this.client.search(BaseDN, options, (error, res) => {
if (error) {
logger.search.error(error);
page(error);
return;
}
res.on('error', (error) => {
logger.search.error(error);
page(error);
return;
});
let entries = [];
const jsonEntries = [];
res.on('searchEntry', (entry) => {
entries.push(entry);
jsonEntries.push(entry.json);
});
searchAllAsync(domain_base, options, callback) {
const self = this;
res.on('page', (result, next) => {
logger.search.debug('Page');
// Force LDAP idle to wait the record processing
this.client._updateIdle(true);
page(null, entries, {end: false, next: () => {
// Reset idle timer
this.client._updateIdle();
next && next();
}});
entries = [];
});
self.client.search(domain_base, options, function(error, res) {
res.on('end', () => {
logger.search.info('Search result count', entries.length);
page(null, [], {end: true, next: () => {
// Reset idle timer
this.client._updateIdle();
}});
// logger.search.debug('Search result', JSON.stringify(jsonEntries, null, 2));
});
});
}
searchAllAsync(BaseDN, options, callback) {
this.bindIfNecessary();
this.client.search(BaseDN, options, (error, res) => {
if (error) {
logger.search.error(error);
callback(error);
return;
}
res.on('error', function(error) {
res.on('error', (error) => {
logger.search.error(error);
callback(error);
return;
@ -373,26 +398,24 @@ LDAP = class LDAP {
const entries = [];
const jsonEntries = [];
res.on('searchEntry', function(entry) {
res.on('searchEntry', (entry) => {
entries.push(entry);
jsonEntries.push(entry.json);
});
res.on('end', function(/*result*/) {
res.on('end', () => {
logger.search.info('Search result count', entries.length);
logger.search.debug('Search result', JSON.stringify(jsonEntries, null, 2));
// logger.search.debug('Search result', JSON.stringify(jsonEntries, null, 2));
callback(null, entries);
});
});
}
authSync(dn, password) {
const self = this;
logger.auth.info('Authenticating', dn);
try {
self.bindSync(dn, password);
this.bindSync(dn, password);
logger.auth.info('Authenticated', dn);
return true;
} catch (error) {
@ -403,10 +426,9 @@ LDAP = class LDAP {
}
disconnect() {
const self = this;
self.connected = false;
this.connected = false;
this.domainBinded = false;
logger.connection.info('Disconecting');
self.client.unbind();
this.client.unbind();
}
};
}

@ -1,6 +1,8 @@
/* globals LDAP, slug, getLdapUsername, getLdapUserUniqueID, syncUserData, addLdapUser */
/* eslint new-cap: [2, {"capIsNewExceptions": ["SHA256"]}] */
import {slug, getLdapUsername, getLdapUserUniqueID, syncUserData, addLdapUser} from './sync';
import LDAP from './ldap';
const logger = new Logger('LDAPHandler', {});
function fallbackDefaultAccountSystem(bind, username, password) {
@ -62,8 +64,6 @@ Accounts.registerLoginHandler('ldap', function(loginRequest) {
logger.error(error);
}
ldap.disconnect();
if (ldapUser === undefined) {
if (RocketChat.settings.get('LDAP_Login_Fallback') === true) {
return fallbackDefaultAccountSystem(self, loginRequest.username, loginRequest.ldapPass);
@ -72,14 +72,6 @@ Accounts.registerLoginHandler('ldap', function(loginRequest) {
throw new Meteor.Error('LDAP-login-error', `LDAP Authentication failed with provided username [${ loginRequest.username }]`);
}
let username;
if (RocketChat.settings.get('LDAP_Username_Field') !== '') {
username = slug(getLdapUsername(ldapUser));
} else {
username = slug(loginRequest.username);
}
// Look to see if user already exists
let userQuery;
@ -97,6 +89,14 @@ Accounts.registerLoginHandler('ldap', function(loginRequest) {
user = Meteor.users.findOne(userQuery);
}
let username;
if (RocketChat.settings.get('LDAP_Username_Field') !== '') {
username = slug(getLdapUsername(ldapUser));
} else {
username = slug(loginRequest.username);
}
if (!user) {
userQuery = {
username
@ -138,6 +138,14 @@ Accounts.registerLoginHandler('ldap', function(loginRequest) {
logger.info('User does not exist, creating', username);
if (RocketChat.settings.get('LDAP_Username_Field') === '') {
username = undefined;
}
if (RocketChat.settings.get('LDAP_Login_Fallback') !== true) {
loginRequest.ldapPass = undefined;
}
// Create new user
return addLdapUser(ldapUser, username, loginRequest.ldapPass);
});

@ -1,60 +1,78 @@
Meteor.startup(function() {
RocketChat.settings.addGroup('LDAP', function() {
const enableQuery = {_id: 'LDAP_Enable', value: true};
const enableTLSQuery = [
{_id: 'LDAP_Enable', value: true},
{_id: 'LDAP_Encryption', value: {$in: ['tls', 'ssl']}}
];
const customBindSearchEnabledQuery = [
{_id: 'LDAP_Enable', value: true},
{_id: 'LDAP_Use_Custom_Domain_Search', value: true}
];
const customBindSearchDisabledQuery = [
{_id: 'LDAP_Enable', value: true},
{_id: 'LDAP_Use_Custom_Domain_Search', value: false}
];
const syncDataQuery = [
{_id: 'LDAP_Enable', value: true},
{_id: 'LDAP_Sync_User_Data', value: true}
];
const groupFilterQuery = [
{_id: 'LDAP_Enable', value: true},
{_id: 'LDAP_Group_Filter_Enable', value: true}
];
this.add('LDAP_Enable', false, { type: 'boolean', public: true });
this.add('LDAP_Login_Fallback', true, { type: 'boolean', enableQuery });
this.add('LDAP_Host', '', { type: 'string', enableQuery });
this.add('LDAP_Port', '389', { type: 'string', enableQuery });
this.add('LDAP_Connect_Timeout', 600000, {type: 'int', enableQuery});
this.add('LDAP_Idle_Timeout', 600000, {type: 'int', enableQuery});
this.add('LDAP_Encryption', 'plain', { type: 'select', values: [ { key: 'plain', i18nLabel: 'No_Encryption' }, { key: 'tls', i18nLabel: 'StartTLS' }, { key: 'ssl', i18nLabel: 'SSL/LDAPS' } ], enableQuery });
this.add('LDAP_CA_Cert', '', { type: 'string', multiline: true, enableQuery: enableTLSQuery });
this.add('LDAP_Reject_Unauthorized', true, { type: 'boolean', enableQuery: enableTLSQuery });
this.add('LDAP_Domain_Base', '', { type: 'string', enableQuery });
this.add('LDAP_Use_Custom_Domain_Search', false, { type: 'boolean', enableQuery });
this.add('LDAP_Custom_Domain_Search', '', { type: 'string', enableQuery: customBindSearchEnabledQuery });
this.add('LDAP_Domain_Search_User', '', { type: 'string', enableQuery: customBindSearchDisabledQuery });
this.add('LDAP_Domain_Search_Password', '', { type: 'password', enableQuery: customBindSearchDisabledQuery });
this.add('LDAP_Domain_Search_Filter', '', { type: 'string', enableQuery: customBindSearchDisabledQuery });
RocketChat.settings.addGroup('LDAP', function() {
const enableQuery = {_id: 'LDAP_Enable', value: true};
const enableAuthentication = [
enableQuery,
{_id: 'LDAP_Authentication', value: true}
];
const enableTLSQuery = [
enableQuery,
{_id: 'LDAP_Encryption', value: {$in: ['tls', 'ssl']}}
];
const syncDataQuery = [
enableQuery,
{_id: 'LDAP_Sync_User_Data', value: true}
];
const groupFilterQuery = [
enableQuery,
{_id: 'LDAP_Group_Filter_Enable', value: true}
];
const backgroundSyncQuery = [
enableQuery,
{_id: 'LDAP_Background_Sync', value: true}
];
this.add('LDAP_Enable', false, { type: 'boolean', public: true });
this.add('LDAP_Login_Fallback', true, { type: 'boolean', enableQuery });
this.add('LDAP_Host', '', { type: 'string', enableQuery });
this.add('LDAP_Port', '389', { type: 'string', enableQuery });
this.add('LDAP_Encryption', 'plain', { type: 'select', values: [ { key: 'plain', i18nLabel: 'No_Encryption' }, { key: 'tls', i18nLabel: 'StartTLS' }, { key: 'ssl', i18nLabel: 'SSL/LDAPS' } ], enableQuery });
this.add('LDAP_CA_Cert', '', { type: 'string', multiline: true, enableQuery: enableTLSQuery });
this.add('LDAP_Reject_Unauthorized', true, { type: 'boolean', enableQuery: enableTLSQuery });
this.add('LDAP_BaseDN', '', { type: 'string', enableQuery });
this.add('LDAP_Test_Connection', 'ldap_test_connection', { type: 'action', actionText: 'Test_Connection' });
this.section('Authentication', function() {
this.add('LDAP_Authentication', false, { type: 'boolean', enableQuery });
this.add('LDAP_Authentication_UserDN', '', { type: 'string', enableQuery: enableAuthentication });
this.add('LDAP_Authentication_Password', '', { type: 'password', enableQuery: enableAuthentication });
});
this.section('Timeouts', function() {
this.add('LDAP_Timeout', 60000, {type: 'int', enableQuery});
this.add('LDAP_Connect_Timeout', 1000, {type: 'int', enableQuery});
this.add('LDAP_Idle_Timeout', 1000, {type: 'int', enableQuery});
});
this.section('User Search', function() {
this.add('LDAP_User_Search_Filter', '(objectclass=*)', { type: 'string', enableQuery });
this.add('LDAP_User_Search_Scope', 'sub', { type: 'string', enableQuery });
this.add('LDAP_User_Search_Field', 'sAMAccountName', { type: 'string', enableQuery });
});
this.section('User Search (Group Validation)', function() {
this.add('LDAP_Group_Filter_Enable', false, { type: 'boolean', enableQuery });
this.add('LDAP_Group_Filter_ObjectClass', 'groupOfUniqueNames', { type: 'string', enableQuery: groupFilterQuery });
this.add('LDAP_Group_Filter_Group_Id_Attribute', 'cn', { type: 'string', enableQuery: groupFilterQuery });
this.add('LDAP_Group_Filter_Group_Member_Attribute', 'uniqueMember', { type: 'string', enableQuery: groupFilterQuery });
this.add('LDAP_Group_Filter_Group_Member_Format', 'uniqueMember', { type: 'string', enableQuery: groupFilterQuery });
this.add('LDAP_Group_Filter_Group_Name', 'ROCKET_CHAT', { type: 'string', enableQuery: groupFilterQuery });
this.add('LDAP_Domain_Search_User_ID', 'sAMAccountName', { type: 'string', enableQuery: customBindSearchDisabledQuery });
this.add('LDAP_Domain_Search_Object_Class', 'user', { type: 'string', enableQuery: customBindSearchDisabledQuery });
this.add('LDAP_Domain_Search_Object_Category', 'person', { type: 'string', enableQuery: customBindSearchDisabledQuery });
});
this.section('Sync / Import', function() {
this.add('LDAP_Username_Field', 'sAMAccountName', { type: 'string', enableQuery });
this.add('LDAP_Unique_Identifier_Field', 'objectGUID,ibm-entryUUID,GUID,dominoUNID,nsuniqueId,uidNumber', { type: 'string', enableQuery });
this.add('LDAP_Sync_User_Data', false, { type: 'boolean', enableQuery });
this.add('LDAP_Sync_User_Avatar', true, { type: 'boolean', enableQuery: syncDataQuery });
this.add('LDAP_Sync_User_Data_FieldMap', '{"cn":"name", "mail":"email"}', { type: 'string', enableQuery: syncDataQuery });
this.add('LDAP_Default_Domain', '', { type: 'string', enableQuery });
this.add('LDAP_Merge_Existing_Users', false, { type: 'boolean', enableQuery });
this.add('LDAP_Import_Users', false, { type: 'boolean', enableQuery: syncDataQuery });
this.add('LDAP_Test_Connection', 'ldap_test_connection', { type: 'action', actionText: 'Test_Connection' });
this.add('LDAP_Sync_Users', 'ldap_sync_users', { type: 'action', actionText: 'Sync_Users' });
this.add('LDAP_Sync_User_Data', false, { type: 'boolean', enableQuery });
this.add('LDAP_Sync_User_Data_FieldMap', '{"cn":"name", "mail":"email"}', { type: 'string', enableQuery: syncDataQuery });
this.add('LDAP_Sync_User_Avatar', true, { type: 'boolean', enableQuery });
this.add('LDAP_Background_Sync', false, { type: 'boolean', enableQuery });
this.add('LDAP_Background_Sync_Interval', 'Every 24 hours', { type: 'string', enableQuery: backgroundSyncQuery });
this.add('LDAP_Background_Sync_Import_New_Users', true, { type: 'boolean', enableQuery: backgroundSyncQuery });
this.add('LDAP_Background_Sync_Keep_Existant_Users_Updated', true, { type: 'boolean', enableQuery: backgroundSyncQuery });
this.add('LDAP_Sync_Now', 'ldap_sync_now', { type: 'action', actionText: 'Execute_Synchronization_Now' });
});
});

@ -1,17 +1,19 @@
/* globals slug:true, slugify, LDAP, getLdapUsername:true, getLdapUserUniqueID:true, getDataToSyncUserData:true, syncUserData:true, sync:true, addLdapUser:true */
/* globals slugify, SyncedCron */
import LDAP from './ldap';
const logger = new Logger('LDAPSync', {});
slug = function slug(text) {
export function slug(text) {
if (RocketChat.settings.get('UTF8_Names_Slugify') !== true) {
return text;
}
text = slugify(text, '.');
return text.replace(/[^0-9a-z-_.]/g, '');
};
}
getLdapUsername = function getLdapUsername(ldapUser) {
export function getLdapUsername(ldapUser) {
const usernameField = RocketChat.settings.get('LDAP_Username_Field');
if (usernameField.indexOf('#{') > -1) {
@ -21,10 +23,10 @@ getLdapUsername = function getLdapUsername(ldapUser) {
}
return ldapUser.object[usernameField];
};
}
getLdapUserUniqueID = function getLdapUserUniqueID(ldapUser) {
export function getLdapUserUniqueID(ldapUser) {
let Unique_Identifier_Field = RocketChat.settings.get('LDAP_Unique_Identifier_Field');
if (Unique_Identifier_Field !== '') {
@ -33,15 +35,15 @@ getLdapUserUniqueID = function getLdapUserUniqueID(ldapUser) {
Unique_Identifier_Field = [];
}
let LDAP_Domain_Search_User_ID = RocketChat.settings.get('LDAP_Domain_Search_User_ID');
let User_Search_Field = RocketChat.settings.get('LDAP_User_Search_Field');
if (LDAP_Domain_Search_User_ID !== '') {
LDAP_Domain_Search_User_ID = LDAP_Domain_Search_User_ID.replace(/\s/g, '').split(',');
if (User_Search_Field !== '') {
User_Search_Field = User_Search_Field.replace(/\s/g, '').split(',');
} else {
LDAP_Domain_Search_User_ID = [];
User_Search_Field = [];
}
Unique_Identifier_Field = Unique_Identifier_Field.concat(LDAP_Domain_Search_User_ID);
Unique_Identifier_Field = Unique_Identifier_Field.concat(User_Search_Field);
if (Unique_Identifier_Field.length > 0) {
Unique_Identifier_Field = Unique_Identifier_Field.find((field) => {
@ -55,10 +57,9 @@ getLdapUserUniqueID = function getLdapUserUniqueID(ldapUser) {
}
return Unique_Identifier_Field;
}
};
}
getDataToSyncUserData = function getDataToSyncUserData(ldapUser, user) {
export function getDataToSyncUserData(ldapUser, user) {
const syncUserData = RocketChat.settings.get('LDAP_Sync_User_Data');
const syncUserDataFieldMap = RocketChat.settings.get('LDAP_Sync_User_Data_FieldMap').trim();
@ -122,13 +123,13 @@ getDataToSyncUserData = function getDataToSyncUserData(ldapUser, user) {
if (_.size(userData)) {
return userData;
}
};
}
syncUserData = function syncUserData(user, ldapUser) {
export function syncUserData(user, ldapUser) {
logger.info('Syncing user data');
logger.debug('user', {'email': user.email, '_id': user._id});
logger.debug('ldapUser', ldapUser);
logger.debug('ldapUser', ldapUser.object);
const userData = getDataToSyncUserData(ldapUser, user);
if (user && user._id && userData) {
@ -173,21 +174,29 @@ syncUserData = function syncUserData(user, ldapUser) {
});
}
}
};
}
addLdapUser = function addLdapUser(ldapUser, username, password) {
const userObject = {
username
};
export function addLdapUser(ldapUser, username, password) {
const uniqueId = getLdapUserUniqueID(ldapUser);
const userObject = {};
if (username) {
userObject.username = username;
}
const userData = getDataToSyncUserData(ldapUser, {});
if (userData && userData.emails) {
userObject.email = userData.emails[0].address;
if (userData && userData.emails && userData.emails[0] && userData.emails[0].address) {
if (Array.isArray(userData.emails[0].address)) {
userObject.email = userData.emails[0].address[0];
} else {
userObject.email = userData.emails[0].address;
}
} else if (ldapUser.object.mail && ldapUser.object.mail.indexOf('@') > -1) {
userObject.email = ldapUser.object.mail;
} else if (RocketChat.settings.get('LDAP_Default_Domain') !== '') {
userObject.email = `${ username }@${ RocketChat.settings.get('LDAP_Default_Domain') }`;
userObject.email = `${ username || uniqueId.value }@${ RocketChat.settings.get('LDAP_Default_Domain') }`;
} else {
const error = new Meteor.Error('LDAP-login-error', 'LDAP Authentication succeded, there is no email to create an account. Have you tried setting your Default Domain in LDAP Settings?');
logger.error(error);
@ -212,79 +221,139 @@ addLdapUser = function addLdapUser(ldapUser, username, password) {
return {
userId: userObject._id
};
};
}
sync = function sync() {
export function importNewUsers(ldap) {
if (RocketChat.settings.get('LDAP_Enable') !== true) {
logger.error('Can\'t run LDAP Import, LDAP is disabled');
return;
}
const ldap = new LDAP();
try {
if (!ldap) {
ldap = new LDAP();
ldap.connectSync();
}
let count = 0;
ldap.searchUsersSync('*', Meteor.bindEnvironment((error, ldapUsers, {next} = {}) => {
if (error) {
throw error;
}
const users = RocketChat.models.Users.findLDAPUsers();
ldapUsers.forEach((ldapUser) => {
count++;
if (RocketChat.settings.get('LDAP_Import_Users') === true && RocketChat.settings.get('LDAP_Username_Field') !== '') {
const ldapUsers = ldap.searchUsersSync('*');
ldapUsers.forEach(function(ldapUser) {
const username = slug(getLdapUsername(ldapUser));
// Look to see if user already exists
const uniqueId = getLdapUserUniqueID(ldapUser);
// Look to see if user already exists
const userQuery = {
'services.ldap.id': uniqueId.value
};
logger.debug('userQuery', userQuery);
let username;
if (RocketChat.settings.get('LDAP_Username_Field') !== '') {
username = slug(getLdapUsername(ldapUser));
}
// Add user if it was not added before
const user = Meteor.users.findOne(userQuery);
if (!user) {
addLdapUser(ldapUser, username);
}
if (!user && username && RocketChat.settings.get('LDAP_Merge_Existing_Users') === true) {
const userQuery = {
username
};
logger.debug('userQuery', userQuery);
logger.debug('userQuery merge', userQuery);
const user = Meteor.users.findOne(userQuery);
if (!user) {
addLdapUser(ldapUser, username);
} else if (user.ldap !== true && RocketChat.settings.get('LDAP_Merge_Existing_Users') === true) {
if (user) {
syncUserData(user, ldapUser);
}
});
}
users.forEach(function(user) {
let ldapUser;
if (user.services && user.services.ldap && user.services.ldap.id) {
ldapUser = ldap.getUserByIdSync(user.services.ldap.id, user.services.ldap.idAttribute);
} else {
ldapUser = ldap.getUserByUsernameSync(user.username);
}
if (ldapUser) {
syncUserData(user, ldapUser);
} else {
logger.info('Can\'t sync user', user.username);
if (count % 1000 === 0) {
logger.info('Imported:', count);
}
});
next && next();
}));
logger.info('Imported:', count);
}
function sync() {
if (RocketChat.settings.get('LDAP_Enable') !== true) {
return;
}
const ldap = new LDAP();
try {
ldap.connectSync();
let users;
if (RocketChat.settings.get('LDAP_Background_Sync_Keep_Existant_Users_Updated') === true) {
users = RocketChat.models.Users.findLDAPUsers();
}
if (RocketChat.settings.get('LDAP_Background_Sync_Import_New_Users') === true) {
importNewUsers(ldap);
}
if (RocketChat.settings.get('LDAP_Background_Sync_Keep_Existant_Users_Updated') === true) {
users.forEach(function(user) {
let ldapUser;
if (user.services && user.services.ldap && user.services.ldap.id) {
ldapUser = ldap.getUserByIdSync(user.services.ldap.id, user.services.ldap.idAttribute);
} else {
ldapUser = ldap.getUserByUsernameSync(user.username);
}
if (ldapUser) {
syncUserData(user, ldapUser);
} else {
logger.info('Can\'t sync user', user.username);
}
});
}
} catch (error) {
logger.error(error);
return error;
}
ldap.disconnect();
return true;
};
}
let interval;
let timeout;
const jobName = 'LDAP_Sync';
RocketChat.settings.get('LDAP_Sync_User_Data', function(key, value) {
Meteor.clearInterval(interval);
Meteor.clearTimeout(timeout);
const addCronJob = _.debounce(Meteor.bindEnvironment(function addCronJobDebounced() {
if (RocketChat.settings.get('LDAP_Background_Sync') !== true) {
logger.info('Disabling LDAP Background Sync');
if (SyncedCron.nextScheduledAtDate(jobName)) {
SyncedCron.remove(jobName);
}
return;
}
if (value === true) {
logger.info('Enabling LDAP user sync');
interval = Meteor.setInterval(sync, 1000 * 60 * 60);
timeout = Meteor.setTimeout(function() {
sync();
}, 1000 * 60 * 10);
} else {
logger.info('Disabling LDAP user sync');
if (RocketChat.settings.get('LDAP_Sync_Interval')) {
logger.info('Enabling LDAP Background Sync');
SyncedCron.add({
name: jobName,
schedule: (parser) => parser.text(RocketChat.settings.get('LDAP_Sync_Interval')),
job() {
sync();
}
});
}
}), 500);
Meteor.startup(() => {
Meteor.defer(() => {
RocketChat.settings.get('LDAP_Background_Sync', addCronJob);
RocketChat.settings.get('LDAP_Background_Sync_Interval', addCronJob);
});
});

@ -1,7 +1,7 @@
/* globals sync */
import {importNewUsers} from './sync';
Meteor.methods({
ldap_sync_users() {
ldap_sync_now() {
const user = Meteor.user();
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'ldap_sync_users' });
@ -15,15 +15,13 @@ Meteor.methods({
throw new Meteor.Error('LDAP_disabled');
}
const result = sync();
this.unblock();
if (result === true) {
return {
message: 'Sync_success',
params: []
};
}
importNewUsers();
throw result;
return {
message: 'Sync_in_progress',
params: []
};
}
});

@ -1,4 +1,4 @@
/* globals LDAP */
import LDAP from './ldap';
Meteor.methods({
ldap_test_connection() {
@ -26,7 +26,6 @@ Meteor.methods({
try {
ldap.bindIfNecessary();
ldap.disconnect();
} catch (error) {
throw new Meteor.Error(error.name || error.message);
}

@ -19,14 +19,14 @@ class MarkdownClass {
const message = {
html: _.escapeHTML(text)
};
return this.parseMessageNotEscaped(message).html;
return this.mountTokensBack(this.parseMessageNotEscaped(message)).html;
}
parseNotEscaped(text) {
const message = {
html: text
};
return this.parseMessageNotEscaped(message).html;
return this.mountTokensBack(this.parseMessageNotEscaped(message)).html;
}
parseMessageNotEscaped(message) {
@ -36,6 +36,16 @@ class MarkdownClass {
}
return parsers['original'](message);
}
mountTokensBack(message) {
if (message.tokens && message.tokens.length > 0) {
for (const {token, text} of message.tokens) {
message.html = message.html.replace(token, () => text); // Uses lambda so doesn't need to escape $
}
}
return message;
}
}
const Markdown = new MarkdownClass;

@ -99,6 +99,7 @@ export const marked = (message) => {
smartLists,
smartypants,
renderer,
sanitize: true,
highlight
});

@ -48,7 +48,7 @@
{{/if}}
{{#each settings}}
<div class="input-line double-col {{#if isSettingChanged _id}}setting-changed{{/if}}" {{isDisabled}}>
<label class="setting-label">{{label}}</label>
<label class="setting-label" title="{{_id}}">{{label}}</label>
<div class="setting-field">
{{#if $eq type 'string'}}
{{#if multiline}}

@ -0,0 +1,136 @@
RocketChat.Migrations.add({
version: 102,
up() {
if (!RocketChat || !RocketChat.models || !RocketChat.models.Settings) {
return;
}
RocketChat.models.Settings.update(
{ _id: 'LDAP_Connect_Timeout', value: 600000 },
{ $set: { value: 1000 } }
);
RocketChat.models.Settings.update(
{ _id: 'LDAP_Idle_Timeout', value: 600000 },
{ $set: { value: 1000 } }
);
const LDAP_Domain_Base = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Base' });
if (LDAP_Domain_Base) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_BaseDN' },
{ $set: { value: LDAP_Domain_Base.value } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Base' });
const LDAP_Domain_Search_User_ID = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Search_User_ID' });
if (LDAP_Domain_Search_User_ID) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_User_Search_Field' },
{ $set: { value: LDAP_Domain_Search_User_ID.value } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Search_User_ID' });
const LDAP_Use_Custom_Domain_Search = RocketChat.models.Settings.findOne({ _id: 'LDAP_Use_Custom_Domain_Search' });
const LDAP_Custom_Domain_Search = RocketChat.models.Settings.findOne({ _id: 'LDAP_Custom_Domain_Search' });
const LDAP_Domain_Search_User = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Search_User' });
const LDAP_Domain_Search_Password = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Search_Password' });
const LDAP_Domain_Search_Filter = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Search_Filter' });
const LDAP_Domain_Search_Object_Class = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Search_Object_Class' });
const LDAP_Domain_Search_Object_Category = RocketChat.models.Settings.findOne({ _id: 'LDAP_Domain_Search_Object_Category' });
if (LDAP_Use_Custom_Domain_Search) {
if (LDAP_Use_Custom_Domain_Search.value === true) {
let Custom_Domain_Search;
try {
Custom_Domain_Search = JSON.parse(LDAP_Custom_Domain_Search.value);
} catch (error) {
throw new Error('Invalid Custom Domain Search JSON');
}
LDAP_Domain_Search_User.value = Custom_Domain_Search.userDN || '';
LDAP_Domain_Search_Password.value = Custom_Domain_Search.password || '';
LDAP_Domain_Search_Filter.value = Custom_Domain_Search.filter;
} else {
const filter = [];
if (LDAP_Domain_Search_Object_Category.value !== '') {
filter.push(`(objectCategory=${ LDAP_Domain_Search_Object_Category.value })`);
}
if (LDAP_Domain_Search_Object_Class.value !== '') {
filter.push(`(objectclass=${ LDAP_Domain_Search_Object_Class.value })`);
}
if (LDAP_Domain_Search_Filter.value !== '') {
filter.push(`(${ LDAP_Domain_Search_Filter.value })`);
}
if (filter.length === 1) {
LDAP_Domain_Search_Filter.value = filter[0];
} else if (filter.length > 1) {
LDAP_Domain_Search_Filter.value = `(&${ filter.join('') })`;
}
}
}
if (LDAP_Domain_Search_Filter && LDAP_Domain_Search_Filter.value) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_User_Search_Filter' },
{ $set: { value: LDAP_Domain_Search_Filter.value } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Search_Filter' });
if (LDAP_Domain_Search_User && LDAP_Domain_Search_User.value) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_Authentication_UserDN' },
{ $set: { value: LDAP_Domain_Search_User.value } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Search_User' });
if (LDAP_Domain_Search_Password && LDAP_Domain_Search_Password.value) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_Authentication_Password' },
{ $set: { value: LDAP_Domain_Search_Password.value } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Search_Password' });
if (LDAP_Domain_Search_User && LDAP_Domain_Search_User.value && LDAP_Domain_Search_Password && LDAP_Domain_Search_Password.value) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_Authentication' },
{ $set: { value: true } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Use_Custom_Domain_Search' });
RocketChat.models.Settings.remove({ _id: 'LDAP_Custom_Domain_Search' });
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Search_Object_Class' });
RocketChat.models.Settings.remove({ _id: 'LDAP_Domain_Search_Object_Category' });
RocketChat.models.Settings.remove({ _id: 'LDAP_Sync_Users' }); //Button
const LDAP_Sync_User_Data = RocketChat.models.Settings.findOne({ _id: 'LDAP_Sync_User_Data' });
if (LDAP_Sync_User_Data && LDAP_Sync_User_Data.value) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_Background_Sync' },
{ $set: { value: true } }
);
}
const LDAP_Import_Users = RocketChat.models.Settings.findOne({ _id: 'LDAP_Import_Users' });
if (LDAP_Import_Users && LDAP_Import_Users.value === false) {
RocketChat.models.Settings.update(
{ _id: 'LDAP_Background_Sync_Import_New_Users' },
{ $set: { value: false } }
);
}
RocketChat.models.Settings.remove({ _id: 'LDAP_Import_Users' });
}
});
Loading…
Cancel
Save