summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/provider/nameservice.rb1
-rw-r--r--lib/puppet/provider/nameservice/objectadd.rb3
-rw-r--r--lib/puppet/provider/user/hpux.rb1
-rw-r--r--lib/puppet/provider/user/user_role_add.rb30
-rw-r--r--lib/puppet/provider/user/useradd.rb35
-rwxr-xr-xlib/puppet/type/user.rb41
-rw-r--r--spec/unit/provider/user/user_role_add_spec.rb19
-rwxr-xr-xspec/unit/provider/user/useradd_spec.rb10
-rwxr-xr-xspec/unit/type/user_spec.rb6
9 files changed, 131 insertions, 15 deletions
diff --git a/lib/puppet/provider/nameservice.rb b/lib/puppet/provider/nameservice.rb
index 7339b646e..9830fab54 100644
--- a/lib/puppet/provider/nameservice.rb
+++ b/lib/puppet/provider/nameservice.rb
@@ -165,6 +165,7 @@ class Puppet::Provider::NameService < Puppet::Provider
begin
execute(self.addcmd)
+ execute(self.passcmd) if self.feature? :manages_password_age
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}"
end
diff --git a/lib/puppet/provider/nameservice/objectadd.rb b/lib/puppet/provider/nameservice/objectadd.rb
index 80c142982..dbb9f306f 100644
--- a/lib/puppet/provider/nameservice/objectadd.rb
+++ b/lib/puppet/provider/nameservice/objectadd.rb
@@ -13,7 +13,8 @@ class ObjectAdd < Puppet::Provider::NameService
end
def modifycmd(param, value)
- cmd = [command(:modify), flag(param), value]
+ cmd = [command(param.to_s =~ /password_.+_age/ ? :password : :modify)]
+ cmd << flag(param) << value
if @resource.allowdupe? && ((param == :uid) || (param == :gid and self.class.name == :groupadd))
cmd << "-o"
end
diff --git a/lib/puppet/provider/user/hpux.rb b/lib/puppet/provider/user/hpux.rb
index 50506c4cd..983970935 100644
--- a/lib/puppet/provider/user/hpux.rb
+++ b/lib/puppet/provider/user/hpux.rb
@@ -26,5 +26,4 @@ Puppet::Type.type(:user).provide :hpuxuseradd, :parent => :useradd do
def modifycmd(param,value)
super.insert(1,"-F")
end
-
end
diff --git a/lib/puppet/provider/user/user_role_add.rb b/lib/puppet/provider/user/user_role_add.rb
index c13125925..7e7ad78e5 100644
--- a/lib/puppet/provider/user/user_role_add.rb
+++ b/lib/puppet/provider/user/user_role_add.rb
@@ -6,13 +6,15 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source =>
defaultfor :operatingsystem => :solaris
- commands :add => "useradd", :delete => "userdel", :modify => "usermod", :role_add => "roleadd", :role_delete => "roledel", :role_modify => "rolemod"
+ commands :add => "useradd", :delete => "userdel", :modify => "usermod", :password => "chage", :role_add => "roleadd", :role_delete => "roledel", :role_modify => "rolemod"
options :home, :flag => "-d", :method => :dir
options :comment, :method => :gecos
options :groups, :flag => "-G"
options :roles, :flag => "-R"
options :auths, :flag => "-A"
options :profiles, :flag => "-P"
+ options :password_min_age, :flag => "-m"
+ options :password_max_age, :flag => "-M"
verify :gid, "GID must be an integer" do |value|
value.is_a? Integer
@@ -22,14 +24,14 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source =>
value !~ /\s/
end
- has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac, :manages_passwords
+ has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac, :manages_passwords, :manages_password_age
#must override this to hand the keyvalue pairs
def add_properties
cmd = []
Puppet::Type.type(:user).validproperties.each do |property|
#skip the password because we can't create it with the solaris useradd
- next if [:ensure, :password].include?(property)
+ next if [:ensure, :password, :password_min_age, :password_max_age].include?(property)
# 1680 Now you can set the hashed passwords on solaris:lib/puppet/provider/user/user_role_add.rb
# the value needs to be quoted, mostly because -c might
# have spaces in it
@@ -79,6 +81,7 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source =>
run(transition("normal"), "transition role to")
else
run(addcmd, "create")
+ run(passcmd, "change password policy for")
end
# added to handle case when password is specified
self.password = @resource[:password] if @resource[:password]
@@ -140,14 +143,23 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source =>
run([command(:modify)] + build_keys_cmd(keys_hash) << @resource[:name], "modify attribute key pairs")
end
- #Read in /etc/shadow, find the line for this user (skipping comments, because who knows) and return the hashed pw (the second entry)
+ #Read in /etc/shadow, find the line for this user (skipping comments, because who knows) and return it
#No abstraction, all esoteric knowledge of file formats, yay
+ def shadow_entry
+ return @shadow_entry if defined? @shadow_entry
+ @shadow_entry = File.readlines("/etc/shadow").reject { |r| r =~ /^[^\w]/ }.collect { |l| l.chomp.split(':') }.find { |user, _| user == @resource[:name] }
+ end
+
def password
- #got perl?
- if ary = File.readlines("/etc/shadow").reject { |r| r =~ /^[^\w]/}.collect { |l| l.split(':')[0..1] }.find { |user, passwd| user == @resource[:name] }
- pass = ary[1]
- end
- pass
+ shadow_entry[1] if shadow_entry
+ end
+
+ def min_age
+ shadow_entry ? shadow_entry[3] : :absent
+ end
+
+ def max_age
+ shadow_entry ? shadow_entry[4] : :absent
end
#Read in /etc/shadow, find the line for our used and rewrite it with the new pw
diff --git a/lib/puppet/provider/user/useradd.rb b/lib/puppet/provider/user/useradd.rb
index 7e68e4b32..9a62db464 100644
--- a/lib/puppet/provider/user/useradd.rb
+++ b/lib/puppet/provider/user/useradd.rb
@@ -3,11 +3,13 @@ require 'puppet/provider/nameservice/objectadd'
Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameService::ObjectAdd do
desc "User management via `useradd` and its ilk. Note that you will need to install the `Shadow Password` Ruby library often known as ruby-libshadow to manage user passwords."
- commands :add => "useradd", :delete => "userdel", :modify => "usermod"
+ commands :add => "useradd", :delete => "userdel", :modify => "usermod", :password => "chage"
options :home, :flag => "-d", :method => :dir
options :comment, :method => :gecos
options :groups, :flag => "-G"
+ options :password_min_age, :flag => "-m"
+ options :password_max_age, :flag => "-M"
verify :gid, "GID must be an integer" do |value|
value.is_a? Integer
@@ -19,7 +21,7 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
has_features :manages_homedir, :allows_duplicates, :manages_expiry
- has_feature :manages_passwords if Puppet.features.libshadow?
+ has_features :manages_passwords, :manages_password_age if Puppet.features.libshadow?
def check_allow_dup
@resource.allowdupe? ? ["-o"] : []
@@ -48,6 +50,7 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
cmd = []
Puppet::Type.type(:user).validproperties.each do |property|
next if property == :ensure
+ next if property.to_s =~ /password_.+_age/
# the value needs to be quoted, mostly because -c might
# have spaces in it
if value = @resource.should(property) and value != ""
@@ -66,6 +69,34 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
cmd << @resource[:name]
end
+ def passcmd
+ cmd = [command(:password)]
+ [:password_min_age, :password_max_age].each do |property|
+ if value = @resource.should(property)
+ cmd << flag(property) << value
+ end
+ end
+ cmd << @resource[:name]
+ end
+
+ def min_age
+ if Puppet.features.libshadow?
+ if ent = Shadow::Passwd.getspnam(@resource.name)
+ return ent.sp_min
+ end
+ end
+ :absent
+ end
+
+ def max_age
+ if Puppet.features.libshadow?
+ if ent = Shadow::Passwd.getspnam(@resource.name)
+ return ent.sp_max
+ end
+ end
+ :absent
+ end
+
# Retrieve the password using the Shadow Password library
def password
if Puppet.features.libshadow?
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index c524a7da9..c8110bb69 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -24,6 +24,10 @@ module Puppet
"The provider can modify user passwords, by accepting a password
hash."
+ feature :manages_password_age,
+ "The provider can set age requirements and restrictions for
+ passwords."
+
feature :manages_solaris_rbac,
"The provider can manage roles and normal users"
@@ -160,6 +164,43 @@ module Puppet
end
end
+ newproperty(:password_min_age, :required_features => :manages_password_age) do
+ desc "The minimum amount of time in days a password must be used before it may be changed"
+
+ munge do |value|
+ case value
+ when String
+ Integer(value)
+ else
+ value
+ end
+ end
+
+ validate do |value|
+ if value.to_s !~ /^\d+$/
+ raise ArgumentError, "Password minimum age must be provided as a number"
+ end
+ end
+ end
+
+ newproperty(:password_max_age, :required_features => :manages_password_age) do
+ desc "The maximum amount of time in days a password may be used before it must be changed"
+
+ munge do |value|
+ case value
+ when String
+ Integer(value)
+ else
+ value
+ end
+ end
+
+ validate do |value|
+ if value.to_s !~ /^\d+$/
+ raise ArgumentError, "Password maximum age must be provided as a number"
+ end
+ end
+ end
newproperty(:groups, :parent => Puppet::Property::List) do
desc "The groups of which the user is a member. The primary
diff --git a/spec/unit/provider/user/user_role_add_spec.rb b/spec/unit/provider/user/user_role_add_spec.rb
index 211f4260a..b3244f19d 100644
--- a/spec/unit/provider/user/user_role_add_spec.rb
+++ b/spec/unit/provider/user/user_role_add_spec.rb
@@ -56,7 +56,7 @@ describe provider_class do
it "should use the add command when the user is not a role" do
@provider.stubs(:is_role?).returns(false)
@provider.expects(:addcmd).returns("useradd")
- @provider.expects(:run)
+ @provider.expects(:run).at_least_once
@provider.create
end
@@ -66,6 +66,15 @@ describe provider_class do
@provider.expects(:run)
@provider.create
end
+
+ it "should set password age rules" do
+ @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :user_role_add
+ @provider = provider_class.new(@resource)
+ @provider.stubs(:user_attributes)
+ @provider.stubs(:execute)
+ @provider.expects(:execute).with { |cmd, *args| args == ["-m", 5, "-M", 10, "myuser"] }
+ @provider.create
+ end
end
describe "when calling destroy" do
@@ -107,6 +116,7 @@ describe provider_class do
before do
@resource.expects(:allowdupe?).returns true
@provider.stubs(:is_role?).returns(false)
+ @provider.stubs(:execute)
@provider.expects(:execute).with { |args| args.include?("-o") }
end
@@ -246,4 +256,11 @@ describe provider_class do
@provider.password=("hashedpassword")
end
end
+
+ describe "#shadow_entry" do
+ it "should return the line for the right user" do
+ File.stubs(:readlines).returns(["someuser:!:10:5:20:7:1::\n", "fakeval:*:20:10:30:7:2::\n", "testuser:*:30:15:40:7:3::\n"])
+ @provider.shadow_entry.should == ["fakeval", "*", "20", "10", "30", "7", "2"]
+ end
+ end
end
diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb
index 9869ba935..26367c584 100755
--- a/spec/unit/provider/user/useradd_spec.rb
+++ b/spec/unit/provider/user/useradd_spec.rb
@@ -15,6 +15,7 @@ describe provider_class do
# #1360
it "should add -o when allowdupe is enabled and the user is being created" do
@resource.expects(:allowdupe?).returns true
+ @provider.stubs(:execute)
@provider.expects(:execute).with { |args| args.include?("-o") }
@provider.create
end
@@ -26,6 +27,15 @@ describe provider_class do
@provider.uid = 150
end
+ it "should set password age rules" do
+ provider_class.has_feature :manages_password_age
+ @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :useradd
+ @provider = provider_class.new(@resource)
+ @provider.stubs(:execute)
+ @provider.expects(:execute).with { |cmd, *args| args == ["-m", 5, "-M", 10, "myuser"] }
+ @provider.create
+ end
+
describe "when checking to add allow dup" do
it "should check allow dup" do
@resource.expects(:allowdupe?)
diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb
index f978b309c..ccea9ee4c 100755
--- a/spec/unit/type/user_spec.rb
+++ b/spec/unit/type/user_spec.rb
@@ -39,6 +39,10 @@ describe user do
user.provider_feature(:manages_expiry).should_not be_nil
end
+ it "should have a manages_password_age feature" do
+ user.provider_feature(:manages_password_age).should_not be_nil
+ end
+
describe "instances" do
it "should have a valid provider" do
user.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider)
@@ -51,7 +55,7 @@ describe user do
end
end
- properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :groups, :roles, :auths, :profiles, :project, :keys, :expiry]
+ properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :password_min_age, :password_max_age, :groups, :roles, :auths, :profiles, :project, :keys, :expiry]
properties.each do |property|
it "should have a #{property} property" do