From 00e4cadda680c2933dc72a108de872146c04f803 Mon Sep 17 00:00:00 2001 From: pvincent Date: Tue, 9 Jun 2026 11:38:11 +0000 Subject: [PATCH] server2 --- examples/ldapdb.yaml | 22 ++++--- examples/{server.rb => server1.rb} | 74 +++++++++++++++++++++--- examples/server2.rb | 93 ++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 16 deletions(-) rename examples/{server.rb => server1.rb} (57%) mode change 100644 => 100755 create mode 100755 examples/server2.rb diff --git a/examples/ldapdb.yaml b/examples/ldapdb.yaml index 2c2d66a..9529bef 100644 --- a/examples/ldapdb.yaml +++ b/examples/ldapdb.yaml @@ -1,17 +1,21 @@ --- -dc=example,dc=com: +dc=zourit,dc=re: cn: - Top object -cn=fred flintstone,dc=example,dc=com: +cn=pvincent,dc=zourit,dc=re: cn: - - Fred Flintstone + - pvincent sn: - - Flintstone + - P.Vincent mail: - - fred@bedrock.org - - fred.flintstone@bedrock.org -cn=wilma flintstone,dc=example,dc=com: + - pvincent@zourit.re +dc=pvincent,dc=re: cn: - - Wilma Flintstone + - Top object +cn=admin,dc=pvincent,dc=re: + cn: + - admin + sn: + - Vincentdmin mail: - - wilma@bedrock.org + - admin@pvincent.re diff --git a/examples/server.rb b/examples/server1.rb old mode 100644 new mode 100755 similarity index 57% rename from examples/server.rb rename to examples/server1.rb index 607d7b7..a07893c --- a/examples/server.rb +++ b/examples/server1.rb @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby -w +#!/usr/bin/env ruby # This is a modified version of rbslapd1.rb which uses a Router instead of # subclassing the LDAP::Server::Operation class. @@ -15,7 +15,45 @@ 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' @@ -35,13 +73,34 @@ class LDAPController $logger.info "Authenticated email=#{user}@#{domain}.#{tld} with password=" end - def self.search(request, baseObject, scope, deref, filter, params) - $logger.info "Catchall search request for #{baseObject}" - raise LDAP::ResultError::UnwillingToPerform, 'Invalid search DN' - 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 @@ -53,7 +112,8 @@ router = LDAP::Server::Router.new($logger) do # 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,ou=Users,dc=:domain,dc=:tld' => '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' diff --git a/examples/server2.rb b/examples/server2.rb new file mode 100755 index 0000000..15faefa --- /dev/null +++ b/examples/server2.rb @@ -0,0 +1,93 @@ +#!/usr/bin/env ruby + +$:.unshift('lib') +$debug = true + +require 'ldap/server' + +$logger = Logger.new($stderr) +# We subclass the Operation class, overriding the methods to do what we need + +class HashOperation < LDAP::Server::Operation + def initialize(connection, messageID, hash) + super(connection, messageID) + @hash = hash # an object reference to our directory data + end + + # ici, c'est bizarre + # systèmatiquement appelé (SEARCH ou AUTH) + # du coup, je retourne super si pas de dn => SEARCH normal sans authentication + # sinon ça passe aussi !!! + def do_bind(protocolOp, controls) # :nodoc: + dn = protocolOp.value[1].value + dn = nil if dn == '' + return super if dn.nil? + + version = protocolOp.value[0].value + authentication = protocolOp.value[2] + password = authentication.value + $logger.info("AUTHENTICATION ..version=#{version}, dn=#{dn}") + super + end + + # def simple_bind(version, dn, password) + # super.simple_bind(version, dn, password) + # $logger.info("SIMPLE BIND...version=#{version}, dn=#{dn}") + # + # raise LDAP::ResultError::ProtocolError, 'version 3 only' if version != 3 + # raise LDAP::ResultError::InvalidCredentials, 'Invalid credentials' if password.nil? || password == '' + # + # $logger.info('authentication SUCCESS') + # end + # + def search(basedn, scope, deref, filter) + $logger.info("SEARCHING...basedn=#{basedn}, scope=#{scope}, deref=#{deref}, filter=#{filter}") + + basedn = basedn.downcase + result = nil + case scope + when LDAP::Server::BaseObject + # client asked for single object by DN + obj = @hash[basedn] + raise LDAP::ResultError::NoSuchObject unless obj + + result = 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? + + result = send_SearchResultEntry(dn, av) + end + + else + raise LDAP::ResultError::UnwillingToPerform, 'OneLevel not implemented' + + end + $logger.info "result=#{result}" + result + end +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 = {} +require 'yaml' +File.open('examples/ldapdb.yaml') { |f| directory = YAML.load(f.read) } +$logger.info("DIRECTORY=#{directory}") + +# 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, + operation_class: HashOperation, + operation_args: [directory] +) + +$logger.info('server2 RUNNING...') +s.run_tcpserver +s.join