SystemDataBackendLdap.rb 3.14 KB
require 'active_support/secure_random'
require 'net/ldap'

class SystemDataBackendLdap

  LDAP_USER_MAP = {
    :uid           => :name,
    :userpassword  => :pass,
    :uidnumber     => :uid,
    :gidnumber     => :gid,
    :loginshell    => :shell,
    :homedirectory => :home
  }

  LDAP_GROUP_MAP = {
    :cn        => :name,
    :gidnumber => :gid,
    :memberuid => :members
  }

  LDAP_SITE_MAP = {:o => :name}

  LDAP_MAP = {
    :User  => LDAP_USER_MAP,
    :Group => LDAP_GROUP_MAP,
    :Site  => LDAP_SITE_MAP
  }

  LDAP_FILTER = {
    :User        => Net::LDAP::Filter::eq('objectClass', 'posixAccount'),
    :Group       => Net::LDAP::Filter::eq('objectClass', 'posixGroup'),
    :Site        => Net::LDAP::Filter::eq('objectClass', 'organization') &
                    (~Net::LDAP::Filter::eq('o', 'hosting')),
    :MailAlias   => Net::LDAP::Filter::eq('objectClass', 'mailAlias'),
    :MailAccount => Net::LDAP::Filter::eq('objectClass', 'mailAccount')
  }

  LDAP_OBJECTCLASS = {
    :User  => [ 'account', 'posixAccount', 'shadowAccount' ],
    :Group => 'posixGroup'
  }

  LDAP_LAMBDA_USER = lambda do |entry|
    entry[:cn]               = entry[:uid]
    entry[:shadowlastchange] = (Time::now.to_i/60/60/24).to_s
    entry[:replace]         += ['shadowreplace'] if entry[:replace]
  end

  LDAP_LAMBDA = {
    :User => LDAP_LAMBDA_USER
  }

  def initialize(host, port, baseDn, args={})
    @baseDn    = baseDn
    @systemDn  = 'o=system,' + @baseDn
    @hostingDn = 'o=hosting,' + @baseDn

    @systemDn  = args[:systemDn] if args[:systemDn]
    @hostingDn = args[:hostingDn] if args[:hostingDn]

    @ldap      = Net::LDAP.new(:host => host, :port => port)
    @ldapData  = Hash.new
  end

  def load!(kind)
    @ldapData[kind] = Hash.new if ! @ldapData[kind]

    @ldapData[kind][:internal] = @ldap.search(
      :base   => ldapBase(kind),
      :filter => LDAP_FILTER[kind]
    )
  end

  def load(kind)
    load!(kind) if ! @ldapData[kind]

    @ldapData[kind][:external] = @ldapData[kind][:internal].map do |data|
      map = { :dn => :id }
      map.merge!(LDAP_MAP[kind]) if LDAP_MAP[kind]

      ydata = {}
      data.each do |key,value|
        ydata.merge!({ map[key] || key => value.size==1?value[0]:value.to_a })
      end
      ydata
    end if ! @ldapData[kind][:external]

    @ldapData[kind][:external].each{|ydata| yield ydata}
  end

  def update(kind, data)
    map = {}
    map.merge!(LDAP_MAP[kind].invert) if LDAP_MAP[kind]

    entry = Net::LDAP::Entry.new(data[:id])

    odata = @ldapData[kind][:external].find{|edata| edata[:id] == data[:id]}
    data = data.find_all{|key,value| value != odata[key]}
    data.delete(:id)

    replace = Array.new
    data.each do |key,value|
      key = map[key] if map[key]
      replace.push(key.to_s)
      entry[key] = value
    end

    if not replace.empty?
      entry[:changetype]  = 'modify'
      entry[:replace]     = replace
      LDAP_LAMBDA[kind].call(entry) if LDAP_LAMBDA[kind]

      puts entry.to_ldif
    else
      puts 'INFO: no changes'
    end
  end

  private

  def ldapBase(kind)
    case(kind)
    when :User, :Group: @systemDn
    when :Site, :MailAlias, :MailAccount: @hostingDn
    end
  end

end