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)
downloadpuppet-113d74aaa630f499c8b7989aac6680e22e8e38c8.tar.gz
puppet-113d74aaa630f499c8b7989aac6680e22e8e38c8.tar.xz
puppet-113d74aaa630f499c8b7989aac6680e22e8e38c8.zip
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")