UNPKG

11.8 kBJavaScriptView Raw
1var 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
8var LDAP_PORT = 1399;
9
10var expressapp = null;
11
12// Base options that are cloned where needed to edit
13var 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},
22BASE_TEST_OPTS = {
23 no_callback: false
24};
25
26var 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
37var stop_servers = function(cb) {
38 appserver.close(function() {
39 ldapserver.close(function() {
40 cb();
41 });
42 });
43};
44
45describe('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() {}; // Just to have this when not run via passport
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() {}; // Just to have this when not run via passport
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 // There can be more than one event emitted and mocha fails
403 // if callback is called more than once
404 if (testCompleted === false) {
405 testCompleted = true;
406 cb();
407 }
408 }
409
410 s.error = function() {}; // Just to have this when not run via passport
411
412 s.authenticate(req);
413 });
414 });
415 });
416});