Add basic LDAP bind functionality

This commit is contained in:
garrettmills
2020-04-17 19:25:33 -05:00
parent 226b90b7bf
commit 68cc90899c
18 changed files with 387 additions and 90 deletions

View File

@@ -0,0 +1,59 @@
const LDAPController = require('./LDAPController')
const LDAP = require('ldapjs')
class UsersController extends LDAPController {
static get services() {
return [...super.services, 'output', 'ldap_server', 'models']
}
async search_people(req, res, next) {
global.ireq = req
}
async bind(req, res, next) {
const auth_dn = this.ldap_server.auth_dn()
// Make sure the DN is valid
if ( !req.dn.childOf(auth_dn) ) {
return next(new LDAP.InvalidCredentialsError())
}
// Get the user
const user = await this.get_user_from_dn(req.dn)
if ( !user ) {
return next(new LDAP.InvalidCredentialsError())
}
// Make sure the password matches the user record
if ( !await user.check_password(req.credentials) ) {
return next(new LDAP.InvalidCredentialsError())
}
// Make sure the user has permission to bind
if ( !user.can('ldap:bind') ) {
return next(new LDAP.InsufficientAccessRightsError())
}
this.output.success(`Successfully bound user ${user.uid} as DN: ${req.dn.format({skipSpace: true})}.`)
return res.end()
}
get_uid_from_dn(dn) {
const uid_field = this.ldap_server.config.schema.auth.user_id
try {
if ( typeof dn === 'string' ) dn = LDAP.parseDN(dn)
return dn.rdns[0].attrs[uid_field].value
} catch (e) {}
}
async get_user_from_dn(dn) {
const uid = this.get_uid_from_dn(dn)
if ( uid ) {
const User = this.models.get('auth:User')
return User.findOne({uid})
}
}
}
module.exports = exports = UsersController

View File

@@ -0,0 +1,33 @@
const LDAPMiddleware = require('./LDAPMiddleware')
const LDAP = require('ldapjs')
class BindUserMiddleware extends LDAPMiddleware {
static get services() {
return [...super.services, 'canon', 'output', 'ldap_server']
}
async test(req, res, next) {
const bind_dn = req.connection.ldap.bindDN
if ( bind_dn.equals(this.ldap_server.anonymous()) ) {
this.output.warn(`Blocked anonymous LDAP request on user-protected route.`)
return next(new LDAP.InsufficientAccessRightsError())
}
const user = this.user_controller().get_uid_from_dn(bind_dn)
if ( !user || !user.can('ldap:bind') ) {
return next(new LDAP.InvalidCredentialsError())
}
req.user = user
req.bindDN = bind_dn
return next()
}
user_controller() {
return this.canon.get('ldap_controller::Users')
}
}
module.exports = exports = BindUserMiddleware

View File

@@ -0,0 +1,15 @@
const LDAPMiddleware = require('./LDAPMiddleware')
class LDAPLoggerMiddleware extends LDAPMiddleware {
static get services() {
return [...super.services, 'app', 'output']
}
async test(req, res, next) {
let bind_dn = req.connection.ldap.bindDN
this.output.info(`${req.json.protocolOp} - as ${bind_dn ? bind_dn.format({skipSpace: true}) : 'N/A'} - target ${req.dn.format({skipSpace: true})}`)
return next()
}
}
module.exports = exports = LDAPLoggerMiddleware

View File

@@ -1,27 +0,0 @@
const example_routes = {
prefix: 'dc=base',
middleware: [
],
search: {
},
bind: {
},
add: {
},
del: {
},
}
module.exports = exports = example_routes

View File

@@ -0,0 +1,30 @@
const users_routes = {
prefix: false, // false | string
middleware: [
'Logger'
],
search: {
'ou=people': [
'ldap_middleware::BindUser',
'ldap_controller::Users.search_people',
],
},
bind: {
'ou=people': ['ldap_controller::Users.bind'],
},
add: {
},
del: {
},
}
module.exports = exports = users_routes