diff options
-rw-r--r-- | lib/puppet/network/formats.rb | 4 | ||||
-rw-r--r-- | lib/puppet/ssl/certificate.rb | 11 | ||||
-rw-r--r-- | lib/puppet/ssl/certificate_request.rb | 11 | ||||
-rw-r--r-- | lib/puppet/ssl/certificate_revocation_list.rb | 10 | ||||
-rw-r--r-- | lib/puppet/ssl/key.rb | 2 | ||||
-rwxr-xr-x | spec/integration/indirector/certificate/rest.rb | 66 | ||||
-rwxr-xr-x | spec/integration/indirector/certificate_request/rest.rb | 86 | ||||
-rwxr-xr-x | spec/integration/indirector/certificate_revocation_list/rest.rb | 71 | ||||
-rwxr-xr-x | spec/unit/ssl/certificate.rb | 16 | ||||
-rwxr-xr-x | spec/unit/ssl/certificate_request.rb | 16 | ||||
-rwxr-xr-x | spec/unit/ssl/certificate_revocation_list.rb | 16 | ||||
-rwxr-xr-x | spec/unit/ssl/host.rb | 8 |
12 files changed, 307 insertions, 10 deletions
diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb index bd171fa7d..e11748ce6 100644 --- a/lib/puppet/network/formats.rb +++ b/lib/puppet/network/formats.rb @@ -53,16 +53,14 @@ Puppet::Network::FormatHandler.create(:marshal, :mime => "text/marshal") do end end -Puppet::Network::FormatHandler.create(:str, :mime => "text/plain") do +Puppet::Network::FormatHandler.create(:s, :mime => "text/plain") do # For now, use the YAML separator. SEPARATOR = "\n---\n" - # Yaml doesn't need the class name; it's serialized. def intern_multiple(klass, text) text.split(SEPARATOR).collect { |inst| intern(klass, inst) } end - # Yaml monkey-patches Array, so this works. def render_multiple(instances) instances.collect { |inst| render(inst) }.join(SEPARATOR) end diff --git a/lib/puppet/ssl/certificate.rb b/lib/puppet/ssl/certificate.rb index 82f251d9c..f9297f380 100644 --- a/lib/puppet/ssl/certificate.rb +++ b/lib/puppet/ssl/certificate.rb @@ -12,10 +12,19 @@ class Puppet::SSL::Certificate < Puppet::SSL::Base extend Puppet::Indirector indirects :certificate, :terminus_class => :file + # Convert a string into an instance. + def self.from_s(string) + instance = wrapped_class.new(string) + name = instance.subject.to_s.sub(/\/CN=/i, '').downcase + result = new(name) + result.content = instance + result + end + # Because of how the format handler class is included, this # can't be in the base class. def self.supported_formats - [:str] + [:s] end def expiration diff --git a/lib/puppet/ssl/certificate_request.rb b/lib/puppet/ssl/certificate_request.rb index 6a55b2bd1..e345f4bb6 100644 --- a/lib/puppet/ssl/certificate_request.rb +++ b/lib/puppet/ssl/certificate_request.rb @@ -7,10 +7,19 @@ class Puppet::SSL::CertificateRequest < Puppet::SSL::Base extend Puppet::Indirector indirects :certificate_request, :terminus_class => :file + # Convert a string into an instance. + def self.from_s(string) + instance = wrapped_class.new(string) + name = instance.subject.to_s.sub(/\/CN=/i, '').downcase + result = new(name) + result.content = instance + result + end + # Because of how the format handler class is included, this # can't be in the base class. def self.supported_formats - [:str] + [:s] end # How to create a certificate request with our system defaults. diff --git a/lib/puppet/ssl/certificate_revocation_list.rb b/lib/puppet/ssl/certificate_revocation_list.rb index 3e48ddba3..f3c1a348a 100644 --- a/lib/puppet/ssl/certificate_revocation_list.rb +++ b/lib/puppet/ssl/certificate_revocation_list.rb @@ -8,10 +8,18 @@ class Puppet::SSL::CertificateRevocationList < Puppet::SSL::Base extend Puppet::Indirector indirects :certificate_revocation_list, :terminus_class => :file + # Convert a string into an instance. + def self.from_s(string) + instance = wrapped_class.new(string) + result = new('foo') # The name doesn't matter + result.content = instance + result + end + # Because of how the format handler class is included, this # can't be in the base class. def self.supported_formats - [:str] + [:s] end # Knows how to create a CRL with our system defaults. diff --git a/lib/puppet/ssl/key.rb b/lib/puppet/ssl/key.rb index 359455b06..d91df03f6 100644 --- a/lib/puppet/ssl/key.rb +++ b/lib/puppet/ssl/key.rb @@ -11,7 +11,7 @@ class Puppet::SSL::Key < Puppet::SSL::Base # Because of how the format handler class is included, this # can't be in the base class. def self.supported_formats - [:str] + [:s] end attr_accessor :password_file diff --git a/spec/integration/indirector/certificate/rest.rb b/spec/integration/indirector/certificate/rest.rb new file mode 100755 index 000000000..0f14998d5 --- /dev/null +++ b/spec/integration/indirector/certificate/rest.rb @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/ssl/certificate' +require 'puppet/network/server' +require 'puppet/network/http/webrick/rest' + +describe "Certificate REST Terminus" do + before do + Puppet[:masterport] = 34343 + Puppet[:server] = "localhost" + + # Get a safe temporary file + @tmpfile = Tempfile.new("webrick_integration_testing") + @dir = @tmpfile.path + "_dir" + + Puppet.settings[:confdir] = @dir + Puppet.settings[:vardir] = @dir + Puppet.settings[:server] = "127.0.0.1" + Puppet.settings[:masterport] = "34343" + Puppet.settings[:http_enable_post_connection_check] = false + + Puppet::Util::Cacher.invalidate + + Puppet[:servertype] = 'webrick' + Puppet[:server] = '127.0.0.1' + Puppet[:certname] = '127.0.0.1' + + # Generate the certificate with a local CA + Puppet::SSL::Host.ca_location = :local + ca = Puppet::SSL::CertificateAuthority.new + ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) + ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) + + @host = Puppet::SSL::Host.new(Puppet[:certname]) + + @params = { :address => "127.0.0.1", :port => 34343, :handlers => [ :certificate ] } + @server = Puppet::Network::Server.new(@params) + @server.listen + + # Then switch to a remote CA, so that we go through REST. + Puppet::SSL::Host.ca_location = :remote + + # LAK:NOTE We need to have a fake model here so that our indirected methods get + # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate + # return. + @mock_model = stub('faked model', :name => "certificate") + Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model) + end + + after do + Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) + Puppet.settings.clear + @server.unlisten + end + + it "should be able to retrieve a remote certificate" do + @mock_model.expects(:find).returns @host.certificate + result = Puppet::SSL::Certificate.find('bar') + + # There's no good '==' method on certs. + result.content.to_s.should == @host.certificate.content.to_s + result.name.should == @host.certificate.name + end +end diff --git a/spec/integration/indirector/certificate_request/rest.rb b/spec/integration/indirector/certificate_request/rest.rb new file mode 100755 index 000000000..decd971dc --- /dev/null +++ b/spec/integration/indirector/certificate_request/rest.rb @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/ssl/certificate_request' +require 'puppet/network/server' +require 'puppet/network/http/webrick/rest' + +describe "Certificate Request REST Terminus" do + before do + Puppet::Util::Cacher.invalidate + + Puppet[:masterport] = 34343 + Puppet[:server] = "localhost" + + # Get a safe temporary file + @tmpfile = Tempfile.new("webrick_integration_testing") + @dir = @tmpfile.path + "_dir" + + Puppet.settings[:confdir] = @dir + Puppet.settings[:vardir] = @dir + Puppet.settings[:server] = "127.0.0.1" + Puppet.settings[:masterport] = "34343" + Puppet.settings[:http_enable_post_connection_check] = false + + Puppet[:servertype] = 'webrick' + Puppet[:server] = '127.0.0.1' + Puppet[:certname] = '127.0.0.1' + + # Generate the certificate with a local CA + Puppet::SSL::Host.ca_location = :local + ca = Puppet::SSL::CertificateAuthority.new + ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) + + # Create the CSR and write it to disk + @host = Puppet::SSL::Host.new("foo.madstop.com") + @host.generate_certificate_request + + # Now remove the cached csr + Puppet::SSL::Host.ca_location = :none + Puppet::SSL::Host.destroy("foo.madstop.com") + + @params = { :address => "127.0.0.1", :port => 34343, :handlers => [ :certificate_request ] } + @server = Puppet::Network::Server.new(@params) + @server.listen + + # Then switch to a remote CA, so that we go through REST. + Puppet::SSL::Host.ca_location = :remote + + # LAK:NOTE We need to have a fake model here so that our indirected methods get + # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate + # return. + @mock_model = stub('faked model', :name => "certificate request") + Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model) + end + + after do + Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) + Puppet.settings.clear + @server.unlisten + end + + it "should be able to save a certificate request to the CA" do + key = Puppet::SSL::Key.new("bar.madstop.com") + key.generate + + csr = Puppet::SSL::CertificateRequest.new("bar.madstop.com") + csr.generate(key.content) + + server_csr = mock 'csr' + server_csr.expects(:save) + @mock_model.expects(:convert_from).with("s", csr.content.to_s).returns server_csr + + csr.save + end + + it "should be able to retrieve a remote certificate request" do + # We're finding the cached value :/ + @mock_model.expects(:find).returns @host.certificate_request + result = Puppet::SSL::CertificateRequest.find('foo.madstop.com') + + # There's no good '==' method on certs. + result.content.to_s.should == @host.certificate_request.content.to_s + result.name.should == @host.certificate_request.name + end +end diff --git a/spec/integration/indirector/certificate_revocation_list/rest.rb b/spec/integration/indirector/certificate_revocation_list/rest.rb new file mode 100755 index 000000000..64c366d38 --- /dev/null +++ b/spec/integration/indirector/certificate_revocation_list/rest.rb @@ -0,0 +1,71 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/ssl/certificate' +require 'puppet/network/server' +require 'puppet/network/http/webrick/rest' + +describe "Certificate REST Terminus" do + before do + Puppet[:masterport] = 34343 + Puppet[:server] = "localhost" + + # Get a safe temporary file + @tmpfile = Tempfile.new("webrick_integration_testing") + @dir = @tmpfile.path + "_dir" + + Puppet.settings[:confdir] = @dir + Puppet.settings[:vardir] = @dir + Puppet.settings[:server] = "127.0.0.1" + Puppet.settings[:masterport] = "34343" + Puppet.settings[:http_enable_post_connection_check] = false + + Puppet::Util::Cacher.invalidate + + Puppet[:servertype] = 'webrick' + Puppet[:server] = '127.0.0.1' + Puppet[:certname] = '127.0.0.1' + + # Generate the certificate with a local CA + Puppet::SSL::Host.ca_location = :local + ca = Puppet::SSL::CertificateAuthority.new + ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) + + @params = { :address => "127.0.0.1", :port => 34343, :handlers => [ :certificate_revocation_list ] } + @server = Puppet::Network::Server.new(@params) + @server.listen + + # And make sure we've generated the CRL + @crl = ca.crl + + # Now remove the cached crl + Puppet::SSL::Host.ca_location = :none + Puppet::SSL::CertificateRevocationList.destroy("ca") + + puts Puppet::Network::HttpPool.ssl_host.ssl_store + + # Then switch to a remote CA, so that we go through REST. + Puppet::SSL::Host.ca_location = :remote + + # LAK:NOTE We need to have a fake model here so that our indirected methods get + # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate + # return. + @mock_model = stub('faked model', :name => "certificate") + Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model) + end + + after do + Puppet::Util::Cacher.invalidate + Puppet.settings.clear + @server.unlisten + end + + it "should be able to retrieve a remote CRL" do + @mock_model.expects(:find).returns @crl + result = Puppet::SSL::CertificateRevocationList.find('bar') + + # There's no good '==' method on certs. + result.content.to_s.should == @crl.content.to_s + end +end diff --git a/spec/unit/ssl/certificate.rb b/spec/unit/ssl/certificate.rb index eb2464a48..34868dcd2 100755 --- a/spec/unit/ssl/certificate.rb +++ b/spec/unit/ssl/certificate.rb @@ -26,7 +26,21 @@ describe Puppet::SSL::Certificate do end it "should only support the text format" do - @class.supported_formats.should == [:str] + @class.supported_formats.should == [:s] + end + + describe "when converting from a string" do + it "should create a certificate instance with its name set to the certificate subject and its content set to the extracted certificate" do + cert = stub 'certificate', :subject => "/CN=Foo.madstop.com" + OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(cert) + + mycert = stub 'sslcert' + mycert.expects(:content=).with(cert) + + @class.expects(:new).with("foo.madstop.com").returns mycert + + @class.from_s("my certificate") + end end describe "when managing instances" do diff --git a/spec/unit/ssl/certificate_request.rb b/spec/unit/ssl/certificate_request.rb index 4a7d655ac..aa6bba625 100755 --- a/spec/unit/ssl/certificate_request.rb +++ b/spec/unit/ssl/certificate_request.rb @@ -27,7 +27,21 @@ describe Puppet::SSL::CertificateRequest do end it "should only support the text format" do - @class.supported_formats.should == [:str] + @class.supported_formats.should == [:s] + end + + describe "when converting from a string" do + it "should create a CSR instance with its name set to the CSR subject and its content set to the extracted CSR" do + csr = stub 'csr', :subject => "/CN=Foo.madstop.com" + OpenSSL::X509::Request.expects(:new).with("my csr").returns(csr) + + mycsr = stub 'sslcsr' + mycsr.expects(:content=).with(csr) + + @class.expects(:new).with("foo.madstop.com").returns mycsr + + @class.from_s("my csr") + end end describe "when managing instances" do diff --git a/spec/unit/ssl/certificate_revocation_list.rb b/spec/unit/ssl/certificate_revocation_list.rb index 39e32976d..3963b407f 100755 --- a/spec/unit/ssl/certificate_revocation_list.rb +++ b/spec/unit/ssl/certificate_revocation_list.rb @@ -17,7 +17,21 @@ describe Puppet::SSL::CertificateRevocationList do end it "should only support the text format" do - @class.supported_formats.should == [:str] + @class.supported_formats.should == [:s] + end + + describe "when converting from a string" do + it "should create a CRL instance with its name set to 'foo' and its content set to the extracted CRL" do + crl = stub 'crl' + OpenSSL::X509::CRL.expects(:new).returns(crl) + + mycrl = stub 'sslcrl' + mycrl.expects(:content=).with(crl) + + @class.expects(:new).with("foo").returns mycrl + + @class.from_s("my crl").should == mycrl + end end describe "when an instance" do diff --git a/spec/unit/ssl/host.rb b/spec/unit/ssl/host.rb index f945d703f..73ce9c7f2 100755 --- a/spec/unit/ssl/host.rb +++ b/spec/unit/ssl/host.rb @@ -394,6 +394,14 @@ describe Puppet::SSL::Host do Puppet::SSL::Host.new("me").should respond_to(:ssl_store) end + it "should always return the same store" do + host = Puppet::SSL::Host.new("foo") + store = mock 'store' + store.stub_everything + OpenSSL::X509::Store.expects(:new).returns store + host.ssl_store.should equal(host.ssl_store) + end + describe "when creating an SSL store" do before do @host = Puppet::SSL::Host.new("me") |