You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CoreID/app/ldap/controllers/LDAPController.js

106 lines
3.3 KiB

const { Injectable } = require('flitter-di')
const { ImplementationError } = require('libflitter')
const LDAP = require('ldapjs')
class LDAPController extends Injectable {
resource_type = 'items'
// TODO make use of this better
check_attribute = 'ldap_visible' // or false
static get services() {
return [...super.services, 'ldap_server', 'ldap_dn_format']
}
async compare(req, res, next) {
if ( !req.user.can(`ldap:compare:${this.resource_type}`)) {
return next(new LDAP.InsufficientAccessRightsError())
}
// Make sure the resource exists
const item = await this.get_resource_from_dn(req.dn)
if ( !item ) {
return next(LDAP.NoSuchObjectError())
}
// Make sure it has the requested attribute
const value = item.to_ldap()[req.attribute]
if ( typeof value === 'undefined' ) {
return next(new LDAP.NoSuchAttributeError())
}
// Check if it matches the request value
const values = Array.isArray(value) ? value : [value]
const matches = values.some(x => x === req.value)
res.end(matches)
return next()
}
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('Cannot bind to DN outsize of the authentication base.'))
}
// Get the item
const item = await this.get_resource_from_dn(req.dn)
if ( !item ) {
return next(new LDAP.NoSuchObject())
}
// If the object is can-able, make sure it can bind
if ( typeof item.can === 'function' && !item.can('ldap:bind') ) {
return next(new LDAP.InsufficientAccessRightsError())
}
// Make sure the password matches the resource record
if ( !await item.check_password(req.credentials) ) {
return next(new LDAP.InvalidCredentialsError())
}
this.output.success(`Successfully bound resource as DN: ${req.dn.format(this.ldap_dn_format)}.`)
res.end()
return next()
}
async delete(req, res, next) {
if ( !req.user.can(`ldap:delete:${this.resource_type}`) ) {
return next(new LDAP.InsufficientAccessRightsError())
}
// Get the base DN
const base_dn = await this.get_base_dn()
// Make sure it's a parent of the request DN
if ( !base_dn.parentOf(req.dn) ) {
this.output.warn(`Attempted to perform resource deletion on invalid DN: ${req.dn.format(this.ldap_dn_format)}`)
return next(new LDAP.InsufficientAccessRightsError(`Target DN must be a member of the base DN: ${base_dn.format(this.ldap_dn_format)}.`))
}
// Fetch the resource (error if not found)
const item = await this.get_resource_from_dn(req.dn)
if ( !item ) {
return next(new LDAP.NoSuchObjectError())
}
// Delete it - TODO full soft delete, or just ldap_visible = false?
await item.delete()
res.end()
return next()
}
async get_resource_from_dn(dn) {
throw new ImplementationError()
}
async get_base_dn() {
throw new ImplementationError()
}
}
module.exports = exports = LDAPController