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.
153 lines
4.3 KiB
153 lines
4.3 KiB
#!/usr/bin/env ruby
|
|
|
|
# This is a modified version of rbslapd1.rb which uses a Router instead of
|
|
# subclassing the LDAP::Server::Operation class.
|
|
|
|
# This is a trivial LDAP server which just stores directory entries in RAM.
|
|
# It does no validation or authentication. This is intended just to
|
|
# demonstrate the API, it's not for real-world use!!
|
|
|
|
$:.unshift('../lib')
|
|
$debug = false
|
|
|
|
require 'ldap/server'
|
|
require 'ldap/server/router'
|
|
|
|
$logger = Logger.new($stderr)
|
|
|
|
class HashOperation < LDAP::Server::Operation
|
|
def initialize
|
|
super
|
|
@hash = YAML.load_from_file('ldapdbp.yaml') # an object reference to our directory data
|
|
end
|
|
|
|
def search(basedn, scope, deref, filter)
|
|
basedn = basedn.downcase
|
|
|
|
case scope
|
|
when LDAP::Server::BaseObject
|
|
# client asked for single object by DN
|
|
obj = @hash[basedn]
|
|
raise LDAP::ResultError::NoSuchObject unless obj
|
|
|
|
send_SearchResultEntry(basedn, obj) if LDAP::Server::Filter.run(filter, obj)
|
|
|
|
when LDAP::Server::WholeSubtree
|
|
@hash.each do |dn, av|
|
|
next unless dn.index(basedn, -basedn.length) # under basedn?
|
|
next unless LDAP::Server::Filter.run(filter, av) # attribute filter?
|
|
|
|
send_SearchResultEntry(dn, av)
|
|
end
|
|
|
|
else
|
|
raise LDAP::ResultError::UnwillingToPerform, 'OneLevel not implemented'
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
class LDAPController
|
|
def initialize
|
|
@directory = {}
|
|
|
|
File.open('ldapdb.yaml') { |f| @directory = YAML.load(f.read) }
|
|
end
|
|
|
|
def self.bind(request, version, dn, password, params)
|
|
$logger.debug 'Catchall bind request'
|
|
raise LDAP::ResultError::UnwillingToPerform, 'Invalid bind DN'
|
|
end
|
|
|
|
def self.bindUser(request, version, dn, password, params)
|
|
user = params[:uid]
|
|
domain = params[:domain]
|
|
tld = params[:tld]
|
|
|
|
# p "bindUser user=#{user} dn=#{dn}, password=#{password}, params=#{params}"
|
|
if user.length < 2
|
|
$logger.warn "Denied access for user #{user}: Size < 2"
|
|
raise LDAP::ResultError::InvalidCredentials, 'Invalid credentials'
|
|
end
|
|
|
|
$logger.info "Authenticated email=#{user}@#{domain}.#{tld} with password=<ANY>"
|
|
end
|
|
|
|
def self.searchUsers(request, baseObject, scope, deref, filter, params)
|
|
$logger.info 'Search users'
|
|
domain = params[:domain]
|
|
tld = params[:tld]
|
|
|
|
basedn = "#{domain}.#{tld}"
|
|
|
|
operation = HashOperation.new
|
|
case scope
|
|
when LDAP::Server::BaseObject
|
|
# client asked for single object by DN
|
|
obj = directory
|
|
raise LDAP::ResultError::NoSuchObject unless obj
|
|
|
|
operation.send_SearchResultEntry(basedn, obj) if LDAP::Server::Filter.run(filter, obj)
|
|
|
|
when LDAP::Server::WholeSubtree
|
|
directory.each do |dn, av|
|
|
next unless dn.index(basedn, -basedn.length) # under basedn?
|
|
next unless LDAP::Server::Filter.run(filter, av) # attribute filter?
|
|
|
|
operation.send_SearchResultEntry(dn, av)
|
|
end
|
|
|
|
else
|
|
raise LDAP::ResultError::UnwillingToPerform, 'OneLevel not implemented'
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
router = LDAP::Server::Router.new($logger) do
|
|
# Different syntax but same thing
|
|
# bind nil => 'LDAPController#bind'
|
|
# route :bind, nil => 'LDAPController#bind'
|
|
|
|
# Bind a route using variables. A hash with the variables will be passed
|
|
# to your function as last argument.
|
|
# bind 'uid=:uid,ou=Users,dc=mydomain,dc=com' => 'LDAPController#bindUser'
|
|
bind 'uid=:uid,dc=:domain,dc=:tld' => 'LDAPController#bindUser'
|
|
search 'dc=:domain,dc=:tld' => 'LDAPController#searchUsers'
|
|
|
|
# search nil => 'LDAPController#search'
|
|
# search 'ou=Users,dc=mydomain,dc=com' => 'LDAPController#searchUsers'
|
|
end
|
|
|
|
# This is the shared object which carries our actual directory entries.
|
|
# It's just a hash of {dn=>entry}, where each entry is {attr=>[val,val,...]}
|
|
|
|
directory = {}
|
|
|
|
# Let's put some backing store on it
|
|
|
|
require 'yaml'
|
|
begin
|
|
File.open('ldapdb.yaml') { |f| directory = YAML.load(f.read) }
|
|
rescue Errno::ENOENT
|
|
end
|
|
|
|
at_exit do
|
|
File.open('ldapdb.new', 'w') { |f| f.write(YAML.dump(directory)) }
|
|
File.rename('ldapdb.new', 'ldapdb.yaml')
|
|
end
|
|
|
|
# Listen for incoming LDAP connections. For each one, create a Connection
|
|
# object, which will invoke a HashOperation object for each request.
|
|
|
|
s = LDAP::Server.new(
|
|
port: 1389,
|
|
nodelay: true,
|
|
listen: 10,
|
|
# :ssl_key_file => "key.pem",
|
|
# :ssl_cert_file => "cert.pem",
|
|
# :ssl_on_connect => true,
|
|
router: router
|
|
)
|
|
s.run_tcpserver
|
|
s.join
|