Files
kami-parse-server/src/Adapters/Auth/ldap.js
Diamond Lewis 1666c3e382 [WIP] Enable test suite to be randomized (#7265)
* initial run

* Update ParseGraphQLServer.spec.js

* temporarily enable reporter

* Bump retry limit

* fix undefined database

* try to catch error

* Handle LiveQueryServers

* Update Config.js

* fast-fail false

* Remove usage of AppCache

* oops

* Update contributing guide

* enable debugger, try network retry attempt 1

* Fix ldap unbinding

* move non specs to support

* add missing mock adapter

* fix Parse.Push

* RestController should match batch.spec.js

* Remove request attempt limit

* handle index.spec.js

* Update CHANGELOG.md

* Handle error: tuple concurrently updated

* test transactions

* Clear RedisCache after every test

* LoggerController.spec.js

* Update schemas.spec.js

* finally fix transactions

* fix geopoint deadlock

* transaction with clean database

* batch.spec.js
2021-03-15 02:04:09 -05:00

113 lines
3.2 KiB
JavaScript

const ldapjs = require('ldapjs');
const Parse = require('parse/node').Parse;
function validateAuthData(authData, options) {
if (!optionsAreValid(options)) {
return new Promise((_, reject) => {
reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP auth configuration missing'));
});
}
const clientOptions = options.url.startsWith('ldaps://')
? { url: options.url, tlsOptions: options.tlsOptions }
: { url: options.url };
const client = ldapjs.createClient(clientOptions);
const userCn =
typeof options.dn === 'string'
? options.dn.replace('{{id}}', authData.id)
: `uid=${authData.id},${options.suffix}`;
return new Promise((resolve, reject) => {
client.bind(userCn, authData.password, ldapError => {
delete authData.password;
if (ldapError) {
let error;
switch (ldapError.code) {
case 49:
error = new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'LDAP: Wrong username or password'
);
break;
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
break;
default:
error = new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'LDAP: Somthing went wrong (' + ldapError.code + ')'
);
}
reject(error);
client.destroy(ldapError);
return;
}
if (typeof options.groupCn === 'string' && typeof options.groupFilter === 'string') {
searchForGroup(client, options, authData.id, resolve, reject);
} else {
client.unbind();
client.destroy();
resolve();
}
});
});
}
function optionsAreValid(options) {
return (
typeof options === 'object' &&
typeof options.suffix === 'string' &&
typeof options.url === 'string' &&
(options.url.startsWith('ldap://') ||
(options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object'))
);
}
function searchForGroup(client, options, id, resolve, reject) {
const filter = options.groupFilter.replace(/{{id}}/gi, id);
const opts = {
scope: 'sub',
filter: filter,
};
let found = false;
client.search(options.suffix, opts, (searchError, res) => {
if (searchError) {
client.unbind();
client.destroy();
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
}
res.on('searchEntry', entry => {
if (entry.object.cn === options.groupCn) {
found = true;
client.unbind();
client.destroy();
return resolve();
}
});
res.on('end', () => {
if (!found) {
client.unbind();
client.destroy();
return reject(
new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP: User not in group')
);
}
});
res.on('error', () => {
client.unbind();
client.destroy();
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
});
});
}
function validateAppId() {
return Promise.resolve();
}
module.exports = {
validateAppId,
validateAuthData,
};