From c8f431cf44ee672b6c2c27dbd818c0ce1b31ed52 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Thu, 8 Nov 2018 14:21:20 -0800 Subject: [PATCH] Prepend computer name to user when created scheduled tasks When running a shell provisioner elevated with winrm a scheduled task is created to bypass permissions issues. If the name of the computer has changed this may no longer work. To prevent errors this PR updates the implementation to fetch the computer name and prepends it to the username before creating the task. --- plugins/communicators/winrm/shell.rb | 26 +++++++++- .../plugins/communicators/winrm/shell_test.rb | 52 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/plugins/communicators/winrm/shell.rb b/plugins/communicators/winrm/shell.rb index 949908a79..e21399d2b 100644 --- a/plugins/communicators/winrm/shell.rb +++ b/plugins/communicators/winrm/shell.rb @@ -71,7 +71,13 @@ module VagrantPlugins def elevated(command, opts = {}, &block) connection.shell(:elevated) do |shell| shell.interactive_logon = opts[:interactive] || false - execute_with_rescue(shell, command, &block) + uname = shell.username + begin + shell.username = elevated_username + execute_with_rescue(shell, command, &block) + ensure + shell.username = uname + end end end @@ -216,6 +222,24 @@ module VagrantPlugins retry_delay: @config.retry_delay, retry_limit: @config.max_tries } end + + def elevated_username + if @elevated_username + return @elevated_username + end + if username.include?("\\") + return @elevated_username = username + end + computername = "" + powershell("Write-Output $env:computername") do |type, data| + computername << data if type == :stdout + end + computername.strip! + if computername.empty? + return @elevated_username = username + end + @elevated_username = "#{computername}\\#{username}" + end end #WinShell class end end diff --git a/test/unit/plugins/communicators/winrm/shell_test.rb b/test/unit/plugins/communicators/winrm/shell_test.rb index daffd48e0..9a32a5df8 100644 --- a/test/unit/plugins/communicators/winrm/shell_test.rb +++ b/test/unit/plugins/communicators/winrm/shell_test.rb @@ -75,6 +75,14 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do end describe ".elevated" do + let(:username) { double("username") } + + before do + allow(subject).to receive(:elevated_username).and_return(username) + allow(shell).to receive(:username).and_return(username) + allow(shell).to receive(:username=) + end + it "should call winrm elevated" do expect(shell).to receive(:run).with("dir").and_return(output) expect(shell).to receive(:interactive_logon=).with(false) @@ -93,6 +101,13 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do expect { subject.powershell("dir") }.to raise_error( VagrantPlugins::CommunicatorWinRM::Errors::ExecutionError) end + + it "should use elevated username" do + expect(subject).to receive(:elevated_username).and_return(username) + expect(shell).to receive(:run).with("dir").and_return(output) + expect(shell).to receive(:interactive_logon=).with(false) + expect(subject.elevated("dir").exitcode).to eq(0) + end end describe ".cmd" do @@ -178,4 +193,41 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do end end + describe "#elevated_username" do + let(:username) { "username" } + + before do + allow(subject).to receive(:username).and_return(username) + allow(subject).to receive(:powershell) + end + + it "should return username" do + expect(subject.send(:elevated_username)).to eq(username) + end + + it "should attempt to get computer name" do + expect(subject).to receive(:powershell).with(/computername/) + subject.send(:elevated_username) + end + + it "should prepend computer name when available" do + expect(subject).to receive(:powershell).with(/computername/).and_yield(:stdout, "COMPUTERNAME") + expect(subject.send(:elevated_username)).to eq("COMPUTERNAME\\#{username}") + end + + it "should only compute elevated username once" do + expect(subject).to receive(:powershell).once.with(/computername/).and_yield(:stdout, "COMPUTERNAME") + expect(subject.send(:elevated_username)).to eq("COMPUTERNAME\\#{username}") + expect(subject.send(:elevated_username)).to eq("COMPUTERNAME\\#{username}") + end + + context "when username includes computer/domain name" do + let(:username) { "machine\\username" } + + it "should not attempt to get computer name" do + expect(subject).not_to receive(:powershell) + expect(subject.send(:elevated_username)).to eq(username) + end + end + end end