Add support for LDAP cn=...+totptoken=.. support

This commit is contained in:
Johannes Zellner
2022-08-02 14:02:35 +02:00
parent afc70ac332
commit a2a60ff426
4 changed files with 53 additions and 7 deletions

View File

@@ -23,6 +23,7 @@ const assert = require('assert'),
reverseproxy = require('./reverseproxy.js'),
safe = require('safetydance'),
settings = require('./settings.js'),
speakeasy = require('speakeasy'),
shell = require('./shell.js'),
users = require('./users.js'),
util = require('util'),
@@ -173,12 +174,13 @@ async function userSearch(req, res, next) {
displayname: displayName,
givenName: firstName,
username: user.username,
twoFactorAuthenticationEnabled: user.twoFactorAuthenticationEnabled || undefined,
samaccountname: user.username, // to support ActiveDirectory clients
// memberof: user.groupIds.map(function (gid) { return `cn=${gid},ou=groups,dc=cloudron`; }) <- use cn=group.name instead of id
}
};
if (user.twoFactorAuthenticationEnabled) obj.attributes.twoFactorAuthenticationEnabled = true;
// http://www.zytrax.com/books/ldap/ape/core-schema.html#sn has 'name' as SUP which is a DirectoryString
// which is required to have atleast one character if present
if (lastName.length !== 0) obj.attributes.sn = lastName;
@@ -235,12 +237,15 @@ async function groupSearch(req, res, next) {
// Will attach req.user if successful
async function userAuth(req, res, next) {
// extract the common name which might have different attribute names
const attributeName = Object.keys(req.dn.rdns[0].attrs)[0];
const commonName = req.dn.rdns[0].attrs[attributeName].value;
const cnAttributeName = Object.keys(req.dn.rdns[0].attrs)[0];
const commonName = req.dn.rdns[0].attrs[cnAttributeName].value;
if (!commonName) return next(new ldap.NoSuchObjectError(req.dn.toString()));
const TOTPTOKEN_ATTRIBUTE_NAME = 'totptoken'; // This has to be in-sync with externalldap.js
const totpToken = req.dn.rdns[0].attrs[TOTPTOKEN_ATTRIBUTE_NAME] ? req.dn.rdns[0].attrs[TOTPTOKEN_ATTRIBUTE_NAME].value : null;
let verifyFunc;
if (attributeName === 'mail') {
if (cnAttributeName === 'mail') {
verifyFunc = users.verifyWithEmail;
} else if (commonName.indexOf('@') !== -1) { // if mail is specified, enforce mail check
verifyFunc = users.verifyWithEmail;
@@ -255,6 +260,12 @@ async function userAuth(req, res, next) {
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
// currently this is only optional if totpToken is provided and user has 2fa enabled
if (totpToken && user.twoFactorAuthenticationEnabled) {
const verified = speakeasy.totp.verify({ secret: user.twoFactorAuthenticationSecret, encoding: 'base32', token: totpToken, window: 2 });
if (!verified) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
}
req.user = user;
next();