require 'puppet/util/loadedfile' require 'puppet/network/rights' module Puppet class ConfigurationError < Puppet::Error; end class Network::AuthConfig < Puppet::Util::LoadedFile def self.main unless defined? @main @main = self.new() end @main end # Just proxy the setting methods to our rights stuff [:allow, :deny].each do |method| define_method(method) do |*args| @rights.send(method, *args) end end # Here we add a little bit of semantics. They can set auth on a whole # namespace or on just a single method in the namespace. def allowed?(request) name = request.call.intern namespace = request.handler.intern method = request.method.intern read() if @rights.include?(name) return @rights[name].allowed?(request.name, request.ip) elsif @rights.include?(namespace) return @rights[namespace].allowed?(request.name, request.ip) else return false end end # Does the file exist? Puppetmasterd does not require it, but # puppetd does. def exists? FileTest.exists?(@file) end def initialize(file = nil, parsenow = true) @file ||= Puppet[:authconfig] unless @file raise Puppet::DevError, "No authconfig file defined" end return unless self.exists? super(@file) @rights = Puppet::Network::Rights.new @configstamp = @configstatted = nil @configtimeout = 60 if parsenow read() end end # Read the configuration file. def read return unless FileTest.exists?(@file) if @configstamp if @configtimeout and @configstatted if Time.now - @configstatted > @configtimeout @configstatted = Time.now tmp = File.stat(@file).ctime if tmp == @configstamp return else Puppet.notice "%s vs %s" % [tmp, @configstamp] end else return end else Puppet.notice "%s and %s" % [@configtimeout, @configstatted] end end parse() @configstamp = File.stat(@file).ctime @configstatted = Time.now end private def parse newrights = Puppet::Network::Rights.new begin File.open(@file) { |f| right = nil count = 1 f.each { |line| case line when /^\s*#/: next # skip comments when /^\s*$/: next # skip blank lines when /\[([\w.]+)\]/: # "namespace" or "namespace.method" name = $1 if newrights.include?(name) raise FileServerError, "%s is already set at %s" % [newrights[name], name] end newrights.newright(name) right = newrights[name] when /^\s*(\w+)\s+(.+)$/: var = $1 value = $2 case var when "allow": value.split(/\s*,\s*/).each { |val| begin right.info "allowing %s access" % val right.allow(val) rescue AuthStoreError => detail raise ConfigurationError, "%s at line %s of %s" % [detail.to_s, count, @config] end } when "deny": value.split(/\s*,\s*/).each { |val| begin right.info "denying %s access" % val right.deny(val) rescue AuthStoreError => detail raise ConfigurationError, "%s at line %s of %s" % [detail.to_s, count, @config] end } else raise ConfigurationError, "Invalid argument '%s' at line %s" % [var, count] end else raise ConfigurationError, "Invalid line %s: %s" % [count, line] end count += 1 } } rescue Errno::EACCES => detail Puppet.err "Configuration error: Cannot read %s; cannot serve" % @file #raise Puppet::Error, "Cannot read %s" % @config rescue Errno::ENOENT => detail Puppet.err "Configuration error: '%s' does not exit; cannot serve" % @file #raise Puppet::Error, "%s does not exit" % @config #rescue FileServerError => detail # Puppet.err "FileServer error: %s" % detail end # Verify each of the rights are valid. # We let the check raise an error, so that it can raise an error # pointing to the specific problem. newrights.each { |name, right| right.valid? } @rights = newrights end end end