#!/usr/bin/ruby

require 'ldap'
require 'open3'
require 'readline'
require 'tempfile'

require 'puavo/etc'
require 'puavo-ds/database_acl'

ldap_suffix_arg = ARGV.first

if ldap_suffix_arg.nil? || ldap_suffix_arg.empty? then
  warn "Usage: #{ $0 } ldapsuffix|--all"
  exit 1
end

puts "******************************************************"
puts "  Initialising ldap base: #{ldap_suffix_arg}"
puts "******************************************************"

puts "#{PUAVO_ETC.ldap_dn} password: (will echo)"
@bindpw = Readline.readline('> ', true)

def update_acls(suffix)
  dn             = ''
  domain         = ''
  kerberos_realm = ''
  samba_domain   = ''

  conn = LDAP::SSLConn.new(host=PUAVO_ETC.ldap_master, port=636)
  conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)

  loop do
    begin
      # COMMAND
      conn.bind(PUAVO_ETC.ldap_dn, @bindpw) do
        conn.search('cn=config', LDAP::LDAP_SCOPE_SUBTREE,
                    "(olcSuffix=#{suffix})") do |e|
          dn = e.dn
          puts "DN: #{dn}"
        end

        conn.search(suffix, LDAP::LDAP_SCOPE_BASE,
                    '(objectClass=eduOrg)') do |e|
          samba_domain = Array(e.get_values('sambaDomainName')).first
          kerberos_realm = Array(e.get_values('puavoKerberosRealm')).first
          domain = Array(e.get_values('puavoDomain')).first
        end
      end
      break
    rescue LDAP::ResultError => e
      warn "LDAP connection failed: #{ e.message }"
      warn 'Press enter to try again, CTRL-C to abort'
      STDIN.gets
    end
  end

  puts
  puts "suffix:         #{suffix}"
  puts "Domain:         #{domain}"
  puts "Kerberos realm: #{kerberos_realm}"
  puts "Samba domain:   #{samba_domain}"

  if domain.empty? or kerberos_realm.empty? or samba_domain.empty? then
    puts "ERROR: Couldn't figure out domain information!"
    exit 1
  end

  Readline.readline('OK? (the only way out is CTRL-C) ', true)

  acls = LdapAcl.generate_acls(suffix, kerberos_realm, samba_domain) \
                .map { |s| "olcAccess: #{ s }\n" }.join('')

  Tempfile.create do |tmpfile|
    tmpfile.write "dn: #{ dn }\n"
    tmpfile.write "changetype: modify\n"
    tmpfile.write "replace: olcAccess\n"
    tmpfile.write acls
    tmpfile.close

    system('ldapmodify', '-c', '-H', "ldap://#{ PUAVO_ETC.ldap_master }", '-x', '-D',
           PUAVO_ETC.ldap_dn, '-Z', '-w', @bindpw, '-f', tmpfile.path) \
      or raise 'error when running ldapmodify'
  end
end

if ldap_suffix_arg == '--all' then
  conn = LDAP::SSLConn.new(host=PUAVO_ETC.ldap_master, port=636)
  conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)

  conn.bind(PUAVO_ETC.ldap_dn, @bindpw) do
    begin
      puts 'Looping through databases'

      conn.search('', LDAP::LDAP_SCOPE_BASE, '(objectClass=*)',
                  %w(namingContexts)) do |e|
        e.get_values('namingContexts').each do |ldap_suffix|
          next if ldap_suffix == 'o=puavo'
          update_acls(ldap_suffix)
        end
      end

      rescue LDAP::ResultError => e
        warn "LDAP connection failed: #{ e.message }"
      end
  end
else
  update_acls(ldap_suffix_arg)
end
