require 'puppet/util/character_encoding'
# Wrapper around Ruby Etc module allowing us to manage encoding in a single
# place.
# This represents a subset of Ruby's Etc module, only the methods required by Puppet.
# On Ruby 2.1.0 and later, Etc returns strings in variable encoding depending on
# environment. The string returned will be labeled with the environment's
# encoding (Encoding.default_external), with one exception: If the environment
# encoding is 7-bit ASCII, and any individual character bit representation is
# equal to or greater than 128 - \x80 - 0b10000000 - signifying the smallest
# 8-bit big-endian value, the returned string will be in BINARY encoding instead
# of environment encoding.
#
# Barring that exception, the returned string will be labeled as encoding
# Encoding.default_external, regardless of validity or byte-width. For example,
# ruby will label a string containing a four-byte characters such as "\u{2070E}"
# as EUC_KR even though EUC_KR is a two-byte width encoding.
#
# On Ruby 2.0.x and earlier, Etc will always return string values in BINARY,
# ignoring encoding altogether.
#
# For Puppet we specifically want UTF-8 as our input from the Etc module - which
# is our input for many resource instance 'is' values. The associated 'should'
# value will basically always be coming from Puppet in UTF-8 - and written to
# disk as UTF-8. Etc is defined for Windows but the majority calls to it return
# nil and Puppet does not use it.
#
# That being said, we have cause to retain the original, pre-override string
# values. `puppet resource user`
# (Puppet::Resource::User.indirection.search('User', {})) uses self.instances to
# query for user(s) and then iterates over the results of that query again to
# obtain state for each user. If we've overridden the original user name and not
# retained the original, we've lost the ability to query the system for it
# later. Hence the Puppet::Etc::Passwd and Puppet::Etc::Group structs.
#
# We only use Etc for retrieving existing property values from the system. For
# setting property values, providers leverage system tools (i.e., `useradd`)
#
# @api private
module Puppet::Etc
class << self
# Etc::getgrent returns an Etc::Group struct object
# On first call opens /etc/group and returns parse of first entry. Each subsquent call
# returns new struct the next entry or nil if EOF. Call ::endgrent to close file.
def getgrent
override_field_values_to_utf8(::Etc.getgrent)
end
# closes handle to /etc/group file
def endgrent
::Etc.endgrent
end
# effectively equivalent to IO#rewind of /etc/group
def setgrent
::Etc.setgrent
end
# Etc::getpwent returns an Etc::Passwd struct object
# On first call opens /etc/passwd and returns parse of first entry. Each subsquent call
# returns new struct for the next entry or nil if EOF. Call ::endgrent to close file.
def getpwent
override_field_values_to_utf8(::Etc.getpwent)
end
# closes handle to /etc/passwd file
def endpwent
::Etc.endpwent
end
#effectively equivalent to IO#rewind of /etc/passwd
def setpwent
::Etc.setpwent
end
# Etc::getpwnam searches /etc/passwd file for an entry corresponding to
# username.
# returns an Etc::Passwd struct corresponding to the entry or raises
# ArgumentError if none
def getpwnam(username)
override_field_values_to_utf8(::Etc.getpwnam(username))
end
# Etc::getgrnam searches /etc/group file for an entry corresponding to groupname.
# returns an Etc::Group struct corresponding to the entry or raises
# ArgumentError if none
def getgrnam(groupname)
override_field_values_to_utf8(::Etc.getgrnam(groupname))
end
# Etc::getgrid searches /etc/group file for an entry corresponding to id.
# returns an Etc::Group struct corresponding to the entry or raises
# ArgumentError if none
def getgrgid(id)
override_field_values_to_utf8(::Etc.getgrgid(id))
end
# Etc::getpwuid searches /etc/passwd file for an entry corresponding to id.
# returns an Etc::Passwd struct corresponding to the entry or raises
# ArgumentError if none
def getpwuid(id)
override_field_values_to_utf8(::Etc.getpwuid(id))
end
# Etc::group returns a Ruby iterator that executes a block for
# each entry in the /etc/group file. The code-block is passed
# a Group struct. See getgrent above for more details.
def group
# The implementation here duplicates the logic in https://github.com/ruby/etc/blob/master/ext/etc/etc.c#L523-L537
# Note that we do not call ::Etc.group directly, because we
# want to use our wrappers for methods like getgrent, setgrent,
# endgrent, etc.
return getgrent unless block_given?
setgrent
begin
while cur_group = getgrent #rubocop:disable Lint/AssignmentInCondition
yield cur_group
end
ensure
endgrent
end
end
private
# @api private
# Defines Puppet::Etc::Passwd struct class. Contains all of the original
# member fields of Etc::Passwd, and additional "canonical_" versions of
# these fields as well. API compatible with Etc::Passwd. Because Struct.new
# defines a new Class object, we memoize to avoid superfluous extra Class
# instantiations.
def puppet_etc_passwd_class
@password_class ||= Struct.new(*Etc::Passwd.members, *Etc::Passwd.members.map { |member| "canonical_#{member}".to_sym })
end
# @api private
# Defines Puppet::Etc::Group struct class. Contains all of the original
# member fields of Etc::Group, and additional "canonical_" versions of these
# fields as well. API compatible with Etc::Group. Because Struct.new
# defines a new Class object, we memoize to avoid superfluous extra Class
# instantiations.
def puppet_etc_group_class
@group_class ||= Struct.new(*Etc::Group.members, *Etc::Group.members.map { |member| "canonical_#{member}".to_sym })
end
# Utility method for overriding the String values of a struct returned by
# the Etc module to UTF-8. Structs returned by the ruby Etc module contain
# members with fields of type String, Integer, or Array of Strings, so we
# handle these types. Otherwise ignore fields.
#
# @api private
# @param [Etc::Passwd or Etc::Group struct]
# @return [Puppet::Etc::Passwd or Puppet::Etc::Group struct] a new struct
# object with the original struct values overridden to UTF-8, if valid. For
# invalid values originating in UTF-8, invalid characters are replaced with
# '?'. For each member the struct also contains a corresponding
# :canonical_<member name> struct member.
def override_field_values_to_utf8(struct)
return nil if struct.nil?
new_struct = struct.is_a?(Etc::Passwd) ? puppet_etc_passwd_class.new : puppet_etc_group_class.new
struct.each_pair do |member, value|
if value.is_a?(String)
new_struct["canonical_#{member}".to_sym] = value.dup
new_struct[member] = Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(value).scrub
elsif value.is_a?(Array)
new_struct["canonical_#{member}".to_sym] = value.inject([]) { |acc, elem| acc << elem.dup }
new_struct[member] = value.inject([]) do |acc, elem|
acc << Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(elem).scrub
end
else
new_struct["canonical_#{member}".to_sym] = value
new_struct[member] = value
end
end
new_struct
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez