summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Hemminger <hemminger@hotmail.com>2022-05-03 14:08:08 -0500
committerGitHub <noreply@github.com>2022-05-03 12:08:08 -0700
commitd4c3b8ad111ec7ff6b32ebccf9edd1b6a2f9e8e2 (patch)
tree58e5d97450a0c1277bd12feb6feea2e89c75cbf6
parent3f2e1ca604c2ab1079589a763058dfeb04986a94 (diff)
downloadchef-d4c3b8ad111ec7ff6b32ebccf9edd1b6a2f9e8e2.tar.gz
add expire and inactive options to linux user resource (#11659)
* add expire and inactive options to linux user resource * fix spell check * add tests Signed-off-by: Corey Hemminger <hemminger@hotmail.com>
-rw-r--r--cspell.json1
-rw-r--r--lib/chef/provider/user.rb36
-rw-r--r--lib/chef/provider/user/linux.rb24
-rw-r--r--lib/chef/resource/user.rb10
-rw-r--r--spec/unit/provider/user/linux_spec.rb50
-rw-r--r--spec/unit/resource/user/linux_user_spec.rb42
6 files changed, 154 insertions, 9 deletions
diff --git a/cspell.json b/cspell.json
index 2d09fea171..7a40dd82ba 100644
--- a/cspell.json
+++ b/cspell.json
@@ -528,6 +528,7 @@
"imageinfo",
"Immutablize",
"immutablize",
+ "inact",
"includedir",
"includepkgs",
"includer",
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index 61de2127bb..2abd7f5f3c 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -66,15 +66,13 @@ class Chef
end
current_resource.comment(user_info.gecos)
- if new_resource.password && current_resource.password == "x"
- begin
- require "shadow"
- rescue LoadError
- @shadow_lib_ok = false
- else
- shadow_info = Shadow::Passwd.getspnam(new_resource.username)
- current_resource.password(shadow_info.sp_pwdp)
- end
+ begin
+ require "shadow"
+ rescue LoadError
+ @shadow_lib_ok = false
+ else
+ @shadow_info = Shadow::Passwd.getspnam(new_resource.username)
+ current_resource.password(@shadow_info.sp_pwdp) if new_resource.password && current_resource.password == "x"
end
convert_group_name if new_resource.gid
@@ -83,6 +81,20 @@ class Chef
current_resource
end
+ def load_shadow_options
+ unless @shadow_info.nil?
+ current_resource.inactive(@shadow_info.sp_inact&.to_i)
+ # sp_expire gives time since epoch in days till expiration. Need to convert that
+ # to time in seconds since epoch and output date format for comparison
+ expire_date = if @shadow_info.sp_expire.nil?
+ @shadow_info.sp_expire
+ else
+ Time.at(@shadow_info.sp_expire * 60 * 60 * 24).strftime("%Y-%m-%d")
+ end
+ current_resource.expire_date(expire_date)
+ end
+ end
+
def define_resource_requirements
requirements.assert(:create, :modify, :manage, :lock, :unlock) do |a|
a.assertion { @group_name_resolved }
@@ -95,6 +107,12 @@ class Chef
a.whyrun "ruby-shadow is not installed. Attempts to set user password will cause failure. Assuming that this gem will have been previously installed." \
"Note that user update converge may report false-positive on the basis of mismatched password. "
end
+ requirements.assert(:all_actions) do |a|
+ # either neither linux-only value is set, or we need to be on Linux.
+ a.assertion { (!new_resource.expire_date && !new_resource.inactive) || linux? }
+ a.failure_message Chef::Exceptions::User, "Properties expire_date and inactive are not supported by this OS or have not been implemented for this OS yet."
+ a.whyrun "Properties expire_date and inactive are ignored as they are not supported by this OS or have not been implemented yet for this OS"
+ end
requirements.assert(:modify, :lock, :unlock) do |a|
a.assertion { @user_exists }
a.failure_message(Chef::Exceptions::User, "Cannot modify user #{new_resource.username} - does not exist!")
diff --git a/lib/chef/provider/user/linux.rb b/lib/chef/provider/user/linux.rb
index 40b5985cb1..ab411d769a 100644
--- a/lib/chef/provider/user/linux.rb
+++ b/lib/chef/provider/user/linux.rb
@@ -23,6 +23,22 @@ class Chef
provides :linux_user
provides :user, os: "linux"
+ def load_current_resource
+ super
+ load_shadow_options
+ end
+
+ def compare_user
+ super
+ %i{expire_date inactive}.each do |user_attrib|
+ new_val = new_resource.send(user_attrib)
+ cur_val = current_resource.send(user_attrib)
+ if !new_val.nil? && new_val.to_s != cur_val.to_s
+ @change_desc << "change #{user_attrib} from #{cur_val} to #{new_val}"
+ end
+ end
+ end
+
def create_user
shell_out!("useradd", universal_options, useradd_options, new_resource.username)
end
@@ -52,7 +68,9 @@ class Chef
def universal_options
opts = []
opts << "-c" << new_resource.comment if should_set?(:comment)
+ opts << "-e" << new_resource.expire_date if prop_is_set?(:expire_date)
opts << "-g" << new_resource.gid if should_set?(:gid)
+ opts << "-f" << new_resource.inactive if prop_is_set?(:inactive)
opts << "-p" << new_resource.password if should_set?(:password)
opts << "-s" << new_resource.shell if should_set?(:shell)
opts << "-u" << new_resource.uid if should_set?(:uid)
@@ -116,6 +134,12 @@ class Chef
# FIXME: should probably go on the current_resource
@locked
end
+
+ def prop_is_set?(prop)
+ v = new_resource.send(prop.to_sym)
+
+ !v.nil? && v != ""
+ end
end
end
end
diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb
index 992935a6b6..0e84a6b645 100644
--- a/lib/chef/resource/user.rb
+++ b/lib/chef/resource/user.rb
@@ -72,6 +72,16 @@ class Chef
description: "The numeric group identifier."
alias_method :group, :gid
+
+ property :expire_date, [ String, NilClass ],
+ description: "(Linux) The date on which the user account will be disabled. The date is specified in the format YYYY-MM-DD.",
+ introduced: "18.0",
+ desired_state: false
+
+ property :inactive, [ String, Integer, NilClass ],
+ description: "(Linux) The number of days after a password expires until the account is permanently disabled. A value of 0 disables the account as soon as the password has expired, and a value of -1 disables the feature.",
+ introduced: "18.0",
+ desired_state: false
end
end
end
diff --git a/spec/unit/provider/user/linux_spec.rb b/spec/unit/provider/user/linux_spec.rb
index 1f22d963da..1fc8e3c6a1 100644
--- a/spec/unit/provider/user/linux_spec.rb
+++ b/spec/unit/provider/user/linux_spec.rb
@@ -70,4 +70,54 @@ describe Chef::Provider::User::Linux do
expect( provider.useradd_options ).to eql(["-m"])
end
end
+
+ describe "expire_date behavior" do
+ before(:each) do
+ @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ end
+
+ it "defaults expire_date to nil" do
+ expect( @new_resource.expire_date ).to be nil
+ end
+
+ it "by default expire_date is nil and we use ''" do
+ expect( provider.universal_options ).to eql([])
+ end
+
+ it "setting expire_date to nil includes ''" do
+ @new_resource.expire_date nil
+ expect( provider.universal_options ).to eql([])
+ end
+
+ it "setting expire_date to 1982-04-16 includes -e" do
+ @new_resource.expire_date "1982-04-16"
+ expect( provider.universal_options ).to eql(["-e", "1982-04-16"])
+ end
+ end
+
+ describe "inactive behavior" do
+ before(:each) do
+ @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ end
+
+ it "defaults inactive to nil" do
+ expect( @new_resource.inactive ).to be nil
+ end
+
+ it "by default inactive is nil and we use ''" do
+ expect( provider.universal_options ).to eql([])
+ end
+
+ it "setting inactive to nil includes ''" do
+ @new_resource.inactive nil
+ expect( provider.universal_options ).to eql([])
+ end
+
+ it "setting inactive to 90 includes -f" do
+ @new_resource.inactive 90
+ expect( provider.universal_options ).to eql(["-f", 90])
+ end
+ end
end
diff --git a/spec/unit/resource/user/linux_user_spec.rb b/spec/unit/resource/user/linux_user_spec.rb
new file mode 100644
index 0000000000..c9bd71b11d
--- /dev/null
+++ b/spec/unit/resource/user/linux_user_spec.rb
@@ -0,0 +1,42 @@
+require "spec_helper"
+
+describe Chef::Resource::User, "initialize" do
+ let(:resource) { Chef::Resource::User::LinuxUser.new("notarealuser") }
+
+ describe "inactive attribute" do
+ it "allows a string" do
+ resource.inactive "100"
+ expect(resource.inactive).to eql("100")
+ end
+
+ it "allows an integer" do
+ resource.inactive 100
+ expect(resource.inactive).to eql(100)
+ end
+
+ it "does not allow a hash" do
+ expect { resource.inactive({ woot: "i found it" }) }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "expire_date attribute" do
+ it "allows a string" do
+ resource.expire_date "100"
+ expect(resource.expire_date).to eql("100")
+ end
+
+ it "does not allow an integer" do
+ expect { resource.expire_date(90) }.to raise_error(ArgumentError)
+ end
+
+ it "does not allow a hash" do
+ expect { resource.expire_date({ woot: "i found it" }) }.to raise_error(ArgumentError)
+ end
+ end
+
+ %w{inactive expire_date}.each do |prop|
+ it "sets #{prop} to nil" do
+ expect(resource.send(prop)).to eql(nil)
+ end
+ end
+end