#!/usr/bin/env ruby $LOAD_PATH.unshift('lib') 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, message_id, hash) super(connection, message_id) @hash = hash 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(operation, _controls) dn = operation.value[1].value dn = nil if dn == '' return super if dn.nil? password = operation.value[2].value email = extract_mail_from_dn(dn) authenticated = valid?(email, password) LOGGER.info("AUTHENTICATION email=#{email} => #{authenticated}") send_BindResponse(authenticated ? 0 : 1) end def extract_mail_from_dn(entry) entry[/mail=(.*?),/, 1] # short for regex first capture end def valid?(_email, password) password == 'toto' end def search(basedn, scope, deref, filter) basedn = basedn.downcase LOGGER.info("SEARCHING...basedn=\"#{basedn}\", scope=#{scope}, deref=#{deref}, filter=#{filter}") case scope when LDAP::Server::BaseObject # client asked for single object by DN obj = @hash[basedn] raise LDAP::ResultError::NoSuchObject unless obj if LDAP::Server::Filter.run(filter, obj) LOGGER.info("1 Base Found: #{obj}") send_SearchResultEntry(basedn, obj) end when LDAP::Server::WholeSubtree found = 0 @hash.each do |dn, av| next unless dn.index(basedn, -basedn.length) # under basedn? next unless LDAP::Server::Filter.run(filter, av) # attribute filter? found += 1 LOGGER.info("Found Filtered #{av}") send_SearchResultEntry(dn, av) end found = 'NO' if found == 0 LOGGER.info("#{found} items found") else raise LDAP::ResultError::UnwillingToPerform, 'OneLevel not implemented' end send_SearchResultDone(0) 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('Ready to perform LDAP operations...') s.run_tcpserver s.join