<commit_before>LDAP = {
  logging: true,
  log: function (message) {
    if (LDAP.logging) {
      console.log(message);
    }
  },
  warn: function (message) {
      this.log(message);
  },
  error: function (message) {
      this.log(message);
  },
  multitenantIdentifier: '',
  searchField: 'cn',
  searchValueType: 'username'
}; // { autoVerifyEmail : false };

// *************************************************
// Public methods that may be optionally overwritten
// *************************************************

// This provides the value that is used along with the user-submitted password to bind to the LDAP server

LDAP.bindValue = function (usernameOrEmail, isEmailAddress, FQDN) {
  return ((isEmailAddress) ? usernameOrEmail.split('@')[0] : usernameOrEmail) + '@' + FQDN;	
}

// This filter, used with default settings for LDAP.searchField assumes that the part of the email address before the @ perfectly matches the cn value for each user
// Overwrite this if you need a custom filter for your particular LDAP configuration
// For example if everyone has the 'mail' field set, but the bit before the @ in the email address doesn't exactly match users' cn values, you could do:
// LDAP.filter = function (isEmailAddress, usernameOrEmail, FQDN) { return '(&(' + ((isEmailAddress) ? 'mail' : 'cn') + '=' + usernameOrEmail + ')(objectClass=user))'; }

LDAP.filter = function (isEmailAddress, usernameOrEmail, FQDN) {
  var searchField = (_.isFunction(LDAP.searchField)) ? LDAP.searchField.call(this) : LDAP.searchField;
  var searchValue = LDAP.searchValue.call(this, isEmailAddress, usernameOrEmail, FQDN);
  return '(&(' + searchField + '=' + searchValue + ')(objectClass=user))';
}

// This is the search value that gets used in the LDAP.filter function above
// which gets called from the LDAP._search function below, when trying to isolate a user
// from the directory.
// Overwrite so that it matches your specific directory structure

LDAP.searchValue = function (isEmailAddress, usernameOrEmail, FQDN) {
  var username = (isEmailAddress) ? usernameOrEmail.split('@')[0] : usernameOrEmail;
  var searchValue;
  var searchValueType = (_.isFunction(LDAP.searchValueType)) ? LDAP.searchValueType.call(this) : LDAP.searchValueType;
  switch (searchValueType) {
	case 'userPrincipalName' :
	  searchValue = username + '@' + FQDN;
	  break;
	case 'email' :
	  searchValue = (isEmailAddress) ? userNameOrEmail : username + '@' + FQDN; // If it's not an email address, we're kind of guessing
	  break;
	case 'username' :
	default :
	  searchValue = username;
  }
  return searchValue;
}

// Flag to tell the loginHandler to have a poke at the app database first
// (will only work if accounts-password package is present)
LDAP.tryDBFirst = false;

// The default 
LDAP.userLookupQuery = function (fieldName, fieldValue, isEmail, isMultitenantIdentifier) {
  // Context (this) is the request sent from client
  var selector = {};
  selector[fieldName] = fieldValue;
  // Must return a mongo selector -- e.g. {username: "jackadams"} or {"email.address": "<EMAIL>"}
  return selector;
}

LDAP.addFields = function (entry) {
  // `this` is the request from the client
  // `entry` is the object returned from the LDAP server
  // return the fields that are to be added when creating a user
  return {};    
}

// Overwrite this function to produce settings based on the incoming request
LDAP.generateSettings = function (request) {
  return null;    
}

// Overwrite this function to modify the condition used to find an existing user
LDAP.modifyCondition = function (condition) {
  // `this` is the request received from the client
  return condition;    
}

LDAP.onSignIn = function (callback) {
  LDAP._addCallback(callback, 'onSignIn');
}

LDAP.onAddMultitenantIdentifier = function (callback) {
  LDAP._addCallback(callback, 'onAddMultitenantIdentifier');
}

// Overwrite this function if the app needs to do something to modify the username of the users in the app database
// i.e. the username field in the app is different from the username field in the directory accessed via LDAP
LDAP.appUsername = function (userNameOrEmail, isEmail, userObj) {
  // userObj is the best guess we've got for email and username, one of which successfully retrieved a user from the directory using LDAP
  // `this` is the request received from the client
  return (isEmail) ? userNameOrEmail.split('@')[0] : userNameOrEmail;	
}

// *****************************************
// Private methods, not intended for app use
// *****************************************

LDAP._stringifyUniqueIdentifier = function (uniqueIdentifier) {
  var stringified = JSON.stringify(uniqueIdentifier);
  return stringified.substr(1, stringified.length - 2);
}

LDAP._serverDnToFQDN = function (serverDn) {
  return serverDn.toLowerCase().replace(/\s+/g, '').split(/,?dc=/).slice(1).join('.');
}

LDAP._callbacks = {
  onAddMultitenantIdentifier: [],
  onSignIn: []
};

LDAP._addCallback = function (callback, target) {
  if (_.isFunction(callback)) {
    LDAP._callbacks[target].push(callback);
  }
  else {
    throw new Meteor.Error(target + ' callback must be a function');  
  }
}

LDAP._settings = function (request) {
  return LDAP.generateSettings(request) || Meteor.settings.ldap;
}

var ldap = Npm.require('ldapjs');
var Future = Npm.require('fibers/future');
var assert = Npm.require('assert');

LDAP._createClient = function () {
  var client = null;
  var settings = this;
  var serverUrl = settings.serverUrl;
  if (serverUrl.indexOf('ldaps://') === 0 && settings.ldapsCertificate) {
	client = ldap.createClient({
	  url: serverUrl,
	  tlsOptions: {
		ca: [settings.ldapsCertificate]
	  }
	});
  }
  else {
	client = ldap.createClient({
      url: serverUrl
    });
  }
  return client;
};

// If next version of ldapjs ever comes out
// TODO - It's out! Update ldapjs version, then test and release this code!
/*LDAP._starttls = function (client) {
  var success = null;
  <commit_msg>Add 'error' event handler to the ldapjs client<commit_after>LDAP = {
  logging: true,
  log: function (message) {
    if (LDAP.logging) {
      console.log(message);
    }
  },
  warn: function (message) {
      this.log(message);
  },
  error: function (message) {
      this.log(message);
  },
  multitenantIdentifier: '',
  searchField: 'cn',
  searchValueType: 'username'
}; // { autoVerifyEmail : false };

// *************************************************
// Public methods that may be optionally overwritten
// *************************************************

// This provides the value that is used along with the user-submitted password to bind to the LDAP server

LDAP.bindValue = function (usernameOrEmail, isEmailAddress, FQDN) {
  return ((isEmailAddress) ? usernameOrEmail.split('@')[0] : usernameOrEmail) + '@' + FQDN;	
}

// This filter, used with default settings for LDAP.searchField assumes that the part of the email address before the @ perfectly matches the cn value for each user
// Overwrite this if you need a custom filter for your particular LDAP configuration
// For example if everyone has the 'mail' field set, but the bit before the @ in the email address doesn't exactly match users' cn values, you could do:
// LDAP.filter = function (isEmailAddress, usernameOrEmail, FQDN) { return '(&(' + ((isEmailAddress) ? 'mail' : 'cn') + '=' + usernameOrEmail + ')(objectClass=user))'; }

LDAP.filter = function (isEmailAddress, usernameOrEmail, FQDN) {
  var searchField = (_.isFunction(LDAP.searchField)) ? LDAP.searchField.call(this) : LDAP.searchField;
  var searchValue = LDAP.searchValue.call(this, isEmailAddress, usernameOrEmail, FQDN);
  return '(&(' + searchField + '=' + searchValue + ')(objectClass=user))';
}

// This is the search value that gets used in the LDAP.filter function above
// which gets called from the LDAP._search function below, when trying to isolate a user
// from the directory.
// Overwrite so that it matches your specific directory structure

LDAP.searchValue = function (isEmailAddress, usernameOrEmail, FQDN) {
  var username = (isEmailAddress) ? usernameOrEmail.split('@')[0] : usernameOrEmail;
  var searchValue;
  var searchValueType = (_.isFunction(LDAP.searchValueType)) ? LDAP.searchValueType.call(this) : LDAP.searchValueType;
  switch (searchValueType) {
	case 'userPrincipalName' :
	  searchValue = username + '@' + FQDN;
	  break;
	case 'email' :
	  searchValue = (isEmailAddress) ? userNameOrEmail : username + '@' + FQDN; // If it's not an email address, we're kind of guessing
	  break;
	case 'username' :
	default :
	  searchValue = username;
  }
  return searchValue;
}

// Flag to tell the loginHandler to have a poke at the app database first
// (will only work if accounts-password package is present)
LDAP.tryDBFirst = false;

// The default 
LDAP.userLookupQuery = function (fieldName, fieldValue, isEmail, isMultitenantIdentifier) {
  // Context (this) is the request sent from client
  var selector = {};
  selector[fieldName] = fieldValue;
  // Must return a mongo selector -- e.g. {username: "jackadams"} or {"email.address": "<EMAIL>"}
  return selector;
}

LDAP.addFields = function (entry) {
  // `this` is the request from the client
  // `entry` is the object returned from the LDAP server
  // return the fields that are to be added when creating a user
  return {};    
}

// Overwrite this function to produce settings based on the incoming request
LDAP.generateSettings = function (request) {
  return null;    
}

// Overwrite this function to modify the condition used to find an existing user
LDAP.modifyCondition = function (condition) {
  // `this` is the request received from the client
  return condition;    
}

LDAP.onSignIn = function (callback) {
  LDAP._addCallback(callback, 'onSignIn');
}

LDAP.onAddMultitenantIdentifier = function (callback) {
  LDAP._addCallback(callback, 'onAddMultitenantIdentifier');
}

// Overwrite this function if the app needs to do something to modify the username of the users in the app database
// i.e. the username field in the app is different from the username field in the directory accessed via LDAP
LDAP.appUsername = function (userNameOrEmail, isEmail, userObj) {
  // userObj is the best guess we've got for email and username, one of which successfully retrieved a user from the directory using LDAP
  // `this` is the request received from the client
  return (isEmail) ? userNameOrEmail.split('@')[0] : userNameOrEmail;	
}

// *****************************************
// Private methods, not intended for app use
// *****************************************

LDAP._stringifyUniqueIdentifier = function (uniqueIdentifier) {
  var stringified = JSON.stringify(uniqueIdentifier);
  return stringified.substr(1, stringified.length - 2);
}

LDAP._serverDnToFQDN = function (serverDn) {
  return serverDn.toLowerCase().replace(/\s+/g, '').split(/,?dc=/).slice(1).join('.');
}

LDAP._callbacks = {
  onAddMultitenantIdentifier: [],
  onSignIn: []
};

LDAP._addCallback = function (callback, target) {
  if (_.isFunction(callback)) {
    LDAP._callbacks[target].push(callback);
  }
  else {
    throw new Meteor.Error(target + ' callback must be a function');  
  }
}

LDAP._settings = function (request) {
  return LDAP.generateSettings(request) || Meteor.settings.ldap;
}

var ldap = Npm.require('ldapjs');
var Future = Npm.require('fibers/future');
var assert = Npm.require('assert');

LDAP._createClient = function () {
  var client = null;
  var settings = this;
  var serverUrl = settings.serverUrl;
  if (serverUrl.indexOf('ldaps://') === 0 && settings.ldapsCertificate) {
	client = ldap.createClient({
	  url: serverUrl,
	  tlsOptions: {
		ca: [settings.ldapsCertificate]
	  }
	});
  }
  else {
	client = ldap.createClient({
      url: serverUrl
    });
  }

  client.on('error', function (error) {
      LDAP.error('ldapjs client reported an error: ', error);
  });

  return client;
};

// If next version of ldapjs ever comes out
// TODO - It's out! Update ldapjs version, then test and release this code!
/*LDAP._starttls = function (client) {
  var success = null;
  
{'fraction_non_alphanumeric': 0.14255055716054477, 'fraction_numerical': 0.0011555922410235245, 'mean_word_length': 4.142614601018676, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2075731', 'n_tokens_mistral': 3612, 'n_tokens_neox': 3555, 'n_words': 1172}