diff --git a/lib/puppet/provider/group/pw.rb b/lib/puppet/provider/group/pw.rb index a054d1ff12a..b6c74f76695 100644 --- a/lib/puppet/provider/group/pw.rb +++ b/lib/puppet/provider/group/pw.rb @@ -1,34 +1,48 @@ require 'puppet/provider/nameservice/pw' Puppet::Type.type(:group).provide :pw, :parent => Puppet::Provider::NameService::PW do - desc "Group management via `pw`. + desc "Group management via `pw` on FreeBSD." - Only works on FreeBSD. + commands :pw => "pw" + has_features :manages_members - " - - commands :pw => "/usr/sbin/pw" defaultfor :operatingsystem => :freebsd + options :members, :flag => "-M", :method => :mem + verify :gid, "GID must be an integer" do |value| value.is_a? Integer end def addcmd cmd = [command(:pw), "groupadd", @resource[:name]] + if gid = @resource.should(:gid) unless gid == :absent cmd << flag(:gid) << gid end end - # Apparently, contrary to the man page, groupadd does - # not accept -o. - #if @parent[:allowdupe] == :true - # cmd << "-o" - #end + if members = @resource.should(:members) + unless members == :absent + if members.is_a?(Array) + members = members.join(",") + end + cmd << "-M" << members + end + end + + cmd << "-o" if @resource.allowdupe? cmd end + + def modifycmd(param, value) + # members may be an array, need a comma separated list + if param == :members and value.is_a?(Array) + value = value.join(",") + end + super(param, value) + end end diff --git a/lib/puppet/provider/user/pw.rb b/lib/puppet/provider/user/pw.rb index a5988cad15c..8423979719e 100644 --- a/lib/puppet/provider/user/pw.rb +++ b/lib/puppet/provider/user/pw.rb @@ -1,16 +1,18 @@ require 'puppet/provider/nameservice/pw' +require 'open3' Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::PW do desc "User management via `pw` on FreeBSD." commands :pw => "pw" - has_features :manages_homedir, :allows_duplicates + has_features :manages_homedir, :allows_duplicates, :manages_passwords, :manages_expiry defaultfor :operatingsystem => :freebsd options :home, :flag => "-d", :method => :dir options :comment, :method => :gecos options :groups, :flag => "-G" + options :expiry, :method => :expire verify :gid, "GID must be an integer" do |value| value.is_a? Integer @@ -23,10 +25,14 @@ def addcmd cmd = [command(:pw), "useradd", @resource[:name]] @resource.class.validproperties.each do |property| - next if property == :ensure + next if property == :ensure or property == :password # the value needs to be quoted, mostly because -c might # have spaces in it if value = @resource.should(property) and value != "" + if property == :expiry + # FreeBSD uses DD-MM-YYYY rather than YYYY-MM-DD + value = value.split("-").reverse.join("-") + end cmd << flag(property) << value end end @@ -37,5 +43,53 @@ def addcmd cmd end + + def modifycmd(param, value) + if param == :expiry + # FreeBSD uses DD-MM-YYYY rather than YYYY-MM-DD + value = value.split("-").reverse.join("-") + end + cmd = super(param, value) + cmd << "-m" if @resource.managehome? + cmd + end + + def create + super + + # Set the password after create if given + self.password = @resource[:password] if @resource[:password] + end + + # use pw to update password hash + def password=(cryptopw) + Puppet.debug "change password for user '#{@resource[:name]}' method called with hash '#{cryptopw}'" + stdin, stdout, stderr = Open3.popen3("pw user mod #{@resource[:name]} -H 0") + stdin.puts(cryptopw) + stdin.close + Puppet.debug "finished password for user '#{@resource[:name]}' method called with hash '#{cryptopw}'" + end + + # get password from /etc/master.passwd + def password + Puppet.debug "checking password for user '#{@resource[:name]}' method called" + current_passline = `getent passwd #{@resource[:name]}` + current_password = current_passline.chomp.split(':')[1] if current_passline + Puppet.debug "finished password for user '#{@resource[:name]}' method called : '#{current_password}'" + current_password + end + + # Get expiry from system and convert to Puppet-style date + def expiry + expiry = self.get(:expiry) + expiry = :absent if expiry == 0 + + if expiry != :absent + t = Time.at(expiry) + expiry = "%4d-%02d-%02d" % [t.year, t.month, t.mday] + end + + expiry + end end diff --git a/spec/unit/provider/group/pw_spec.rb b/spec/unit/provider/group/pw_spec.rb new file mode 100755 index 00000000000..3dfc5ec7108 --- /dev/null +++ b/spec/unit/provider/group/pw_spec.rb @@ -0,0 +1,81 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +provider_class = Puppet::Type.type(:group).provider(:pw) + +describe provider_class do + let :resource do + Puppet::Type.type(:group).new(:name => "testgroup", :provider => :pw) + end + + let :provider do + resource.provider + end + + describe "when creating groups" do + let :provider do + prov = resource.provider + prov.expects(:exists?).returns nil + prov + end + + it "should run pw with no additional flags when no properties are given" do + provider.addcmd.must == [provider_class.command(:pw), "groupadd", "testgroup"] + provider.expects(:execute).with([provider_class.command(:pw), "groupadd", "testgroup"]) + provider.create + end + + it "should use -o when allowdupe is enabled" do + resource[:allowdupe] = true + provider.expects(:execute).with(includes("-o")) + provider.create + end + + it "should use -g with the correct argument when the gid property is set" do + resource[:gid] = 12345 + provider.expects(:execute).with(all_of(includes("-g"), includes(12345))) + provider.create + end + + it "should use -M with the correct argument when the members property is set" do + resource[:members] = "user1" + provider.expects(:execute).with(all_of(includes("-M"), includes("user1"))) + provider.create + end + + it "should use -M with all the given users when the members property is set to an array" do + resource[:members] = ["user1", "user2"] + provider.expects(:execute).with(all_of(includes("-M"), includes("user1,user2"))) + provider.create + end + end + + describe "when deleting groups" do + it "should run pw with no additional flags" do + provider.expects(:exists?).returns true + provider.deletecmd.must == [provider_class.command(:pw), "groupdel", "testgroup"] + provider.expects(:execute).with([provider_class.command(:pw), "groupdel", "testgroup"]) + provider.delete + end + end + + describe "when modifying groups" do + it "should run pw with the correct arguments" do + provider.modifycmd("gid", 12345).must == [provider_class.command(:pw), "groupmod", "testgroup", "-g", 12345] + provider.expects(:execute).with([provider_class.command(:pw), "groupmod", "testgroup", "-g", 12345]) + provider.gid = 12345 + end + + it "should use -M with the correct argument when the members property is changed" do + resource[:members] = "user1" + provider.expects(:execute).with(all_of(includes("-M"), includes("user2"))) + provider.members = "user2" + end + + it "should use -M with all the given users when the members property is changed with an array" do + resource[:members] = ["user1", "user2"] + provider.expects(:execute).with(all_of(includes("-M"), includes("user3,user4"))) + provider.members = ["user3", "user4"] + end + end +end diff --git a/spec/unit/provider/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb new file mode 100755 index 00000000000..495fef35b38 --- /dev/null +++ b/spec/unit/provider/user/pw_spec.rb @@ -0,0 +1,183 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:pw) + +describe provider_class do + let :resource do + Puppet::Type.type(:user).new(:name => "testuser", :provider => :pw) + end + + describe "when creating users" do + let :provider do + prov = resource.provider + prov.expects(:exists?).returns nil + prov + end + + it "should run pw with no additional flags when no properties are given" do + provider.addcmd.must == [provider_class.command(:pw), "useradd", "testuser"] + provider.expects(:execute).with([provider_class.command(:pw), "useradd", "testuser"]) + provider.create + end + + it "should use -o when allowdupe is enabled" do + resource[:allowdupe] = true + provider.expects(:execute).with(includes("-o")) + provider.create + end + + it "should use -c with the correct argument when the comment property is set" do + resource[:comment] = "Testuser Name" + provider.expects(:execute).with(all_of(includes("-c"), includes("Testuser Name"))) + provider.create + end + + it "should use -e with the correct argument when the expiry property is set" do + resource[:expiry] = "2010-02-19" + provider.expects(:execute).with(all_of(includes("-e"), includes("19-02-2010"))) + provider.create + end + + it "should use -g with the correct argument when the gid property is set" do + resource[:gid] = 12345 + provider.expects(:execute).with(all_of(includes("-g"), includes(12345))) + provider.create + end + + it "should use -G with the correct argument when the groups property is set" do + resource[:groups] = "group1" + provider.expects(:execute).with(all_of(includes("-G"), includes("group1"))) + provider.create + end + + it "should use -G with all the given groups when the groups property is set to an array" do + resource[:groups] = ["group1", "group2"] + provider.expects(:execute).with(all_of(includes("-G"), includes("group1,group2"))) + provider.create + end + + it "should use -d with the correct argument when the home property is set" do + resource[:home] = "/home/testuser" + provider.expects(:execute).with(all_of(includes("-d"), includes("/home/testuser"))) + provider.create + end + + it "should use -m when the managehome property is enabled" do + resource[:managehome] = true + provider.expects(:execute).with(includes("-m")) + provider.create + end + + it "should call the password set function with the correct argument when the password property is set" do + resource[:password] = "*" + provider.expects(:execute) + provider.expects(:password=).with("*") + provider.create + end + + it "should use -s with the correct argument when the shell property is set" do + resource[:shell] = "/bin/sh" + provider.expects(:execute).with(all_of(includes("-s"), includes("/bin/sh"))) + provider.create + end + + it "should use -u with the correct argument when the uid property is set" do + resource[:uid] = 12345 + provider.expects(:execute).with(all_of(includes("-u"), includes(12345))) + provider.create + end + + # (#7500) -p should not be used to set a password (it means something else) + it "should not use -p when a password is given" do + resource[:password] = "*" + provider.addcmd.should_not include("-p") + provider.expects(:password=) + provider.expects(:execute).with(Not(includes("-p"))) + provider.create + end + end + + describe "when deleting users" do + it "should run pw with no additional flags" do + provider = resource.provider + provider.expects(:exists?).returns true + provider.deletecmd.must == [provider_class.command(:pw), "userdel", "testuser"] + provider.expects(:execute).with([provider_class.command(:pw), "userdel", "testuser"]) + provider.delete + end + end + + describe "when modifying users" do + let :provider do + resource.provider + end + + it "should run pw with the correct arguments" do + provider.modifycmd("uid", 12345).must == [provider_class.command(:pw), "usermod", "testuser", "-u", 12345] + provider.expects(:execute).with([provider_class.command(:pw), "usermod", "testuser", "-u", 12345]) + provider.uid = 12345 + end + + it "should use -c with the correct argument when the comment property is changed" do + resource[:comment] = "Testuser Name" + provider.expects(:execute).with(all_of(includes("-c"), includes("Testuser New Name"))) + provider.comment = "Testuser New Name" + end + + it "should use -e with the correct argument when the expiry property is changed" do + resource[:expiry] = "2010-02-19" + provider.expects(:execute).with(all_of(includes("-e"), includes("19-02-2011"))) + provider.expiry = "2011-02-19" + end + + it "should use -g with the correct argument when the gid property is changed" do + resource[:gid] = 12345 + provider.expects(:execute).with(all_of(includes("-g"), includes(54321))) + provider.gid = 54321 + end + + it "should use -G with the correct argument when the groups property is changed" do + resource[:groups] = "group1" + provider.expects(:execute).with(all_of(includes("-G"), includes("group2"))) + provider.groups = "group2" + end + + it "should use -G with all the given groups when the groups property is changed with an array" do + resource[:groups] = ["group1", "group2"] + provider.expects(:execute).with(all_of(includes("-G"), includes("group3,group4"))) + provider.groups = "group3,group4" + end + + it "should use -d with the correct argument when the home property is changed" do + resource[:home] = "/home/testuser" + provider.expects(:execute).with(all_of(includes("-d"), includes("/newhome/testuser"))) + provider.home = "/newhome/testuser" + end + + it "should use -m and -d with the correct argument when the home property is changed and managehome is enabled" do + resource[:home] = "/home/testuser" + resource[:managehome] = true + provider.expects(:execute).with(all_of(includes("-d"), includes("/newhome/testuser"), includes("-m"))) + provider.home = "/newhome/testuser" + end + + it "should call the password set function with the correct argument when the password property is changed" do + resource[:password] = "*" + provider.expects(:password=).with("!") + provider.password = "!" + end + + it "should use -s with the correct argument when the shell property is changed" do + resource[:shell] = "/bin/sh" + provider.expects(:execute).with(all_of(includes("-s"), includes("/bin/tcsh"))) + provider.shell = "/bin/tcsh" + end + + it "should use -u with the correct argument when the uid property is changed" do + resource[:uid] = 12345 + provider.expects(:execute).with(all_of(includes("-u"), includes(54321))) + provider.uid = 54321 + end + end +end