diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-04-21 19:14:59 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-04-21 19:14:59 +0000 |
commit | a0b4553d3ad64d45c393443d84f0b02298e582b5 (patch) | |
tree | 55b5ff23edc624f7b5b9d988b64466930f095d72 /lib | |
parent | 63cdc6cf6fa2c680173532cbbf989ee97c664e83 (diff) | |
download | puppet-a0b4553d3ad64d45c393443d84f0b02298e582b5.tar.gz puppet-a0b4553d3ad64d45c393443d84f0b02298e582b5.tar.xz puppet-a0b4553d3ad64d45c393443d84f0b02298e582b5.zip |
Final commit before 0.16.0
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1129 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/client/master.rb | 15 | ||||
-rw-r--r-- | lib/puppet/server.rb | 11 | ||||
-rw-r--r-- | lib/puppet/server/authconfig.rb | 166 | ||||
-rwxr-xr-x | lib/puppet/server/authstore.rb | 2 | ||||
-rwxr-xr-x | lib/puppet/server/pelement.rb | 37 | ||||
-rwxr-xr-x | lib/puppet/server/rights.rb | 27 | ||||
-rw-r--r-- | lib/puppet/server/servlet.rb | 25 | ||||
-rw-r--r-- | lib/puppet/type.rb | 6 | ||||
-rw-r--r-- | lib/puppet/type/package.rb | 6 | ||||
-rwxr-xr-x | lib/puppet/type/package/rpm.rb | 7 | ||||
-rwxr-xr-x | lib/puppet/type/parsedtype/port.rb | 8 |
11 files changed, 277 insertions, 33 deletions
diff --git a/lib/puppet/client/master.rb b/lib/puppet/client/master.rb index 30e923d76..999938284 100644 --- a/lib/puppet/client/master.rb +++ b/lib/puppet/client/master.rb @@ -19,6 +19,8 @@ class Puppet::Client::MasterClient < Puppet::Client @drivername = :Master + attr_accessor :objects + def self.facts facts = {} Facter.each { |name,fact| @@ -32,29 +34,20 @@ class Puppet::Client::MasterClient < Puppet::Client facts end - # This method is how the client receives the tree of Transportable - # objects. For now, just descend into the tree and perform and - # necessary manipulations. + # This method actually applies the configuration. def apply dostorage() unless defined? @objects raise Puppet::Error, "Cannot apply; objects not defined" end - #Puppet.err :yay - #p @objects - #Puppet.err :mark - #@objects = @objects.to_type # this is a gross hack... but i don't see a good way around it # set all of the variables to empty Puppet::Transaction.init - # For now we just evaluate the top-level object, but eventually - # there will be schedules and such associated with each object, - # and probably with the container itself. transaction = @objects.evaluate - #transaction = Puppet::Transaction.new(objects) transaction.toplevel = true + begin transaction.evaluate rescue Puppet::Error => detail diff --git a/lib/puppet/server.rb b/lib/puppet/server.rb index 4fadc3987..911785e3f 100644 --- a/lib/puppet/server.rb +++ b/lib/puppet/server.rb @@ -27,6 +27,16 @@ module Puppet class Server < WEBrick::HTTPServer include Puppet::Daemon + # Create our config object if necessary. This works even if + # there's no configuration file. + def authconfig + unless defined? @authconfig + @authconfig = Puppet::Server::AuthConfig.new() + end + + @authconfig + end + def initialize(hash = {}) Puppet.info "Starting server for Puppet version %s" % Puppet.version daemonize = nil @@ -158,6 +168,7 @@ module Puppet end require 'puppet/server/authstore' +require 'puppet/server/authconfig' require 'puppet/server/servlet' require 'puppet/server/master' require 'puppet/server/ca' diff --git a/lib/puppet/server/authconfig.rb b/lib/puppet/server/authconfig.rb new file mode 100644 index 000000000..05901a207 --- /dev/null +++ b/lib/puppet/server/authconfig.rb @@ -0,0 +1,166 @@ +require 'puppet/parsedfile' +require 'puppet/server/rights' + +module Puppet +class Server + +class ConfigurationError < Puppet::Error; end + +class AuthConfig < Puppet::ParsedFile + Puppet.config.setdefaults(:puppet, + :authconfig => [ "$confdir/namespaceauth.conf", + "The configuration file that defines the rights to the different + namespaces and methods. This can be used as a coarse-grained + authorization system for both ``puppetd`` and ``puppetmasterd``." + ] + ) + + # 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?(name, host, ip) + namespace, method = name.to_s.split(".") + unless namespace and method + raise ArgumentError, "Invalid method name %s" % name + end + + name = name.intern if name.is_a? String + namespace = namespace.intern + method = method.intern + + if @rights.include?(name) + return @rights[name].allowed?(host, ip) + elsif @rights.include?(namespace) + return @rights[namespace].allowed?(host, 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] + return unless self.exists? + super(file) + @rights = Rights.new + @configstamp = @configtimeout = @configstatted = nil + + 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 + end + else + return + end + end + end + + parse() + + @configstamp = File.stat(@file).ctime + @configstatted = Time.now + end + + private + + def parse + newrights = Puppet::Server::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 +end + +# $Id$ diff --git a/lib/puppet/server/authstore.rb b/lib/puppet/server/authstore.rb index 70f518f7c..0573a2d36 100755 --- a/lib/puppet/server/authstore.rb +++ b/lib/puppet/server/authstore.rb @@ -15,7 +15,7 @@ class Server [:name, [:hostname, :domain]] ] - Puppet::Util.logmethods(self, false) + Puppet::Util.logmethods(self, true) def allow(pattern) # a simple way to allow anyone at all to connect diff --git a/lib/puppet/server/pelement.rb b/lib/puppet/server/pelement.rb index 9799a36af..b7fe35f7c 100755 --- a/lib/puppet/server/pelement.rb +++ b/lib/puppet/server/pelement.rb @@ -8,10 +8,45 @@ class Server::PElement < Server::Handler attr_accessor :local @interface = XMLRPC::Service::Interface.new("pelementserver") { |iface| + iface.add_method("string apply(string, string)") iface.add_method("string describe(string, string, array, array)") iface.add_method("string list(string, array, string)") } + # Apply a TransBucket as a transaction. + def apply(bucket, format = "yaml", client = nil, clientip = nil) + unless @local + begin + case format + when "yaml": + tmp = YAML::load(CGI.unescape(bucket)) + bucket = tmp + else + raise Puppet::Error, "Unsupported format '%s'" % format + end + rescue => detail + raise Puppet::Error, "Could not load YAML TransBucket: %s" % detail + end + end + + component = bucket.to_type + + # Create a client, but specify the remote machine as the server + # because the class requires it, even though it's unused + client = Puppet::Client::MasterClient.new(:Server => client||"localhost") + + # Set the objects + client.objects = component + + # And then apply the configuration. This way we're reusing all + # the code in there. It should probably just be separated out, though. + transaction = client.apply + + # It'd be nice to return some kind of report, but... at this point + # we have no such facility. + return "success" + end + # Describe a given object. This returns the 'is' values for every state # available on the object type. def describe(type, name, retrieve = nil, ignore = [], format = "yaml", client = nil, clientip = nil) @@ -58,7 +93,7 @@ class Server::PElement < Server::Handler str = nil case format when "yaml": - str = YAML.dump(trans) + str = CGI.escape(YAML::dump(trans)) else raise XMLRPC::FaultException.new( 1, "Unavailable config format %s" % format diff --git a/lib/puppet/server/rights.rb b/lib/puppet/server/rights.rb index cd4b4b978..0ed12a122 100755 --- a/lib/puppet/server/rights.rb +++ b/lib/puppet/server/rights.rb @@ -4,11 +4,13 @@ require 'puppet/server/authstore' module Puppet class Server # Define a set of rights and who has access to them. - class Rights + class Rights < Hash # We basically just proxy directly to our rights. Each Right stores # its own auth abilities. [:allow, :allowed?, :deny].each do |method| define_method(method) do |name, *args| + name = name.intern if name.is_a? String + if obj = right(name) obj.send(method, *args) else @@ -17,18 +19,19 @@ class Server end end - def initialize - @rights = {} + def [](name) + name = name.intern if name.is_a? String + super(name) end # Define a new right to which access can be provided. def newright(name) name = name.intern if name.is_a? String shortname = Right.shortname(name) - if @rights.include? shortname + if self.include? name raise ArgumentError, "Right '%s' is already defined" % name else - @rights[shortname] = Right.new(name, shortname) + self[name] = Right.new(name, shortname) end end @@ -36,13 +39,16 @@ class Server # Retrieve a right by name. def right(name) - @rights[Right.shortname(name)] + name = name.intern if name.is_a? String + self[name] end # A right. class Right < AuthStore attr_accessor :name, :shortname + Puppet::Util.logmethods(self, true) + def self.shortname(name) name.to_s[0..0] end @@ -55,6 +61,15 @@ class Server end super() end + + def to_s + "access[%s]" % @name + end + + # There's no real check to do at this point + def valid? + true + end end end end diff --git a/lib/puppet/server/servlet.rb b/lib/puppet/server/servlet.rb index f0b32efb0..dd34fcd03 100644 --- a/lib/puppet/server/servlet.rb +++ b/lib/puppet/server/servlet.rb @@ -34,15 +34,29 @@ class Server end # Verify that our client has access. We allow untrusted access to - # puppetca methods but none others. + # puppetca methods but no others. def authorize(request, method) namespace = method.sub(/\..+/, '') client = request.peeraddr[2] ip = request.peeraddr[3] if request.client_cert - Servlet.log "Allowing %s(%s) trusted access to %s" % - [client, ip, method] - return true + if @puppetserver.authconfig.exists? + return @puppetserver.authconfig.allowed?(method, client, ip) + else + # This is pretty hackish, but... + # This means we can't actually test this method at this point. + # The next release of Puppet will almost definitely require + # this file to exist or will default to denying all access. + if Puppet.name == "puppetmasterd" or defined? Test::Unit::TestCase + Servlet.log "Allowing %s(%s) trusted access to %s" % + [client, ip, method] + return true + else + Servlet.log "Denying %s(%s) trusted access to %s on %s" % + [client, ip, method, Puppet.name] + return false + end + end else if method =~ /^puppetca\./ Puppet.notice "Allowing %s(%s) untrusted access to CA methods" % @@ -69,8 +83,7 @@ class Server end def initialize(server, handlers) - #Puppet.info server.inspect - + @puppetserver = server # the servlet base class does not consume any arguments # and its BasicServer base class only accepts a 'class_delim' # option which won't change in Puppet at all diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index e28888833..0d727c522 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -1837,8 +1837,8 @@ class Type < Puppet::Element states.each { |state| unless state.insync? - #state.debug("Not in sync: %s vs %s" % - # [state.is.inspect, state.should.inspect]) + state.debug("Not in sync: %s vs %s" % + [state.is.inspect, state.should.inspect]) insync = false #else # state.debug("In sync") @@ -2197,7 +2197,7 @@ class Type < Puppet::Element newvalues(:verbose) munge do |loglevel| - val = super + val = super(loglevel) if val == :verbose val = :info end diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index e01555df0..5dee20c7e 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -84,6 +84,7 @@ module Puppet # Autoload the package types, if they're not already defined. def self.pkgtype(name) + #name = name[0] if name.is_a? Array name = name.intern if name.is_a? String @pkgtypes ||= {} unless @pkgtypes.include? name @@ -91,8 +92,9 @@ module Puppet require "puppet/type/package/#{name}" unless @pkgtypes.include? name + Puppet.warning @pkgtypes.keys raise Puppet::DevError, - "Loaded %s but pkgtype was not created" % name + "Loaded %s but pkgtype was not created" % name.inspect end rescue LoadError raise Puppet::Error, "Could not load package type %s" % name @@ -284,6 +286,7 @@ module Puppet validate do |value| + value = value[0] if value.is_a? Array unless @parent.class.pkgtype(value) raise ArgumentError, "Invalid package type '%s'" % value end @@ -291,6 +294,7 @@ module Puppet munge do |type| + type = type[0] if type.is_a? Array if type.is_a? String type = type.intern end diff --git a/lib/puppet/type/package/rpm.rb b/lib/puppet/type/package/rpm.rb index 2bcb41fbd..bfdc871fe 100755 --- a/lib/puppet/type/package/rpm.rb +++ b/lib/puppet/type/package/rpm.rb @@ -72,6 +72,13 @@ module Puppet fields.zip(match.captures) { |field,value| hash[field] = value } + if self.is_a? Puppet::Type and type = self[:type] + hash[:type] = type + elsif self.is_a? Module and self.respond_to? :name + hash[:type] = self.name + else + raise Puppet::DevError, "Cannot determine package type" + end packages.push Puppet.type(:package).installedpkg(hash) else raise "failed to match rpm line %s" % line diff --git a/lib/puppet/type/parsedtype/port.rb b/lib/puppet/type/parsedtype/port.rb index 48cabfe52..8eac31def 100755 --- a/lib/puppet/type/parsedtype/port.rb +++ b/lib/puppet/type/parsedtype/port.rb @@ -131,13 +131,14 @@ module Puppet # Parse a services file # - # This method also stores existing comments, and it stores all host - # jobs in order, mostly so that comments are retained in the order - # they were written and in proximity to the same jobs. + # This method also stores existing comments, and it stores all port + # info in order, mostly so that comments are retained in the order + # they were written and in proximity to the same ports. def self.parse(text) count = 0 hash = {} text.chomp.split("\n").each { |line| + hash.clear case line when /^#/, /^\s*$/: # add comments and blank lines to the list as they are @@ -185,7 +186,6 @@ module Puppet hash2obj(hash) - hash.clear count += 1 end } |