summaryrefslogtreecommitdiffstats
path: root/spec
diff options
context:
space:
mode:
authorBrice Figureau <brice-puppet@daysofwonder.com>2009-12-29 15:27:54 +0100
committerJames Turnbull <james@lovedthanlost.net>2010-01-19 08:37:23 +1100
commit3e9677f00a09d0249713ed2fa503e42b07f6d978 (patch)
tree0b99bb4cd9039bb220ee75f2520b37920a6b7628 /spec
parent91c44b439794a87111ab1a0726a2ad08981c839e (diff)
downloadpuppet-3e9677f00a09d0249713ed2fa503e42b07f6d978.tar.gz
puppet-3e9677f00a09d0249713ed2fa503e42b07f6d978.tar.xz
puppet-3e9677f00a09d0249713ed2fa503e42b07f6d978.zip
Feature #2839 - fingerprint certificate
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 <brice-puppet@daysofwonder.com>
Diffstat (limited to 'spec')
-rw-r--r--spec/unit/application/puppetca.rb15
-rwxr-xr-xspec/unit/application/puppetd.rb75
-rwxr-xr-xspec/unit/ssl/base.rb40
-rwxr-xr-xspec/unit/ssl/certificate_authority.rb37
-rwxr-xr-xspec/unit/ssl/certificate_authority/interface.rb114
-rwxr-xr-xspec/unit/ssl/certificate_request.rb14
6 files changed, 258 insertions, 37 deletions
diff --git a/spec/unit/application/puppetca.rb b/spec/unit/application/puppetca.rb
index 3a535f394..132a03c1f 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
+ @puppetca.handle_digest(:digest)
+
+ @puppetca.digest.should == :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"])
+ @puppetca.handle_digest(:digest)
+
+ @ca.expects(:apply).with { |mode,to| to[:digest] == :digest}
+
+ @puppetca.main
+ 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 dc061ea10..246c39958 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
+ @puppetd.should 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
+ @puppetd.run_preinit
+
+ @puppetd.options[:digest].should == :MD5
+ end
+
+ it "should not fingerprint by default" do
+ @puppetd.run_preinit
+
+ @puppetd.options[: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
@@ -299,6 +314,13 @@ describe "puppetd" do
@puppetd.run_setup
end
+ it "should install a none ca location in fingerprint mode" do
+ @puppetd.options.stubs(:[]).with(:fingerprint).returns(true)
+ Puppet::SSL::Host.expects(:ca_location=).with(:none)
+
+ @puppetd.run_setup
+ end
+
it "should tell the report handler to use REST" do
Puppet::Transaction::Report.expects(:terminus_class=).with(:rest)
@@ -382,6 +404,14 @@ describe "puppetd" do
@puppetd.run_setup
end
+ it "should not wait for a certificate in fingerprint mode" do
+ @puppetd.options.stubs(:[]).with(:fingerprint).returns(true)
+ @puppetd.options.stubs(:[]).with(:waitforcert).returns(123)
+ @host.expects(:wait_for_cert).never
+
+ @puppetd.run_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)
@@ -440,6 +470,13 @@ describe "puppetd" do
before :each do
@puppetd.agent = @agent
@puppetd.daemon = @daemon
+ @puppetd.options.stubs(:[]).with(:fingerprint).returns(false)
+ end
+
+ it "should dispatch to fingerprint if --fingerprint is used" do
+ @puppetd.options.stubs(:[]).with(:fingerprint).returns(true)
+
+ @puppetd.get_command.should == :fingerprint
end
it "should dispatch to onetime if --onetime is used" do
@@ -448,7 +485,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
@@ -516,7 +553,39 @@ describe "puppetd" do
end
end
- describe "without --onetime" do
+ describe "with --fingerprint" do
+ before :each do
+ @cert = stub_everything 'cert'
+ @puppetd.options.stubs(:[]).with(:fingerprint).returns(true)
+ @puppetd.options.stubs(:[]).with(:digest).returns(:MD5)
+ @host = stub_everything 'host'
+ @puppetd.stubs(:host).returns(@host)
+ end
+
+ it "should fingerprint the certificate if it exists" do
+ @host.expects(:certificate).returns(@cert)
+ @cert.expects(:fingerprint).with(:MD5)
+ @puppetd.fingerprint
+ end
+
+ it "should fingerprint the certificate request if no certificate have been signed" do
+ @host.expects(:certificate).returns(nil)
+ @host.expects(:certificate_request).returns(@cert)
+ @cert.expects(:fingerprint).with(:MD5)
+ @puppetd.fingerprint
+ end
+
+ it "should display the fingerprint" do
+ @host.stubs(:certificate).returns(@cert)
+ @cert.stubs(:fingerprint).with(:MD5).returns("DIGEST")
+
+ Puppet.expects(:notice).with("DIGEST")
+
+ @puppetd.fingerprint
+ 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 000000000..dfab3c843
--- /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"
+ @base.stubs(: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
+ @cert.expects(:to_der).returns("DER")
+ OpenSSL::Digest.expects(:hexdigest).with("MD5", "DER").returns "digest"
+
+ @base.fingerprint.should == "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"
+
+ @base.fingerprint(: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 80114300e..4d2303a60 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))
- @ca.apply(: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))
+ @ca.apply(: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
+ @cert.expects(:fingerprint)
+ @ca.fingerprint("myhost")
+ end
+
+ it "should delegate to the certificate fingerprinting" do
+ @cert.expects(:fingerprint)
+ @ca.fingerprint("myhost")
+ end
+
+ it "should propagate the digest algorithm to the certificate fingerprinting system" do
+ @cert.expects(:fingerprint).with(:digest)
+ @ca.fingerprint("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 d741ec400..bcba298b2 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)
- @class.new(:generate, :all)
+ @class.new(:generate, :to => :all)
end
it "should set its subjects using the settor" do
@class.any_instance.expects(:subjects=).with(:all)
- @class.new(:generate, :all)
+ @class.new(: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
- @class.new(:generate, :all).method.should == :generate
+ @class.new(: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
- @class.new(:generate, :all).subjects.should == :all
+ @class.new(: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
- @class.new(:generate, :all).should respond_to(:apply)
+ @class.new(: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)
+ @ca.stubs(: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)
- @applier.expects(:puts).with "host1"
- @applier.expects(:puts).with "host2"
- @applier.expects(:puts).with "- host3 (certificate revoked)"
- @applier.expects(:puts).with "+ host4"
+ @applier.expects(:puts).with "host1 (fingerprint)"
+ @applier.expects(:puts).with "host2 (fingerprint)"
+ @applier.expects(:puts).with "- host3 (fingerprint) (certificate revoked)"
+ @applier.expects(: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}
+ @ca.stubs(: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})
- @applier.expects(:puts).with "host1"
- @applier.expects(:puts).with "host2"
- @applier.expects(:puts).with "+ host3"
- @applier.expects(:puts).with "+ host4"
+ @applier.expects(:puts).with "host1 (fingerprint)"
+ @applier.expects(:puts).with "host2 (fingerprint)"
+ @applier.expects(:puts).with "+ host3 (fingerprint)"
+ @applier.expects(: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)
+
+ @ca.expects(:fingerprint).with("host1", :digest).returns "fingerprint1"
+ @applier.expects(:puts).with "host1 fingerprint1"
+
+ @applier.apply(@ca)
+ end
+
+ describe "and :all was provided" do
+ it "should fingerprint all certificates (including waiting ones)" do
+ @ca.expects(:list).returns %w{host1}
+ @ca.expects(:waiting?).returns %w{host2}
+
+ @applier = @class.new(:fingerprint, :to => :all)
+
+ @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1"
+ @applier.expects(:puts).with "host1 fingerprint1"
+
+ @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2"
+ @applier.expects(:puts).with "host2 fingerprint2"
+
+ @applier.apply(@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})
+
+ @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1"
+ @applier.expects(:puts).with "host1 fingerprint1"
+
+ @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2"
+ @applier.expects(:puts).with "host2 fingerprint2"
+
+ @applier.apply(@ca)
+ end
+ end
+ end
end
end
diff --git a/spec/unit/ssl/certificate_request.rb b/spec/unit/ssl/certificate_request.rb
index 29bbc7bc1..a4eee92d6 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
+ @instance.expects(:fingerprint)
+ @instance.generate(@key)
+ end
+
+ it "should display the fingerprint" do
+ Puppet.stubs(:info)
+ @instance.stubs(:fingerprint).returns("FINGERPRINT")
+ Puppet.expects(:info).with { |s| s =~ /FINGERPRINT/ }
+ @instance.generate(@key)
+ end
+
it "should return the generated request" do
@instance.generate(@key).should equal(@request)
end