# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Msf::Exploit::Remote::LDAP do
  include_context 'Msf::Simple::Framework'

  subject do
    mod = ::Msf::Exploit.new
    mod.extend described_class

    mod
  end

  before(:each) do
    allow(subject).to receive(:framework).and_return(framework)
  end

  let(:rhost) do
    'rhost.example.com'
  end

  let(:rport) do
    1234
  end

  let(:ldap_connect_timeout) do
    30
  end

  let(:ssl) do
    false
  end

  let(:ldap_proc) do
    proc {}
  end

  before do
    subject.datastore['LDAP::ConnectTimeout'] = ldap_connect_timeout
    subject.datastore['ssl'] = ssl
    allow(subject).to receive(:rhost).and_return(rhost)
    allow(subject).to receive(:rport).and_return(rport)
  end

  after do
    # Do nothing
  end

  describe '#get_connect_opts' do
    it { expect(subject.get_connect_opts).to be_a(Hash) }
    it do
      mock = {}
      expect(subject).to receive(:ldap_connect_opts).with(rhost, rport, ldap_connect_timeout, ssl: ssl, opts: Hash).and_return({})
      subject.get_connect_opts
    end
  end

  describe '#ldap_open' do
    it do
      opts = {}
      expect(Net::LDAP).to receive(:open).with(opts, &ldap_proc)
      expect(subject).not_to receive(:get_connect_opts)
      expect(subject).to receive(:resolve_connect_opts).and_return(opts)
      subject.ldap_open(opts, &ldap_proc)
    end
  end

  describe '#ldap_connect' do
    it do
      opts = { x: 0 }
      connect_opts = { y: 1 }
      merged_opts = opts.merge(connect_opts)

      expect(subject).to receive(:ldap_open).with(merged_opts, &ldap_proc)
      expect(subject).to receive(:get_connect_opts).and_return(connect_opts)
      subject.ldap_connect(opts, &ldap_proc)
    end
  end

  describe '#ldap_new' do
    it do
      opts = { x: 0 }
      connect_opts = { y: 1 }
      merged_opts = opts.merge(connect_opts)
      expect(Net::LDAP).to receive(:new).with(merged_opts)
      expect(subject).to receive(:get_connect_opts).and_return(connect_opts)
      expect(subject).to receive(:resolve_connect_opts).and_return(merged_opts)
      subject.ldap_new(opts, &ldap_proc)
    end
  end

  describe '#ldap_escape_filter' do
    let(:string) do
      'John Doe (Developer) *'
    end

    it do
      expect(subject.ldap_escape_filter(string)).to eq("John Doe \\28Developer\\29 \\2A")
    end
  end

  describe '#resolve_connect_opts' do
    let(:cred) do
      'I am a cred'
    end

    context 'with no credential proc' do
      let(:connect_opts) do
        { auth: { initial_credential: cred } }
      end
      it { expect(subject.resolve_connect_opts(connect_opts)).to be(connect_opts) }
    end

    context 'with credential proc' do
      let(:cred_proc) do
        proc { cred }
      end

      let(:connect_opts) do
        { auth: { initial_credential: cred_proc } }
      end

      it do
        resolved_connect_opts = subject.resolve_connect_opts(connect_opts)
        expect(resolved_connect_opts[:auth][:initial_credential]).not_to be_a(Proc)
        expect(resolved_connect_opts[:auth][:initial_credential]).to eq(cred)
      end
    end
  end
end
