summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2018-05-21 09:21:51 -0700
committerGitHub <noreply@github.com>2018-05-21 09:21:51 -0700
commitffbfdc336fbb18e08eed958fcb300e69164b01e2 (patch)
tree16be148618e6f70668ed9c8b65f8e1279868b0da
parentd2cd93486f8183a96d83024d8c2e9d8b0b5088b6 (diff)
parentb3a1cc9165904e6be09b8a62065e9289a905bfb4 (diff)
downloadchef-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.rb19
-rw-r--r--lib/chef/provider/user/aix.rb48
-rw-r--r--lib/chef/provider/user/dscl.rb10
-rw-r--r--lib/chef/provider/user/linux.rb12
-rw-r--r--lib/chef/provider/user/solaris.rb83
-rw-r--r--lib/chef/provider/user/useradd.rb5
-rw-r--r--lib/chef/providers.rb3
-rw-r--r--spec/functional/resource/user/useradd_spec.rb709
-rw-r--r--spec/support/shared/unit/provider/useradd_based_user_provider.rb6
-rw-r--r--spec/unit/provider/user/solaris_spec.rb4
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")