LT-103: Authenticate via LDAP
This commit is contained in:
parent
beaa0d4e65
commit
3c60a6dda1
@ -36,7 +36,7 @@ var routerOptions = {
|
||||
Router.route('/', function() {
|
||||
// Check user is logged in
|
||||
if(Meteor.user() && Meteor.userId()) {
|
||||
if (!Meteor.user().emails[0].verified && verifyEmail) {
|
||||
if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
|
||||
this.render('emailVerification', routerOptions);
|
||||
} else {
|
||||
this.render('worklist', routerOptions);
|
||||
@ -50,7 +50,7 @@ Router.route('/', function() {
|
||||
Router.route('/worklist', function() {
|
||||
// Check user is logged in
|
||||
if(Meteor.user() && Meteor.userId()) {
|
||||
if (!Meteor.user().emails[0].verified && verifyEmail) {
|
||||
if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
|
||||
this.render('emailVerification', routerOptions);
|
||||
} else {
|
||||
this.render('worklist', routerOptions);
|
||||
|
||||
43
Packages/accounts-ldap/ldap_client.js
Normal file
43
Packages/accounts-ldap/ldap_client.js
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 (user, 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
|
||||
user = 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 = _.defaults({
|
||||
username: user,
|
||||
ldapPass: password
|
||||
}, {
|
||||
ldap: true,
|
||||
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) {
|
||||
callback && callback(error);
|
||||
} else {
|
||||
callback && callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
366
Packages/accounts-ldap/ldap_server.js
Normal file
366
Packages/accounts-ldap/ldap_server.js
Normal file
@ -0,0 +1,366 @@
|
||||
Future = Npm.require('fibers/future');
|
||||
|
||||
// At a minimum, set up LDAP_DEFAULTS.url and .dn according to
|
||||
// your needs. url should appear as 'ldap://your.url.here'
|
||||
// dn should appear in normal ldap format of comma separated attribute=value
|
||||
// e.g. 'uid=someuser,cn=users,dc=somevalue'
|
||||
LDAP_DEFAULTS = {
|
||||
url: false,
|
||||
port: '389',
|
||||
dn: false,
|
||||
searchDN: false,
|
||||
searchCredentials: false,
|
||||
createNewUser: true,
|
||||
defaultDomain: false,
|
||||
searchResultsProfileMap: false,
|
||||
base: null,
|
||||
search: '(objectclass=*)',
|
||||
ldapsCertificate: false
|
||||
};
|
||||
|
||||
/**
|
||||
@class LDAP
|
||||
@constructor
|
||||
*/
|
||||
var LDAP = function (options) {
|
||||
// Set options
|
||||
this.options = _.defaults(options, LDAP_DEFAULTS);
|
||||
|
||||
// Make sure options have been set
|
||||
try {
|
||||
check(this.options.url, String);
|
||||
//check(this.options.dn, String);
|
||||
} catch (e) {
|
||||
throw new Meteor.Error('Bad Defaults', 'Options not set. Make sure to set LDAP_DEFAULTS.url and LDAP_DEFAULTS.dn!');
|
||||
}
|
||||
|
||||
// Because NPM ldapjs module has some binary builds,
|
||||
// We had to create a wraper package for it and build for
|
||||
// certain architectures. The package typ:ldap-js exports
|
||||
// 'MeteorWrapperLdapjs' which is a wrapper for the npm module
|
||||
this.ldapjs = MeteorWrapperLdapjs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to bind (authenticate) ldap
|
||||
* and perform a dn search if specified
|
||||
*
|
||||
* @method ldapCheck
|
||||
*
|
||||
* @param {Object} options Object with username, ldapPass and overrides for LDAP_DEFAULTS object.
|
||||
* Additionally the searchBeforeBind parameter can be specified, which is used to search for the DN
|
||||
* if not provided.
|
||||
*/
|
||||
LDAP.prototype.ldapCheck = function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (options.hasOwnProperty('username') && options.hasOwnProperty('ldapPass')) {
|
||||
|
||||
var ldapAsyncFut = new Future();
|
||||
|
||||
|
||||
// Create ldap client
|
||||
var fullUrl = self.options.url + ':' + self.options.port;
|
||||
var client = null;
|
||||
|
||||
if (self.options.url.indexOf('ldaps://') === 0) {
|
||||
client = self.ldapjs.createClient({
|
||||
url: fullUrl,
|
||||
tlsOptions: {
|
||||
ca: [self.options.ldapsCertificate]
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
client = self.ldapjs.createClient({
|
||||
url: fullUrl
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Slide @xyz.whatever from username if it was passed in
|
||||
// and replace it with the domain specified in defaults
|
||||
var emailSliceIndex = options.username.indexOf('@');
|
||||
var username;
|
||||
var domain = self.options.defaultDomain;
|
||||
|
||||
// If user appended email domain, strip it out
|
||||
// And use the defaults.defaultDomain if set
|
||||
if (emailSliceIndex !== -1) {
|
||||
username = options.username.substring(0, emailSliceIndex);
|
||||
domain = domain || options.username.substring((emailSliceIndex + 1), options.username.length);
|
||||
} else {
|
||||
username = options.username;
|
||||
}
|
||||
|
||||
|
||||
// If DN is provided, use it to bind
|
||||
if (self.options.dn) {
|
||||
// Attempt to bind to ldap server with provided info
|
||||
client.bind(self.options.searchDN || self.options.dn, self.options.searchCredentials || options.ldapPass, function (err) {
|
||||
try {
|
||||
if (err) {
|
||||
// Bind failure, return error
|
||||
throw new Meteor.Error(err.code, err.message);
|
||||
}
|
||||
|
||||
var handleSearchProfile = function (retObject, bindAfterSearch) {
|
||||
retObject.emptySearch = true;
|
||||
|
||||
// construct list of ldap attributes to fetch
|
||||
var attributes = [];
|
||||
if (self.options.searchResultsProfileMap) {
|
||||
self.options.searchResultsProfileMap.map(function (item) {
|
||||
attributes.push(item.resultKey);
|
||||
});
|
||||
}
|
||||
|
||||
// use base if given, else the dn for the ldap search
|
||||
var searchBase = self.options.base || self.options.dn;
|
||||
var searchOptions = {
|
||||
scope: 'sub',
|
||||
sizeLimit: 1,
|
||||
attributes: attributes,
|
||||
filter: self.options.search
|
||||
};
|
||||
|
||||
client.search(searchBase, searchOptions, function (err, res) {
|
||||
if (err) {
|
||||
ldapAsyncFut.return({
|
||||
error: err
|
||||
});
|
||||
}
|
||||
|
||||
res.on('searchEntry', function (entry) {
|
||||
retObject.emptySearch = false;
|
||||
// Add entry results to return object
|
||||
retObject.searchResults = entry.object;
|
||||
if (bindAfterSearch) {
|
||||
client.bind(retObject.searchResults.dn, options.ldapPass, function (err) {
|
||||
try {
|
||||
if (err) {
|
||||
throw new Meteor.Error(err.code, err.message);
|
||||
}
|
||||
ldapAsyncFut.return(retObject);
|
||||
} catch (e) {
|
||||
ldapAsyncFut.return({
|
||||
error: e
|
||||
});
|
||||
}
|
||||
})
|
||||
} else ldapAsyncFut.return(retObject);
|
||||
});
|
||||
|
||||
// This call causes Future issue: "Future resolved more than once"
|
||||
// Because it is already called in searchEntry handler
|
||||
/*res.on('end', function () {
|
||||
ldapAsyncFut.return(retObject);
|
||||
});*/
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
var retObject = {
|
||||
username: username,
|
||||
searchResults: null,
|
||||
email: domain ? username + '@' + domain : false
|
||||
};
|
||||
|
||||
if (self.options.searchDN) {
|
||||
handleSearchProfile(retObject, true);
|
||||
} else if (self.options.searchResultsProfileMap) {
|
||||
handleSearchProfile(retObject, false);
|
||||
} else {
|
||||
ldapAsyncFut.return(retObject);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
ldapAsyncFut.return({
|
||||
error: e
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// DN not provided, search for DN and use result to bind
|
||||
else if (typeof self.options.searchBeforeBind !== undefined) {
|
||||
// initialize result
|
||||
var retObject = {
|
||||
username: username,
|
||||
email: domain ? username + '@' + domain : false,
|
||||
emptySearch: true,
|
||||
searchResults: {}
|
||||
};
|
||||
|
||||
// compile attribute list to return
|
||||
var searchAttributes = ['dn'];
|
||||
self.options.searchResultsProfileMap.map(function (item) {
|
||||
searchAttributes.push(item.resultKey);
|
||||
});
|
||||
|
||||
|
||||
var filter = self.options.search;
|
||||
Object.keys(options.ldapOptions.searchBeforeBind).forEach(function (searchKey) {
|
||||
filter = '&' + filter + '(' + searchKey + '=' + options.ldapOptions.searchBeforeBind[searchKey] + ')';
|
||||
});
|
||||
var searchOptions = {
|
||||
scope: 'sub',
|
||||
sizeLimit: 1,
|
||||
filter: filter
|
||||
};
|
||||
|
||||
// perform LDAP search to determine DN
|
||||
client.search(self.options.base, searchOptions, function (err, res) {
|
||||
retObject.emptySearch = true;
|
||||
res.on('searchEntry', function (entry) {
|
||||
retObject.dn = entry.objectName;
|
||||
retObject.username = retObject.dn;
|
||||
retObject.emptySearch = false;
|
||||
|
||||
// Return search results if specified
|
||||
if (self.options.searchResultsProfileMap) {
|
||||
// construct list of ldap attributes to fetch
|
||||
self.options.searchResultsProfileMap.map(function (item) {
|
||||
retObject.searchResults[item.resultKey] = entry.object[item.resultKey];
|
||||
});
|
||||
}
|
||||
|
||||
// use the determined DN to bind
|
||||
client.bind(entry.objectName, options.ldapPass, function (err) {
|
||||
try {
|
||||
if (err) {
|
||||
throw new Meteor.Error(err.code, err.message);
|
||||
}
|
||||
else {
|
||||
ldapAsyncFut.return(retObject);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
ldapAsyncFut.return({
|
||||
error: e
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
// If no dn is found, return as is.
|
||||
res.on('end', function (result) {
|
||||
if (retObject.dn === undefined) {
|
||||
ldapAsyncFut.return(retObject);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return ldapAsyncFut.wait();
|
||||
|
||||
} else {
|
||||
throw new Meteor.Error(403, 'Missing LDAP Auth Parameter');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Register login handler with Meteor
|
||||
// Here we create a new LDAP instance with options passed from
|
||||
// Meteor.loginWithLDAP on client side
|
||||
// @param {Object} loginRequest will consist of username, ldapPass, ldap, and ldapOptions
|
||||
Accounts.registerLoginHandler('ldap', function (loginRequest) {
|
||||
// If 'ldap' isn't set in loginRequest object,
|
||||
// then this isn't the proper handler (return undefined)
|
||||
if (!loginRequest.ldap) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Instantiate LDAP with options
|
||||
var userOptions = loginRequest.ldapOptions || {};
|
||||
var ldapObj = new LDAP(userOptions);
|
||||
|
||||
// Call ldapCheck and get response
|
||||
var ldapResponse = ldapObj.ldapCheck(loginRequest);
|
||||
|
||||
if (ldapResponse.error) {
|
||||
return {
|
||||
userId: null,
|
||||
error: ldapResponse.error
|
||||
};
|
||||
}
|
||||
else if (ldapResponse.emptySearch) {
|
||||
return {
|
||||
userId: null,
|
||||
error: new Meteor.Error(403, 'User not found in LDAP')
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Set initial userId and token vals
|
||||
var userId = null;
|
||||
var stampedToken = {
|
||||
token: null
|
||||
};
|
||||
|
||||
// Look to see if user already exists
|
||||
var user = Meteor.users.findOne({
|
||||
username: ldapResponse.username
|
||||
});
|
||||
|
||||
// Login user if they exist
|
||||
if (user) {
|
||||
userId = user._id;
|
||||
|
||||
// Create hashed token so user stays logged in
|
||||
stampedToken = Accounts._generateStampedLoginToken();
|
||||
var hashStampedToken = Accounts._hashStampedToken(stampedToken);
|
||||
// Update the user's token in mongo
|
||||
Meteor.users.update(userId, {
|
||||
$push: {
|
||||
'services.resume.loginTokens': hashStampedToken
|
||||
}
|
||||
});
|
||||
}
|
||||
// Otherwise create user if option is set
|
||||
else if (ldapObj.options.createNewUser) {
|
||||
var userObject = {
|
||||
username: ldapResponse.username
|
||||
};
|
||||
// Set email
|
||||
if (ldapResponse.email) userObject.email = ldapResponse.email;
|
||||
|
||||
// Set profile values if specified in searchResultsProfileMap
|
||||
if (ldapResponse.searchResults && ldapObj.options.searchResultsProfileMap.length > 0) {
|
||||
|
||||
var profileMap = ldapObj.options.searchResultsProfileMap;
|
||||
var profileObject = {};
|
||||
|
||||
// Loop through profileMap and set values on profile object
|
||||
for (var i = 0; i < profileMap.length; i++) {
|
||||
var resultKey = profileMap[i].resultKey;
|
||||
|
||||
// If our search results have the specified property, set the profile property to its value
|
||||
if (ldapResponse.searchResults.hasOwnProperty(resultKey)) {
|
||||
profileObject[profileMap[i].profileProperty] = ldapResponse.searchResults[resultKey];
|
||||
}
|
||||
|
||||
}
|
||||
// Set userObject profile
|
||||
userObject.profile = profileObject;
|
||||
}
|
||||
|
||||
|
||||
userId = Accounts.createUser(userObject);
|
||||
} else {
|
||||
// Ldap success, but no user created
|
||||
console.log('LDAP Authentication succeeded for ' + ldapResponse.username + ', but no user exists in Meteor. Either create the user manually or set LDAP_DEFAULTS.createNewUser to true');
|
||||
return {
|
||||
userId: null,
|
||||
error: new Meteor.Error(403, 'User found in LDAP but not in application')
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
userId: userId,
|
||||
token: stampedToken.token
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
28
Packages/accounts-ldap/package.js
Normal file
28
Packages/accounts-ldap/package.js
Normal file
@ -0,0 +1,28 @@
|
||||
Package.describe({
|
||||
name: 'typ:accounts-ldap',
|
||||
version: '1.0.1',
|
||||
summary: 'Accounts login for LDAP using ldapjs. Supports anonymous DN search & LDAPS.',
|
||||
git: 'https://github.com/typ90/meteor-accounts-ldap',
|
||||
documentation: 'README.md'
|
||||
});
|
||||
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('1.0.3.1');
|
||||
|
||||
api.use(['templating'], 'client');
|
||||
api.use(['typ:ldapjs@0.7.3'], 'server');
|
||||
|
||||
|
||||
api.use('accounts-base', 'server');
|
||||
api.imply('accounts-base');
|
||||
api.imply('accounts-password');
|
||||
|
||||
api.use('check');
|
||||
|
||||
api.addFiles(['ldap_client.js'], 'client');
|
||||
api.addFiles(['ldap_server.js'], 'server');
|
||||
|
||||
api.export('LDAP', 'server');
|
||||
api.export('LDAP_DEFAULTS', 'server');
|
||||
});
|
||||
@ -16,27 +16,47 @@
|
||||
<h1 id="signInPageTitle" class="title-auth">Sign In.</h1>
|
||||
<p id="signInPageMessage" class="subtitle-auth" style="{{getSignInMessageColor}}">{{{getSignInMessage}}}</p>
|
||||
|
||||
<form>
|
||||
<div class="input-symbol">
|
||||
<input id="signInPageEmailInput" type="text" name="email" placeholder="Your Email" style="{{getEmailValidationStyling}}" />
|
||||
<i class="fa fa-envelope-o" title="Your Email"></i>
|
||||
</div>
|
||||
{{#if isLDAPSet}}
|
||||
<form>
|
||||
<div class="input-symbol">
|
||||
<input id="signInLDAPUsernameInput" type="text" name="email" placeholder="Username" style="{{getEmailValidationStyling}}" />
|
||||
<i class="fa fa-envelope-o" title="Your Email"></i>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<div class="input-symbol">
|
||||
<input id="signInLDAPPasswordInput" type="password" name="password" placeholder="Password" style={{getPasswordValidationStyling}} />
|
||||
<span class="fa fa-lock" title="Password"></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br><br>
|
||||
<button id="signInLDAPToAppButton" class="btn-primary btn-main btn-large" style="{{getButtonColor}}">{{getButtonText}}</button>
|
||||
{{else}}
|
||||
<form>
|
||||
<div class="input-symbol">
|
||||
<input id="signInPageEmailInput" type="text" name="email" placeholder="Your Email" style="{{getEmailValidationStyling}}" />
|
||||
<i class="fa fa-envelope-o" title="Your Email"></i>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<div class="input-symbol">
|
||||
<input id="signInPagePasswordInput" type="password" name="password" placeholder="Password" style={{getPasswordValidationStyling}} />
|
||||
<span class="fa fa-lock" title="Password"></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br><br>
|
||||
<button id="signInToAppButton" class="btn-primary btn-main btn-large disabledButton" disabled style="{{getButtonColor}}">{{getButtonText}}</button>
|
||||
|
||||
{{/if}}
|
||||
<br><br>
|
||||
<button id="needAnAccountButton" class="btn-gray btn-main btn-large">Need an account?</button>
|
||||
|
||||
<div class="input-symbol">
|
||||
<input id="signInPagePasswordInput" type="password" name="password" placeholder="Password" style={{getPasswordValidationStyling}} />
|
||||
<span class="fa fa-lock" title="Password"></span>
|
||||
</div>
|
||||
</form>
|
||||
<br><br>
|
||||
<button id="forgotPasswordButton" class="btn-gray btn-main btn-large">Forgot password?</button>
|
||||
|
||||
<br><br>
|
||||
<button id="signInToAppButton" class="btn-primary btn-main btn-large disabledButton" disabled style="{{getButtonColor}}">{{getButtonText}}</button>
|
||||
|
||||
<br><br>
|
||||
<button id="needAnAccountButton" class="btn-gray btn-main btn-large">Need an account?</button>
|
||||
|
||||
<br><br>
|
||||
<button id="forgotPasswordButton" class="btn-gray btn-main btn-large">Forgot password?</button>
|
||||
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
@ -64,6 +64,10 @@ Template.entrySignIn.helpers({
|
||||
} else {
|
||||
return "border: 1px solid gray";
|
||||
}
|
||||
},
|
||||
|
||||
isLDAPSet: function() {
|
||||
return Session.get('isLDAPSet');
|
||||
}
|
||||
|
||||
});
|
||||
@ -134,6 +138,11 @@ Template.entrySignIn.events({
|
||||
if(event.keyCode == 13) {
|
||||
$("#signInToAppButton").click();
|
||||
}
|
||||
},
|
||||
'click #signInLDAPToAppButton': function(e, template) {
|
||||
var username = template.$("#signInLDAPUsernameInput").val();
|
||||
var password = template.$("#signInLDAPPasswordInput").val();
|
||||
ActiveEntry.loginWithLDAP(username, password);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -32,14 +32,8 @@ if (Meteor.isClient) {
|
||||
passwordExpirationDays: 90,
|
||||
inactivityPeriodDays: 180
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// requireRegexValidation toggles regex
|
||||
// reqiureStrongPasswords toggles zxcvbn
|
||||
|
||||
if (Meteor.isClient) {
|
||||
ActiveEntry.errorMessages = new ReactiveDict('errorMessages');
|
||||
ActiveEntry.errorMessages.set('signInError', false);
|
||||
|
||||
@ -48,6 +42,15 @@ if (Meteor.isClient) {
|
||||
|
||||
// Change password warning message according to whether zxcvbn is turned on
|
||||
Session.set('passwordWarning', 'Password must have at least 8 characters. It must contain at least 1 uppercase, 1 lowercase, 1 number and 1 special character.');
|
||||
|
||||
Meteor.call('isLDAPSet', function(error, isSet) {
|
||||
Session.set('isLDAPSet', isSet);
|
||||
});
|
||||
}
|
||||
|
||||
if (Meteor.isServer) {
|
||||
LDAP_DEFAULTS.url = Meteor.settings.ldap && Meteor.settings.ldap.url;
|
||||
LDAP_DEFAULTS.port = Meteor.settings.ldap && Meteor.settings.ldap.port;
|
||||
}
|
||||
|
||||
ActiveEntry.configure = function (configObject) {
|
||||
@ -128,6 +131,7 @@ ActiveEntry.signIn = function (emailValue, passwordValue){
|
||||
|
||||
ActiveEntry.verifyPassword(passwordValue);
|
||||
ActiveEntry.verifyEmail(emailValue);
|
||||
// TODO: Find a solution nested calling
|
||||
|
||||
var ActiveEntryConfig = Session.get('Photonic.ActiveEntry');
|
||||
var failedAttemptsLimit = ActiveEntryConfig && ActiveEntryConfig.passwordOptions && ActiveEntryConfig.passwordOptions.failedAttemptsLimit || 5;
|
||||
@ -217,6 +221,44 @@ ActiveEntry.signIn = function (emailValue, passwordValue){
|
||||
|
||||
};
|
||||
|
||||
ActiveEntry.loginWithLDAP = function(username, password) {
|
||||
if (!username || !password) {
|
||||
return;
|
||||
}
|
||||
|
||||
Meteor.loginWithLDAP(username, password, {
|
||||
// The dn value depends on what you want to search/auth against
|
||||
// The structure will depend on how your ldap server
|
||||
// is configured or structured.
|
||||
dn: "uid=" + username + ",ou=users,ou=system",
|
||||
// The search value is optional. Set it if your search does not
|
||||
// work with the bind dn.
|
||||
searchResultsProfileMap: [
|
||||
{
|
||||
resultKey: 'cn',
|
||||
profileProperty: 'fullName'
|
||||
},
|
||||
{
|
||||
resultKey: 'mail',
|
||||
profileProperty: 'email'
|
||||
}
|
||||
]
|
||||
}, function(error) {
|
||||
if (error) {
|
||||
ActiveEntry.errorMessages.set('signInError', error.errorType+" ["+error.error+"]");
|
||||
return;
|
||||
}
|
||||
ActiveEntry.errorMessages.set('signInError', null);
|
||||
|
||||
// Update last login time
|
||||
Meteor.call("updateLastLoginDate");
|
||||
|
||||
var ActiveEntryConfig = Session.get('Photonic.ActiveEntry');
|
||||
Router.go(ActiveEntryConfig.signIn.destination);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
ActiveEntry.signUp = function (emailValue, passwordValue, confirmPassword, fullName){
|
||||
ActiveEntry.verifyEmail(emailValue);
|
||||
ActiveEntry.verifyPassword(passwordValue);
|
||||
@ -235,14 +277,16 @@ ActiveEntry.signUp = function (emailValue, passwordValue, confirmPassword, fullN
|
||||
return;
|
||||
}
|
||||
|
||||
// Capitalize first letter of every word in fullName
|
||||
var capitalizedFullName = fullName.replace(/[^\s]+/g, function(str){
|
||||
return str.substr(0,1).toUpperCase()+str.substr(1).toLowerCase();
|
||||
});
|
||||
|
||||
Accounts.createUser({
|
||||
email: emailValue,
|
||||
password: passwordValue,
|
||||
profile: {
|
||||
fullName: fullName
|
||||
},
|
||||
testCase: {
|
||||
createdAt: new Date()
|
||||
fullName: capitalizedFullName
|
||||
}
|
||||
}, function (error, result) {
|
||||
if (error) {
|
||||
|
||||
@ -15,7 +15,7 @@ Package.onUse(function (api) {
|
||||
'clinical:router@2.0.17',
|
||||
'grove:less@0.1.1',
|
||||
'session',
|
||||
'reactive-dict',
|
||||
'reactive-dict'
|
||||
//'codetheweb:zxcvbn'
|
||||
], ['client']);
|
||||
|
||||
@ -25,7 +25,8 @@ Package.onUse(function (api) {
|
||||
]);
|
||||
|
||||
api.use([
|
||||
'zuuk:stale-session@1.0.8'
|
||||
'zuuk:stale-session@1.0.8',
|
||||
'typ:accounts-ldap'
|
||||
], ['client', 'server']);
|
||||
|
||||
api.addFiles([
|
||||
@ -60,8 +61,7 @@ Package.onUse(function (api) {
|
||||
|
||||
'components/changePassword/changePassword.html',
|
||||
'components/changePassword/changePassword.js',
|
||||
'components/changePassword/changePassword.less',
|
||||
|
||||
'components/changePassword/changePassword.less'
|
||||
], ['client']);
|
||||
|
||||
|
||||
|
||||
@ -147,6 +147,14 @@ Meteor.methods({
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
isLDAPSet: function() {
|
||||
if (LDAP_DEFAULTS.url && LDAP_DEFAULTS.port) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,10 @@ Template.userAccountMenu.helpers({
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Meteor.user().emails) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Meteor.user().emails[0].verified) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -31,6 +31,9 @@
|
||||
"password": "",
|
||||
"server": "",
|
||||
"port": ""
|
||||
},
|
||||
"ldap": {
|
||||
"url": "ldap://localhost",
|
||||
"port": "10389"
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user