ldap.rb 2.57 KB
require 'net/ldap'

class DsAdmin::Storage::Ldap
  include DsAdmin::Storage

  def initialize(config)
    super(config)
    @ldap = Net::LDAP.new(@config.con(self))
  end

  def read
    query = @config.query(self)

    ##
    # two things.
    # - create a hash from the ldap search result
    # - map the id's from the ldap search resulte into id's used in
    #   the models. The mapping is read from the config.
    #
    @ldap.search(query).map do |data|
      map = { :dn => :id }
      map.merge!(@config.map(self))

      remap(data, map)
    end
  end

  def write(model)
    @config.model = model

    data  = model.to_h
    odata = read.find{|od| od[:id] == data[:id]}

    return create(data) unless odata

    update(odata, data)
  end

  protected
  def create(data)
    map      = @config.map(self).invert
    scan_exp = /(^|, *)([^=]*=)(([^#][^,]*)|#\{([^|}]*)(\|([^}]*))?\})/

    dn = String.new
    @config.dn_pat(self).scan(scan_exp) do |m|
      val = m[3] if m[3]
      val = data[m[4][1..m[4].length].to_sym] if m[4]
      val = eval('"' + val + '".send ' + m[6]) if data && m[6]

      dn += m[0] + m[1] + val
    end
    dn += ',' + @config.query(self)[:base]

    data.delete(:id)

    entry = Net::LDAP::Entry.new(dn)
    entry[:changetype]  = 'add'
    entry[:objectclass] = @config.object_class(self)
    remap(data, @config.map(self).invert).each {|key,value| entry[key] = value}

    puts entry.to_ldif # TODO: make real writes
    return dn
  end

  def update(old, new)
    new.delete(:id)
    replace = remap(
      new.find_all{|key,value| value != old[key]},
      @config.map(self).invert
    )

    ##
    # if the given model already has an id;
    #
    # check if ldap dn has to be changed in order to
    # reflect the attributes.
    # if so, remove old entry
    #
    replace.each do |key,value|
      if old[:id] =~ /(^|, *)#{key.to_s}=([^, ]+)/ && $2 != value
        delete(old[:id])
        return create(new)
      end
    end

    entry = Net::LDAP::Entry.new(old[:id])
    entry[:changetype] = 'modify'
    entry[:replace]    = replace.keys
    replace.each {|key,value| entry[key] = value }

    puts entry.to_ldif # TODO: make real writes
    return old[:id]
  end

  def delete(id)
    entry = Net::LDAP::Entry.new(id)
    entry[:changetype] = 'delete'

    puts entry.to_ldif # TODO: make real writes
    return true
  end

  def remap(data, map)
      remapped = Hash.new
      data.each do |key,value|
        key   = map[key] || key
        value = value.size==1 ? value[0] : value.to_a 

        remapped.merge!({ key => value })
      end if data
      remapped
  end
end