require 'spec_helper'
require 'puppet/util/windows'

describe "Puppet::Util::Windows::AccessControlList", :if => Puppet::Util::Platform.windows? do
  let(:klass) { Puppet::Util::Windows::AccessControlList }
  let(:system_sid) { 'S-1-5-18' }
  let(:admins_sid) { 'S-1-5-544' }
  let(:none_sid)   { 'S-1-0-0' }

  let(:system_ace) do
    Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1)
  end
  let(:admins_ace) do
    Puppet::Util::Windows::AccessControlEntry.new(admins_sid, 0x2)
  end
  let(:none_ace) do
    Puppet::Util::Windows::AccessControlEntry.new(none_sid, 0x3)
  end

  it "constructs an empty list" do
    acl = klass.new

    expect(acl.to_a).to be_empty
  end

  it "supports copy constructor" do
    aces = klass.new([system_ace]).to_a

    expect(aces.to_a).to eq([system_ace])
  end

  context "appending" do
    it "appends an allow ace" do
      acl = klass.new
      acl.allow(system_sid, 0x1, 0x2)

      expect(acl.first.type).to eq(klass::ACCESS_ALLOWED_ACE_TYPE)
    end

    it "appends a deny ace" do
      acl = klass.new
      acl.deny(system_sid, 0x1, 0x2)

      expect(acl.first.type).to eq(klass::ACCESS_DENIED_ACE_TYPE)
    end

    it "always appends, never overwrites an ACE" do
      acl = klass.new([system_ace])
      acl.allow(admins_sid, admins_ace.mask, admins_ace.flags)

      aces = acl.to_a
      expect(aces.size).to eq(2)
      expect(aces[0]).to eq(system_ace)
      expect(aces[1].sid).to eq(admins_sid)
      expect(aces[1].mask).to eq(admins_ace.mask)
      expect(aces[1].flags).to eq(admins_ace.flags)
    end
  end

  context "reassigning" do
    it "preserves the mask from the old sid when reassigning to the new sid" do
      dacl = klass.new([system_ace])

      dacl.reassign!(system_ace.sid, admins_ace.sid)
      # we removed system, so ignore prepended ace
      ace = dacl.to_a[1]
      expect(ace.sid).to eq(admins_sid)
      expect(ace.mask).to eq(system_ace.mask)
    end

    it "matches multiple sids" do
      dacl = klass.new([system_ace, system_ace])

      dacl.reassign!(system_ace.sid, admins_ace.sid)
      # we removed system, so ignore prepended ace
      aces = dacl.to_a
      expect(aces.size).to eq(3)
      aces.to_a[1,2].each do |ace|
        expect(ace.sid).to eq(admins_ace.sid)
      end
    end

    it "preserves aces for sids that don't match, in their original order" do
      dacl = klass.new([system_ace, admins_ace])

      dacl.reassign!(system_sid, none_sid)
      aces = dacl.to_a
      aces[1].sid == admins_ace.sid
    end

    it "preserves inherited aces, even if the sids match" do
      flags = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE
      inherited_ace = Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1, flags)
      dacl = klass.new([inherited_ace, system_ace])
      dacl.reassign!(system_sid, none_sid)
      aces = dacl.to_a

      expect(aces[0].sid).to eq(system_sid)
    end

    it "prepends an explicit ace for the new sid with the same mask and basic inheritance as the inherited ace" do
      expected_flags =
        Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |
        Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE |
        Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE

      flags = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE | expected_flags

      inherited_ace = Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1, flags)
      dacl = klass.new([inherited_ace])
      dacl.reassign!(system_sid, none_sid)
      aces = dacl.to_a

      expect(aces.size).to eq(2)
      expect(aces[0].sid).to eq(none_sid)
      expect(aces[0]).not_to be_inherited
      expect(aces[0].flags).to eq(expected_flags)

      expect(aces[1].sid).to eq(system_sid)
      expect(aces[1]).to be_inherited
    end

    it "makes a copy of the ace prior to modifying it" do
      arr = [system_ace]

      acl = klass.new(arr)
      acl.reassign!(system_sid, none_sid)

      expect(arr[0].sid).to eq(system_sid)
    end
  end
end
