summaryrefslogtreecommitdiff
path: root/spec/functional/mixin
diff options
context:
space:
mode:
authoradamedx <adamedx@gmail.com>2016-04-02 23:22:14 -0700
committerBryan McLellan <btm@loftninjas.org>2017-09-05 20:09:30 -0400
commitd8fa21b9851b2a6d3c1ee370c8d0cfee8be278b4 (patch)
treeef63f7b277c30c4d1d48d944c3be3e219c56a7ef /spec/functional/mixin
parent2e9c29b0658136f0536d423ca71899a825f3920d (diff)
downloadchef-d8fa21b9851b2a6d3c1ee370c8d0cfee8be278b4.tar.gz
Windows thread alternate user impersonation support
Diffstat (limited to 'spec/functional/mixin')
-rw-r--r--spec/functional/mixin/user_context_spec.rb118
1 files changed, 118 insertions, 0 deletions
diff --git a/spec/functional/mixin/user_context_spec.rb b/spec/functional/mixin/user_context_spec.rb
new file mode 100644
index 0000000000..e157f959af
--- /dev/null
+++ b/spec/functional/mixin/user_context_spec.rb
@@ -0,0 +1,118 @@
+#
+# Copyright:: Copyright (c) 2015 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"
+
+require "chef/win32/api" if Chef::Platform.windows?
+require "chef/win32/api/error" if Chef::Platform.windows?
+require "chef/mixin/user_context"
+
+describe Chef::Mixin::UserContext, windows_only: true do
+ include Chef::Mixin::UserContext
+
+ let(:get_user_name_a) do
+ FFI.ffi_lib "advapi32.dll"
+ FFI.attach_function :GetUserNameA, [ :pointer, :pointer ], :bool
+ end
+
+ let(:process_username) do
+ name_size = FFI::Buffer.new(:long).write_long(0)
+ succeeded = get_user_name_a.call(nil, name_size)
+ last_error = FFI::LastError.error
+ if succeeded || last_error != Chef::ReservedNames::Win32::API::Error::ERROR_INSUFFICIENT_BUFFER
+ raise Chef::Exceptions::Win32APIError, "Expected ERROR_INSUFFICIENT_BUFFER from GetUserNameA but it returned the following error: #{last_error}"
+ end
+ user_name = FFI::MemoryPointer.new :char, (name_size.read_long)
+ succeeded = get_user_name_a.call(user_name, name_size)
+ last_error = FFI::LastError.error
+ if succeeded == 0 || last_error != 0
+ raise Chef::Exceptions::Win32APIError, "GetUserNameA failed with #{lasterror}"
+ end
+ user_name.read_string
+ end
+
+ let(:test_user) { "chefuserctx3" }
+ let(:test_domain) { windows_nonadmin_user_domain }
+ let(:test_password) { "j823jfxK3;2Xe1" }
+
+ let(:username_domain_qualification) { nil }
+ let(:username_with_conditional_domain) { username_domain_qualification.nil? ? username_to_impersonate : "#{username_domain_qualification}\\#{username_to_impersonate}" }
+
+ let(:windows_nonadmin_user) { test_user }
+ let(:windows_nonadmin_user_password) { test_password }
+
+ let(:username_while_impersonating) do
+ username = nil
+ with_user_context(username_with_conditional_domain, username_to_impersonate_password, domain_to_impersonate) do
+ username = process_username
+ end
+ username
+ end
+
+ shared_examples_for "method that executes the block while impersonating the alternate user" do
+ it "sets the current thread token to that of the alternate user when the correct password is specified" do
+ expect(username_while_impersonating.downcase).to eq(username_to_impersonate.downcase)
+ end
+ end
+
+ describe "#with_user_context" do
+ context "when the user and domain are both nil" do
+ let(:username_to_impersonate) { nil }
+ let(:domain_to_impersonate) { nil }
+ let(:username_to_impersonate_password) { nil }
+
+ it "has the same token and username as the process" do
+ expect(username_while_impersonating.downcase).to eq(ENV["username"].downcase)
+ end
+ end
+
+ context "when a non-nil user is specified" do
+ include_context "a non-admin Windows user"
+ context "when a username different than the process user is specified" do
+ let(:username_to_impersonate) { test_user }
+ let(:username_to_impersonate_password) { test_password }
+ context "when an explicit domain is given with a valid password" do
+ let(:domain_to_impersonate) { test_domain }
+ it "sets the current thread token to that of the alternate user when the correct password is specified" do
+ expect(username_while_impersonating.downcase).to eq(username_to_impersonate.downcase)
+ end
+ end
+
+ context "when a valid password and a non-qualified user is given and no domain is specified" do
+ let(:domain_to_impersonate) { nil }
+ it_behaves_like "method that executes the block while impersonating the alternate user"
+ end
+
+ context "when a valid password and a qualified user is given and no domain is specified" do
+ let(:domain_to_impersonate) { nil }
+ let(:username_domain_qualification) { test_domain }
+ it_behaves_like "method that executes the block while impersonating the alternate user"
+ end
+
+ it "raises an error user if specified with the wrong password" do
+ expect { with_user_context(username_to_impersonate, username_to_impersonate_password + "1", nil) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context "when invalid arguments are passed" do
+ it "raises an ArgumentError exception if the password is not specified but the user is specified" do
+ expect { with_user_context(test_user, nil, nil) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+end