2020-04-17 00:59:48 +00:00
const { Injectable } = require ( 'flitter-di' )
2020-04-21 03:46:19 +00:00
const { ImplementationError } = require ( 'libflitter' )
const LDAP = require ( 'ldapjs' )
2020-04-17 00:59:48 +00:00
class LDAPController extends Injectable {
2020-04-21 03:46:19 +00:00
resource _type = 'items'
2020-04-17 00:59:48 +00:00
2020-04-21 03:46:19 +00:00
// TODO make use of this better
check _attribute = 'ldap_visible' // or false
static get services ( ) {
2020-05-04 01:16:54 +00:00
return [ ... super . services , 'ldap_server' , 'configs' ]
2020-04-21 03:46:19 +00:00
}
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
2020-05-04 01:16:54 +00:00
const value = ( await item . to _ldap ( ) ) [ req . attribute ]
2020-04-21 03:46:19 +00:00
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 ) ) {
2020-05-04 01:16:54 +00:00
this . output . debug ( ` Bind failure: ${ req . dn } not in ${ auth _dn } ` )
2020-04-21 03:46:19 +00:00
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 ) {
2020-05-04 01:16:54 +00:00
this . output . debug ( ` Bind failure: ${ req . dn } not found ` )
2020-10-19 04:04:01 +00:00
return next ( new LDAP . NoSuchObjectError ( ) )
2020-04-21 03:46:19 +00:00
}
// If the object is can-able, make sure it can bind
if ( typeof item . can === 'function' && ! item . can ( 'ldap:bind' ) ) {
2020-05-04 01:16:54 +00:00
this . output . debug ( ` Bind failure: User not allowed to bind ` )
2020-04-21 03:46:19 +00:00
return next ( new LDAP . InsufficientAccessRightsError ( ) )
}
2022-10-26 18:45:05 +00:00
// Check if the credentials are valid
if ( ! ( await item . check _credential _string ( req . credentials ) ) ) {
2020-04-21 03:46:19 +00:00
return next ( new LDAP . InvalidCredentialsError ( ) )
}
2020-05-22 14:47:01 +00:00
// Check if the resource has a trap. If so, deny access.
if ( item . trap ) {
return next ( new LDAP . InvalidCredentialsError ( 'This resource currently has a login trap set. Please visit the web UI to release.' ) )
}
2020-05-04 01:16:54 +00:00
this . output . info ( ` Successfully bound resource as DN: ${ req . dn . format ( this . configs . get ( 'ldap:server.format' ) ) } . ` )
2020-04-21 03:46:19 +00:00
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 ) ) {
2020-05-04 01:16:54 +00:00
this . output . warn ( ` Attempted to perform resource deletion on invalid DN: ${ req . dn . format ( this . configs . get ( 'ldap:server.format' ) ) } ` )
return next ( new LDAP . InsufficientAccessRightsError ( ` Target DN must be a member of the base DN: ${ base _dn . format ( this . configs . get ( 'ldap:server.format' ) ) } . ` ) )
2020-04-21 03:46:19 +00:00
}
// 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 ( )
}
2020-04-17 00:59:48 +00:00
}
module . exports = exports = LDAPController