#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "spec_helper"

unless ChefUtils.windows?
  class Chef
    module ReservedNames
      module Win32
        module Security
          ACL = Object.new
          SecurableObject = Object.new
        end
      end
    end
  end
end

require "chef/file_content_management/deploy/mv_windows"

describe Chef::FileContentManagement::Deploy::MvWindows do

  let(:content_deployer) { described_class.new }
  let(:target_file_path) { "/etc/my_app.conf" }

  describe "creating the file" do

    it "touches the file to create it" do
      expect(FileUtils).to receive(:touch).with(target_file_path)
      content_deployer.create(target_file_path)
    end
  end

  describe "updating the file" do

    let(:staging_file_path) { "/tmp/random-dir/staging-file.tmp" }

    let(:target_file_security_object) do
      double "Securable Object for target file"
    end

    let(:updated_target_security_object) do
      double "Securable Object for target file after staging file deploy"
    end

    before do
      allow(Chef::ReservedNames::Win32::Security::SecurableObject)
        .to receive(:new)
        .with(target_file_path)
        .and_return(target_file_security_object, updated_target_security_object)

    end

    context "when run without administrator privileges" do
      before do
        expect(target_file_security_object).to receive(:security_descriptor).and_raise(Chef::Exceptions::Win32APIError)
      end

      it "errors out with a WindowsNotAdmin error" do
        expect { content_deployer.deploy(staging_file_path, target_file_path) }.to raise_error(Chef::Exceptions::WindowsNotAdmin)
      end

    end

    context "when run with administrator privileges" do

      let(:original_target_file_owner) { double("original target file owner") }
      let(:original_target_file_group) { double("original target file group") }

      let(:target_file_security_descriptor) do
        double "security descriptor for target file",
          group: original_target_file_group,
          owner: original_target_file_owner
      end

      let(:updated_target_security_descriptor) do
        double "security descriptor for target file"
      end

      before do
        allow(target_file_security_object).to receive(:security_descriptor).and_return(target_file_security_descriptor)

        expect(FileUtils).to receive(:mv).with(staging_file_path, target_file_path)

        expect(updated_target_security_object).to receive(:group=).with(original_target_file_group)
        expect(updated_target_security_object).to receive(:owner=).with(original_target_file_owner)
      end

      context "and the target file has no dacl or sacl" do

        before do
          allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(false)
          allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(false)
        end

        it "fixes up permissions and moves the file into place" do
          content_deployer.deploy(staging_file_path, target_file_path)
        end

      end

      context "and the target file has null dacl and sacl" do

        before do
          allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(true)
          allow(target_file_security_descriptor).to receive(:dacl).and_return(nil)
          allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(false)

          allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
          allow(target_file_security_descriptor).to receive(:sacl).and_return(nil)
          allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(false)

          expect(updated_target_security_object).to receive(:set_dacl).with(nil, false)
          expect(updated_target_security_object).to receive(:set_sacl).with(nil, false)
        end

        it "fixes up permissions and moves the file into place" do
          content_deployer.deploy(staging_file_path, target_file_path)
        end

      end

      context "and the target has an empty dacl and sacl" do
        let(:original_target_file_dacl) { [] }
        let(:original_target_file_sacl) { [] }

        let(:empty_dacl) { double("Windows ACL with no dacl ACEs") }
        let(:empty_sacl) { double("Windows ACL with no sacl ACEs") }

        before do
          allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(true)
          allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(false)

          allow(target_file_security_descriptor).to receive(:dacl).and_return(original_target_file_dacl)
          expect(Chef::ReservedNames::Win32::Security::ACL)
            .to receive(:create)
            .with([])
            .and_return(empty_dacl)

          allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
          allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(false)

          allow(target_file_security_descriptor).to receive(:sacl).and_return(original_target_file_sacl)
          expect(Chef::ReservedNames::Win32::Security::ACL)
            .to receive(:create)
            .with([])
            .and_return(empty_sacl)

          expect(updated_target_security_object).to receive(:set_dacl).with(empty_dacl, false)
          expect(updated_target_security_object).to receive(:set_sacl).with(empty_sacl, false)
        end

        it "fixes up permissions and moves the file into place" do
          content_deployer.deploy(staging_file_path, target_file_path)
        end
      end

      context "and the target has a dacl and sacl" do
        let(:inherited_dacl_ace) { double("Windows dacl ace (inherited)", inherited?: true) }
        let(:not_inherited_dacl_ace) { double("Windows dacl ace (not inherited)", inherited?: false) }

        let(:original_target_file_dacl) { [inherited_dacl_ace, not_inherited_dacl_ace] }

        let(:inherited_sacl_ace) { double("Windows sacl ace (inherited)", inherited?: true) }
        let(:not_inherited_sacl_ace) { double("Windows sacl ace (not inherited)", inherited?: false) }
        let(:original_target_file_sacl) { [inherited_sacl_ace, not_inherited_sacl_ace] }

        let(:custom_dacl) { double("Windows ACL for non-inherited dacl aces") }
        let(:custom_sacl) { double("Windows ACL for non-inherited sacl aces") }

        before do
          allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(true)
          allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(dacl_inherits?)

          allow(target_file_security_descriptor).to receive(:dacl).and_return(original_target_file_dacl)
          expect(Chef::ReservedNames::Win32::Security::ACL)
            .to receive(:create)
            .with([not_inherited_dacl_ace])
            .and_return(custom_dacl)

          allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
          allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(sacl_inherits?)

          allow(target_file_security_descriptor).to receive(:sacl).and_return(original_target_file_sacl)
          expect(Chef::ReservedNames::Win32::Security::ACL)
            .to receive(:create)
            .with([not_inherited_sacl_ace])
            .and_return(custom_sacl)

          expect(updated_target_security_object).to receive(:set_dacl).with(custom_dacl, dacl_inherits?)
          expect(updated_target_security_object).to receive(:set_sacl).with(custom_sacl, sacl_inherits?)
        end

        context "and the dacl and sacl don't inherit" do
          let(:dacl_inherits?) { false }
          let(:sacl_inherits?) { false }

          it "fixes up permissions and moves the file into place" do
            content_deployer.deploy(staging_file_path, target_file_path)
          end
        end

        context "and the dacl and sacl inherit" do
          let(:dacl_inherits?) { true }
          let(:sacl_inherits?) { true }

          it "fixes up permissions and moves the file into place" do
            content_deployer.deploy(staging_file_path, target_file_path)
          end
        end

      end

    end

  end
end
