Skip to content

Instantly share code, notes, and snippets.

@ackerleytng
Created October 22, 2020 02:22
Show Gist options
  • Save ackerleytng/531a43ec4f4220ef9e685617c30abc26 to your computer and use it in GitHub Desktop.
Save ackerleytng/531a43ec4f4220ef9e685617c30abc26 to your computer and use it in GitHub Desktop.
LDAP adapter for Keycloak
const ldap = require('ldapjs');
const fs = require('fs');
const axios = require("axios");
const https = require("https");
const querystring = require('querystring');
const server = ldap.createServer();
const authenticate = async (req, res, next) => {
if (req.dn.toString() !== 'cn=keycloak' ||
req.credentials !== 'secret') {
return next(new ldap.InvalidCredentialsError());
}
res.end();
return next();
};
server.bind('cn=keycloak', authenticate);
const authorize = async (req, res, next) => {
if (!req.connection.ldap.bindDN.equals('cn=keycloak')) {
return next(new ldap.InsufficientAccessRightsError());
}
return next();
}
const getUsers = async (req, res, next) => {
const disableSsl = {
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
}
const tokenResponse = await axios.post(
'https://keycloak.localhost/auth/realms/master/protocol/openid-connect/token',
querystring.stringify({
client_id: 'admin-cli',
username: 'admin',
password: 'password',
grant_type: 'password'
}),
disableSsl
)
const token = tokenResponse.data.access_token
const response = await axios.get(
'https://keycloak.localhost/auth/admin/realms/applications/users',
{
headers: {
authorization: `Bearer ${token}`
},
...disableSsl
}
)
const users = response.data
const usersDetails = await Promise.all(users.map(async u => {
const addr = `https://keycloak.localhost/auth/admin/realms/applications/users/${u.id}/groups`
const r = await axios
.get(
addr,
{
headers: {
authorization: `Bearer ${token}`
},
...disableSsl
}
)
const groups = r.data;
const {id, username} = u;
return {
id,
username,
groups
}
}));
req.users = usersDetails;
return next();
}
const buildData = (u) => ({
dn: `cn=${u.username},ou=users,o=myhost`,
attributes: {
memberOf: [...u.groups.map(({name}) => name), 'Foobar'],
cn: u.username,
username: u.username,
objectClass: 'person'
}
})
const doFilter = async (req, res, next) => {
req.users
.map(buildData)
.filter(u => req.filter.matches(u.attributes))
.map(d => {console.log(d); return d;})
.forEach(d => res.send(d));
res.end();
return next();
}
server.search('ou=users', [authorize, getUsers], doFilter)
server.listen(1389, function() {
console.log('Keycloak LDAP server up at: %s', server.url);
});
{
"name": "ldap-keycloak",
"version": "0.0.1",
"description": "LDAP adapter for Keycloak",
"main": "index.js",
"author": "Ackerley Tng",
"license": "MIT",
"dependencies": {
"axios": "^0.20.0",
"ldapjs": "^2.2.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment