Permalink
Browse files

(#11318) Add password management on FreeBSD

This adds the manages_passwords feature to the pw user provider. It is based
on the patch by Andrew Hust that was integrated into FreeBSD puppet port. It
adds tests covering the create, delete and modify processes of the provider.

This integrates a fix for #7500 that was introduced by the original patch.
The existing code takes the first character of each property and uses it as a
flag. However, with pw, the -p flag is for setting the password expiration.
The result is that the password isn't set at create time and that the password
is set to expire. The next run of puppet correctly sets the password but the
expiry is still set. The new code avoids using -p for passwords, and also sets
the password correctly when an account is created.

Reviewed-by: Patrick Carlisle <patrick@puppetlabs.com>
  • Loading branch information...
tdb authored and pcarlisle committed Nov 18, 2011
1 parent 9348fe2 commit 884381f7d6bd7ed3fb3ad9028932f45861aff99a
Showing with 192 additions and 2 deletions.
  1. +28 −2 lib/puppet/provider/user/pw.rb
  2. +164 −0 spec/unit/provider/user/pw_spec.rb
@@ -1,10 +1,11 @@
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
defaultfor :operatingsystem => :freebsd
@@ -23,7 +24,7 @@
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 != ""
@@ -37,5 +38,30 @@ def addcmd
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
end
@@ -0,0 +1,164 @@
+#!/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 -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 -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 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

0 comments on commit 884381f

Please sign in to comment.