1 | var should = require('chai').Should(),
|
2 | LdapStrategy = require('passport-ldapauth'),
|
3 | request = require('supertest'),
|
4 | basicAuth = require('basic-auth'),
|
5 | ldapserver = require('./ldapserver'),
|
6 | appserver = require('./appserver');
|
7 |
|
8 | var LDAP_PORT = 1399;
|
9 |
|
10 | var expressapp = null;
|
11 |
|
12 |
|
13 | var BASE_OPTS = {
|
14 | server: {
|
15 | url: 'ldap://localhost:' + LDAP_PORT.toString(),
|
16 | bindDn: 'cn=root',
|
17 | bindCredentials: 'secret',
|
18 | searchBase: 'ou=passport-ldapauth',
|
19 | searchFilter: '(uid={{username}})'
|
20 | }
|
21 | },
|
22 | BASE_TEST_OPTS = {
|
23 | no_callback: false
|
24 | };
|
25 |
|
26 | var start_servers = function(opts, test_opts) {
|
27 | return function(cb) {
|
28 | ldapserver.start(LDAP_PORT, function() {
|
29 | appserver.start(opts, test_opts, function(app) {
|
30 | expressapp = app;
|
31 | cb();
|
32 | });
|
33 | });
|
34 | }
|
35 | }
|
36 |
|
37 | var stop_servers = function(cb) {
|
38 | appserver.close(function() {
|
39 | ldapserver.close(function() {
|
40 | cb();
|
41 | });
|
42 | });
|
43 | };
|
44 |
|
45 | describe('LDAP authentication strategy', function() {
|
46 |
|
47 | describe('by itself', function() {
|
48 |
|
49 | it('should export Strategy constructor directly', function(cb) {
|
50 | require('passport-ldapauth').should.be.a('function');
|
51 | cb();
|
52 | });
|
53 |
|
54 | it('should export Strategy constructor separately as well', function(cb) {
|
55 | var strategy = require('passport-ldapauth').Strategy;
|
56 | strategy.should.be.a('function');
|
57 | (function() {
|
58 | new strategy(BASE_OPTS);
|
59 | }).should.not.throw(Error);
|
60 | cb();
|
61 | });
|
62 |
|
63 | it('should be named ldapauth', function(cb) {
|
64 | var s = new LdapStrategy(BASE_OPTS);
|
65 | s.name.should.equal('ldapauth');
|
66 | cb();
|
67 | });
|
68 |
|
69 | it('should throw an error if no arguments are provided', function(cb) {
|
70 | (function() {
|
71 | new LdapStrategy();
|
72 | }).should.throw(Error);
|
73 | cb();
|
74 | });
|
75 |
|
76 | it('should throw an error if options are not accepted by ldapauth', function(cb) {
|
77 | var s = new LdapStrategy({}, function() {});
|
78 | (function() {
|
79 | s.authenticate({body: {username: 'valid', password: 'valid'}});
|
80 | }).should.throw(Error);
|
81 | cb();
|
82 | });
|
83 |
|
84 | it('should initialize without a verify callback', function(cb) {
|
85 | (function() {
|
86 | new LdapStrategy({server: {}})
|
87 | }).should.not.throw(Error);
|
88 | cb();
|
89 | });
|
90 |
|
91 | });
|
92 |
|
93 | describe('with basic settings', function() {
|
94 |
|
95 | before(start_servers(BASE_OPTS, BASE_TEST_OPTS));
|
96 |
|
97 | after(stop_servers);
|
98 |
|
99 | it('should return unauthorized if credentials are not given', function(cb) {
|
100 | request(expressapp)
|
101 | .post('/login')
|
102 | .send({})
|
103 | .expect(400)
|
104 | .end(cb);
|
105 | });
|
106 |
|
107 | it('should allow access with valid credentials', function(cb) {
|
108 | request(expressapp)
|
109 | .post('/login')
|
110 | .send({username: 'valid', password: 'valid'})
|
111 | .expect(200)
|
112 | .end(cb);
|
113 | });
|
114 |
|
115 | it('should allow access with valid credentials in query string', function(cb) {
|
116 | request(expressapp)
|
117 | .post('/login?username=valid&password=valid')
|
118 | .expect(200)
|
119 | .end(cb);
|
120 | });
|
121 |
|
122 | it('should return unauthorized with invalid credentials', function(cb) {
|
123 | request(expressapp)
|
124 | .post('/login')
|
125 | .send({username: 'valid', password: 'invalid'})
|
126 | .expect(401)
|
127 | .end(cb);
|
128 | });
|
129 |
|
130 | it('should return unauthorized with non-existing user', function(cb) {
|
131 | request(expressapp)
|
132 | .post('/login')
|
133 | .send({username: 'nonexisting', password: 'invalid'})
|
134 | .expect(401)
|
135 | .end(cb);
|
136 | });
|
137 |
|
138 | it('should return more specific flash message for AD reply', function(cb) {
|
139 | request(expressapp)
|
140 | .post('/custom-cb-login')
|
141 | .send({username: 'ms-ad', password: 'invalid'})
|
142 | .expect(401)
|
143 | .end(function(err, res) {
|
144 | should.not.exist(err);
|
145 | res.body.message.should.equal('Account disabled')
|
146 | cb(err, res);
|
147 | });
|
148 | });
|
149 | });
|
150 |
|
151 | describe('without a verify callback', function() {
|
152 | before(start_servers(BASE_OPTS, {no_callback: true}));
|
153 |
|
154 | after(stop_servers);
|
155 |
|
156 | it('should still authenticate', function(cb) {
|
157 | request(expressapp)
|
158 | .post('/login')
|
159 | .send({username: 'valid', password: 'valid'})
|
160 | .expect(200)
|
161 | .end(cb);
|
162 | });
|
163 |
|
164 | it('should reject invalid event', function(cb) {
|
165 | request(expressapp)
|
166 | .post('/login')
|
167 | .send({username: 'valid', password: 'invalid'})
|
168 | .expect(401)
|
169 | .end(cb);
|
170 | });
|
171 | });
|
172 |
|
173 | describe('with optional options', function() {
|
174 |
|
175 | afterEach(stop_servers);
|
176 |
|
177 | it('should read given fields instead of defaults', function(cb) {
|
178 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
179 | OPTS.usernameField = 'ldapuname';
|
180 | OPTS.passwordField = 'ldappwd';
|
181 |
|
182 | start_servers(OPTS, BASE_TEST_OPTS)(function() {
|
183 | request(expressapp)
|
184 | .post('/login')
|
185 | .send({ldapuname: 'valid', ldappwd: 'valid'})
|
186 | .expect(200)
|
187 | .end(cb);
|
188 | });
|
189 | });
|
190 |
|
191 | it('should pass request to verify callback if defined so', function(cb) {
|
192 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
193 | OPTS.passReqToCallback = true;
|
194 |
|
195 | start_servers(OPTS, BASE_TEST_OPTS)(function() {
|
196 | var req = {body: {username: 'valid', password: 'valid', testkey: 1}},
|
197 | s = new LdapStrategy(OPTS, function(req, user, done) {
|
198 | req.should.have.keys('body');
|
199 | req.body.should.have.keys(['username', 'password', 'testkey']);
|
200 | done(null, user);
|
201 | });
|
202 |
|
203 | s.success = function(user) {
|
204 | should.exist(user);
|
205 | user.uid.should.equal('valid');
|
206 | cb();
|
207 | };
|
208 |
|
209 | s.error = function() {};
|
210 |
|
211 | s.authenticate(req);
|
212 | });
|
213 | });
|
214 |
|
215 | it('should allow access with valid credentials in the header', function(cb) {
|
216 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
217 | OPTS.credentialsLookup = basicAuth;
|
218 |
|
219 | start_servers(OPTS, BASE_TEST_OPTS)(function() {
|
220 | request(expressapp)
|
221 | .post('/login')
|
222 | .set('Authorization', 'Basic dmFsaWQ6dmFsaWQ=')
|
223 | .expect(200)
|
224 | .end(cb);
|
225 | });
|
226 | });
|
227 | });
|
228 |
|
229 | describe('with options as function', function() {
|
230 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
231 | OPTS.usernameField = 'cb_uname';
|
232 | OPTS.passwordField = 'cb_pwd';
|
233 |
|
234 | var opts = function(cb) {
|
235 | process.nextTick(function() {
|
236 | cb(null, OPTS);
|
237 | });
|
238 | };
|
239 |
|
240 | before(start_servers(opts, BASE_TEST_OPTS));
|
241 | after(stop_servers);
|
242 |
|
243 | it('should use the options returned from the function', function(cb) {
|
244 | request(expressapp)
|
245 | .post('/login')
|
246 | .send({cb_uname: 'valid', cb_pwd: 'valid'})
|
247 | .expect(200)
|
248 | .end(cb);
|
249 | });
|
250 |
|
251 | it('should not allow login if using wrong fields', function(cb) {
|
252 | request(expressapp)
|
253 | .post('/login')
|
254 | .send({username: 'valid', password: 'valid'})
|
255 | .expect(400)
|
256 | .end(cb);
|
257 | });
|
258 | });
|
259 |
|
260 | describe('with options as function returning dynamic sets', function() {
|
261 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
262 | OPTS.usernameField = 'first_uname';
|
263 | OPTS.passwordField = 'first_pwd';
|
264 |
|
265 | var OPTS2 = JSON.parse(JSON.stringify(BASE_OPTS));
|
266 | OPTS2.usernameField = 'second_uname';
|
267 | OPTS2.passwordField = 'second_pwd';
|
268 |
|
269 | var opts = function(req, cb) {
|
270 | process.nextTick(function() {
|
271 | if (req.body.set == 'first') {
|
272 | cb(null, OPTS);
|
273 | } else {
|
274 | cb(null, OPTS2);
|
275 | }
|
276 | });
|
277 | };
|
278 |
|
279 | before(start_servers(opts, BASE_TEST_OPTS));
|
280 | after(stop_servers);
|
281 |
|
282 | it('should use the first set options returned from the function', function(cb) {
|
283 | request(expressapp)
|
284 | .post('/login')
|
285 | .send({first_uname: 'valid', first_pwd: 'valid', set: 'first'})
|
286 | .expect(200)
|
287 | .end(cb);
|
288 | });
|
289 |
|
290 | it('should not allow first set login if using wrong fields', function(cb) {
|
291 | request(expressapp)
|
292 | .post('/login')
|
293 | .send({second_uname: 'valid', second_pwd: 'valid', set: 'first'})
|
294 | .expect(400)
|
295 | .end(cb);
|
296 | });
|
297 |
|
298 | it('should use the second set options returned from the function', function(cb) {
|
299 | request(expressapp)
|
300 | .post('/login')
|
301 | .send({second_uname: 'valid', second_pwd: 'valid', set: 'second'})
|
302 | .expect(200)
|
303 | .end(cb);
|
304 | });
|
305 |
|
306 | it('should not allow second set login if using wrong fields', function(cb) {
|
307 | request(expressapp)
|
308 | .post('/login')
|
309 | .send({first_uname: 'valid', first_pwd: 'valid', set: 'second'})
|
310 | .expect(400)
|
311 | .end(cb);
|
312 | });
|
313 | });
|
314 |
|
315 | describe('with group fetch settings defined', function() {
|
316 | var OPTS;
|
317 |
|
318 | var groupTest = function(opts, cb) {
|
319 | start_servers(opts, BASE_TEST_OPTS)(function() {
|
320 | var req = {body: {username: 'valid', password: 'valid'}},
|
321 | s = new LdapStrategy(opts, function(user, done) {
|
322 | req.should.have.keys('body');
|
323 | req.body.should.have.keys(['username', 'password']);
|
324 | done(null, user);
|
325 | });
|
326 |
|
327 | s.success = function(user) {
|
328 | should.exist(user);
|
329 | user.uid.should.equal('valid');
|
330 | user._groups.length.should.equal(2);
|
331 | user._groups[0].name.should.equal('Group 1');
|
332 | user._groups[1].name.should.equal('Group 2');
|
333 | cb();
|
334 | };
|
335 |
|
336 | s.error = function() {};
|
337 |
|
338 | s.authenticate(req);
|
339 | });
|
340 | }
|
341 |
|
342 | beforeEach(function(cb) {
|
343 | OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
344 | OPTS.server.groupSearchBase = 'ou=passport-ldapauth';
|
345 | OPTS.server.groupSearchScope = 'sub';
|
346 | cb();
|
347 | });
|
348 |
|
349 | afterEach(stop_servers);
|
350 |
|
351 | it('should return groups for user with string filter', function(cb) {
|
352 | OPTS.server.groupSearchFilter = '(member={{dn}})';
|
353 | groupTest(OPTS, cb);
|
354 | });
|
355 |
|
356 | it('should return groups for user with function filter', function(cb) {
|
357 | OPTS.server.groupSearchFilter = function(user) {
|
358 | return '(member={{dn}})'.replace(/{{dn}}/, user.dn)
|
359 | };
|
360 | groupTest(OPTS, cb);
|
361 | });
|
362 | });
|
363 |
|
364 | describe('with invalid LDAP url', function() {
|
365 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
366 | OPTS.server.url = 'ldap://nonexistingdomain.fi:389';
|
367 |
|
368 | before(start_servers(OPTS, BASE_TEST_OPTS));
|
369 |
|
370 | after(stop_servers);
|
371 |
|
372 | it('should return with an error', function(cb) {
|
373 | request(expressapp)
|
374 | .post('/login')
|
375 | .send({username: 'valid', password: 'valid'})
|
376 | .expect(500)
|
377 | .end(cb);
|
378 | });
|
379 | });
|
380 |
|
381 | describe('with handleErrorsAsFailures', function() {
|
382 | after(stop_servers);
|
383 |
|
384 | it('should return with a failure', function(cb) {
|
385 | var callbackCalled = false;
|
386 | var testCompleted = false;
|
387 | var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
|
388 | OPTS.server.url = 'ldap://nonexistingdomain.fi:389';
|
389 | OPTS.handleErrorsAsFailures = true;
|
390 | OPTS.failureErrorCallback = function(err) {
|
391 | should.exist(err);
|
392 | callbackCalled = true;
|
393 | }
|
394 |
|
395 | start_servers(OPTS, BASE_TEST_OPTS)(function() {
|
396 | var req = {body: {username: 'valid', password: 'valid'}},
|
397 | s = new LdapStrategy(OPTS);
|
398 |
|
399 | s.fail = function(msg, code) {
|
400 | code.should.equal(500);
|
401 | callbackCalled.should.be.true;
|
402 |
|
403 |
|
404 | if (testCompleted === false) {
|
405 | testCompleted = true;
|
406 | cb();
|
407 | }
|
408 | }
|
409 |
|
410 | s.error = function() {};
|
411 |
|
412 | s.authenticate(req);
|
413 | });
|
414 | });
|
415 | });
|
416 | });
|