+1 for inclusion in testing/master (TBD)

On Tue, Dec 29, 2009 at 6:53 AM, Brice Figureau
<[email protected]> wrote:
> This patch adds several things:
>  * certificate fingerprinting in --list mode
>  * a puppetca action called "--fingerprint" to display fingerprints
> of given certificates (or all including CSR)
>  * a --fingerprint puppetd option to display client certificates
>  * each time a CSR is generated, its fingerprint is displayed in the log
>
> It is also possible to use --digest in puppetca and puppetd to specify a 
> specific digest
> algorithm.
>
> Signed-off-by: Brice Figureau <[email protected]>
> ---
>  lib/puppet/application/puppetca.rb                |    8 +-
>  lib/puppet/application/puppetd.rb                 |   33 +++++--
>  lib/puppet/ssl/base.rb                            |   17 +++
>  lib/puppet/ssl/certificate_authority.rb           |    9 ++-
>  lib/puppet/ssl/certificate_authority/interface.rb |   28 ++++--
>  lib/puppet/ssl/certificate_request.rb             |    2 +
>  sbin/puppetca                                     |   11 ++-
>  sbin/puppetd                                      |   23 ++++-
>  spec/unit/application/puppetca.rb                 |   15 +++
>  spec/unit/application/puppetd.rb                  |   75 +++++++++++++-
>  spec/unit/ssl/base.rb                             |   40 +++++++
>  spec/unit/ssl/certificate_authority.rb            |   37 ++++++-
>  spec/unit/ssl/certificate_authority/interface.rb  |  114 
> +++++++++++++++------
>  spec/unit/ssl/certificate_request.rb              |   14 +++-
>  14 files changed, 367 insertions(+), 59 deletions(-)
>  create mode 100755 spec/unit/ssl/base.rb
>
> diff --git a/lib/puppet/application/puppetca.rb 
> b/lib/puppet/application/puppetca.rb
> index adc1a6f..7362f2a 100644
> --- a/lib/puppet/application/puppetca.rb
> +++ b/lib/puppet/application/puppetca.rb
> @@ -6,7 +6,7 @@ Puppet::Application.new(:puppetca) do
>
>     should_parse_config
>
> -    attr_accessor :mode, :all, :ca
> +    attr_accessor :mode, :all, :ca, :digest
>
>     def find_mode(opt)
>         modes = 
> Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS
> @@ -22,6 +22,10 @@ Puppet::Application.new(:puppetca) do
>         @all = true
>     end
>
> +    option("--digest DIGEST") do |arg|
> +       �...@digest = arg
> +    end
> +
>     option("--debug", "-d") do |arg|
>         Puppet::Util::Log.level = :debug
>     end
> @@ -44,7 +48,7 @@ Puppet::Application.new(:puppetca) do
>         end
>         begin
>             @ca.apply(:revoke, :to => hosts) if @mode == :destroy
> -           �[email protected](@mode, :to => hosts)
> +           �[email protected](@mode, :to => hosts, :digest => @digest)
>         rescue => detail
>             puts detail.backtrace if Puppet[:trace]
>             puts detail.to_s
> diff --git a/lib/puppet/application/puppetd.rb 
> b/lib/puppet/application/puppetd.rb
> index c99b9ea..ed2c450 100644
> --- a/lib/puppet/application/puppetd.rb
> +++ b/lib/puppet/application/puppetd.rb
> @@ -9,7 +9,7 @@ Puppet::Application.new(:puppetd) do
>
>     should_parse_config
>
> -    attr_accessor :explicit_waitforcert, :args, :agent, :daemon
> +    attr_accessor :explicit_waitforcert, :args, :agent, :daemon, :host
>
>     preinit do
>         # Do an initial trap, so that cancels don't get a stack trace.
> @@ -30,7 +30,9 @@ Puppet::Application.new(:puppetd) do
>             :disable => false,
>             :client => true,
>             :fqdn => nil,
> -            :serve => []
> +            :serve => [],
> +            :digest => :MD5,
> +            :fingerprint => false,
>         }.each do |opt,val|
>             options[opt] = val
>         end
> @@ -49,6 +51,9 @@ Puppet::Application.new(:puppetd) do
>     option("--test","-t")
>     option("--verbose","-v")
>
> +    option("--fingerprint")
> +    option("--digest DIGEST")
> +
>     option("--serve HANDLER", "-s") do |arg|
>         if Puppet::Network::Handler.handler(arg)
>             options[:serve] << arg.to_sym
> @@ -92,10 +97,20 @@ Puppet::Application.new(:puppetd) do
>     end
>
>     dispatch do
> +        return :fingerprint if options[:fingerprint]
>         return :onetime if options[:onetime]
>         return :main
>     end
>
> +    command(:fingerprint) do
> +        unless cert = host.certificate || host.certificate_request
> +           $stderr.puts "Fingerprint asked but no certificate nor 
> certificate request have yet been issued"
> +           exit(1)
> +           return
> +        end
> +        Puppet.notice cert.fingerprint(options[:digest])
> +    end
> +
>     command(:onetime) do
>         unless options[:client]
>             $stderr.puts "onetime is specified but there is no client"
> @@ -220,10 +235,10 @@ Puppet::Application.new(:puppetd) do
>
>         Puppet.settings.use :main, :puppetd, :ssl
>
> -        # We need to specify a ca location for things to work, but
> -        # until the REST cert transfers are working, it needs to
> -        # be local.
> -        Puppet::SSL::Host.ca_location = :remote
> +        # We need to specify a ca location for things to work
> +        # in fingerprint mode we just need access to the local files and
> +        # we don't need a ca.
> +        Puppet::SSL::Host.ca_location = options[:fingerprint] ? :none : 
> :remote
>
>         Puppet::Transaction::Report.terminus_class = :rest
>
> @@ -246,8 +261,10 @@ Puppet::Application.new(:puppetd) do
>             @daemon.daemonize
>         end
>
> -        host = Puppet::SSL::Host.new
> -        cert = host.wait_for_cert(options[:waitforcert])
> +       �...@host = Puppet::SSL::Host.new
> +        unless options[:fingerprint]
> +            cert = @host.wait_for_cert(options[:waitforcert])
> +        end
>
>         @objects = []
>
> diff --git a/lib/puppet/ssl/base.rb b/lib/puppet/ssl/base.rb
> index d67861f..6c74b75 100644
> --- a/lib/puppet/ssl/base.rb
> +++ b/lib/puppet/ssl/base.rb
> @@ -54,6 +54,23 @@ class Puppet::SSL::Base
>         content.to_text
>     end
>
> +    def fingerprint(md = :MD5)
> +        require 'openssl/digest'
> +
> +        # ruby 1.8.x openssl digest constants are string
> +        # but in 1.9.x they are symbols
> +        mds = md.to_s.upcase
> +        if OpenSSL::Digest.constants.include?(mds)
> +            md = mds
> +        elsif OpenSSL::Digest.constants.include?(mds.to_sym)
> +            md = mds.to_sym
> +        else
> +            raise ArgumentError, "#{md} is not a valid digest algorithm for 
> fingerprinting certificate #{name}"
> +        end
> +
> +        OpenSSL::Digest.hexdigest(md, 
> content.to_der).scan(/../).join(':').upcase
> +    end
> +
>     private
>
>     def wrapped_class
> diff --git a/lib/puppet/ssl/certificate_authority.rb 
> b/lib/puppet/ssl/certificate_authority.rb
> index 8e4fd7a..9fe67cc 100644
> --- a/lib/puppet/ssl/certificate_authority.rb
> +++ b/lib/puppet/ssl/certificate_authority.rb
> @@ -53,7 +53,7 @@ class Puppet::SSL::CertificateAuthority
>         unless options[:to]
>             raise ArgumentError, "You must specify the hosts to apply to; 
> valid values are an array or the symbol :all"
>         end
> -        applier = Interface.new(method, options[:to])
> +        applier = Interface.new(method, options)
>
>         applier.apply(self)
>     end
> @@ -291,6 +291,13 @@ class Puppet::SSL::CertificateAuthority
>         end
>     end
>
> +    def fingerprint(name, md = :MD5)
> +        unless cert = Puppet::SSL::Certificate.find(name) || 
> Puppet::SSL::CertificateRequest.find(name)
> +            raise ArgumentError, "Could not find a certificate or csr for 
> %s" % name
> +        end
> +        cert.fingerprint(md)
> +    end
> +
>     # List the waiting certificate requests.
>     def waiting?
>         Puppet::SSL::CertificateRequest.search("*").collect { |r| r.name }
> diff --git a/lib/puppet/ssl/certificate_authority/interface.rb 
> b/lib/puppet/ssl/certificate_authority/interface.rb
> index 3f91434..d2dc7b9 100644
> --- a/lib/puppet/ssl/certificate_authority/interface.rb
> +++ b/lib/puppet/ssl/certificate_authority/interface.rb
> @@ -2,11 +2,11 @@
>  # on the CA.  It's only used by the 'puppetca' executable, and its
>  # job is to provide a CLI-like interface to the CA class.
>  class Puppet::SSL::CertificateAuthority::Interface
> -    INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, 
> :verify]
> +    INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, 
> :verify, :fingerprint]
>
>     class InterfaceError < ArgumentError; end
>
> -    attr_reader :method, :subjects
> +    attr_reader :method, :subjects, :digest
>
>     # Actually perform the work.
>     def apply(ca)
> @@ -38,9 +38,10 @@ class Puppet::SSL::CertificateAuthority::Interface
>         end
>     end
>
> -    def initialize(method, subjects)
> +    def initialize(method, options)
>         self.method = method
> -        self.subjects = subjects
> +        self.subjects = options[:to]
> +       �...@digest = options[:digest] || :MD5
>     end
>
>     # List the hosts.
> @@ -67,11 +68,11 @@ class Puppet::SSL::CertificateAuthority::Interface
>                 invalid = details.to_s
>             end
>             if not invalid and signed.include?(host)
> -                puts "+ " + host
> +                puts "+ #{host} (#{ca.fingerprint(host, @digest)})"
>             elsif invalid
> -                puts "- " + host + " (" + invalid + ")"
> +                puts "- #{host} (#{ca.fingerprint(host, @digest)}) 
> (#{invalid})"
>             else
> -                puts host
> +                puts "#{host} (#{ca.fingerprint(host, @digest)})"
>             end
>         end
>     end
> @@ -84,7 +85,7 @@ class Puppet::SSL::CertificateAuthority::Interface
>
>     # Print certificate information.
>     def print(ca)
> -        (subjects == :all ? ca.list : subjects).each do |host|
> +        (subjects == :all ? ca.list  : subjects).each do |host|
>             if value = ca.print(host)
>                 puts value
>             else
> @@ -93,6 +94,17 @@ class Puppet::SSL::CertificateAuthority::Interface
>         end
>     end
>
> +    # Print certificate information.
> +    def fingerprint(ca)
> +        (subjects == :all ? ca.list + ca.waiting?: subjects).each do |host|
> +            if value = ca.fingerprint(host, @digest)
> +                puts "#{host} #{value}"
> +            else
> +                Puppet.err "Could not find certificate for %s" % host
> +            end
> +        end
> +    end
> +
>     # Sign a given certificate.
>     def sign(ca)
>         list = subjects == :all ? ca.waiting? : subjects
> diff --git a/lib/puppet/ssl/certificate_request.rb 
> b/lib/puppet/ssl/certificate_request.rb
> index 4008aba..f18fe4a 100644
> --- a/lib/puppet/ssl/certificate_request.rb
> +++ b/lib/puppet/ssl/certificate_request.rb
> @@ -43,6 +43,8 @@ class Puppet::SSL::CertificateRequest < Puppet::SSL::Base
>         raise Puppet::Error, "CSR sign verification failed; you need to clean 
> the certificate request for %s on the server" % name unless 
> csr.verify(key.public_key)
>
>         @content = csr
> +        Puppet.info "Certificate Request fingerprint (md5): #{fingerprint}"
> +       �...@content
>     end
>
>     def save(args = {})
> diff --git a/sbin/puppetca b/sbin/puppetca
> index 27ba916..eab594b 100755
> --- a/sbin/puppetca
> +++ b/sbin/puppetca
> @@ -10,7 +10,8 @@
>  #
>  #   puppetca [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
>  #               [-g|--generate] [-l|--list] [-s|--sign] [-r|--revoke]
> -#               [-p|--print] [-c|--clean] [--verify] [host]
> +#               [-p|--print] [-c|--clean] [--verify] [--digest DIGEST]
> +#               [--fingerprint] [host]
>  #
>  # = Description
>  #
> @@ -35,6 +36,11 @@
>  #   Operate on all items.  Currently only makes sense with '--sign',
>  #   '--clean', or '--list'.
>  #
> +# digest::
> +#   Set the digest for fingerprinting (defaults to md5). Valid values depends
> +#   on your openssl and openssl ruby extension version, but should contain at
> +#   least md5, sha1, md2, sha256.
> +#
>  # clean::
>  #    Remove all files related to a host from puppetca's storage. This is
>  #    useful when rebuilding hosts, since new certificate signing requests
> @@ -62,6 +68,9 @@
>  # print::
>  #   Print the full-text version of a host's certificate.
>  #
> +# fingerprint::
> +#   Print the DIGEST (defaults to md5) fingerprint of a host's certificate.
> +#
>  # revoke::
>  #   Revoke the certificate of a client. The certificate can be specified
>  #   either by its serial number, given as a decimal number or a hexadecimal
> diff --git a/sbin/puppetd b/sbin/puppetd
> index bf7d028..fd78dc6 100755
> --- a/sbin/puppetd
> +++ b/sbin/puppetd
> @@ -12,7 +12,8 @@
>  #       [--detailed-exitcodes] [--disable] [--enable]
>  #       [-h|--help] [--fqdn <host name>] [-l|--logdest syslog|<file>|console]
>  #       [-o|--onetime] [--serve <handler>] [-t|--test] [--noop]
> -#       [-V|--version] [-v|--verbose] [-w|--waitforcert <seconds>]
> +#       [--digest <digest>] [--fingerprint] [-V|--version]
> +#       [-v|--verbose] [-w|--waitforcert <seconds>]
>  #
>  # = Description
>  #
> @@ -35,7 +36,7 @@
>  # configuration every 30 minutes.
>  #
>  # Some flags are meant specifically for interactive use -- in particular,
> -# +test+ and +tags+ are useful.  +test+ enables verbose logging, causes
> +# +test+, +tags+ or +fingerprint+ are useful. +test+ enables verbose 
> logging, causes
>  # the daemon to stay in the foreground, exits if the server's configuration 
> is
>  # invalid (this happens if, for instance, you've left a syntax error on the
>  # server), and exits after running the configuration once (rather than 
> hanging
> @@ -51,6 +52,15 @@
>  # which would only apply that small portion of the configuration during your
>  # testing, rather than applying the whole thing.
>  #
> +# +fingerprint+ is a one-time flag. In this mode +puppetd+ will run once and
> +# display on the console (and in the log) the current certificate (or 
> certificate
> +# request) fingerprint. Providing the +--digest+ option allows to use a 
> different
> +# digest algorithm to generate the fingerprint. The main use is to verify 
> that
> +# before signing a certificate request on the master, the certificate 
> request the
> +# master received is the same as the one the client sent (to prevent against
> +# man-in-the-middle attacks when signing certificates).
> +#
> +#
>  # = Options
>  #
>  # Note that any configuration parameter that's valid in the configuration 
> file
> @@ -72,6 +82,11 @@
>  # debug::
>  #   Enable full debugging.
>  #
> +# digest::
> +#   Change the certificate fingerprinting digest algorithm. The default is 
> MD5.
> +#   Valid values depends on the version of OpenSSL installed, but should 
> always
> +#   at least contain MD5, MD2, SHA1 and SHA256.
> +#
>  # detailed-exitcodes::
>  #   Provide transaction information via exit codes.  If this is enabled, an
>  #   exit code of '2' means there were changes, and an exit code of '4' means
> @@ -119,6 +134,10 @@
>  #   Run the configuration once, rather than as a long-running daemon.  This 
> is
>  #   useful for interactively running puppetd.
>  #
> +# fingerprint::
> +#   Display the current certificate or certificate signing request 
> fingerprint
> +#   and then exit. Use the +--digest+ option to change the digest algorithm 
> used.
> +#
>  # serve::
>  #   Start another type of server.  By default, +puppetd+ will start
>  #   a service handler that allows authenticated and authorized remote nodes 
> to
> diff --git a/spec/unit/application/puppetca.rb 
> b/spec/unit/application/puppetca.rb
> index 3a535f3..132a03c 100644
> --- a/spec/unit/application/puppetca.rb
> +++ b/spec/unit/application/puppetca.rb
> @@ -39,6 +39,12 @@ describe "PuppetCA" do
>         @puppetca.handle_debug(0)
>     end
>
> +    it "should set the fingerprint digest with the --digest option" do
> +       �[email protected]_digest(:digest)
> +
> +       �[email protected] == :digest
> +    end
> +
>     it "should set mode to :destroy for --clean" do
>         @puppetca.handle_clean(0)
>         @puppetca.mode.should == :destroy
> @@ -129,6 +135,15 @@ describe "PuppetCA" do
>             @puppetca.main
>         end
>
> +        it "should send the currently set digest" do
> +            ARGV.stubs(:collect).returns(["host"])
> +           �[email protected]_digest(:digest)
> +
> +           �[email protected](:apply).with { |mode,to| to[:digest] == :digest}
> +
> +           �[email protected]
> +        end
> +
>         it "should delegate to ca.apply with current set mode" do
>             @puppetca.mode = "currentmode"
>             ARGV.stubs(:collect).returns(["host"])
> diff --git a/spec/unit/application/puppetd.rb 
> b/spec/unit/application/puppetd.rb
> index 9977607..2be018b 100755
> --- a/spec/unit/application/puppetd.rb
> +++ b/spec/unit/application/puppetd.rb
> @@ -34,6 +34,10 @@ describe "puppetd" do
>         @puppetd.should respond_to(:onetime)
>     end
>
> +    it "should declare a fingerprint command" do
> +       �[email protected] respond_to(:fingerprint)
> +    end
> +
>     it "should declare a preinit block" do
>         @puppetd.should respond_to(:run_preinit)
>     end
> @@ -73,6 +77,17 @@ describe "puppetd" do
>             @puppetd.options[:serve].should == []
>         end
>
> +        it "should use MD5 as default digest algorithm" do
> +           �[email protected]_preinit
> +
> +           �[email protected][:digest].should == :MD5
> +        end
> +
> +        it "should not fingerprint by default" do
> +           �[email protected]_preinit
> +
> +           �[email protected][:fingerprint].should be_false
> +        end
>     end
>
>     describe "when handling options" do
> @@ -86,7 +101,7 @@ describe "puppetd" do
>             @old_argv.each { |a| ARGV << a }
>         end
>
> -        [:centrallogging, :disable, :enable, :debug, :fqdn, :test, 
> :verbose].each do |option|
> +        [:centrallogging, :disable, :enable, :debug, :fqdn, :test, :verbose, 
> :digest].each do |option|
>             it "should declare handle_#{option} method" do
>                 @puppetd.should respond_to("handle_#{option}".to_sym)
>             end
> @@ -298,6 +313,13 @@ describe "puppetd" do
>             @puppetd.run_setup
>         end
>
> +        it "should install a none ca location in fingerprint mode" do
> +           �[email protected](:[]).with(:fingerprint).returns(true)
> +            Puppet::SSL::Host.expects(:ca_location=).with(:none)
> +
> +           �[email protected]_setup
> +        end
> +
>         it "should tell the report handler to use REST" do
>             Puppet::Transaction::Report.expects(:terminus_class=).with(:rest)
>
> @@ -381,6 +403,14 @@ describe "puppetd" do
>             @puppetd.run_setup
>         end
>
> +        it "should not wait for a certificate in fingerprint mode" do
> +           �[email protected](:[]).with(:fingerprint).returns(true)
> +           �[email protected](:[]).with(:waitforcert).returns(123)
> +           �[email protected](:wait_for_cert).never
> +
> +           �[email protected]_setup
> +        end
> +
>         it "should setup listen if told to and not onetime" do
>             Puppet.stubs(:[]).with(:listen).returns(true)
>             @puppetd.options.stubs(:[]).with(:onetime).returns(false)
> @@ -439,6 +469,13 @@ describe "puppetd" do
>         before :each do
>             @puppetd.agent = @agent
>             @puppetd.daemon = @daemon
> +           �[email protected](:[]).with(:fingerprint).returns(false)
> +        end
> +
> +        it "should dispatch to fingerprint if --fingerprint is used" do
> +           �[email protected](:[]).with(:fingerprint).returns(true)
> +
> +           �[email protected]_command.should == :fingerprint
>         end
>
>         it "should dispatch to onetime if --onetime is used" do
> @@ -447,7 +484,7 @@ describe "puppetd" do
>             @puppetd.get_command.should == :onetime
>         end
>
> -        it "should dispatch to main if --onetime is not used" do
> +        it "should dispatch to main if --onetime and --fingerprint are not 
> used" do
>             @puppetd.options.stubs(:[]).with(:onetime).returns(false)
>
>             @puppetd.get_command.should == :main
> @@ -515,7 +552,39 @@ describe "puppetd" do
>             end
>         end
>
> -        describe "without --onetime" do
> +        describe "with --fingerprint" do
> +            before :each do
> +               �...@cert = stub_everything 'cert'
> +               
> �[email protected](:[]).with(:fingerprint).returns(true)
> +               �[email protected](:[]).with(:digest).returns(:MD5)
> +               �...@host = stub_everything 'host'
> +               �[email protected](:host).returns(@host)
> +            end
> +
> +            it "should fingerprint the certificate if it exists" do
> +               �[email protected](:certificate).returns(@cert)
> +               �[email protected](:fingerprint).with(:MD5)
> +               �[email protected]
> +            end
> +
> +            it "should fingerprint the certificate request if no certificate 
> have been signed" do
> +               �[email protected](:certificate).returns(nil)
> +               �[email protected](:certificate_request).returns(@cert)
> +               �[email protected](:fingerprint).with(:MD5)
> +               �[email protected]
> +            end
> +
> +            it "should display the fingerprint" do
> +               �[email protected](:certificate).returns(@cert)
> +               �[email protected](:fingerprint).with(:MD5).returns("DIGEST")
> +
> +                Puppet.expects(:notice).with("DIGEST")
> +
> +               �[email protected]
> +            end
> +        end
> +
> +        describe "without --onetime and --fingerprint" do
>             before :each do
>                 Puppet.stubs(:notice)
>                 @puppetd.options.stubs(:[]).with(:client)
> diff --git a/spec/unit/ssl/base.rb b/spec/unit/ssl/base.rb
> new file mode 100755
> index 0000000..dfab3c8
> --- /dev/null
> +++ b/spec/unit/ssl/base.rb
> @@ -0,0 +1,40 @@
> +#!/usr/bin/env ruby
> +
> +require File.dirname(__FILE__) + '/../../spec_helper'
> +
> +require 'puppet/ssl/certificate'
> +
> +class TestCertificate < Puppet::SSL::Base; end
> +
> +describe Puppet::SSL::Certificate do
> +    before :each do
> +       �...@base = TestCertificate.new("name")
> +    end
> +
> +    describe "when fingerprinting content" do
> +        before :each do
> +           �...@cert = stub 'cert', :to_der => "DER"
> +           �[email protected](:content).returns(@cert)
> +            OpenSSL::Digest.stubs(:constants).returns ["MD5", "DIGEST"]
> +        end
> +
> +        it "should digest the certificate DER value and return a ':' 
> seperated nibblet string" do
> +           �[email protected](:to_der).returns("DER")
> +            OpenSSL::Digest.expects(:hexdigest).with("MD5", "DER").returns 
> "digest"
> +
> +           �[email protected] == "DI:GE:ST"
> +        end
> +
> +        it "should raise an error if the digest algorithm is not defined" do
> +            OpenSSL::Digest.expects(:constants).returns []
> +
> +            lambda { @base.fingerprint }.should raise_error
> +        end
> +
> +        it "should use the given digest algorithm" do
> +            OpenSSL::Digest.expects(:hexdigest).with("DIGEST", 
> "DER").returns "digest"
> +
> +           �[email protected](:digest).should == "DI:GE:ST"
> +        end
> +    end
> +end
> \ No newline at end of file
> diff --git a/spec/unit/ssl/certificate_authority.rb 
> b/spec/unit/ssl/certificate_authority.rb
> index 8011430..4d2303a 100755
> --- a/spec/unit/ssl/certificate_authority.rb
> +++ b/spec/unit/ssl/certificate_authority.rb
> @@ -532,9 +532,9 @@ describe Puppet::SSL::CertificateAuthority do
>                 lambda { @ca.apply(:generate) }.should 
> raise_error(ArgumentError)
>             end
>
> -            it "should create an Interface instance with the specified 
> method and the subjects" do
> -                
> Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, 
> :hosts).returns(stub('applier', :apply => nil))
> -               �[email protected](:generate, :to => :hosts)
> +            it "should create an Interface instance with the specified 
> method and the options" do
> +                
> Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, 
> :to => :host).returns(stub('applier', :apply => nil))
> +               �[email protected](:generate, :to => :host)
>             end
>
>             it "should apply the Interface with itself as the argument" do
> @@ -583,6 +583,37 @@ describe Puppet::SSL::CertificateAuthority do
>             end
>         end
>
> +        describe "and fingerprinting certificates" do
> +            before :each do
> +               �...@cert = stub 'cert', :name => "cert", :fingerprint => 
> "DIGEST"
> +                Puppet::SSL::Certificate.stubs(:find).with("myhost").returns 
> @cert
> +                Puppet::SSL::CertificateRequest.stubs(:find).with("myhost")
> +            end
> +
> +            it "should raise an error if the certificate or CSR cannot be 
> found" do
> +                
> Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil
> +                
> Puppet::SSL::CertificateRequest.expects(:find).with("myhost").returns nil
> +                lambda { @ca.fingerprint("myhost") }.should raise_error
> +            end
> +
> +            it "should try to find a CSR if no certificate can be found" do
> +                
> Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil
> +                
> Puppet::SSL::CertificateRequest.expects(:find).with("myhost").returns @cert
> +               �[email protected](:fingerprint)
> +               �[email protected]("myhost")
> +            end
> +
> +            it "should delegate to the certificate fingerprinting" do
> +               �[email protected](:fingerprint)
> +               �[email protected]("myhost")
> +            end
> +
> +            it "should propagate the digest algorithm to the certificate 
> fingerprinting system" do
> +               �[email protected](:fingerprint).with(:digest)
> +               �[email protected]("myhost", :digest)
> +            end
> +        end
> +
>         describe "and verifying certificates" do
>             before do
>                 @store = stub 'store', :verify => true, :add_file => nil, 
> :purpose= => nil, :add_crl => true, :flags= => nil
> diff --git a/spec/unit/ssl/certificate_authority/interface.rb 
> b/spec/unit/ssl/certificate_authority/interface.rb
> index d741ec4..bcba298 100755
> --- a/spec/unit/ssl/certificate_authority/interface.rb
> +++ b/spec/unit/ssl/certificate_authority/interface.rb
> @@ -9,7 +9,7 @@ describe "a normal interface method", :shared => true do
>         @ca.expects(@method).with("host1")
>         @ca.expects(@method).with("host2")
>
> -       �...@applier = 
> Puppet::SSL::CertificateAuthority::Interface.new(@method, %w{host1 host2})
> +       �...@applier = 
> Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => %w{host1 
> host2})
>
>         @applier.apply(@ca)
>     end
> @@ -20,7 +20,7 @@ describe "a normal interface method", :shared => true do
>         @ca.expects(@method).with("host1")
>         @ca.expects(@method).with("host2")
>
> -       �...@applier = 
> Puppet::SSL::CertificateAuthority::Interface.new(@method, :all)
> +       �...@applier = 
> Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => :all)
>
>         @applier.apply(@ca)
>     end
> @@ -33,30 +33,40 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>     describe "when initializing" do
>         it "should set its method using its settor" do
>             @class.any_instance.expects(:method=).with(:generate)
> -           �[email protected](:generate, :all)
> +           �[email protected](:generate, :to => :all)
>         end
>
>         it "should set its subjects using the settor" do
>             @class.any_instance.expects(:subjects=).with(:all)
> -           �[email protected](:generate, :all)
> +           �[email protected](:generate, :to => :all)
> +        end
> +
> +        it "should set the digest if given" do
> +            interface = @class.new(:generate, :to => :all, :digest => 
> :digest)
> +            interface.digest.should == :digest
> +        end
> +
> +        it "should set the digest to md5 if none given" do
> +            interface = @class.new(:generate, :to => :all)
> +            interface.digest.should == :MD5
>         end
>     end
>
>     describe "when setting the method" do
>         it "should set the method" do
> -           �[email protected](:generate, :all).method.should == :generate
> +           �[email protected](:generate, :to => :all).method.should == :generate
>         end
>
>         it "should fail if the method isn't a member of the INTERFACE_METHODS 
> array" do
>             
> Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.expects(:include?).with(:thing).returns
>  false
>
> -            lambda { @class.new(:thing, :all) }.should 
> raise_error(ArgumentError)
> +            lambda { @class.new(:thing, :to => :all) }.should 
> raise_error(ArgumentError)
>         end
>     end
>
>     describe "when setting the subjects" do
>         it "should set the subjects" do
> -           �[email protected](:generate, :all).subjects.should == :all
> +           �[email protected](:generate, :to => :all).subjects.should == :all
>         end
>
>         it "should fail if the subjects setting isn't :all or an array" do
> @@ -65,7 +75,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>     end
>
>     it "should have a method for triggering the application" do
> -       �[email protected](:generate, :all).should respond_to(:apply)
> +       �[email protected](:generate, :to => :all).should respond_to(:apply)
>     end
>
>     describe "when applying" do
> @@ -75,7 +85,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>         end
>
>         it "should raise InterfaceErrors" do
> -           �...@applier = @class.new(:revoke, :all)
> +           �...@applier = @class.new(:revoke, :to => :all)
>
>             @ca.expects(:list).raises 
> Puppet::SSL::CertificateAuthority::Interface::InterfaceError
>
> @@ -83,7 +93,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>         end
>
>         it "should log non-Interface failures rather than failing" do
> -           �...@applier = @class.new(:revoke, :all)
> +           �...@applier = @class.new(:revoke, :to => :all)
>
>             @ca.expects(:list).raises ArgumentError
>
> @@ -94,19 +104,19 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>
>         describe "with an empty array specified and the method is not list" do
>             it "should fail" do
> -               �...@applier = @class.new(:sign, [])
> +               �...@applier = @class.new(:sign, :to => [])
>                 lambda { @applier.apply(@ca) }.should 
> raise_error(ArgumentError)
>             end
>         end
>
>         describe ":generate" do
>             it "should fail if :all was specified" do
> -               �...@applier = @class.new(:generate, :all)
> +               �...@applier = @class.new(:generate, :to => :all)
>                 lambda { @applier.apply(@ca) }.should 
> raise_error(ArgumentError)
>             end
>
>             it "should call :generate on the CA for each host specified" do
> -               �...@applier = @class.new(:generate, %w{host1 host2})
> +               �...@applier = @class.new(:generate, :to => %w{host1 host2})
>
>                 @ca.expects(:generate).with("host1")
>                 @ca.expects(:generate).with("host2")
> @@ -141,7 +151,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>         describe ":sign" do
>             describe "and an array of names was provided" do
>                 before do
> -                   �...@applier = @class.new(:sign, %w{host1 host2})
> +                   �...@applier = @class.new(:sign, :to => %w{host1 host2})
>                 end
>
>                 it "should sign the specified waiting certificate requests" do
> @@ -159,14 +169,14 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                     @ca.expects(:sign).with("cert1")
>                     @ca.expects(:sign).with("cert2")
>
> -                   �...@applier = @class.new(:sign, :all)
> +                   �...@applier = @class.new(:sign, :to => :all)
>                     @applier.apply(@ca)
>                 end
>
>                 it "should fail if there are no waiting certificate requests" 
> do
>                     @ca.stubs(:waiting?).returns([])
>
> -                   �...@applier = @class.new(:sign, :all)
> +                   �...@applier = @class.new(:sign, :to => :all)
>                     lambda { @applier.apply(@ca) }.should 
> raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError)
>                 end
>             end
> @@ -178,7 +188,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                     @ca.expects(:waiting?).returns %w{host1 host2}
>                     @ca.stubs(:verify)
>
> -                   �...@applier = @class.new(:list, [])
> +                   �...@applier = @class.new(:list, :to => [])
>
>                     @applier.expects(:puts).with "host1\nhost2"
>
> @@ -191,14 +201,15 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                     @ca.expects(:waiting?).returns %w{host1 host2}
>                     @ca.expects(:list).returns %w{host3 host4}
>                     @ca.stubs(:verify)
> +                   �[email protected](:fingerprint).returns "fingerprint"
>                     
> @ca.expects(:verify).with("host3").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23),
>  "certificate revoked")
>
> -                   �...@applier = @class.new(:list, :all)
> +                   �...@applier = @class.new(:list, :to => :all)
>
> -                   �[email protected](:puts).with "host1"
> -                   �[email protected](:puts).with "host2"
> -                   �[email protected](:puts).with "- host3 (certificate 
> revoked)"
> -                   �[email protected](:puts).with "+ host4"
> +                   �[email protected](:puts).with "host1 (fingerprint)"
> +                   �[email protected](:puts).with "host2 (fingerprint)"
> +                   �[email protected](:puts).with "- host3 (fingerprint) 
> (certificate revoked)"
> +                   �[email protected](:puts).with "+ host4 (fingerprint)"
>
>                     @applier.apply(@ca)
>                 end
> @@ -208,14 +219,15 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                 it "should print a string of all named hosts that have a 
> waiting request" do
>                     @ca.expects(:waiting?).returns %w{host1 host2}
>                     @ca.expects(:list).returns %w{host3 host4}
> +                   �[email protected](:fingerprint).returns "fingerprint"
>                     @ca.stubs(:verify)
>
> -                   �...@applier = @class.new(:list, %w{host1 host2 host3 
> host4})
> +                   �...@applier = @class.new(:list, :to => %w{host1 host2 
> host3 host4})
>
> -                   �[email protected](:puts).with "host1"
> -                   �[email protected](:puts).with "host2"
> -                   �[email protected](:puts).with "+ host3"
> -                   �[email protected](:puts).with "+ host4"
> +                   �[email protected](:puts).with "host1 (fingerprint)"
> +                   �[email protected](:puts).with "host2 (fingerprint)"
> +                   �[email protected](:puts).with "+ host3 (fingerprint)"
> +                   �[email protected](:puts).with "+ host4 (fingerprint)"
>
>                     @applier.apply(@ca)
>                 end
> @@ -227,7 +239,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                 it "should print all certificates" do
>                     @ca.expects(:list).returns %w{host1 host2}
>
> -                   �...@applier = @class.new(:print, :all)
> +                   �...@applier = @class.new(:print, :to => :all)
>
>                     @ca.expects(:print).with("host1").returns "h1"
>                     @applier.expects(:puts).with "h1"
> @@ -241,7 +253,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>
>             describe "and an array of names was provided" do
>                 it "should print each named certificate if found" do
> -                   �...@applier = @class.new(:print, %w{host1 host2})
> +                   �...@applier = @class.new(:print, :to => %w{host1 host2})
>
>                     @ca.expects(:print).with("host1").returns "h1"
>                     @applier.expects(:puts).with "h1"
> @@ -253,7 +265,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                 end
>
>                 it "should log any named but not found certificates" do
> -                   �...@applier = @class.new(:print, %w{host1 host2})
> +                   �...@applier = @class.new(:print, :to => %w{host1 host2})
>
>                     @ca.expects(:print).with("host1").returns "h1"
>                     @applier.expects(:puts).with "h1"
> @@ -265,5 +277,47 @@ describe Puppet::SSL::CertificateAuthority::Interface do
>                 end
>             end
>         end
> +
> +        describe ":fingerprint" do
> +            it "should fingerprint with the set digest algorithm" do
> +               �...@applier = @class.new(:fingerprint, :to => %w{host1}, 
> :digest => :digest)
> +
> +               �[email protected](:fingerprint).with("host1", :digest).returns 
> "fingerprint1"
> +               �[email protected](:puts).with "host1 fingerprint1"
> +
> +               �[email protected](@ca)
> +            end
> +
> +            describe "and :all was provided" do
> +                it "should fingerprint all certificates (including waiting 
> ones)" do
> +                   �[email protected](:list).returns %w{host1}
> +                   �[email protected](:waiting?).returns %w{host2}
> +
> +                   �...@applier = @class.new(:fingerprint, :to => :all)
> +
> +                   �[email protected](:fingerprint).with("host1", :MD5).returns 
> "fingerprint1"
> +                   �[email protected](:puts).with "host1 fingerprint1"
> +
> +                   �[email protected](:fingerprint).with("host2", :MD5).returns 
> "fingerprint2"
> +                   �[email protected](:puts).with "host2 fingerprint2"
> +
> +                   �[email protected](@ca)
> +                end
> +            end
> +
> +            describe "and an array of names was provided" do
> +                it "should print each named certificate if found" do
> +                   �...@applier = @class.new(:fingerprint, :to => %w{host1 
> host2})
> +
> +                   �[email protected](:fingerprint).with("host1", :MD5).returns 
> "fingerprint1"
> +                   �[email protected](:puts).with "host1 fingerprint1"
> +
> +                   �[email protected](:fingerprint).with("host2", :MD5).returns 
> "fingerprint2"
> +                   �[email protected](:puts).with "host2 fingerprint2"
> +
> +                   �[email protected](@ca)
> +                end
> +            end
> +        end
>     end
>  end
> diff --git a/spec/unit/ssl/certificate_request.rb 
> b/spec/unit/ssl/certificate_request.rb
> index 29bbc7b..a4eee92 100755
> --- a/spec/unit/ssl/certificate_request.rb
> +++ b/spec/unit/ssl/certificate_request.rb
> @@ -106,7 +106,7 @@ describe Puppet::SSL::CertificateRequest do
>         end
>
>         it "should log that it is creating a new certificate request" do
> -            Puppet.expects(:info)
> +            Puppet.expects(:info).twice
>             @instance.generate(@key)
>         end
>
> @@ -164,6 +164,18 @@ describe Puppet::SSL::CertificateRequest do
>             lambda { @instance.generate(@key) }.should 
> raise_error(Puppet::Error)
>         end
>
> +        it "should fingerprint the request" do
> +           �[email protected](:fingerprint)
> +           �[email protected](@key)
> +        end
> +
> +        it "should display the fingerprint" do
> +            Puppet.stubs(:info)
> +           �[email protected](:fingerprint).returns("FINGERPRINT")
> +            Puppet.expects(:info).with { |s| s =~ /FINGERPRINT/ }
> +           �[email protected](@key)
> +        end
> +
>         it "should return the generated request" do
>             @instance.generate(@key).should equal(@request)
>         end
> --
> 1.6.5.2
>
> --
>
> You received this message because you are subscribed to the Google Groups 
> "Puppet Developers" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> For more options, visit this group at 
> http://groups.google.com/group/puppet-dev?hl=en.
>
>
>



-- 
-----------------------------------------------------------
The power of accurate observation is
commonly called cynicism by those
who have not got it.  ~George Bernard Shaw
------------------------------------------------------------
-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.


Reply via email to