chef 12.0.0.alpha.0-x86-mingw32 → 12.0.0.alpha.1-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +3 -5
- data/lib/chef/api_client.rb +1 -1
- data/lib/chef/application.rb +16 -8
- data/lib/chef/chef_fs/chef_fs_data_store.rb +1 -1
- data/lib/chef/chef_fs/command_line.rb +1 -1
- data/lib/chef/chef_fs/file_system.rb +1 -1
- data/lib/chef/chef_fs/file_system/acl_entry.rb +1 -1
- data/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb +3 -3
- data/lib/chef/chef_fs/file_system/cookbook_file.rb +2 -2
- data/lib/chef/chef_fs/file_system/rest_list_dir.rb +2 -2
- data/lib/chef/chef_fs/file_system/rest_list_entry.rb +4 -4
- data/lib/chef/config.rb +6 -5
- data/lib/chef/config_fetcher.rb +1 -1
- data/lib/chef/cookbook/cookbook_version_loader.rb +126 -43
- data/lib/chef/cookbook/metadata.rb +102 -53
- data/lib/chef/cookbook/syntax_check.rb +1 -1
- data/lib/chef/cookbook_loader.rb +62 -14
- data/lib/chef/cookbook_site_streaming_uploader.rb +12 -1
- data/lib/chef/cookbook_version.rb +13 -4
- data/lib/chef/data_bag.rb +28 -15
- data/lib/chef/data_bag_item.rb +5 -7
- data/lib/chef/digester.rb +5 -9
- data/lib/chef/dsl/recipe.rb +14 -0
- data/lib/chef/encrypted_data_bag_item.rb +1 -0
- data/lib/chef/encrypted_data_bag_item/assertions.rb +57 -0
- data/lib/chef/encrypted_data_bag_item/decryptor.rb +52 -28
- data/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb +37 -0
- data/lib/chef/encrypted_data_bag_item/encryption_failure.rb +22 -0
- data/lib/chef/encrypted_data_bag_item/encryptor.rb +79 -8
- data/lib/chef/environment.rb +1 -3
- data/lib/chef/exceptions.rb +18 -3
- data/lib/chef/formatters/base.rb +7 -0
- data/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb +1 -1
- data/lib/chef/handler/json_file.rb +0 -1
- data/lib/chef/http/json_output.rb +1 -1
- data/lib/chef/json_compat.rb +24 -6
- data/lib/chef/knife/bootstrap.rb +2 -2
- data/lib/chef/knife/client_delete.rb +1 -1
- data/lib/chef/knife/cookbook_site_download.rb +1 -1
- data/lib/chef/knife/cookbook_site_list.rb +1 -1
- data/lib/chef/knife/cookbook_site_search.rb +1 -1
- data/lib/chef/knife/cookbook_site_share.rb +2 -2
- data/lib/chef/knife/cookbook_site_show.rb +3 -3
- data/lib/chef/knife/cookbook_site_unshare.rb +1 -1
- data/lib/chef/knife/core/node_editor.rb +2 -3
- data/lib/chef/knife/core/ui.rb +2 -2
- data/lib/chef/knife/deps.rb +2 -3
- data/lib/chef/mixin/shell_out.rb +1 -1
- data/lib/chef/mixin/windows_architecture_helper.rb +1 -0
- data/lib/chef/node.rb +1 -2
- data/lib/chef/platform/provider_mapping.rb +33 -6
- data/lib/chef/provider.rb +0 -2
- data/lib/chef/provider/cookbook_file/content.rb +1 -1
- data/lib/chef/provider/cron.rb +11 -0
- data/lib/chef/provider/deploy.rb +3 -2
- data/lib/chef/provider/deploy/revision.rb +2 -2
- data/lib/chef/provider/env.rb +1 -1
- data/lib/chef/provider/env/windows.rb +5 -9
- data/lib/chef/provider/file.rb +84 -33
- data/lib/chef/provider/git.rb +2 -1
- data/lib/chef/provider/group/aix.rb +17 -2
- data/lib/chef/provider/group/dscl.rb +27 -9
- data/lib/chef/provider/group/pw.rb +8 -1
- data/lib/chef/provider/http_request.rb +4 -4
- data/lib/chef/provider/log.rb +4 -14
- data/lib/chef/provider/mount/mount.rb +2 -2
- data/lib/chef/provider/package/ips.rb +17 -23
- data/lib/chef/provider/package/paludis.rb +2 -2
- data/lib/chef/provider/package/rpm.rb +2 -2
- data/lib/chef/provider/package/rubygems.rb +2 -0
- data/lib/chef/provider/package/yum.rb +2 -0
- data/lib/chef/provider/package/zypper.rb +1 -1
- data/lib/chef/provider/remote_file/cache_control_data.rb +2 -2
- data/lib/chef/provider/service/windows.rb +87 -21
- data/lib/chef/provider/user/aix.rb +95 -0
- data/lib/chef/provider/user/dscl.rb +544 -156
- data/lib/chef/provider/user/useradd.rb +1 -0
- data/lib/chef/providers.rb +1 -0
- data/lib/chef/resource.rb +4 -3
- data/lib/chef/resource/freebsd_package.rb +10 -2
- data/lib/chef/resource/paludis_package.rb +1 -0
- data/lib/chef/resource/scm.rb +10 -0
- data/lib/chef/resource/user.rb +27 -0
- data/lib/chef/resource/windows_service.rb +53 -0
- data/lib/chef/resource_collection.rb +23 -12
- data/lib/chef/resource_reporter.rb +10 -10
- data/lib/chef/resources.rb +1 -0
- data/lib/chef/role.rb +3 -3
- data/lib/chef/run_list.rb +6 -3
- data/lib/chef/user.rb +1 -1
- data/lib/chef/util/diff.rb +1 -2
- data/lib/chef/version.rb +1 -1
- data/lib/chef/version_constraint.rb +4 -4
- data/spec/data/cookbooks/angrybash/metadata.rb +2 -0
- data/spec/data/cookbooks/apache2/metadata.rb +2 -0
- data/spec/data/cookbooks/borken/metadata.rb +2 -0
- data/spec/data/cookbooks/ignorken/metadata.rb +2 -0
- data/spec/data/cookbooks/java/metadata.rb +2 -0
- data/spec/data/cookbooks/name-mismatch-versionnumber/README.md +4 -0
- data/spec/data/cookbooks/name-mismatch-versionnumber/metadata.rb +8 -0
- data/spec/data/cookbooks/name-mismatch-versionnumber/recipes/default.rb +8 -0
- data/spec/data/cookbooks/openldap/files/default/remotedir/not_a_template.erb +2 -0
- data/spec/data/cookbooks/preseed/metadata.rb +2 -0
- data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/README.md +4 -0
- data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/metadata.rb +13 -0
- data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/default.rb +8 -0
- data/spec/data/invalid-metadata-chef-repo/invalid-metadata/README.md +4 -0
- data/spec/data/invalid-metadata-chef-repo/invalid-metadata/metadata.rb +10 -0
- data/spec/data/invalid-metadata-chef-repo/invalid-metadata/recipes/default.rb +8 -0
- data/spec/data/mac_users/10.7-8.plist.xml +559 -0
- data/spec/data/mac_users/10.7-8.shadow.xml +11 -0
- data/spec/data/mac_users/10.7.plist.xml +559 -0
- data/spec/data/mac_users/10.7.shadow.xml +11 -0
- data/spec/data/mac_users/10.8.plist.xml +559 -0
- data/spec/data/mac_users/10.8.shadow.xml +21 -0
- data/spec/data/mac_users/10.9.plist.xml +560 -0
- data/spec/data/mac_users/10.9.shadow.xml +21 -0
- data/spec/data/object_loader/environments/test.json +2 -0
- data/spec/data/object_loader/environments/test_json_class.json +2 -0
- data/spec/data/object_loader/nodes/test.json +2 -0
- data/spec/data/object_loader/nodes/test_json_class.json +2 -0
- data/spec/data/object_loader/roles/test.json +2 -0
- data/spec/data/object_loader/roles/test_json_class.json +2 -0
- data/spec/functional/resource/bff_spec.rb +1 -1
- data/spec/functional/resource/cron_spec.rb +20 -1
- data/spec/functional/resource/env_spec.rb +137 -0
- data/spec/functional/resource/group_spec.rb +7 -5
- data/spec/functional/resource/remote_file_spec.rb +12 -1
- data/spec/functional/resource/user/dscl_spec.rb +198 -0
- data/spec/functional/resource/{user_spec.rb → user/useradd_spec.rb} +175 -37
- data/spec/integration/client/client_spec.rb +6 -4
- data/spec/integration/client/ipv6_spec.rb +16 -14
- data/spec/integration/knife/chef_fs_data_store_spec.rb +57 -46
- data/spec/integration/knife/chef_repo_path_spec.rb +105 -78
- data/spec/integration/knife/chef_repository_file_system_spec.rb +100 -84
- data/spec/integration/knife/chefignore_spec.rb +76 -46
- data/spec/integration/knife/common_options_spec.rb +16 -21
- data/spec/integration/knife/cookbook_api_ipv6_spec.rb +3 -3
- data/spec/integration/knife/delete_spec.rb +66 -46
- data/spec/integration/knife/deps_spec.rb +145 -94
- data/spec/integration/knife/diff_spec.rb +176 -110
- data/spec/integration/knife/download_spec.rb +229 -133
- data/spec/integration/knife/list_spec.rb +62 -54
- data/spec/integration/knife/raw_spec.rb +24 -9
- data/spec/integration/knife/redirection_spec.rb +2 -2
- data/spec/integration/knife/serve_spec.rb +2 -2
- data/spec/integration/knife/show_spec.rb +32 -26
- data/spec/integration/knife/upload_spec.rb +308 -165
- data/spec/integration/recipes/lwrp_inline_resources_spec.rb +10 -8
- data/spec/integration/solo/solo_spec.rb +22 -11
- data/spec/spec_helper.rb +3 -0
- data/spec/support/lib/chef/resource/zen_follower.rb +46 -0
- data/spec/support/platform_helpers.rb +12 -0
- data/spec/support/shared/functional/file_resource.rb +10 -0
- data/spec/support/shared/integration/chef_zero_support.rb +130 -0
- data/spec/support/shared/integration/integration_helper.rb +100 -98
- data/spec/support/shared/integration/knife_support.rb +0 -1
- data/spec/support/shared/unit/provider/file.rb +6 -4
- data/spec/support/shared/unit/provider/useradd_based_user_provider.rb +10 -1
- data/spec/unit/api_client/registration_spec.rb +83 -74
- data/spec/unit/application_spec.rb +32 -9
- data/spec/unit/cookbook/cookbook_version_loader_spec.rb +179 -0
- data/spec/unit/cookbook/metadata_spec.rb +190 -150
- data/spec/unit/cookbook/syntax_check_spec.rb +3 -2
- data/spec/unit/cookbook_loader_spec.rb +114 -53
- data/spec/unit/{cookbook_site_streaming_uploader.rb → cookbook_site_streaming_uploader_spec.rb} +21 -1
- data/spec/unit/data_bag_spec.rb +88 -13
- data/spec/unit/deprecation_spec.rb +1 -2
- data/spec/unit/encrypted_data_bag_item_spec.rb +145 -9
- data/spec/unit/environment_spec.rb +1 -1
- data/spec/unit/formatters/base_spec.rb +48 -0
- data/spec/unit/json_compat_spec.rb +48 -17
- data/spec/unit/knife/client_delete_spec.rb +4 -4
- data/spec/unit/knife/client_show_spec.rb +15 -5
- data/spec/unit/knife/cookbook_site_download_spec.rb +1 -1
- data/spec/unit/knife/cookbook_site_share_spec.rb +3 -3
- data/spec/unit/knife/data_bag_from_file_spec.rb +0 -2
- data/spec/unit/knife/data_bag_show_spec.rb +23 -14
- data/spec/unit/knife/node_show_spec.rb +32 -15
- data/spec/unit/knife/role_show_spec.rb +59 -0
- data/spec/unit/platform_spec.rb +10 -0
- data/spec/unit/provider/deploy_spec.rb +4 -0
- data/spec/unit/provider/env_spec.rb +19 -0
- data/spec/unit/provider/git_spec.rb +22 -2
- data/spec/unit/provider/group/dscl_spec.rb +38 -1
- data/spec/unit/provider/group/pw_spec.rb +2 -2
- data/spec/unit/provider/http_request_spec.rb +8 -8
- data/spec/unit/provider/log_spec.rb +33 -53
- data/spec/unit/provider/mount/mount_spec.rb +12 -3
- data/spec/unit/provider/package/ips_spec.rb +96 -63
- data/spec/unit/provider/package/paludis_spec.rb +5 -5
- data/spec/unit/provider/package/rpm_spec.rb +12 -0
- data/spec/unit/provider/package/zypper_spec.rb +28 -16
- data/spec/unit/provider/service/windows_spec.rb +77 -17
- data/spec/unit/provider/user/dscl_spec.rb +659 -264
- data/spec/unit/provider/user/useradd_spec.rb +1 -0
- data/spec/unit/recipe_spec.rb +41 -0
- data/spec/unit/resource/scm_spec.rb +11 -0
- data/spec/unit/resource/user_spec.rb +4 -0
- data/spec/unit/resource/windows_service_spec.rb +46 -0
- data/spec/unit/resource_collection_spec.rb +33 -0
- data/spec/unit/resource_reporter_spec.rb +48 -0
- data/spec/unit/resource_spec.rb +9 -2
- data/spec/unit/role_spec.rb +6 -0
- data/spec/unit/version_constraint_spec.rb +28 -0
- metadata +61 -4
@@ -0,0 +1,95 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
class Chef
|
18
|
+
class Provider
|
19
|
+
class User
|
20
|
+
class Aix < Chef::Provider::User::Useradd
|
21
|
+
|
22
|
+
UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
|
23
|
+
|
24
|
+
def create_user
|
25
|
+
super
|
26
|
+
add_password
|
27
|
+
end
|
28
|
+
|
29
|
+
def manage_user
|
30
|
+
add_password
|
31
|
+
manage_home
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
# Aix does not support -r like other unix, sytem account is created by adding to 'system' group
|
36
|
+
def useradd_options
|
37
|
+
opts = []
|
38
|
+
opts << "-g" << "system" if new_resource.system
|
39
|
+
opts
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_lock
|
43
|
+
lock_info = shell_out!("lsuser -a account_locked #{new_resource.username}")
|
44
|
+
if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.match(/does not exist/)
|
45
|
+
# if we're in whyrun mode and the user is not yet created we assume it would be
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if lock_info.stdout.empty?
|
49
|
+
|
50
|
+
status = /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)
|
51
|
+
if status && status[1] == "true"
|
52
|
+
@locked = true
|
53
|
+
else
|
54
|
+
@locked = false
|
55
|
+
end
|
56
|
+
|
57
|
+
@locked
|
58
|
+
end
|
59
|
+
|
60
|
+
def lock_user
|
61
|
+
shell_out!("chuser account_locked=true #{new_resource.username}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def unlock_user
|
65
|
+
shell_out!("chuser account_locked=false #{new_resource.username}")
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def add_password
|
70
|
+
if @current_resource.password != @new_resource.password && @new_resource.password
|
71
|
+
Chef::Log.debug("#{@new_resource.username} setting password to #{@new_resource.password}")
|
72
|
+
command = "echo '#{@new_resource.username}:#{@new_resource.password}' | chpasswd -e"
|
73
|
+
shell_out!(command)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Aix specific handling to update users home directory.
|
78
|
+
def manage_home
|
79
|
+
# -m option does not work on aix, so move dir.
|
80
|
+
if updating_home? and managing_home_dir?
|
81
|
+
universal_options.delete("-m")
|
82
|
+
if ::File.directory?(@current_resource.home)
|
83
|
+
Chef::Log.debug("Changing users home directory from #{@current_resource.home} to #{new_resource.home}")
|
84
|
+
shell_out!("mv #{@current_resource.home} #{new_resource.home}")
|
85
|
+
else
|
86
|
+
Chef::Log.debug("Creating users home directory #{new_resource.home}")
|
87
|
+
shell_out!("mkdir -p #{new_resource.home}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -16,40 +16,209 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
+
require 'mixlib/shellout'
|
19
20
|
require 'chef/provider/user'
|
20
21
|
require 'openssl'
|
22
|
+
require 'plist'
|
21
23
|
|
22
24
|
class Chef
|
23
25
|
class Provider
|
24
26
|
class User
|
27
|
+
#
|
28
|
+
# The most tricky bit of this provider is the way it deals with user passwords.
|
29
|
+
# Mac OS X has different password shadow calculations based on the version.
|
30
|
+
# < 10.7 => password shadow calculation format SALTED-SHA1
|
31
|
+
# => stored in: /var/db/shadow/hash/#{guid}
|
32
|
+
# => shadow binary length 68 bytes
|
33
|
+
# => First 4 bytes salt / Next 64 bytes shadow value
|
34
|
+
# = 10.7 => password shadow calculation format SALTED-SHA512
|
35
|
+
# => stored in: /var/db/dslocal/nodes/Default/users/#{name}.plist
|
36
|
+
# => shadow binary length 68 bytes
|
37
|
+
# => First 4 bytes salt / Next 64 bytes shadow value
|
38
|
+
# > 10.7 => password shadow calculation format SALTED-SHA512-PBKDF2
|
39
|
+
# => stored in: /var/db/dslocal/nodes/Default/users/#{name}.plist
|
40
|
+
# => shadow binary length 128 bytes
|
41
|
+
# => Salt / Iterations are stored seperately in the same file
|
42
|
+
#
|
43
|
+
# This provider only supports Mac OSX versions 10.7 and above
|
25
44
|
class Dscl < Chef::Provider::User
|
26
45
|
|
27
|
-
|
28
|
-
|
46
|
+
def define_resource_requirements
|
47
|
+
super
|
48
|
+
|
49
|
+
requirements.assert(:all_actions) do |a|
|
50
|
+
a.assertion { mac_osx_version_less_than_10_7? == false }
|
51
|
+
a.failure_message(Chef::Exceptions::User, "Chef::Provider::User::Dscl only supports Mac OS X versions 10.7 and above.")
|
52
|
+
end
|
53
|
+
|
54
|
+
requirements.assert(:all_actions) do |a|
|
55
|
+
a.assertion { ::File.exists?("/usr/bin/dscl") }
|
56
|
+
a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{@new_resource}!")
|
57
|
+
end
|
58
|
+
|
59
|
+
requirements.assert(:all_actions) do |a|
|
60
|
+
a.assertion { ::File.exists?("/usr/bin/plutil") }
|
61
|
+
a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{@new_resource}!")
|
62
|
+
end
|
63
|
+
|
64
|
+
requirements.assert(:create, :modify, :manage) do |a|
|
65
|
+
a.assertion do
|
66
|
+
if @new_resource.password && mac_osx_version_greater_than_10_7?
|
67
|
+
# SALTED-SHA512 password shadow hashes are not supported on 10.8 and above.
|
68
|
+
!salted_sha512?(@new_resource.password)
|
69
|
+
else
|
70
|
+
true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
a.failure_message(Chef::Exceptions::User, "SALTED-SHA512 passwords are not supported on Mac 10.8 and above. \
|
74
|
+
If you want to set the user password using shadow info make sure you specify a SALTED-SHA512-PBKDF2 shadow hash \
|
75
|
+
in 'password', with the associated 'salt' and 'iterations'.")
|
76
|
+
end
|
77
|
+
|
78
|
+
requirements.assert(:create, :modify, :manage) do |a|
|
79
|
+
a.assertion do
|
80
|
+
if @new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(@new_resource.password)
|
81
|
+
# salt and iterations should be specified when
|
82
|
+
# SALTED-SHA512-PBKDF2 password shadow hash is given
|
83
|
+
!@new_resource.salt.nil? && !@new_resource.iterations.nil?
|
84
|
+
else
|
85
|
+
true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hash is given without associated \
|
89
|
+
'salt' and 'iterations'. Please specify 'salt' and 'iterations' in order to set the user password using shadow hash.")
|
90
|
+
end
|
91
|
+
|
92
|
+
requirements.assert(:create, :modify, :manage) do |a|
|
93
|
+
a.assertion do
|
94
|
+
if @new_resource.password && !mac_osx_version_greater_than_10_7?
|
95
|
+
# On 10.7 SALTED-SHA512-PBKDF2 is not supported
|
96
|
+
!salted_sha512_pbkdf2?(@new_resource.password)
|
97
|
+
else
|
98
|
+
true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hashes are not supported on \
|
102
|
+
Mac OS X version 10.7. Please specify a SALTED-SHA512 shadow hash in 'password' attribute to set the \
|
103
|
+
user password using shadow hash.")
|
104
|
+
end
|
29
105
|
|
30
|
-
def dscl(*args)
|
31
|
-
shell_out("dscl . -#{args.join(' ')}")
|
32
106
|
end
|
33
107
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
108
|
+
def load_current_resource
|
109
|
+
@current_resource = Chef::Resource::User.new(@new_resource.username)
|
110
|
+
@current_resource.username(@new_resource.username)
|
111
|
+
|
112
|
+
user_info = read_user_info
|
113
|
+
if user_info
|
114
|
+
@current_resource.uid(dscl_get(user_info, :uid))
|
115
|
+
@current_resource.gid(dscl_get(user_info, :gid))
|
116
|
+
@current_resource.home(dscl_get(user_info, :home))
|
117
|
+
@current_resource.shell(dscl_get(user_info, :shell))
|
118
|
+
@current_resource.comment(dscl_get(user_info, :comment))
|
119
|
+
@authentication_authority = dscl_get(user_info, :auth_authority)
|
120
|
+
|
121
|
+
if @new_resource.password && dscl_get(user_info, :password) == "********"
|
122
|
+
# A password is set. Let's get the password information from shadow file
|
123
|
+
shadow_hash_binary = dscl_get(user_info, :shadow_hash)
|
124
|
+
|
125
|
+
# Calling shell_out directly since we want to give an input stream
|
126
|
+
shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string)
|
127
|
+
shadow_hash = Plist::parse_xml(shadow_hash_xml)
|
128
|
+
|
129
|
+
if shadow_hash["SALTED-SHA512"]
|
130
|
+
# Convert the shadow value from Base64 encoding to hex before consuming them
|
131
|
+
@password_shadow_conversion_algorithm = "SALTED-SHA512"
|
132
|
+
@current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack('H*').first)
|
133
|
+
elsif shadow_hash["SALTED-SHA512-PBKDF2"]
|
134
|
+
@password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2"
|
135
|
+
# Convert the entropy from Base64 encoding to hex before consuming them
|
136
|
+
@current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first)
|
137
|
+
@current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"])
|
138
|
+
# Convert the salt from Base64 encoding to hex before consuming them
|
139
|
+
@current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack('H*').first)
|
140
|
+
else
|
141
|
+
raise(Chef::Exceptions::User,"Unknown shadow_hash format: #{shadow_hash.keys.join(' ')}")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
convert_group_name if @new_resource.gid
|
146
|
+
else
|
147
|
+
@user_exists = false
|
148
|
+
Chef::Log.debug("#{@new_resource} user does not exist")
|
149
|
+
end
|
150
|
+
|
151
|
+
@current_resource
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Provider Actions
|
156
|
+
#
|
157
|
+
|
158
|
+
def create_user
|
159
|
+
dscl_create_user
|
160
|
+
dscl_create_comment
|
161
|
+
dscl_set_uid
|
162
|
+
dscl_set_gid
|
163
|
+
dscl_set_home
|
164
|
+
dscl_set_shell
|
165
|
+
set_password
|
40
166
|
end
|
41
167
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
168
|
+
def manage_user
|
169
|
+
dscl_create_user if diverged?(:username)
|
170
|
+
dscl_create_comment if diverged?(:comment)
|
171
|
+
dscl_set_uid if diverged?(:uid)
|
172
|
+
dscl_set_gid if diverged?(:gid)
|
173
|
+
dscl_set_home if diverged?(:home)
|
174
|
+
dscl_set_shell if diverged?(:shell)
|
175
|
+
set_password if diverged_password?
|
176
|
+
end
|
47
177
|
|
48
|
-
#
|
178
|
+
#
|
179
|
+
# Action Helpers
|
180
|
+
#
|
181
|
+
|
182
|
+
#
|
183
|
+
# Create a user using dscl
|
184
|
+
#
|
185
|
+
def dscl_create_user
|
186
|
+
run_dscl("create /Users/#{@new_resource.username}")
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Saves the specified Chef user `comment` into RealName attribute
|
191
|
+
# of Mac user.
|
192
|
+
#
|
193
|
+
def dscl_create_comment
|
194
|
+
run_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Sets the user id for the user using dscl.
|
199
|
+
# If a `uid` is not specified, it finds the next available one starting
|
200
|
+
# from 200 if `system` is set, 500 otherwise.
|
201
|
+
#
|
202
|
+
def dscl_set_uid
|
203
|
+
@new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '')
|
204
|
+
|
205
|
+
if uid_used?(@new_resource.uid)
|
206
|
+
raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use")
|
207
|
+
end
|
208
|
+
|
209
|
+
run_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# Find the next available uid on the system. starting with 200 if `system` is set,
|
214
|
+
# 500 otherwise.
|
215
|
+
#
|
49
216
|
def get_free_uid(search_limit=1000)
|
50
|
-
uid = nil
|
51
|
-
|
52
|
-
|
217
|
+
uid = nil
|
218
|
+
base_uid = @new_resource.system ? 200 : 500
|
219
|
+
next_uid_guess = base_uid
|
220
|
+
users_uids = run_dscl("list /Users uid")
|
221
|
+
while(next_uid_guess < search_limit + base_uid)
|
53
222
|
if users_uids =~ Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n")
|
54
223
|
next_uid_guess += 1
|
55
224
|
else
|
@@ -60,22 +229,41 @@ class Chef
|
|
60
229
|
return uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
|
61
230
|
end
|
62
231
|
|
232
|
+
#
|
233
|
+
# Returns true if uid is in use by a different account, false otherwise.
|
234
|
+
#
|
63
235
|
def uid_used?(uid)
|
64
236
|
return false unless uid
|
65
|
-
users_uids =
|
237
|
+
users_uids = run_dscl("list /Users uid")
|
66
238
|
!! ( users_uids =~ Regexp.new("#{Regexp.escape(uid.to_s)}\n") )
|
67
239
|
end
|
68
240
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
241
|
+
#
|
242
|
+
# Sets the group id for the user using dscl. Fails if a group doesn't
|
243
|
+
# exist on the system with given group id.
|
244
|
+
#
|
245
|
+
def dscl_set_gid
|
246
|
+
unless @new_resource.gid && @new_resource.gid.to_s.match(/^\d+$/)
|
247
|
+
begin
|
248
|
+
possible_gid = run_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last
|
249
|
+
rescue Chef::Exceptions::DsclCommandFailed => e
|
250
|
+
raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}")
|
251
|
+
end
|
252
|
+
@new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
|
73
253
|
end
|
74
|
-
|
254
|
+
run_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
|
75
255
|
end
|
76
256
|
|
77
|
-
|
78
|
-
|
257
|
+
#
|
258
|
+
# Sets the home directory for the user. If `:manage_home` is set home
|
259
|
+
# directory is managed (moved / created) for the user.
|
260
|
+
#
|
261
|
+
def dscl_set_home
|
262
|
+
if @new_resource.home.nil? || @new_resource.home.empty?
|
263
|
+
run_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory")
|
264
|
+
return
|
265
|
+
end
|
266
|
+
|
79
267
|
if @new_resource.supports[:manage_home]
|
80
268
|
validate_home_dir_specification!
|
81
269
|
|
@@ -87,199 +275,399 @@ class Chef
|
|
87
275
|
move_home
|
88
276
|
end
|
89
277
|
end
|
90
|
-
|
278
|
+
run_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
|
279
|
+
end
|
280
|
+
|
281
|
+
def validate_home_dir_specification!
|
282
|
+
unless @new_resource.home =~ /^\//
|
283
|
+
raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'")
|
284
|
+
end
|
91
285
|
end
|
92
286
|
|
93
|
-
def
|
94
|
-
|
287
|
+
def current_home_exists?
|
288
|
+
::File.exist?("#{@current_resource.home}")
|
95
289
|
end
|
96
290
|
|
97
|
-
def
|
98
|
-
|
291
|
+
def new_home_exists?
|
292
|
+
::File.exist?("#{@new_resource.home}")
|
99
293
|
end
|
100
294
|
|
101
|
-
def
|
102
|
-
|
295
|
+
def ditto_home
|
296
|
+
skel = "/System/Library/User Template/English.lproj"
|
297
|
+
raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
|
298
|
+
shell_out! "ditto '#{skel}' '#{@new_resource.home}'"
|
299
|
+
::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
|
300
|
+
end
|
301
|
+
|
302
|
+
def move_home
|
303
|
+
Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}")
|
304
|
+
|
305
|
+
src = @current_resource.home
|
306
|
+
FileUtils.mkdir_p(@new_resource.home)
|
307
|
+
files = ::Dir.glob("#{src}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
|
308
|
+
::FileUtils.mv(files,@new_resource.home, :force => true)
|
309
|
+
::FileUtils.rmdir(src)
|
310
|
+
::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
|
103
311
|
end
|
104
312
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
313
|
+
#
|
314
|
+
# Sets the shell for the user using dscl.
|
315
|
+
#
|
316
|
+
def dscl_set_shell
|
317
|
+
if @new_resource.shell || ::File.exists?("#{@new_resource.shell}")
|
318
|
+
run_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
|
109
319
|
else
|
110
|
-
false
|
320
|
+
run_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
|
111
321
|
end
|
112
322
|
end
|
113
323
|
|
114
|
-
|
115
|
-
|
116
|
-
|
324
|
+
#
|
325
|
+
# Sets the password for the user based on given password parameters.
|
326
|
+
# Chef supports specifying plain-text passwords and password shadow
|
327
|
+
# hash data.
|
328
|
+
#
|
329
|
+
def set_password
|
330
|
+
# Return if there is no password to set
|
331
|
+
return if @new_resource.password.nil?
|
332
|
+
|
333
|
+
shadow_info = prepare_password_shadow_info
|
334
|
+
|
335
|
+
# Shadow info is saved as binary plist. Convert the info to binary plist.
|
336
|
+
shadow_info_binary = StringIO.new
|
337
|
+
command = Mixlib::ShellOut.new("plutil -convert binary1 -o - -",
|
338
|
+
:input => shadow_info.to_plist, :live_stream => shadow_info_binary)
|
339
|
+
command.run_command
|
340
|
+
|
341
|
+
# Replace the shadow info in user's plist
|
342
|
+
user_info = read_user_info
|
343
|
+
dscl_set(user_info, :shadow_hash, shadow_info_binary)
|
344
|
+
|
345
|
+
#
|
346
|
+
# Before saving the user's plist file we need to wait for dscl to
|
347
|
+
# update its caches and flush them to disk. In order to achieve this
|
348
|
+
# we need to wait first for our changes to get into the dscl cache
|
349
|
+
# and then flush the cache to disk before saving password into the
|
350
|
+
# plist file. 3 seconds is the minimum experimental value for dscl
|
351
|
+
# cache to be updated. We can get rid of this sleep when we find a
|
352
|
+
# trigger to update dscl cache.
|
353
|
+
#
|
354
|
+
sleep 3
|
355
|
+
shell_out("dscacheutil '-flushcache'")
|
356
|
+
save_user_info(user_info)
|
357
|
+
end
|
117
358
|
|
118
|
-
|
119
|
-
|
120
|
-
|
359
|
+
#
|
360
|
+
# Prepares the password shadow info based on the platform version.
|
361
|
+
#
|
362
|
+
def prepare_password_shadow_info
|
363
|
+
shadow_info = { }
|
364
|
+
entropy = nil
|
365
|
+
salt = nil
|
366
|
+
iterations = nil
|
367
|
+
|
368
|
+
if mac_osx_version_10_7?
|
369
|
+
hash_value = if salted_sha512?(@new_resource.password)
|
370
|
+
@new_resource.password
|
121
371
|
else
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
OpenSSL::Random.random_bytes(10).each_byte { |b| hex_salt << b.to_i.to_s(16) }
|
127
|
-
hex_salt = hex_salt.slice(0...8)
|
128
|
-
salt = [hex_salt].pack("H*")
|
129
|
-
sha1 = ::OpenSSL::Digest::SHA1.hexdigest(salt+@new_resource.password)
|
130
|
-
salted_sha1 = (hex_salt+sha1).upcase
|
131
|
-
end
|
132
|
-
shadow_hash = String.new("00000000"*155)
|
133
|
-
shadow_hash[168] = salted_sha1
|
372
|
+
# Create a random 4 byte salt
|
373
|
+
salt = OpenSSL::Random.random_bytes(4)
|
374
|
+
encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + @new_resource.password)
|
375
|
+
hash_value = salt.unpack('H*').first + encoded_password
|
134
376
|
end
|
135
377
|
|
136
|
-
|
137
|
-
|
378
|
+
shadow_info["SALTED-SHA512"] = StringIO.new
|
379
|
+
shadow_info["SALTED-SHA512"].string = convert_to_binary(hash_value)
|
380
|
+
shadow_info
|
381
|
+
else
|
382
|
+
if salted_sha512_pbkdf2?(@new_resource.password)
|
383
|
+
entropy = convert_to_binary(@new_resource.password)
|
384
|
+
salt = convert_to_binary(@new_resource.salt)
|
385
|
+
iterations = @new_resource.iterations
|
386
|
+
else
|
387
|
+
salt = OpenSSL::Random.random_bytes(32)
|
388
|
+
iterations = @new_resource.iterations # Use the default if not specified by the user
|
389
|
+
|
390
|
+
entropy = OpenSSL::PKCS5::pbkdf2_hmac(
|
391
|
+
@new_resource.password,
|
392
|
+
salt,
|
393
|
+
iterations,
|
394
|
+
128,
|
395
|
+
OpenSSL::Digest::SHA512.new
|
396
|
+
)
|
138
397
|
end
|
139
398
|
|
140
|
-
|
141
|
-
|
399
|
+
pbkdf_info = { }
|
400
|
+
pbkdf_info["entropy"] = StringIO.new
|
401
|
+
pbkdf_info["entropy"].string = entropy
|
402
|
+
pbkdf_info["salt"] = StringIO.new
|
403
|
+
pbkdf_info["salt"].string = salt
|
404
|
+
pbkdf_info["iterations"] = iterations
|
405
|
+
|
406
|
+
shadow_info["SALTED-SHA512-PBKDF2"] = pbkdf_info
|
407
|
+
end
|
408
|
+
|
409
|
+
shadow_info
|
410
|
+
end
|
411
|
+
|
412
|
+
#
|
413
|
+
# Removes the user from the system after removing user from his groups
|
414
|
+
# and deleting home directory if needed.
|
415
|
+
#
|
416
|
+
def remove_user
|
417
|
+
if @new_resource.supports[:manage_home]
|
418
|
+
# Remove home directory
|
419
|
+
FileUtils.rm_rf(@current_resource.home)
|
420
|
+
end
|
421
|
+
|
422
|
+
# Remove the user from its groups
|
423
|
+
run_dscl("list /Groups").each_line do |group|
|
424
|
+
if member_of_group?(group.chomp)
|
425
|
+
run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{@new_resource.username}'")
|
142
426
|
end
|
143
427
|
end
|
428
|
+
|
429
|
+
# Remove user account
|
430
|
+
run_dscl("delete /Users/#{@new_resource.username}")
|
144
431
|
end
|
145
432
|
|
146
|
-
|
147
|
-
|
148
|
-
|
433
|
+
#
|
434
|
+
# Locks the user.
|
435
|
+
#
|
436
|
+
def lock_user
|
437
|
+
run_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
|
149
438
|
end
|
150
439
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
dscl_set_shell
|
158
|
-
modify_password
|
440
|
+
#
|
441
|
+
# Unlocks the user
|
442
|
+
#
|
443
|
+
def unlock_user
|
444
|
+
auth_string = @authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip
|
445
|
+
run_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'")
|
159
446
|
end
|
160
447
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
448
|
+
#
|
449
|
+
# Returns true if the user is locked, false otherwise.
|
450
|
+
#
|
451
|
+
def locked?
|
452
|
+
if @authentication_authority
|
453
|
+
!!(@authentication_authority =~ /DisabledUser/ )
|
454
|
+
else
|
455
|
+
false
|
456
|
+
end
|
169
457
|
end
|
170
458
|
|
171
|
-
|
172
|
-
|
459
|
+
#
|
460
|
+
# This is the interface base User provider requires to provide idempotency.
|
461
|
+
#
|
462
|
+
def check_lock
|
463
|
+
return @locked = locked?
|
173
464
|
end
|
174
465
|
|
175
|
-
|
176
|
-
|
466
|
+
#
|
467
|
+
# Helper functions
|
468
|
+
#
|
469
|
+
|
470
|
+
#
|
471
|
+
# Returns true if the system state and desired state is different for
|
472
|
+
# given attribute.
|
473
|
+
#
|
474
|
+
def diverged?(parameter)
|
475
|
+
parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?)
|
177
476
|
end
|
178
477
|
|
179
|
-
def
|
180
|
-
|
181
|
-
begin
|
182
|
-
possible_gid = safe_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last
|
183
|
-
rescue Chef::Exceptions::DsclCommandFailed => e
|
184
|
-
raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}")
|
185
|
-
end
|
186
|
-
@new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
|
187
|
-
end
|
188
|
-
safe_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
|
478
|
+
def parameter_updated?(parameter)
|
479
|
+
not (@new_resource.send(parameter) == @current_resource.send(parameter))
|
189
480
|
end
|
190
481
|
|
191
|
-
|
192
|
-
|
193
|
-
|
482
|
+
#
|
483
|
+
# We need a special check function for password since we support both
|
484
|
+
# plain text and shadow hash data.
|
485
|
+
#
|
486
|
+
# Checks if password needs update based on platform version and the
|
487
|
+
# type of the password specified.
|
488
|
+
#
|
489
|
+
def diverged_password?
|
490
|
+
return false if @new_resource.password.nil?
|
491
|
+
|
492
|
+
# Dscl provider supports both plain text passwords and shadow hashes.
|
493
|
+
if mac_osx_version_10_7?
|
494
|
+
if salted_sha512?(@new_resource.password)
|
495
|
+
diverged?(:password)
|
496
|
+
else
|
497
|
+
!salted_sha512_password_match?
|
498
|
+
end
|
194
499
|
else
|
195
|
-
|
500
|
+
# When a system is upgraded to a version 10.7+ shadow hashes of the users
|
501
|
+
# will be updated when the user logs in. So it's possible that we will have
|
502
|
+
# SALTED-SHA512 password in the current_resource. In that case we will force
|
503
|
+
# password to be updated.
|
504
|
+
return true if salted_sha512?(@current_resource.password)
|
505
|
+
|
506
|
+
if salted_sha512_pbkdf2?(@new_resource.password)
|
507
|
+
diverged?(:password) || diverged?(:salt) || diverged?(:iterations)
|
508
|
+
else
|
509
|
+
!salted_sha512_pbkdf2_password_match?
|
510
|
+
end
|
196
511
|
end
|
197
512
|
end
|
198
513
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
end
|
209
|
-
# remove the user from its groups
|
210
|
-
groups = []
|
211
|
-
Etc.group do |group|
|
212
|
-
groups << group.name if group.mem.include?(@new_resource.username)
|
213
|
-
end
|
214
|
-
groups.each do |group_name|
|
215
|
-
safe_dscl("delete /Groups/#{group_name} GroupMembership '#{@new_resource.username}'")
|
514
|
+
#
|
515
|
+
# Returns true if user is member of the specified group, false otherwise.
|
516
|
+
#
|
517
|
+
def member_of_group?(group_name)
|
518
|
+
membership_info = ""
|
519
|
+
begin
|
520
|
+
membership_info = run_dscl("read /Groups/#{group_name}")
|
521
|
+
rescue Chef::Exceptions::DsclCommandFailed
|
522
|
+
# Raised if the group doesn't contain any members
|
216
523
|
end
|
217
|
-
#
|
218
|
-
|
524
|
+
# Output is something like:
|
525
|
+
# GroupMembership: root admin etc
|
526
|
+
members = membership_info.split(" ")
|
527
|
+
members.shift # Get rid of GroupMembership: string
|
528
|
+
members.include?(@new_resource.username)
|
219
529
|
end
|
220
530
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
531
|
+
#
|
532
|
+
# DSCL Helper functions
|
533
|
+
#
|
534
|
+
|
535
|
+
# A simple map of Chef's terms to DSCL's terms.
|
536
|
+
DSCL_PROPERTY_MAP = {
|
537
|
+
:uid => "generateduid",
|
538
|
+
:gid => "gid",
|
539
|
+
:home => "home",
|
540
|
+
:shell => "shell",
|
541
|
+
:comment => "realname",
|
542
|
+
:password => "passwd",
|
543
|
+
:auth_authority => "authentication_authority",
|
544
|
+
:shadow_hash => "ShadowHashData"
|
545
|
+
}.freeze
|
546
|
+
|
547
|
+
# Directory where the user plist files are stored for versions 10.7 and above
|
548
|
+
USER_PLIST_DIRECTORY = "/var/db/dslocal/nodes/Default/users".freeze
|
549
|
+
|
550
|
+
#
|
551
|
+
# Reads the user plist and returns a hash keyed with DSCL properties specified
|
552
|
+
# in DSCL_PROPERTY_MAP. Return nil if the user is not found.
|
553
|
+
#
|
554
|
+
def read_user_info
|
555
|
+
user_info = nil
|
556
|
+
|
557
|
+
begin
|
558
|
+
user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
|
559
|
+
user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}")
|
560
|
+
user_info = Plist::parse_xml(user_plist_info)
|
561
|
+
rescue Chef::Exceptions::PlistUtilCommandFailed
|
227
562
|
end
|
563
|
+
|
564
|
+
user_info
|
228
565
|
end
|
229
566
|
|
230
|
-
|
231
|
-
|
567
|
+
#
|
568
|
+
# Saves the given hash keyed with DSCL properties specified
|
569
|
+
# in DSCL_PROPERTY_MAP to the disk.
|
570
|
+
#
|
571
|
+
def save_user_info(user_info)
|
572
|
+
user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
|
573
|
+
Plist::Emit.save_plist(user_info, user_plist_file)
|
574
|
+
run_plutil("convert binary1 #{user_plist_file}")
|
232
575
|
end
|
233
576
|
|
234
|
-
|
235
|
-
|
577
|
+
#
|
578
|
+
# Sets a value in user information hash using Chef attributes as keys.
|
579
|
+
#
|
580
|
+
def dscl_set(user_hash, key, value)
|
581
|
+
raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key)
|
582
|
+
user_hash[DSCL_PROPERTY_MAP[key]] = [ value ]
|
583
|
+
user_hash
|
236
584
|
end
|
237
585
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
586
|
+
#
|
587
|
+
# Gets a value from user information hash using Chef attributes as keys.
|
588
|
+
#
|
589
|
+
def dscl_get(user_hash, key)
|
590
|
+
raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key)
|
591
|
+
# DSCL values are set as arrays
|
592
|
+
value = user_hash[DSCL_PROPERTY_MAP[key]]
|
593
|
+
value.nil? ? value : value.first
|
242
594
|
end
|
243
595
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
596
|
+
#
|
597
|
+
# System Helpets
|
598
|
+
#
|
599
|
+
|
600
|
+
def mac_osx_version
|
601
|
+
# This provider will only be invoked on node[:platform] == "mac_os_x"
|
602
|
+
# We do not check or assert that here.
|
603
|
+
node[:platform_version]
|
248
604
|
end
|
249
605
|
|
250
|
-
def
|
251
|
-
|
606
|
+
def mac_osx_version_10_7?
|
607
|
+
mac_osx_version.start_with?("10.7.")
|
252
608
|
end
|
253
609
|
|
254
|
-
def
|
255
|
-
|
610
|
+
def mac_osx_version_less_than_10_7?
|
611
|
+
versions = mac_osx_version.split(".")
|
612
|
+
# Make integer comparison in order not to report 10.10 less than 10.7
|
613
|
+
(versions[0].to_i <= 10 && versions[1].to_i < 7)
|
256
614
|
end
|
257
615
|
|
258
|
-
def
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
|
616
|
+
def mac_osx_version_greater_than_10_7?
|
617
|
+
versions = mac_osx_version.split(".")
|
618
|
+
# Make integer comparison in order not to report 10.10 less than 10.7
|
619
|
+
(versions[0].to_i >= 10 && versions[1].to_i > 7)
|
263
620
|
end
|
264
621
|
|
265
|
-
def
|
266
|
-
|
622
|
+
def run_dscl(*args)
|
623
|
+
result = shell_out("dscl . -#{args.join(' ')}")
|
624
|
+
return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 )
|
625
|
+
raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") unless result.exitstatus == 0
|
626
|
+
raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") if result.stdout =~ /No such key: /
|
627
|
+
result.stdout
|
628
|
+
end
|
267
629
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
::FileUtils.rmdir(src)
|
273
|
-
::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
|
630
|
+
def run_plutil(*args)
|
631
|
+
result = shell_out("plutil -#{args.join(' ')}")
|
632
|
+
raise(Chef::Exceptions::PlistUtilCommandFailed,"plutil error: #{result.inspect}") unless result.exitstatus == 0
|
633
|
+
result.stdout
|
274
634
|
end
|
275
635
|
|
276
|
-
def
|
277
|
-
|
636
|
+
def convert_binary_plist_to_xml(binary_plist_string)
|
637
|
+
Mixlib::ShellOut.new("plutil -convert xml1 -o - -", :input => binary_plist_string).run_command.stdout
|
278
638
|
end
|
279
639
|
|
280
|
-
def
|
281
|
-
|
640
|
+
def convert_to_binary(string)
|
641
|
+
string.unpack('a2'*(string.size/2)).collect { |i| i.hex.chr }.join
|
642
|
+
end
|
643
|
+
|
644
|
+
def salted_sha512?(string)
|
645
|
+
!!(string =~ /^[[:xdigit:]]{136}$/)
|
646
|
+
end
|
647
|
+
|
648
|
+
def salted_sha512_password_match?
|
649
|
+
# Salt is included in the first 4 bytes of shadow data
|
650
|
+
salt = @current_resource.password.slice(0,8)
|
651
|
+
shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + @new_resource.password)
|
652
|
+
@current_resource.password == salt + shadow
|
282
653
|
end
|
654
|
+
|
655
|
+
def salted_sha512_pbkdf2?(string)
|
656
|
+
!!(string =~ /^[[:xdigit:]]{256}$/)
|
657
|
+
end
|
658
|
+
|
659
|
+
def salted_sha512_pbkdf2_password_match?
|
660
|
+
salt = convert_to_binary(@current_resource.salt)
|
661
|
+
|
662
|
+
OpenSSL::PKCS5::pbkdf2_hmac(
|
663
|
+
@new_resource.password,
|
664
|
+
salt,
|
665
|
+
@current_resource.iterations,
|
666
|
+
128,
|
667
|
+
OpenSSL::Digest::SHA512.new
|
668
|
+
).unpack('H*').first == @current_resource.password
|
669
|
+
end
|
670
|
+
|
283
671
|
end
|
284
672
|
end
|
285
673
|
end
|