summaryrefslogtreecommitdiff
path: root/lib/chef/provider/user/dscl.rb
diff options
context:
space:
mode:
authorNick McSpadden <nmcspadden@gmail.com>2018-05-14 23:33:45 -0700
committerNick McSpadden <nmcspadden@gmail.com>2018-05-14 23:33:45 -0700
commitc935ce85f591e53f2f200e55cb252185e1801387 (patch)
treedfeee6151c50d805138a3d10761a531c2ffbf984 /lib/chef/provider/user/dscl.rb
parentc48fd22a51deb74a3d4bc3abca308043689b494a (diff)
parentc0609e449135fae43d436136a4f0fd3889a9b8f1 (diff)
downloadchef-c935ce85f591e53f2f200e55cb252185e1801387.tar.gz
Merge branch 'master' into mac_uid
Diffstat (limited to 'lib/chef/provider/user/dscl.rb')
-rw-r--r--lib/chef/provider/user/dscl.rb130
1 files changed, 69 insertions, 61 deletions
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index 541160bf93..a1ff30ef1c 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-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,8 +48,14 @@ class Chef
attr_accessor :authentication_authority
attr_accessor :password_shadow_conversion_algorithm
+ provides :dscl_user
provides :user, os: "darwin"
+ # Just-in-case a recipe calls the user dscl provider without specifying
+ # a gid property. Avoids chown issues in move_home when the manage_home
+ # property is in use. #5393
+ STAFF_GROUP_ID = 20
+
def define_resource_requirements
super
@@ -59,12 +65,12 @@ class Chef
end
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/bin/dscl") }
+ a.assertion { ::File.exist?("/usr/bin/dscl") }
a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{new_resource}!")
end
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/bin/plutil") }
+ a.assertion { ::File.exist?("/usr/bin/plutil") }
a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{new_resource}!")
end
@@ -151,7 +157,7 @@ user password using shadow hash.")
convert_group_name if new_resource.gid
else
@user_exists = false
- Chef::Log.debug("#{new_resource} user does not exist")
+ logger.trace("#{new_resource} user does not exist")
end
current_resource
@@ -193,7 +199,7 @@ user password using shadow hash.")
# Create a user using dscl
#
def dscl_create_user
- run_dscl("create /Users/#{new_resource.username}")
+ run_dscl("create", "/Users/#{new_resource.username}")
end
#
@@ -202,7 +208,7 @@ user password using shadow hash.")
#
def dscl_create_comment
comment = new_resource.comment || new_resource.username
- run_dscl("create /Users/#{new_resource.username} RealName '#{comment}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "RealName", comment)
end
#
@@ -218,7 +224,7 @@ user password using shadow hash.")
raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{new_resource.uid} is already in use")
end
- run_dscl("create /Users/#{new_resource.username} UniqueID #{new_resource.uid}")
+ run_dscl("create", "/Users/#{new_resource.username}", "UniqueID", new_resource.uid)
end
#
@@ -229,7 +235,7 @@ user password using shadow hash.")
uid = nil
base_uid = new_resource.system ? 200 : 501
next_uid_guess = base_uid
- users_uids = run_dscl("list /Users uid")
+ users_uids = run_dscl("list", "/Users", "uid")
while next_uid_guess < search_limit + base_uid
if users_uids =~ Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n")
next_uid_guess += 1
@@ -238,7 +244,7 @@ user password using shadow hash.")
break
end
end
- return uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
+ uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
end
#
@@ -246,39 +252,39 @@ user password using shadow hash.")
#
def uid_used?(uid)
return false unless uid
- users_uids = run_dscl("list /Users uid").split("\n")
- uid_map = users_uids.inject({}) do |tmap, tuid|
+ users_uids = run_dscl("list", "/Users", "uid").split("\n")
+ uid_map = users_uids.each_with_object({}) do |tuid, tmap|
x = tuid.split
tmap[x[1]] = x[0]
tmap
end
if uid_map[uid.to_s]
- unless uid_map[uid.to_s] == new_resource.username.to_s
+ unless uid_map[uid.to_s] == new_resource.username
return true
end
end
- return false
+ false
end
#
# Sets the group id for the user using dscl. Fails if a group doesn't
# exist on the system with given group id. If `gid` is not specified, it
- # sets a default Mac user group "staff", with id 20.
+ # sets a default Mac user group "staff", with id 20 using the CONSTANT
#
def dscl_set_gid
if new_resource.gid.nil?
# XXX: mutates the new resource
- new_resource.gid(20)
+ new_resource.gid(STAFF_GROUP_ID)
elsif !new_resource.gid.to_s.match(/^\d+$/)
begin
- possible_gid = run_dscl("read /Groups/#{new_resource.gid} PrimaryGroupID").split(" ").last
- rescue Chef::Exceptions::DsclCommandFailed => e
- raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{new_resource.gid} when creating user #{new_resource.username}")
+ possible_gid = run_dscl("read", "/Groups/#{new_resource.gid}", "PrimaryGroupID").split(" ").last
+ rescue Chef::Exceptions::DsclCommandFailed
+ raise Chef::Exceptions::GroupIDNotFound, "Group not found for #{new_resource.gid} when creating user #{new_resource.username}"
end
# XXX: mutates the new resource
new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
end
- run_dscl("create /Users/#{new_resource.username} PrimaryGroupID '#{new_resource.gid}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "PrimaryGroupID", new_resource.gid)
end
#
@@ -287,13 +293,11 @@ user password using shadow hash.")
#
def dscl_set_home
if new_resource.home.nil? || new_resource.home.empty?
- run_dscl("delete /Users/#{new_resource.username} NFSHomeDirectory")
+ run_dscl("delete", "/Users/#{new_resource.username}", "NFSHomeDirectory")
return
end
- run_dscl("create /Users/#{new_resource.username} NFSHomeDirectory '#{new_resource.home}'")
-
- if new_resource.supports[:manage_home]
+ if new_resource.manage_home
validate_home_dir_specification!
if (current_resource.home == new_resource.home) && !new_home_exists?
@@ -304,6 +308,7 @@ user password using shadow hash.")
move_home
end
end
+ run_dscl("create", "/Users/#{new_resource.username}", "NFSHomeDirectory", new_resource.home)
end
def validate_home_dir_specification!
@@ -313,24 +318,24 @@ user password using shadow hash.")
end
def current_home_exists?
- ::File.exist?("#{current_resource.home}")
+ !!current_resource.home && ::File.exist?(current_resource.home)
end
def new_home_exists?
- ::File.exist?("#{new_resource.home}")
+ ::File.exist?(new_resource.home)
end
def ditto_home
- shell_out! "/usr/sbin/createhomedir -c -u #{new_resource.username}"
+ shell_out_compact!("/usr/sbin/createhomedir -c -u #{new_resource.username}")
end
def move_home
- Chef::Log.debug("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}")
-
+ logger.trace("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}")
+ new_resource.gid(STAFF_GROUP_ID) if new_resource.gid.nil?
src = current_resource.home
FileUtils.mkdir_p(new_resource.home)
files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob_dir(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.", "#{src}/.."]
- ::FileUtils.mv(files, new_resource.home, :force => true)
+ ::FileUtils.mv(files, new_resource.home, force: true)
::FileUtils.rmdir(src)
::FileUtils.chown_R(new_resource.username, new_resource.gid.to_s, new_resource.home)
end
@@ -339,10 +344,10 @@ user password using shadow hash.")
# Sets the shell for the user using dscl.
#
def dscl_set_shell
- if new_resource.shell || ::File.exists?("#{new_resource.shell}")
- run_dscl("create /Users/#{new_resource.username} UserShell '#{new_resource.shell}'")
+ if new_resource.shell
+ run_dscl("create", "/Users/#{new_resource.username}", "UserShell", new_resource.shell)
else
- run_dscl("create /Users/#{new_resource.username} UserShell '/usr/bin/false'")
+ run_dscl("create", "/Users/#{new_resource.username}", "UserShell", "/usr/bin/false")
end
end
@@ -359,9 +364,8 @@ user password using shadow hash.")
# Shadow info is saved as binary plist. Convert the info to binary plist.
shadow_info_binary = StringIO.new
- command = Mixlib::ShellOut.new("plutil -convert binary1 -o - -",
- :input => shadow_info.to_plist, :live_stream => shadow_info_binary)
- command.run_command
+ shell_out_compact("plutil", "-convert", "binary1", "-o", "-", "-",
+ input: shadow_info.to_plist, live_stream: shadow_info_binary)
if user_info.nil?
# User is just created. read_user_info() will read the fresh information
@@ -393,7 +397,7 @@ user password using shadow hash.")
# Create a random 4 byte salt
salt = OpenSSL::Random.random_bytes(4)
encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + new_resource.password)
- hash_value = salt.unpack("H*").first + encoded_password
+ salt.unpack("H*").first + encoded_password
end
shadow_info["SALTED-SHA512"] = StringIO.new
@@ -435,27 +439,27 @@ user password using shadow hash.")
# and deleting home directory if needed.
#
def remove_user
- if new_resource.supports[:manage_home]
+ if new_resource.manage_home
# Remove home directory
FileUtils.rm_rf(current_resource.home)
end
# Remove the user from its groups
- run_dscl("list /Groups").each_line do |group|
+ run_dscl("list", "/Groups").each_line do |group|
if member_of_group?(group.chomp)
- run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{new_resource.username}'")
+ run_dscl("delete", "/Groups/#{group.chomp}", "GroupMembership", new_resource.username)
end
end
# Remove user account
- run_dscl("delete /Users/#{new_resource.username}")
+ run_dscl("delete", "/Users/#{new_resource.username}")
end
#
# Locks the user.
#
def lock_user
- run_dscl("append /Users/#{new_resource.username} AuthenticationAuthority ';DisabledUser;'")
+ run_dscl("append", "/Users/#{new_resource.username}", "AuthenticationAuthority", ";DisabledUser;")
end
#
@@ -463,7 +467,7 @@ user password using shadow hash.")
#
def unlock_user
auth_string = authentication_authority.gsub(/AuthenticationAuthority: /, "").gsub(/;DisabledUser;/, "").strip
- run_dscl("create /Users/#{new_resource.username} AuthenticationAuthority '#{auth_string}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "AuthenticationAuthority", auth_string)
end
#
@@ -481,7 +485,7 @@ user password using shadow hash.")
# This is the interface base User provider requires to provide idempotency.
#
def check_lock
- return @locked = locked?
+ @locked = locked?
end
#
@@ -493,11 +497,11 @@ user password using shadow hash.")
# given attribute.
#
def diverged?(parameter)
- parameter_updated?(parameter) && (not new_resource.send(parameter).nil?)
+ parameter_updated?(parameter) && !new_resource.send(parameter).nil?
end
def parameter_updated?(parameter)
- not (new_resource.send(parameter) == current_resource.send(parameter))
+ !(new_resource.send(parameter) == current_resource.send(parameter))
end
#
@@ -543,7 +547,7 @@ user password using shadow hash.")
def member_of_group?(group_name)
membership_info = ""
begin
- membership_info = run_dscl("read /Groups/#{group_name}")
+ membership_info = run_dscl("read", "/Groups/#{group_name}")
rescue Chef::Exceptions::DsclCommandFailed
# Raised if the group doesn't contain any members
end
@@ -560,14 +564,14 @@ user password using shadow hash.")
# A simple map of Chef's terms to DSCL's terms.
DSCL_PROPERTY_MAP = {
- :uid => "uid",
- :gid => "gid",
- :home => "home",
- :shell => "shell",
- :comment => "realname",
- :password => "passwd",
- :auth_authority => "authentication_authority",
- :shadow_hash => "ShadowHashData",
+ uid: "uid",
+ gid: "gid",
+ home: "home",
+ shell: "shell",
+ comment: "realname",
+ password: "passwd",
+ auth_authority: "authentication_authority",
+ shadow_hash: "ShadowHashData",
}.freeze
# Directory where the user plist files are stored for versions 10.7 and above
@@ -582,11 +586,11 @@ user password using shadow hash.")
# We flush the cache here in order to make sure that we read fresh information
# for the user.
- shell_out("dscacheutil '-flushcache'")
+ shell_out_compact("dscacheutil", "-flushcache") # FIXME: this is MacOS version dependent
begin
user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
- user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}")
+ user_plist_info = run_plutil("convert", "xml1", "-o", "-", user_plist_file)
user_info = Plist.parse_xml(user_plist_info)
rescue Chef::Exceptions::PlistUtilCommandFailed
end
@@ -601,7 +605,7 @@ user password using shadow hash.")
def save_user_info(user_info)
user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
Plist::Emit.save_plist(user_info, user_plist_file)
- run_plutil("convert binary1 #{user_plist_file}")
+ run_plutil("convert", "binary1", user_plist_file)
end
#
@@ -650,7 +654,9 @@ user password using shadow hash.")
end
def run_dscl(*args)
- result = shell_out("dscl . -#{args.join(' ')}")
+ argdup = args.dup
+ cmd = argdup.shift
+ result = shell_out_compact("dscl", ".", "-#{cmd}", argdup)
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: /
@@ -658,17 +664,19 @@ user password using shadow hash.")
end
def run_plutil(*args)
- result = shell_out("plutil -#{args.join(' ')}")
+ argdup = args.dup
+ cmd = argdup.shift
+ result = shell_out_compact("plutil", "-#{cmd}", argdup)
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 => "?")
+ result.stdout.encode("utf-8", "binary", undef: :replace, invalid: :replace, replace: "?")
else
result.stdout
end
end
def convert_binary_plist_to_xml(binary_plist_string)
- Mixlib::ShellOut.new("plutil -convert xml1 -o - -", :input => binary_plist_string).run_command.stdout
+ shell_out_compact("plutil", "-convert", "xml1", "-o", "-", "-", input: binary_plist_string).stdout
end
def convert_to_binary(string)