diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2018-05-21 09:21:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-21 09:21:51 -0700 |
commit | ffbfdc336fbb18e08eed958fcb300e69164b01e2 (patch) | |
tree | 16be148618e6f70668ed9c8b65f8e1279868b0da | |
parent | d2cd93486f8183a96d83024d8c2e9d8b0b5088b6 (diff) | |
parent | b3a1cc9165904e6be09b8a62065e9289a905bfb4 (diff) | |
download | chef-ffbfdc336fbb18e08eed958fcb300e69164b01e2.tar.gz |
Merge pull request #7249 from chef/lcg/cleanup-user
Cleanup AIX and Solaris user resources.
-rw-r--r-- | lib/chef/provider/user.rb | 19 | ||||
-rw-r--r-- | lib/chef/provider/user/aix.rb | 48 | ||||
-rw-r--r-- | lib/chef/provider/user/dscl.rb | 10 | ||||
-rw-r--r-- | lib/chef/provider/user/linux.rb | 12 | ||||
-rw-r--r-- | lib/chef/provider/user/solaris.rb | 83 | ||||
-rw-r--r-- | lib/chef/provider/user/useradd.rb | 5 | ||||
-rw-r--r-- | lib/chef/providers.rb | 3 | ||||
-rw-r--r-- | spec/functional/resource/user/useradd_spec.rb | 709 | ||||
-rw-r--r-- | spec/support/shared/unit/provider/useradd_based_user_provider.rb | 6 | ||||
-rw-r--r-- | spec/unit/provider/user/solaris_spec.rb | 4 |
10 files changed, 129 insertions, 770 deletions
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb index 18cf2d4d99..7145045785 100644 --- a/lib/chef/provider/user.rb +++ b/lib/chef/provider/user.rb @@ -1,6 +1,6 @@ # # Author:: Adam Jacob (<adam@chef.io>) -# Copyright:: Copyright 2008-2017, Chef Software Inc. +# Copyright:: Copyright 2008-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -199,6 +199,23 @@ class Chef def check_lock raise NotImplementedError end + + private + + # + # helpers for subclasses + # + + def should_set?(sym) + current_resource.send(sym).to_s != new_resource.send(sym).to_s && new_resource.send(sym) + end + + def updating_home? + return false if new_resource.home.nil? + return true if current_resource.home.nil? + # Pathname#cleanpath matches more edge conditions than File.expand_path() + new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath + end end end end diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb index 64a088dd5c..be6ff9d750 100644 --- a/lib/chef/provider/user/aix.rb +++ b/lib/chef/provider/user/aix.rb @@ -1,5 +1,5 @@ # -# Copyright:: Copyright 2012-2016, Chef Software Inc. +# Copyright:: Copyright 2012-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,35 +14,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -require "chef/provider/user/useradd" +require "chef/provider/user" class Chef class Provider class User - class Aix < Chef::Provider::User::Useradd + class Aix < Chef::Provider::User provides :user, os: "aix" provides :aix_user - UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]].freeze - def create_user - super + shell_out_compact!("useradd", universal_options, useradd_options, new_resource.username) add_password end def manage_user add_password manage_home - super + return if universal_options.empty? && usermod_options.empty? + shell_out_compact!("usermod", universal_options, usermod_options, new_resource.username) + end + + def remove_user + shell_out_compact!("userdel", userdel_options, new_resource.username) end # Aix does not support -r like other unix, sytem account is created by adding to 'system' group def useradd_options opts = [] opts << "-g" << "system" if new_resource.system + if updating_home? + if new_resource.manage_home + logger.trace("#{new_resource} managing the users home directory") + opts << "-m" + else + logger.trace("#{new_resource} setting home to #{new_resource.home}") + end + end + opts + end + + def userdel_options + opts = [] + opts << "-r" if new_resource.manage_home + opts << "-f" if new_resource.force opts end + def usermod_options + [] + end + def check_lock lock_info = shell_out_compact!("lsuser", "-a", "account_locked", new_resource.username) if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.match(/does not exist/) @@ -70,6 +92,17 @@ class Chef shell_out_compact!("chuser", "account_locked=false", new_resource.username) end + def universal_options + opts = [] + opts << "-c" << new_resource.comment if should_set?(:comment) + opts << "-g" << new_resource.gid if should_set?(:gid) + opts << "-s" << new_resource.shell if should_set?(:shell) + opts << "-u" << new_resource.uid if should_set?(:uid) + opts << "-d" << new_resource.home if updating_home? + opts << "-o" if new_resource.non_unique + opts + end + private def add_password @@ -83,7 +116,6 @@ class Chef def manage_home return unless updating_home? && new_resource.manage_home # -m option does not work on aix, so move dir. - universal_options.delete("-m") if ::File.directory?(current_resource.home) logger.trace("Changing users home directory from #{current_resource.home} to #{new_resource.home}") FileUtils.mv current_resource.home, new_resource.home diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index 79a2c73339..67fe8f3762 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -1,6 +1,6 @@ # # Author:: Dreamcat4 (<dreamcat4@gmail.com>) -# Copyright:: Copyright 2009-2017, Chef Software Inc. +# Copyright:: Copyright 2009-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -654,9 +654,7 @@ user password using shadow hash.") end def run_dscl(*args) - argdup = args.dup - cmd = argdup.shift - result = shell_out_compact("dscl", ".", "-#{cmd}", argdup) + result = shell_out_compact("dscl", ".", "-#{args[0]}", args[1..-1]) return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 ) raise(Chef::Exceptions::DsclCommandFailed, "dscl error: #{result.inspect}") unless result.exitstatus == 0 raise(Chef::Exceptions::DsclCommandFailed, "dscl error: #{result.inspect}") if result.stdout =~ /No such key: / @@ -664,9 +662,7 @@ user password using shadow hash.") end def run_plutil(*args) - argdup = args.dup - cmd = argdup.shift - result = shell_out_compact("plutil", "-#{cmd}", argdup) + result = shell_out_compact("plutil", "-#{args[0]}", args[1..-1]) raise(Chef::Exceptions::PlistUtilCommandFailed, "plutil error: #{result.inspect}") unless result.exitstatus == 0 if result.stdout.encoding == Encoding::ASCII_8BIT result.stdout.encode("utf-8", "binary", undef: :replace, invalid: :replace, replace: "?") diff --git a/lib/chef/provider/user/linux.rb b/lib/chef/provider/user/linux.rb index 2db6c218bd..a846d2657a 100644 --- a/lib/chef/provider/user/linux.rb +++ b/lib/chef/provider/user/linux.rb @@ -1,5 +1,5 @@ # -# Copyright:: Copyright 2016-2017, Chef Software Inc. +# Copyright:: Copyright 2016-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -85,16 +85,6 @@ class Chef opts end - def should_set?(sym) - current_resource.send(sym).to_s != new_resource.send(sym).to_s && new_resource.send(sym) - end - - def updating_home? - return false unless new_resource.home - return true unless current_resource.home - new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath - end - def check_lock # there's an old bug in rhel (https://bugzilla.redhat.com/show_bug.cgi?id=578534) # which means that both 0 and 1 can be success. diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb index 59074d5ba8..1abe660cfd 100644 --- a/lib/chef/provider/user/solaris.rb +++ b/lib/chef/provider/user/solaris.rb @@ -2,7 +2,7 @@ # Author:: Stephen Nelson-Smith (<sns@chef.io>) # Author:: Jon Ramsey (<jonathon.ramsey@gmail.com>) # Author:: Dave Eddy (<dave@daveeddy.com>) -# Copyright:: Copyright 2012-2017, Chef Software Inc. +# Copyright:: Copyright 2012-2018, Chef Software Inc. # Copyright:: Copyright 2015-2016, Dave Eddy # License:: Apache License, Version 2.0 # @@ -18,35 +18,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -require "chef/provider/user/useradd" +require "chef/provider/user" class Chef class Provider class User - class Solaris < Chef::Provider::User::Useradd + class Solaris < Chef::Provider::User provides :solaris_user - provides :user, os: %w{omnios solaris2} - UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]].freeze + provides :user, os: %w{openindiana opensolaris illumos omnios solaris2 smartos} - attr_writer :password_file - - def initialize(new_resource, run_context) - @password_file = "/etc/shadow" - super - end + PASSWORD_FILE = "/etc/shadow" def create_user - super + shell_out_compact!("useradd", universal_options, useradd_options, new_resource.username) manage_password end def manage_user manage_password - super + return if universal_options.empty? && usermod_options.empty? + shell_out_compact!("usermod", universal_options, usermod_options, new_resource.username) + end + + def remove_user + shell_out_compact!("userdel", userdel_options, new_resource.username) end def check_lock - user = IO.read(@password_file).match(/^#{Regexp.escape(new_resource.username)}:([^:]*):/) + user = IO.read(PASSWORD_FILE).match(/^#{Regexp.escape(new_resource.username)}:([^:]*):/) # If we're in whyrun mode, and the user is not created, we assume it will be return false if whyrun_mode? && user.nil? @@ -66,15 +65,45 @@ class Chef private - # Override the version from {#Useradd} because Solaris doesn't support - # system users and therefore has no `-r` option. This also inverts the - # logic for manage_home as Solaris defaults to no-manage-home and only - # offers `-m`. - # - # @since 12.15 - # @api private - # @see Useradd#useradd_options - # @return [Array<String>] + def universal_options + opts = [] + opts << "-c" << new_resource.comment if should_set?(:comment) + opts << "-g" << new_resource.gid if should_set?(:gid) + opts << "-s" << new_resource.shell if should_set?(:shell) + opts << "-u" << new_resource.uid if should_set?(:uid) + opts << "-d" << new_resource.home if updating_home? + opts << "-o" if new_resource.non_unique + if updating_home? + if new_resource.manage_home + logger.trace("#{new_resource} managing the users home directory") + opts << "-m" + else + logger.trace("#{new_resource} setting home to #{new_resource.home}") + end + end + opts + end + + def usermod_options + opts = [] + opts += [ "-u", new_resource.uid ] if new_resource.non_unique + if updating_home? + if new_resource.manage_home + opts << "-m" + end + end + opts + end + + def userdel_options + opts = [] + opts << "-r" if new_resource.manage_home + opts << "-f" if new_resource.force + opts + end + + # Solaris does not support system users and has no '-r' option, solaris also + # lacks '-M' and defaults to no-manage-home. def useradd_options opts = [] opts << "-m" if new_resource.manage_home @@ -87,9 +116,11 @@ class Chef write_shadow_file end + # XXX: this was straight copypasta'd back in 2013 and I don't think we've ever evaluted using + # a pipe to passwd(1) or evaluating modern ruby-shadow. See https://github.com/chef/chef/pull/721 def write_shadow_file buffer = Tempfile.new("shadow", "/etc") - ::File.open(@password_file) do |shadow_file| + ::File.open(PASSWORD_FILE) do |shadow_file| shadow_file.each do |entry| user = entry.split(":").first if user == new_resource.username @@ -102,7 +133,7 @@ class Chef buffer.close # FIXME: mostly duplicates code with file provider deploying a file - s = ::File.stat(@password_file) + s = ::File.stat(PASSWORD_FILE) mode = s.mode & 0o7777 uid = s.uid gid = s.gid @@ -110,7 +141,7 @@ class Chef FileUtils.chown uid, gid, buffer.path FileUtils.chmod mode, buffer.path - FileUtils.mv buffer.path, @password_file + FileUtils.mv buffer.path, PASSWORD_FILE end def updated_password(entry) diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb index 47c0ece101..c09cc0d3a5 100644 --- a/lib/chef/provider/user/useradd.rb +++ b/lib/chef/provider/user/useradd.rb @@ -1,6 +1,6 @@ # # Author:: Adam Jacob (<adam@chef.io>) -# Copyright:: Copyright 2008-2017, Chef Software Inc. +# Copyright:: Copyright 2008-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,9 @@ class Chef class Provider class User class Useradd < Chef::Provider::User + + Chef::Log.warn("the Chef::Provider::User::Useradd provider is deprecated, please subclass Chef::Provider::User directly") + # the linux version of this has been forked off, this is the base class now of solaris and AIX and should be abandoned # and those provider should be rewritten like the linux version. diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index 1b9aa84697..cd265b0618 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -1,6 +1,6 @@ # # Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2010-2016, Chef Software, Inc. +# Copyright:: Copyright 2010-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -109,7 +109,6 @@ require "chef/provider/user/dscl" require "chef/provider/user/linux" require "chef/provider/user/pw" require "chef/provider/user/solaris" -require "chef/provider/user/useradd" require "chef/provider/user/windows" require "chef/provider/group/aix" diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb deleted file mode 100644 index 175809b5c0..0000000000 --- a/spec/functional/resource/user/useradd_spec.rb +++ /dev/null @@ -1,709 +0,0 @@ -# encoding: UTF-8 -# -# Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2013-2016, 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 "functional/resource/base" -require "chef/mixin/shell_out" - -def resource_for_platform(username, run_context) - Chef::Resource.resource_for_node(:user, node).new(username, run_context) -end - -# ideally we could somehow pass an array of [ ...::Aix, ...::Linux ] to the -# filter, but we have to pick the right one for the O/S. -def user_provider_filter - case ohai[:os] - when "aix" - Chef::Provider::User::Aix - when "linux" - Chef::Provider::User::Linux - end -end - -metadata = { - :unix_only => true, - :requires_root => true, - :not_supported_on_mac_osx => true, - :provider => { :user => user_provider_filter }, -} - -describe Chef::Provider::User::Useradd, metadata do - - include Chef::Mixin::ShellOut - - # Utility code for /etc/passwd interaction, avoid any caching of user records: - PwEntry = Struct.new(:name, :passwd, :uid, :gid, :gecos, :home, :shell) - - class UserNotFound < StandardError; end - - def pw_entry - passwd_file = File.open("/etc/passwd", "rb") { |f| f.read } - matcher = /^#{Regexp.escape(username)}.+$/ - if passwd_entry = passwd_file.scan(matcher).first - PwEntry.new(*passwd_entry.split(":")) - else - raise UserNotFound, "no entry matching #{matcher.inspect} found in /etc/passwd" - end - end - - def etc_shadow - case ohai[:platform] - when "aix" - File.open("/etc/security/passwd") { |f| f.read } - else - File.open("/etc/shadow") { |f| f.read } - end - end - - def self.quote_in_username_unsupported? - if OHAI_SYSTEM["platform_family"] == "debian" - false - else - "Only debian family systems support quotes in username" - end - end - - def password_should_be_set - if ohai[:platform] == "aix" - expect(pw_entry.passwd).to eq("!") - else - expect(pw_entry.passwd).to eq("x") - end - end - - def try_cleanup - ["/home/cheftestfoo", "/home/cheftestbar", "/home/cf-test"].each do |f| - FileUtils.rm_rf(f) if File.exists? f - end - - ["cf-test"].each do |u| - r = resource_for_platform("DELETE USER", run_context) - r.manage_home true - r.username("cf-test") - r.run_action(:remove) - end - end - - before do - # Silence shell_out live stream - Chef::Log.level = :warn - try_cleanup - end - - after do - max_retries = 3 - while max_retries > 0 - begin - pw_entry # will raise if the user doesn't exist - status = shell_out!("userdel", "-r", username, :returns => [0, 8, 12]) - - # Error code 8 during userdel indicates that the user is logged in. - # This occurs randomly because the accounts daemon holds a lock due to which userdel fails. - # The work around is to retry userdel for 3 times. - break if status.exitstatus != 8 - - sleep 1 - max_retries -= 1 - rescue UserNotFound - break - end - end - - status.error! if max_retries == 0 - end - - let(:node) do - n = Chef::Node.new - n.consume_external_attrs(OHAI_SYSTEM.data.dup, {}) - n - end - - let(:events) do - Chef::EventDispatch::Dispatcher.new - end - - let(:run_context) do - Chef::RunContext.new(node, {}, events) - end - - let(:username) { "cf-test" } - let(:uid) { nil } - let(:home) { nil } - let(:manage_home) { false } - let(:password) { nil } - let(:system) { false } - let(:comment) { nil } - - let(:user_resource) do - r = resource_for_platform("TEST USER RESOURCE", run_context) - r.username(username) - r.uid(uid) - r.home(home) - r.comment(comment) - r.manage_home(manage_home) - r.password(password) - r.system(system) - r - end - - let(:expected_shadow) do - if ohai[:platform] == "aix" - expected_shadow = "cf-test" # For aix just check user entry in shadow file - else - expected_shadow = "cf-test:$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - describe "action :create" do - - context "when the user does not exist beforehand" do - before do - user_resource.run_action(:create) - expect(user_resource).to be_updated_by_last_action - end - - it "ensures the user exists" do - expect(pw_entry.name).to eq(username) - end - - # On Debian, the only constraints are that usernames must neither start - # with a dash ('-') nor plus ('+') nor tilde ('~') nor contain a colon - # (':'), a comma (','), or a whitespace (space: ' ', end of line: '\n', - # tabulation: '\t', etc.). Note that using a slash ('/') may break the - # default algorithm for the definition of the user's home directory. - - context "and the username contains a single quote", skip: quote_in_username_unsupported? do - - let(:username) { "t'bilisi" } - - it "ensures the user exists" do - expect(pw_entry.name).to eq(username) - end - end - - context "when uid is set" do - # Should verify uid not in use... - let(:uid) { 1999 } - - it "ensures the user has the given uid" do - expect(pw_entry.uid).to eq("1999") - end - end - - context "when comment is set" do - let(:comment) { "hello this is dog" } - - it "ensures the comment is set" do - expect(pw_entry.gecos).to eq("hello this is dog") - end - - context "in standard gecos format" do - let(:comment) { "Bobo T. Clown,some building,555-555-5555,@boboclown" } - - it "ensures the comment is set" do - expect(pw_entry.gecos).to eq(comment) - end - end - - context "to a string containing multibyte characters" do - let(:comment) { "(╯°□°)╯︵ ┻━┻" } - - it "ensures the comment is set" do - actual = pw_entry.gecos - actual.force_encoding(Encoding::UTF_8) if "".respond_to?(:force_encoding) - expect(actual).to eq(comment) - end - end - - context "to a string containing an apostrophe `'`" do - let(:comment) { "don't go" } - - it "ensures the comment is set" do - expect(pw_entry.gecos).to eq(comment) - end - end - end - - context "when home is set" do - let(:home) { "/home/#{username}" } - - it "ensures the user's home is set to the given path" do - expect(pw_entry.home).to eq(home) - end - - it "does not create the home dir without `manage_home'" do - expect(File).not_to exist(home) - end - - context "and manage_home is enabled" do - let(:manage_home) { true } - - it "ensures the user's home directory exists" do - expect(File).to exist(home) - end - end - - context "and manage_home is the default" do - let(:manage_home) { nil } - - it "does not create the home dir without `manage_home'" do - expect(File).not_to exist(home) - end - end - end - - context "when a password is specified" do - # openssl passwd -1 "secretpassword" - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - it "sets the user's shadow password" do - password_should_be_set - expect(etc_shadow).to include(expected_shadow) - end - end - - context "when a system user is specified", skip: aix? do - let(:system) { true } - let(:uid_min) do - # from `man useradd`, login user means uid will be between - # UID_SYS_MIN and UID_SYS_MAX defined in /etc/login.defs. On my - # Ubuntu 13.04 system, these are commented out, so we'll look at - # UID_MIN to find the lower limit of the non-system-user range, and - # use that value in our assertions. - login_defs = File.open("/etc/login.defs", "rb") { |f| f.read } - uid_min_scan = /^UID_MIN\s+(\d+)/ - login_defs.match(uid_min_scan)[1] - end - - it "ensures the user has the properties of a system user" do - expect(pw_entry.uid.to_i).to be < uid_min.to_i - end - end - end # when the user does not exist beforehand - - context "when the user already exists" do - - let(:expect_updated?) { true } - - let(:existing_uid) { nil } - let(:existing_home) { nil } - let(:existing_manage_home) { false } - let(:existing_password) { nil } - let(:existing_system) { false } - let(:existing_comment) { nil } - - let(:existing_user) do - r = resource_for_platform("TEST USER RESOURCE", run_context) - # username is identity attr, must match. - r.username(username) - r.uid(existing_uid) - r.home(existing_home) - r.comment(existing_comment) - r.manage_home(existing_manage_home) - r.password(existing_password) - r.system(existing_system) - r - end - - before do - if reason = skip - skip(reason) - end - existing_user.run_action(:create) - expect(existing_user).to be_updated_by_last_action - user_resource.run_action(:create) - expect(user_resource.updated_by_last_action?).to eq(expect_updated?) - end - - context "and all properties are in the desired state" do - let(:uid) { 1999 } - let(:home) { "/home/bobo" } - let(:manage_home) { true } - # openssl passwd -1 "secretpassword" - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - let(:system) { false } - let(:comment) { "hello this is dog" } - - let(:existing_uid) { uid } - let(:existing_home) { home } - let(:existing_manage_home) { manage_home } - let(:existing_password) { password } - let(:existing_system) { false } - let(:existing_comment) { comment } - - let(:expect_updated?) { false } - - it "does not update the user" do - expect(user_resource).not_to be_updated - end - end - - context "and the uid is updated" do - let(:uid) { 1999 } - let(:existing_uid) { 1998 } - - it "ensures the uid is set to the desired value" do - expect(pw_entry.uid).to eq("1999") - end - end - - context "and the comment is updated" do - let(:comment) { "hello this is dog" } - let(:existing_comment) { "woof" } - - it "ensures the comment field is set to the desired value" do - expect(pw_entry.gecos).to eq("hello this is dog") - end - end - - context "and home directory is updated" do - let(:existing_home) { "/home/cheftestfoo" } - let(:home) { "/home/cheftestbar" } - it "ensures the home directory is set to the desired value" do - expect(pw_entry.home).to eq("/home/cheftestbar") - end - - context "and manage_home is enabled" do - let(:existing_manage_home) { true } - let(:manage_home) { true } - it "moves the home directory to the new location" do - expect(File).not_to exist("/home/cheftestfoo") - expect(File).to exist("/home/cheftestbar") - end - end - - context "and manage_home wasn't enabled but is now" do - let(:existing_manage_home) { false } - let(:manage_home) { true } - - if %w{rhel fedora}.include?(OHAI_SYSTEM["platform_family"]) - # Inconsistent behavior. See: CHEF-2205 - it "created the home dir b/c of CHEF-2205 so it still exists" do - # This behavior seems contrary to expectation and non-convergent. - expect(File).not_to exist("/home/cheftestfoo") - expect(File).to exist("/home/cheftestbar") - end - elsif ohai[:platform] == "aix" - it "creates the home dir in the desired location" do - expect(File).not_to exist("/home/cheftestfoo") - expect(File).to exist("/home/cheftestbar") - end - else - it "does not create the home dir in the desired location (XXX)" do - # This behavior seems contrary to expectation and non-convergent. - expect(File).not_to exist("/home/cheftestfoo") - expect(File).not_to exist("/home/cheftestbar") - end - end - end - - context "and manage_home was enabled but is not now" do - let(:existing_manage_home) { true } - let(:manage_home) { false } - - it "leaves the old home directory around (XXX)" do - # Would it be better to remove the old home? - expect(File).to exist("/home/cheftestfoo") - expect(File).not_to exist("/home/cheftestbar") - end - end - end - - context "and a password is added" do - # openssl passwd -1 "secretpassword" - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - it "ensures the password is set" do - password_should_be_set - expect(etc_shadow).to include(expected_shadow) - end - - end - - context "and the password is updated" do - # openssl passwd -1 "OLDpassword" - let(:existing_password) do - case ohai[:platform] - when "aix" - "jkzG6MvUxjk2g" - else - "$1$1dVmwm4z$CftsFn8eBDjDRUytYKkXB." - end - end - - # openssl passwd -1 "secretpassword" - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - it "ensures the password is set to the desired value" do - password_should_be_set - expect(etc_shadow).to include(expected_shadow) - end - end - - context "and the user is changed from not-system to system" do - let(:existing_system) { false } - let(:system) { true } - - let(:expect_updated?) { false } - - it "does not modify the user at all" do - end - end - - context "and the user is changed from system to not-system" do - let(:existing_system) { true } - let(:system) { false } - - let(:expect_updated?) { false } - - it "does not modify the user at all" do - end - end - - end # when the user already exists - end # action :create - - shared_context "user exists for lock/unlock" do - let(:user_locked_context?) { false } - - def shadow_entry - etc_shadow.lines.find { |l| l.include?(username) } - end - - def shadow_password - shadow_entry.split(":")[1] - end - - def aix_user_lock_status - lock_info = shell_out!("lsuser -a account_locked #{username}") - /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)[1] - end - - def user_account_should_be_locked - case ohai[:platform] - when "aix" - expect(aix_user_lock_status).to eq("true") - else - expect(shadow_password).to include("!") - end - end - - def user_account_should_be_unlocked - case ohai[:platform] - when "aix" - expect(aix_user_lock_status).to eq("false") - else - expect(shadow_password).not_to include("!") - end - end - - def lock_user_account - case ohai[:platform] - when "aix" - shell_out!("chuser account_locked=true #{username}") - else - shell_out!("usermod -L #{username}") - end - end - - before do - # create user and setup locked/unlocked state - user_resource.dup.run_action(:create) - - if user_locked_context? - lock_user_account - user_account_should_be_locked - elsif password - user_account_should_be_unlocked - end - end - end - - describe "action :lock" do - context "when the user does not exist" do - it "raises a sensible error" do - expect { user_resource.run_action(:lock) }.to raise_error(Chef::Exceptions::User) - end - end - - context "when the user exists" do - - include_context "user exists for lock/unlock" - - before do - user_resource.run_action(:lock) - end - - context "and the user is not locked" do - # user will be locked if it has no password - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - it "locks the user's password" do - user_account_should_be_locked - end - end - - context "and the user is locked" do - # user will be locked if it has no password - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - let(:user_locked_context?) { true } - it "does not update the user" do - expect(user_resource).not_to be_updated_by_last_action - end - end - end - end # action :lock - - describe "action :unlock" do - context "when the user does not exist" do - it "raises a sensible error" do - expect { user_resource.run_action(:unlock) }.to raise_error(Chef::Exceptions::User) - end - end - - context "when the user exists" do - - include_context "user exists for lock/unlock" - - before do - begin - user_resource.run_action(:unlock) - @error = nil - rescue Exception => e - @error = e - end - end - - context "and has no password" do - - # TODO: platform_family should be setup in spec_helper w/ tags - if %w{opensuse}.include?(OHAI_SYSTEM["platform_family"]) || - (%w{suse}.include?(OHAI_SYSTEM["platform_family"]) && - OHAI_SYSTEM["platform_version"].to_f < 12.0) - # suse 11.x gets this right: - it "errors out trying to unlock the user" do - expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed) - expect(@error.message).to include("Cannot unlock the password") - end - elsif %w{rhel}.include?(OHAI_SYSTEM["platform_family"]) && - (Chef::VersionConstraint.new("~> 6.8").include?(OHAI_SYSTEM["platform_version"].to_f) || Chef::VersionConstraint.new("~> 7.3").include?(OHAI_SYSTEM["platform_version"].to_f)) - # RHEL 6.8 and 7.3 ship with a fixed `usermod` command - # Reference: https://access.redhat.com/errata/RHBA-2016:0864 - # Reference: https://access.redhat.com/errata/RHBA-2016:2322 - it "errors out trying to unlock the user" do - expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed) - expect(@error.message).to include("You should set a password") - end - else - - # borked on all other platforms: - it "is marked as updated but doesn't modify the user (XXX)" do - # This should be an error instead; note that usermod still exits 0 - # (which is probably why this case silently fails): - # - # DEBUG: ---- Begin output of usermod -U chef-functional-test ---- - # DEBUG: STDOUT: - # DEBUG: STDERR: usermod: unlocking the user's password would result in a passwordless account. - # You should set a password with usermod -p to unlock this user's password. - # DEBUG: ---- End output of usermod -U chef-functional-test ---- - # DEBUG: Ran usermod -U chef-functional-test returned 0 - expect(@error).to be_nil - if ohai[:platform] == "aix" - expect(pw_entry.passwd).to eq("*") - user_account_should_be_unlocked - else - expect(pw_entry.passwd).to eq("x") - expect(shadow_password).to include("!") - end - end - end - end - - context "and has a password" do - let(:password) do - case ohai[:platform] - when "aix" - "eL5qfEVznSNss" - else - "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" - end - end - - context "and the user is not locked" do - it "does not update the user" do - expect(user_resource).not_to be_updated_by_last_action - end - end - - context "and the user is locked" do - let(:user_locked_context?) { true } - - it "unlocks the user's password" do - user_account_should_be_unlocked - end - end - end - end - end # action :unlock - -end diff --git a/spec/support/shared/unit/provider/useradd_based_user_provider.rb b/spec/support/shared/unit/provider/useradd_based_user_provider.rb index cc2d22f64f..e123e8e0e2 100644 --- a/spec/support/shared/unit/provider/useradd_based_user_provider.rb +++ b/spec/support/shared/unit/provider/useradd_based_user_provider.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2018, Chef Software Inc. # # License:: Apache License, Version 2.0 # @@ -385,7 +385,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option expect(Pathname).to receive(:new).with(@new_resource.home).and_return(@new_home_mock) expect(@new_home_mock).to receive(:cleanpath).and_return(home_check["new_resource_home"].last) - expect(provider.updating_home?).to eq(home_check["expected_result"]) + expect(provider.send(:updating_home?)).to eq(home_check["expected_result"]) end end it "should return true if the current home does not exist but a home is specified by the new resource" do @@ -396,7 +396,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option @current_resource.home nil @new_resource.home "/home/kitten" - expect(provider.updating_home?).to eq(true) + expect(provider.send(:updating_home?)).to eq(true) end end end diff --git a/spec/unit/provider/user/solaris_spec.rb b/spec/unit/provider/user/solaris_spec.rb index 1935336308..ecc85677e7 100644 --- a/spec/unit/provider/user/solaris_spec.rb +++ b/spec/unit/provider/user/solaris_spec.rb @@ -2,7 +2,7 @@ # Author:: Adam Jacob (<adam@chef.io>) # Author:: Daniel DeLeo (<dan@chef.io>) # Author:: Dave Eddy (<dave@daveeddy.com>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2018, Chef Software Inc. # Copyright:: Copyright 2015-2016, Dave Eddy # # License:: Apache License, Version 2.0 @@ -68,7 +68,7 @@ describe Chef::Provider::User::Solaris do password_file = Tempfile.new("shadow") password_file.puts "adam:existingpassword:15441::::::" password_file.close - provider.password_file = password_file.path + stub_const("Chef::Provider::User::Solaris::PASSWORD_FILE", password_file.path) allow(provider).to receive(:shell_out!).and_return(true) # may not be able to write to /etc for tests... temp_file = Tempfile.new("shadow") |