@ -5,11 +5,8 @@ require 'ldap/server/request'
require 'ldap/server/filter'
require 'ldap/server/filter'
module LDAP
module LDAP
class Server
class Router
@logger
@routes
class Server
class Router
# Scope
# Scope
BaseObject = 0
BaseObject = 0
SingleLevel = 1
SingleLevel = 1
@ -24,14 +21,14 @@ class Router
def initialize ( logger , & block )
def initialize ( logger , & block )
@logger = logger
@logger = logger
@routes = Hash . new
@routes = { }
@routes = Trie . new do | trie |
@routes = Trie . new do | trie |
# Add an artificial LDAP component
# Add an artificial LDAP component
trie << " op=bind "
trie << " op=search "
trie << 'op=bind'
trie << 'op=search'
end
end
self . instance_eval ( & block )
instance_eval ( & block )
end
end
def log_exception ( e , level = :error )
def log_exception ( e , level = :error )
@ -46,30 +43,29 @@ class Router
def route ( operation , hash )
def route ( operation , hash )
hash . each do | key , value |
hash . each do | key , value |
if key . nil?
if key . nil?
@routes . insert " op= #{ operation . to_s } " , value
@logger . debug " map operation #{ operation . to_s } all routes to #{ value } "
@routes . insert " op= #{ operation } " , value
@logger . debug " map operation #{ operation } all routes to #{ value } "
else
else
@routes . insert " #{ key } ,op= #{ operation . to_s } " , value
@logger . debug " map #{ operation . to_s } #{ key } to #{ value } "
@routes . insert " #{ key } ,op= #{ operation } " , value
@logger . debug " map #{ operation } #{ key } to #{ value } "
end
end
end
end
end
end
def method_missing ( name , * args , & block )
def method_missing ( name , * args , & block )
if [ :bind , :search , :add , :modify , :modifydn , :del , : compare] . include? name
if % i [ bind search add modify modifydn del compare ] . include? name
send :route , name , * args
send :route , name , * args
else
else
super
super
end
end
end
end
####################################################
####################################################
### Methods to parse and route each request type ###
### Methods to parse and route each request type ###
####################################################
####################################################
def parse_route ( dn , method )
def parse_route ( dn , method )
route , action = @routes . match ( " #{ dn } ,op= #{ method . to_s } " )
if not route or route . empty?
route , action = @routes . match ( " #{ dn } ,op= #{ method } " )
if ! route or route . empty?
@logger . warn " No route defined for \' #{ route } \' "
@logger . warn " No route defined for \' #{ route } \' "
raise LDAP :: ResultError :: UnwillingToPerform
raise LDAP :: ResultError :: UnwillingToPerform
end
end
@ -81,12 +77,12 @@ class Router
class_name = action . split ( '#' ) . first
class_name = action . split ( '#' ) . first
method_name = action . split ( '#' ) . last
method_name = action . split ( '#' ) . last
params = LDAP :: Server :: DN . new ( " #{ dn } ,op= #{ method . to_s } " ) . parse ( route )
params = LDAP :: Server :: DN . new ( " #{ dn } ,op= #{ method } " ) . parse ( route )
return class_name , method_name , params
[ class_name , method_name , params ]
end
end
def do_bind ( connection , messageId , protocolOp , controls ) # :nodoc:
def do_bind ( connection , messageId , protocolOp , _ controls) # :nodoc:
request = Request . new ( connection , messageId )
request = Request . new ( connection , messageId )
version = protocolOp . value [ 0 ] . value
version = protocolOp . value [ 0 ] . value
dn = protocolOp . value [ 1 ] . value
dn = protocolOp . value [ 1 ] . value
@ -102,28 +98,28 @@ class Router
when 0
when 0
Object . const_get ( class_name ) . send method_name , request , version , dn , authentication . value , params
Object . const_get ( class_name ) . send method_name , request , version , dn , authentication . value , params
when 3
when 3
mechanism = authentication . value [ 0 ] . value
credentials = authentication . value [ 1 ] . value
authentication . value [ 0 ] . value
authentication . value [ 1 ] . value
# sasl_bind(version, dn, mechanism, credentials)
# sasl_bind(version, dn, mechanism, credentials)
# FIXME: needs to exchange further BindRequests
# FIXME: needs to exchange further BindRequests
# route_sasl_bind(request, version, dn, mechanism, credentials)
# route_sasl_bind(request, version, dn, mechanism, credentials)
raise LDAP :: ResultError :: AuthMethodNotSupported
raise LDAP :: ResultError :: AuthMethodNotSupported
else
else
raise LDAP :: ResultError :: ProtocolError , " BindRequest bad AuthenticationChoice "
raise LDAP :: ResultError :: ProtocolError , 'BindRequest bad AuthenticationChoice'
end
end
request . send_BindResponse ( 0 )
request . send_BindResponse ( 0 )
return dn , version
[ dn , version ]
rescue NoMethodError = > e
rescue NoMethodError = > e
log_exception e
log_exception e
request . send_BindResponse ( LDAP :: ResultError :: OperationsError . new . to_i , :errorMessage = > e . message )
return nil , version
request . send_BindResponse ( LDAP :: ResultError :: OperationsError . new . to_i , errorMessage : e . message )
[ nil , version ]
rescue LDAP :: ResultError = > e
rescue LDAP :: ResultError = > e
log_exception e
log_exception e
request . send_BindResponse ( e . to_i , :errorMessage = > e . message )
return nil , version
request . send_BindResponse ( e . to_i , errorMessage : e . message )
[ nil , version ]
end
end
def do_search ( connection , messageId , protocolOp , controls ) # :nodoc:
def do_search ( connection , messageId , protocolOp , _ controls) # :nodoc:
request = Request . new ( connection , messageId )
request = Request . new ( connection , messageId )
server = connection . opt [ :server ]
server = connection . opt [ :server ]
schema = connection . opt [ :schema ]
schema = connection . opt [ :schema ]
@ -133,8 +129,8 @@ class Router
client_sizelimit = protocolOp . value [ 3 ] . value
client_sizelimit = protocolOp . value [ 3 ] . value
client_timelimit = protocolOp . value [ 4 ] . value . to_i
client_timelimit = protocolOp . value [ 4 ] . value . to_i
request . typesOnly = protocolOp . value [ 5 ] . value
request . typesOnly = protocolOp . value [ 5 ] . value
filter = LDAP :: Server :: Filter :: parse ( protocolOp . value [ 6 ] , schema )
request . attributes = protocolOp . value [ 7 ] . value . collect { | x | x . value }
filter = LDAP :: Server :: Filter . parse ( protocolOp . value [ 6 ] , schema )
request . attributes = protocolOp . value [ 7 ] . value . collect { | x | x . value }
sizelimit = request . server_sizelimit
sizelimit = request . server_sizelimit
sizelimit = client_sizelimit if client_sizelimit > 0 and
sizelimit = client_sizelimit if client_sizelimit > 0 and
@ -142,7 +138,7 @@ class Router
request . sizelimit = sizelimit
request . sizelimit = sizelimit
if baseObject . empty? and scope == BaseObject
if baseObject . empty? and scope == BaseObject
request . send_SearchResultEntry ( " " , server . root_dse ) if
request . send_SearchResultEntry ( '' , server . root_dse ) if
server . root_dse and LDAP :: Server :: Filter . run ( filter , server . root_dse )
server . root_dse and LDAP :: Server :: Filter . run ( filter , server . root_dse )
request . send_SearchResultDone ( 0 )
request . send_SearchResultDone ( 0 )
return
return
@ -162,15 +158,14 @@ class Router
# Find a route in the routing tree
# Find a route in the routing tree
class_name , method_name , params = parse_route ( baseObject , :search )
class_name , method_name , params = parse_route ( baseObject , :search )
Timeout :: timeout ( t , LDAP :: ResultError :: TimeLimitExceeded ) do
Timeout . timeout ( t , LDAP :: ResultError :: TimeLimitExceeded ) do
Object . const_get ( class_name ) . send method_name , request , baseObject , scope , deref , filter , params
Object . const_get ( class_name ) . send method_name , request , baseObject , scope , deref , filter , params
end
end
request . send_SearchResultDone ( 0 )
request . send_SearchResultDone ( 0 )
# Note that TimeLimitExceeded is a subclass of LDAP::ResultError
# Note that TimeLimitExceeded is a subclass of LDAP::ResultError
rescue LDAP :: ResultError = > e
rescue LDAP :: ResultError = > e
request . send_SearchResultDone ( e . to_i , :errorMessage = > e . message )
request . send_SearchResultDone ( e . to_i , errorMessage : e . message )
rescue Abandon
rescue Abandon
# send no response
# send no response
@ -178,13 +173,11 @@ class Router
# catch all other exceptions. Otherwise, in the event of a programming
# catch all other exceptions. Otherwise, in the event of a programming
# error, this thread will silently terminate and the client will wait
# error, this thread will silently terminate and the client will wait
# forever for a response.
# forever for a response.
rescue Exception = > e
rescue Exception = > e
log_exception e
log_exception e
request . send_SearchResultDone ( LDAP :: ResultError :: OperationsError . new . to_i , :errorMessage = > e . message )
request . send_SearchResultDone ( LDAP :: ResultError :: OperationsError . new . to_i , errorMessage : e . message )
end
end
###########################################################
###########################################################
### Methods to actually perform the work requested ###
### Methods to actually perform the work requested ###
### Use the signatures below to write your own handlers ###
### Use the signatures below to write your own handlers ###
@ -195,14 +188,14 @@ class Router
#
#
# Write your own class method using this signature
# Write your own class method using this signature
# def simple_bind(request, version, dn, password, params)
# if version != 3
# raise LDAP::ResultError::ProtocolError, "version 3 only"
# end
# if dn
# raise LDAP::ResultError::InappropriateAuthentication, "This server only supports anonymous bind"
# end
# end
# def simple_bind(request, version, dn, password, params)
# if version != 3
# raise LDAP::ResultError::ProtocolError, "version 3 only"
# end
# if dn
# raise LDAP::ResultError::InappropriateAuthentication, "This server only supports anonymous bind"
# end
# end
# Handle a search request
# Handle a search request
#
#
@ -211,10 +204,10 @@ class Router
# typesOnly are taken care of, but you need to perform all
# typesOnly are taken care of, but you need to perform all
# authorisation checks yourself, using @connection.binddn
# authorisation checks yourself, using @connection.binddn
# def search(basedn, scope, deref, filter)
# debug "search(#{basedn}, #{scope}, #{deref}, #{filter})"
# raise LDAP::ResultError::UnwillingToPerform, "search not implemented"
# end
end
end
# def search(basedn, scope, deref, filter)
# debug "search(#{basedn}, #{scope}, #{deref}, #{filter})"
# raise LDAP::ResultError::UnwillingToPerform, "search not implemented"
# end
end
end
end
end