-
Notifications
You must be signed in to change notification settings - Fork 15
/
index.js
171 lines (143 loc) · 5.79 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var util = _interopDefault(require('util'));
var passport = _interopDefault(require('passport'));
var ActiveDirectory = _interopDefault(require('activedirectory2'));
/*
* modified version of passport-windowsauth (https://github.com/auth0/passport-windowsauth)
* using activedirectory (https://github.com/gheeres/node-activedirectory)
*
* Additional features are that the ad connection is passed to the verify function so that
* the user can take advantage of the query functions provided by activedirectory
* when using ldap authentication
*
* Signature: verify ( [req], profile, [adClient], done)
*
*/
var DEFAULT_GROUP_VALUE = 'users';
var DEFAULT_USERNAME_FIELD = 'username';
var DEFAULT_PASSWORD_FIELD = 'password';
var DEFAULT_ATTRS = ['cn', 'dn', 'sn', 'displayName', 'givenName', 'title', 'userPrincipalName', 'sAMAccountName', 'mail', 'description', 'telephoneNumber', 'memberOf'];
var DEFAULT_FILTER = function DEFAULT_FILTER(username) {
return '(&(objectclass=user)(|(sAMAccountName=' + username + ')(UserPrincipalName=' + username + ')))';
};
function getUserNameFromHeader(req) {
if (!req.headers['x-iisnode-logon_user']) return null;
return req.headers['x-iisnode-logon_user'].split('\\')[1];
}
function Strategy(options, verify) {
if (typeof options === 'function') {
verify = options;
options = {};
}
if (!verify) throw new Error('windows authentication strategy requires a verify function');
passport.Strategy.call(this);
this.name = 'ActiveDirectory';
this._verify = verify;
this._options = options;
this._passReqToCallback = options.passReqToCallback;
this._integrated = options.integrated === false ? options.integrated : true;
this._getUserNameFromHeader = options.getUserNameFromHeader || getUserNameFromHeader;
this._group = options.group;
if (!this._integrated) {
this._usernameField = options.usernameField || DEFAULT_USERNAME_FIELD;
this._passwordField = options.passwordField || DEFAULT_PASSWORD_FIELD;
}
if (options.ldap instanceof ActiveDirectory) {
this._ad = options.ldap;
} else {
if (this._options.ldap.attributes) {
if (typeof (this._options.ldap.attributes) === 'string') {
this._options.ldap.attributes = { user: [this._options.ldap.attributes] }
} else if (Array.isArray(this._options.ldap.attributes)) {
this._options.ldap.attributes = { user: this._options.ldap.attributes }
}
}
this._ad = new ActiveDirectory(this._options.ldap);
}
}
util.inherits(Strategy, passport.Strategy);
Strategy.prototype.mapProfile = function (i) {
if (!i) return i;
// allow custom profile mapper
if (typeof this._options.mapProfile === 'function') {
var userProfile = this._options.mapProfile(i);
userProfile._json = i;
return userProfile;
}
// default profile mapper
return {
id: i.objectGUID || i.uid,
displayName: i.displayName,
name: {
familyName: i.sn || i.surName,
givenName: i.gn || i.givenName
},
emails: i.mail ? [{ value: i.mail }] : undefined,
_json: i
};
};
Strategy.prototype.authenticate = function (req) {
var _this = this;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var username = null,
password = null;
// get username and password
if (this._integrated) {
username = this._getUserNameFromHeader(req);
if (!username) {
return this.fail();
}
} else {
username = req.body[this._usernameField] || req.query[this._usernameField];
password = req.body[this._passwordField] || req.query[this._passwordField];
}
// helper functions
var verified = function verified(err, user, info) {
if (err) {
return _this.error(err);
}
if (!user) {
return _this.fail(info);
}
_this.success(user, info);
};
var verify = function verify(userProfile) {
if (_this._passReqToCallback) {
if (_this._ad) return _this._verify(req, userProfile, _this._ad, verified);else return _this._verify(req, userProfile, verified);
} else {
if (_this._ad) return _this._verify(userProfile, _this._ad, verified);else return _this._verify(userProfile, verified);
}
};
var auth = function auth(userProfile) {
return _this._ad.authenticate(userProfile._json.dn, password, function (err, auth) {
var authFailureMessage = 'Authentication failed for ' + username;
if (err) {
return err.name === 'InvalidCredentialsError' ? _this.fail('' + authFailureMessage) : _this.error(err);
}
if (!auth) return _this.fail(authFailureMessage);
return verify(userProfile);
});
};
// look for the user if using ldap auth
if (this._ad) {
var group = req.body.group || req.query.group || this._group || DEFAULT_GROUP_VALUE;
var ldap = this._options.ldap;
var filter = typeof ldap.filter === 'function' ? ldap.filter(username) : DEFAULT_FILTER(username);
var attributes = (ldap.attributes && ldap.attributes.user) || DEFAULT_ATTRS;
attributes = Array.isArray(attributes) ? attributes : [attributes];
// require the dn attribute which will be used during authentication
if (attributes.indexOf('dn') === -1) attributes.push('dn');
return this._ad.find({ filter: filter, attributes: attributes }, function (err, results) {
if (err) return _this.error(err);
if (!results || !results[group] || !Array.isArray(results[group]) || !results[group].length) {
return _this.fail('The user "' + username + '" was not found');
}
var userProfile = _this.mapProfile(results[group][0]);
return _this._integrated ? verify(userProfile) : auth(userProfile);
});
}
// non-ldap auth
return verify({ name: username, id: username });
};
module.exports = Strategy;