Skip to content

Commit c958c46

Browse files
authored
Add LDAPS-support to LDAP-Authcontroller (#7014)
* Add LDAPS-support to LDAP-Authcontroller * Add Testcase that failed with valid certificate but wrong credendtials to LDAP-Authcontroller * change scope of 'error' and remove 'case undefined', because it's not needed anymore
1 parent ccb045b commit c958c46

File tree

6 files changed

+220
-13
lines changed

6 files changed

+220
-13
lines changed

spec/LdapAuth.spec.js

+82
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const ldap = require('../lib/Adapters/Auth/ldap');
22
const mockLdapServer = require('./MockLdapServer');
3+
const fs = require('fs');
34
const port = 12345;
5+
const sslport = 12346;
46

57
it('Should fail with missing options', done => {
68
ldap
@@ -31,6 +33,86 @@ it('Should succeed with right credentials', done => {
3133
});
3234
});
3335

36+
it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', done => {
37+
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
38+
const options = {
39+
suffix: 'o=example',
40+
url: `ldaps://localhost:${sslport}`,
41+
dn: 'uid={{id}}, o=example',
42+
tlsOptions: { rejectUnauthorized: false }
43+
};
44+
ldap
45+
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
46+
.then(done)
47+
.catch(done.fail)
48+
.finally(() => server.close());
49+
});
50+
});
51+
52+
it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', done => {
53+
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
54+
const options = {
55+
suffix: 'o=example',
56+
url: `ldaps://localhost:${sslport}`,
57+
dn: 'uid={{id}}, o=example',
58+
tlsOptions: {
59+
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
60+
rejectUnauthorized: true
61+
}
62+
};
63+
ldap
64+
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
65+
.then(done)
66+
.catch(done.fail)
67+
.finally(() => server.close());
68+
});
69+
});
70+
71+
it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', done => {
72+
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
73+
const options = {
74+
suffix: 'o=example',
75+
url: `ldaps://localhost:${sslport}`,
76+
dn: 'uid={{id}}, o=example',
77+
tlsOptions: {
78+
ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'),
79+
rejectUnauthorized: true
80+
}
81+
};
82+
ldap
83+
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
84+
.then(done.fail)
85+
.catch(err => {
86+
jequal(err.message, 'LDAPS: Certificate mismatch');
87+
done();
88+
})
89+
.finally(() => server.close());
90+
});
91+
});
92+
93+
it('Should fail when LDAPS is used certifcate matches but credentials are wrong', done => {
94+
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
95+
const options = {
96+
suffix: 'o=example',
97+
url: `ldaps://localhost:${sslport}`,
98+
dn: 'uid={{id}}, o=example',
99+
tlsOptions: {
100+
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
101+
rejectUnauthorized: true
102+
}
103+
};
104+
ldap
105+
.validateAuthData({ id: 'testuser', password: 'wrong!' }, options)
106+
.then(done.fail)
107+
.catch(err => {
108+
jequal(err.message, 'LDAP: Wrong username or password');
109+
done();
110+
})
111+
.finally(() => server.close());
112+
});
113+
});
114+
115+
34116
it('Should fail with wrong credentials', done => {
35117
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
36118
const options = {

spec/MockLdapServer.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
const ldapjs = require('ldapjs');
2+
const fs = require('fs');
23

3-
function newServer(port, dn, provokeSearchError = false) {
4-
const server = ldapjs.createServer();
4+
const tlsOptions = {
5+
key: fs.readFileSync(__dirname + '/support/cert/key.pem'),
6+
certificate: fs.readFileSync(__dirname + '/support/cert/cert.pem')
7+
}
8+
9+
function newServer(port, dn, provokeSearchError = false, ssl = false) {
10+
const server = ssl ? ldapjs.createServer(tlsOptions) : ldapjs.createServer();
511

612
server.bind('o=example', function (req, res, next) {
713
if (req.dn.toString() !== dn || req.credentials !== 'secret')

spec/support/cert/anothercert.pem

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIE8DCCAtgCCQDjXCYv/hK1rjANBgkqhkiG9w0BAQsFADA5MRIwEAYDVQQDDAls
3+
b2NhbGhvc3QxIzAhBgkqhkiG9w0BCQEWFG5vLXJlcGx5QGV4YW1wbGUuY29tMCAX
4+
DTIwMTExNzEzMTAwMFoYDzIxMjAxMDI0MTMxMDAwWjA5MRIwEAYDVQQDDAlsb2Nh
5+
bGhvc3QxIzAhBgkqhkiG9w0BCQEWFG5vLXJlcGx5QGV4YW1wbGUuY29tMIICIjAN
6+
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmsOhxCNw3cEA3TLyqZXMI5p/LNSu
7+
W9doIvLEs1Ah8L/Gbl7xmSagkTZYzkTJDxITy0d45NVfmDsm0ctQrPV5MEbFE571
8+
lLQRnCFMpB3dqejfqQWpVCMfJKR1p8p5FTtcC5u5g7bcf2YeujwbUVDEtbeHwUeo
9+
XBnKfmv0UdGiLQf0uel5dcGWNp8dFo+hO4wCTA/risIdWawG8RHtzfhRIT2PqUa8
10+
ljgPyuPU2NQ19gUkV1LkXKJby+6VHhD6pSfzptbsJjalaGawTku7ZgBoZiax8wRk
11+
Bdwyd3ScMQg2VLGIn7YaMwb4ANtHqREekl0q7tPTu+PBmYqGXqa3lKa/s1OebUyS
12+
GQQXZB5T/Brm2fvJWqO9oJjZiTZzZIkBWDP0Cn+pmW/T4dADUms/vONEJE9IPFn1
13+
id5Q8vjSf5V1MaZJjWek38Y98xfYlKecHIqBAYQAydxdxuzG/DJu+2GzOZeffETD
14+
lzNwrLZp5lBzSrOwVntonvFo04lIq+DepVF+OqK8qV+7pnKCij5bGvdwxaY290pW
15+
+VTzK8kw0VUmpyYrDWIr7C52txaleY/AqsHy6wlVgdMbwXDjQ00twkJJT3tecL9I
16+
eWtLOuh7BeokvDFOXRVI2ZB2KN0sOBXsPfM6G4o9RK305Q9TFEXARnly9cwoV/i9
17+
8yeJ5teQHw3dm7kCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAIWUqZSMCGlzWsCtU
18+
Xayr4qshfhqhm6PzgCWGjg8Xsm8gwrbYtQRwsKdJvw7ynLhbeX65i7p3/3AO0W6k
19+
8Zpf58MHgepMOHmVT/KVBy7tUb83wJuoEvZzH50sO0rcA32c3p2FFUvt3JW+Dfo5
20+
BMX6GDlymtZPAplD9Rw5S5CXkZAgraDCbx1JMGFh0FfbP9v7jdo+so35y8UqmJ10
21+
3U0NX2UJoWGE6RvV2P/1TE0v4pWyFzz1dF2k/gcmzYtMgIkJGGO8qhIGo2rSVJhC
22+
gVlYxyW/Rxogxz4wN0EqPIJNnkRby/g40OkPN8ATkHs09F4Jyax+cU0iJ3Hbn5t/
23+
0Ou5oaAs4t1+u11iahUMP6evaXooZONawM7h0RT4HHHZkXT95+kmaMz/+JZRp9DA
24+
Cafp9IsTjLzHvRy5DLX2kithqXaKRdpgTylx0qwW+8HxRjCcJEsFN3lXWqX12R8D
25+
OM8DnVsFX61Ygp7kTj2CQ+Y3Wqrj+jEkyJLRvMeTNPlxfazwudgFuDYsDErMCUwG
26+
U67vPoCkvIShFrnR9X4ojpG8aqWF8M/o8nvKIQp+FEW0Btm6rZT9lGba6nZw76Yj
27+
+48bsJCQ7UzhKkeFO4Bmj0fDkBTAElV2oEJXbHbB6+0DQE48uLWAr4xb7Vswph8c
28+
wHgxPsgsd2h0gr21doWB1BsdAu8=
29+
-----END CERTIFICATE-----

spec/support/cert/cert.pem

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIE8DCCAtgCCQDaLjopNQCJuTANBgkqhkiG9w0BAQsFADA5MRIwEAYDVQQDDAls
3+
b2NhbGhvc3QxIzAhBgkqhkiG9w0BCQEWFG5vLXJlcGx5QGV4YW1wbGUuY29tMCAX
4+
DTIwMTExNzExNDEzM1oYDzIxMjAxMDI0MTE0MTMzWjA5MRIwEAYDVQQDDAlsb2Nh
5+
bGhvc3QxIzAhBgkqhkiG9w0BCQEWFG5vLXJlcGx5QGV4YW1wbGUuY29tMIICIjAN
6+
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvFf3I2RnIbp82Dd0AooAMamxMCgu
7+
g4zurMdA40mV8G+MA4Y5XFcGmOYT7LC94Z2nZ4tI+MNSiLKQY3Zq+OYGGmn/zVkr
8+
e8+02afxTjGmLVJWJXxXV2rsf8+UuJMOPbmVq87nJmD2gs9T6czOE3eQdDTRUzTg
9+
ubWhp3hV291gMfCIQeBbSqfbBscz0Nboj8NHStWDif5Io94l08tdW9oHIu99NYE0
10+
DMWIfBeztHpmSfkgPKH8lNar1dMsuCRW2Q/b01TNPKCNp8ZxyIhzkOq2gC5l60i5
11+
/iALWeEJii8g71V3DMbU5KoPEB+jFZ/z7qAi8TH9VqgaUycs/M96VXMIZbDhXywJ
12+
pg7qHxG/RT16bXwFotreThcla2M3VxsZEnYPEVmQEyVQeG7XyvqFMC3DhGCflW35
13+
dumJlkuGn9e9Lg6oiidp2RMnZuTsie+y3e3XJz2ZjFihGQNy2VzUrDz4ymi2fosV
14+
GMeHn3iK2nEqxf1mx021j3v40/8I5gtkS+zZuchclae0gRHaNN1tO0osedUdlV7D
15+
0dvi9xezsfelqSqJjChLfl4R3HqC8k7cwUfK4RmKXhI5GX4ESr+1KWPIaqH5AxYB
16+
+ee2WYBQGhi6aXKpVcj9dvq+OAmDMPCJr0xnWMMZqR5dnxY1eEq2x28n2b1SyIw1
17+
+IctNX0nLwGAMgUCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAEYTLXvHWmNuYlG6e
18+
CAiK1ubJ98nO6PSJsl+qosB1kWKlPeWPOLLAeZxSDh0tKRPvQoXoH/AtMRGHFGLS
19+
lk7fbCAbgEqvfA9+8VhgpWSRXD2iodt444P+m93NiMNeusiRFzozXKZZvU4Ie97H
20+
mDuwLjpGgi8DUShebM2Ngif8t4DmSgSfLQ3OEac7oKUP6ffHMXbqnDwjh8ZCCh1m
21+
DN+0i4Y5WpKD7Z+JjGHJRm1Cx/G5pwP16Et6YejQMnNU70VDOzGSvNABmiexiR5p
22+
m8pOTkyxrYViYqamLZG5to5vpI6RmEoA/5vbU59dZ5DzPmSoyNbIeaz+dkSGoy6D
23+
SWKZMwGTf++xS5y+oy2lNS2iddc845qCcDy4jeel3N9JPlJPwrArfapATcrX3Rpy
24+
GsVPvWsKA3q7kwIQo3qscg0CkYwHo5VCnWHDNqgOeFo35J7y+CKxYRolD9/lCtAU
25+
Pw8CBGp1x8jgIv7yKNiPVDtWYztqfsFrplLf/yiZSH53zghSY3v5qnFRkmGq1HRC
26+
G6lz0yjI7RUEA2a/XA2dv9Hv6CdmWUzrsXvocH5VgQz2RtkyvSaLFzRv8gnESrY1
27+
7qq55D1QIkO8UzzmCSpYPi5tUTGAYE1aHP/B1S5LpBrpaJ8Q9nfqA/9Bb+aho2ze
28+
N0vpdSSemKGQcrzquNqDJhUoXgQ=
29+
-----END CERTIFICATE-----

spec/support/cert/key.pem

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
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
51+
-----END RSA PRIVATE KEY-----

src/Adapters/Auth/ldap.js

+21-11
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,32 @@ function validateAuthData(authData, options) {
1212
);
1313
});
1414
}
15+
const clientOptions = (options.url.startsWith("ldaps://")) ?
16+
{ url: options.url, tlsOptions: options.tlsOptions } : { url: options.url };
1517

16-
const client = ldapjs.createClient({ url: options.url });
18+
const client = ldapjs.createClient(clientOptions);
1719
const userCn =
1820
typeof options.dn === 'string'
1921
? options.dn.replace('{{id}}', authData.id)
2022
: `uid=${authData.id},${options.suffix}`;
2123

2224
return new Promise((resolve, reject) => {
23-
client.bind(userCn, authData.password, err => {
24-
if (err) {
25-
client.destroy(err);
26-
return reject(
27-
new Parse.Error(
28-
Parse.Error.OBJECT_NOT_FOUND,
29-
'LDAP: Wrong username or password'
30-
)
31-
);
25+
client.bind(userCn, authData.password, ldapError => {
26+
if (ldapError) {
27+
let error;
28+
switch (ldapError.code) {
29+
case 49:
30+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Wrong username or password');
31+
break;
32+
case "DEPTH_ZERO_SELF_SIGNED_CERT":
33+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
34+
break;
35+
default:
36+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Somthing went wrong (' + ldapError.code + ')');
37+
}
38+
reject(error);
39+
client.destroy(ldapError);
40+
return;
3241
}
3342

3443
if (
@@ -50,7 +59,8 @@ function optionsAreValid(options) {
5059
typeof options === 'object' &&
5160
typeof options.suffix === 'string' &&
5261
typeof options.url === 'string' &&
53-
options.url.startsWith('ldap://')
62+
(options.url.startsWith('ldap://') ||
63+
options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object')
5464
);
5565
}
5666

0 commit comments

Comments
 (0)