summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-08-07 17:39:13 -0700
committerLuke Kanies <luke@madstop.com>2008-08-07 17:39:13 -0700
commit113d74aaa630f499c8b7989aac6680e22e8e38c8 (patch)
tree250783a4ef765ca37b70f98d6df6782f986d99ba
parent2cad30a18c5e0e4fb93603ab422c290a62d45131 (diff)
Certificates now work over REST.
All of the format work is done, they all support plaintext successfully, and I've got integration tests that demonstrate that it actually works. Signed-off-by: Luke Kanies <luke@madstop.com>
-rw-r--r--lib/puppet/network/formats.rb4
-rw-r--r--lib/puppet/ssl/certificate.rb11
-rw-r--r--lib/puppet/ssl/certificate_request.rb11
-rw-r--r--lib/puppet/ssl/certificate_revocation_list.rb10
-rw-r--r--lib/puppet/ssl/key.rb2
-rwxr-xr-xspec/integration/indirector/certificate/rest.rb66
-rwxr-xr-xspec/integration/indirector/certificate_request/rest.rb86
-rwxr-xr-xspec/integration/indirector/certificate_revocation_list/rest.rb71
-rwxr-xr-xspec/unit/ssl/certificate.rb16
-rwxr-xr-xspec/unit/ssl/certificate_request.rb16
-rwxr-xr-xspec/unit/ssl/certificate_revocation_list.rb16
-rwxr-xr-xspec/unit/ssl/host.rb8
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")