1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
# frozen_string_literal: true
# A file.
class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting
class SettingError < StandardError; end
# An unspecified user or group
#
# @api private
class Unspecified
def value
nil
end
end
# A "root" user or group
#
# @api private
class Root
def value
"root"
end
end
# A "service" user or group that picks up values from settings when the
# referenced user or group is safe to use (it exists or will be created), and
# uses the given fallback value when not safe.
#
# @api private
class Service
# @param name [Symbol] the name of the setting to use as the service value
# @param fallback [String, nil] the value to use when the service value cannot be used
# @param settings [Puppet::Settings] the puppet settings object
# @param available_method [Symbol] the name of the method to call on
# settings to determine if the value in settings is available on the system
#
def initialize(name, fallback, settings, available_method)
@settings = settings
@available_method = available_method
@name = name
@fallback = fallback
end
def value
if safe_to_use_settings_value?
@settings[@name]
else
@fallback
end
end
private
def safe_to_use_settings_value?
@settings[:mkusers] or @settings.send(@available_method)
end
end
attr_accessor :mode
def initialize(args)
@group = Unspecified.new
@owner = Unspecified.new
super(args)
end
# @param value [String] the group to use on the created file (can only be "root" or "service")
# @api public
def group=(value)
@group = case value
when "root"
Root.new
when "service"
# Group falls back to `nil` because we cannot assume that a "root" group exists.
# Some systems have root group, others have wheel, others have something else.
Service.new(:group, nil, @settings, :service_group_available?)
else
unknown_value(':group', value)
end
end
# @param value [String] the owner to use on the created file (can only be "root" or "service")
# @api public
def owner=(value)
@owner = case value
when "root"
Root.new
when "service"
Service.new(:user, "root", @settings, :service_user_available?)
else
unknown_value(':owner', value)
end
end
# @return [String, nil] the name of the group to use for the file or nil if the group should not be managed
# @api public
def group
@group.value
end
# @return [String, nil] the name of the user to use for the file or nil if the user should not be managed
# @api public
def owner
@owner.value
end
def set_meta(meta)
self.owner = meta.owner if meta.owner
self.group = meta.group if meta.group
self.mode = meta.mode if meta.mode
end
def munge(value)
if value.is_a?(String) and value != ':memory:' # for sqlite3 in-memory tests
value = File.expand_path(value)
end
value
end
def type
:file
end
# Turn our setting thing into a Puppet::Resource instance.
def to_resource
type = self.type
return nil unless type
path = value
return nil unless path.is_a?(String)
# Make sure the paths are fully qualified.
path = File.expand_path(path)
return nil unless type == :directory || Puppet::FileSystem.exist?(path)
return nil if path =~ %r{^/dev} || path =~ %r{^[A-Z]:/dev}i
resource = Puppet::Resource.new(:file, path)
if Puppet[:manage_internal_file_permissions]
if mode
# This ends up mimicking the munge method of the mode
# parameter to make sure that we're always passing the string
# version of the octal number. If we were setting the
# 'should' value for mode rather than the 'is', then the munge
# method would be called for us automatically. Normally, one
# wouldn't need to call the munge method manually, since
# 'should' gets set by the provider and it should be able to
# provide the data in the appropriate format.
mode = self.mode
mode = mode.to_i(8) if mode.is_a?(String)
mode = mode.to_s(8)
resource[:mode] = mode
end
# REMIND fails on Windows because chown/chgrp functionality not supported yet
if Puppet.features.root? and !Puppet::Util::Platform.windows?
resource[:owner] = owner if owner
resource[:group] = group if group
end
end
resource[:ensure] = type
resource[:loglevel] = :debug
resource[:links] = :follow
resource[:backup] = false
resource.tag(section, name, "settings")
resource
end
# @api private
# @param option [String] Extra file operation mode information to use
# (defaults to read-only mode 'r')
# This is the standard mechanism Ruby uses in the IO class, and therefore
# encoding may be explicitly like fmode : encoding or fmode : "BOM|UTF-*"
# for example, a:ASCII or w+:UTF-8
def exclusive_open(option = 'r', &block)
controlled_access do |mode|
Puppet::FileSystem.exclusive_open(file(), mode, option, &block)
end
end
# @api private
# @param option [String] Extra file operation mode information to use
# (defaults to read-only mode 'r')
# This is the standard mechanism Ruby uses in the IO class, and therefore
# encoding may be explicitly like fmode : encoding or fmode : "BOM|UTF-*"
# for example, a:ASCII or w+:UTF-8
def open(option = 'r', &block)
controlled_access do |mode|
Puppet::FileSystem.open(file, mode, option, &block)
end
end
private
def file
Puppet::FileSystem.pathname(value)
end
def unknown_value(parameter, value)
raise SettingError, _("The %{parameter} parameter for the setting '%{name}' must be either 'root' or 'service', not '%{value}'") % { parameter: parameter, name: name, value: value }
end
def controlled_access(&block)
chown = nil
if Puppet.features.root?
chown = [owner, group]
else
chown = [nil, nil]
end
Puppet::Util::SUIDManager.asuser(*chown) do
# Update the umask to make non-executable files
Puppet::Util.withumask(File.umask ^ 0o111) do
yielded_value = case mode
when String
mode.to_i(8)
when NilClass
0o640
else
mode
end
yield yielded_value
end
end
end
end
|