summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Martin <max@puppetlabs.com>2011-04-05 15:56:29 -0700
committerMax Martin <max@puppetlabs.com>2011-04-05 15:56:29 -0700
commitff9b24fa7d60e7a224c78904ab451c4c39cb231f (patch)
treefa38972bd3e446d8eedd17de3b15dbd7a69ca2dd
parentb82bf9faa84447ac13b09d9d2db310fb3a35bd12 (diff)
parente20e6185f7f26d02c7ea275f8adf43c088169129 (diff)
downloadpuppet-ff9b24fa7d60e7a224c78904ab451c4c39cb231f.tar.gz
puppet-ff9b24fa7d60e7a224c78904ab451c4c39cb231f.tar.xz
puppet-ff9b24fa7d60e7a224c78904ab451c4c39cb231f.zip
Merge branch 'feature/master/5528-certificates_signing_api' into next
* feature/master/5528-certificates_signing_api: (#5528) Add REST API for signing, revoking, retrieving, cleaning certs
-rw-r--r--lib/puppet/indirector/certificate_status.rb4
-rw-r--r--lib/puppet/indirector/certificate_status/file.rb82
-rw-r--r--lib/puppet/indirector/certificate_status/rest.rb10
-rw-r--r--lib/puppet/network/http/api/v1.rb1
-rw-r--r--lib/puppet/ssl/host.rb78
-rw-r--r--spec/unit/indirector/certificate_status/file_spec.rb188
-rw-r--r--spec/unit/indirector/certificate_status/rest_spec.rb15
-rwxr-xr-xspec/unit/ssl/host_spec.rb192
8 files changed, 502 insertions, 68 deletions
diff --git a/lib/puppet/indirector/certificate_status.rb b/lib/puppet/indirector/certificate_status.rb
new file mode 100644
index 000000000..47c3adcd4
--- /dev/null
+++ b/lib/puppet/indirector/certificate_status.rb
@@ -0,0 +1,4 @@
+require 'puppet/indirector'
+
+class Puppet::Indirector::CertificateStatus
+end
diff --git a/lib/puppet/indirector/certificate_status/file.rb b/lib/puppet/indirector/certificate_status/file.rb
new file mode 100644
index 000000000..9061d9423
--- /dev/null
+++ b/lib/puppet/indirector/certificate_status/file.rb
@@ -0,0 +1,82 @@
+require 'puppet'
+require 'puppet/indirector/certificate_status'
+require 'puppet/ssl/certificate'
+require 'puppet/ssl/certificate_authority'
+require 'puppet/ssl/certificate_request'
+require 'puppet/ssl/host'
+require 'puppet/ssl/key'
+
+class Puppet::Indirector::CertificateStatus::File < Puppet::Indirector::Code
+ def ca
+ raise ArgumentError, "This process is not configured as a certificate authority" unless Puppet::SSL::CertificateAuthority.ca?
+ Puppet::SSL::CertificateAuthority.new
+ end
+
+ def destroy(request)
+ deleted = []
+ [
+ Puppet::SSL::Certificate,
+ Puppet::SSL::CertificateRequest,
+ Puppet::SSL::Key,
+ ].collect do |part|
+ if part.indirection.destroy(request.key)
+ deleted << "#{part}"
+ end
+ end
+
+ return "Nothing was deleted" if deleted.empty?
+ "Deleted for #{request.key}: #{deleted.join(", ")}"
+ end
+
+ def save(request)
+ if request.instance.desired_state == "signed"
+ certificate_request = Puppet::SSL::CertificateRequest.indirection.find(request.key)
+ raise Puppet::Error, "Cannot sign for host #{request.key} without a certificate request" unless certificate_request
+ ca.sign(request.key)
+ elsif request.instance.desired_state == "revoked"
+ certificate = Puppet::SSL::Certificate.indirection.find(request.key)
+ raise Puppet::Error, "Cannot revoke host #{request.key} because has it doesn't have a signed certificate" unless certificate
+ ca.revoke(request.key)
+ else
+ raise Puppet::Error, "State #{request.instance.desired_state} invalid; Must specify desired state of 'signed' or 'revoked' for host #{request.key}"
+ end
+
+ end
+
+ def search(request)
+ # Support historic interface wherein users provide classes to filter
+ # the search. When used via the REST API, the arguments must be
+ # a Symbol or an Array containing Symbol objects.
+ klasses = case request.options[:for]
+ when Class
+ [request.options[:for]]
+ when nil
+ [
+ Puppet::SSL::Certificate,
+ Puppet::SSL::CertificateRequest,
+ Puppet::SSL::Key,
+ ]
+ else
+ [request.options[:for]].flatten.map do |klassname|
+ indirection.class.model(klassname.to_sym)
+ end
+ end
+
+ klasses.collect do |klass|
+ klass.indirection.search(request.key, request.options)
+ end.flatten.collect do |result|
+ result.name
+ end.uniq.collect &Puppet::SSL::Host.method(:new)
+ end
+
+ def find(request)
+ ssl_host = Puppet::SSL::Host.new(request.key)
+ public_key = Puppet::SSL::Certificate.indirection.find(request.key)
+
+ if ssl_host.certificate_request || public_key
+ ssl_host
+ else
+ nil
+ end
+ end
+end
diff --git a/lib/puppet/indirector/certificate_status/rest.rb b/lib/puppet/indirector/certificate_status/rest.rb
new file mode 100644
index 000000000..c53b663b5
--- /dev/null
+++ b/lib/puppet/indirector/certificate_status/rest.rb
@@ -0,0 +1,10 @@
+require 'puppet/ssl/host'
+require 'puppet/indirector/rest'
+require 'puppet/indirector/certificate_status'
+
+class Puppet::Indirector::CertificateStatus::Rest < Puppet::Indirector::REST
+ desc "Sign, revoke, search for, or clean certificates & certificate requests over HTTP."
+
+ use_server_setting(:ca_server)
+ use_port_setting(:ca_port)
+end
diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb
index dcb0e0a22..5fe143979 100644
--- a/lib/puppet/network/http/api/v1.rb
+++ b/lib/puppet/network/http/api/v1.rb
@@ -61,6 +61,7 @@ module Puppet::Network::HTTP::API::V1
# that leads to the fix being too long.
return :singular if indirection == "facts"
return :singular if indirection == "status"
+ return :singular if indirection == "certificate_status"
return :plural if indirection == "inventory"
result = (indirection =~ /s$|_search$/) ? :plural : :singular
diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb
index 7f71ced99..b9215effd 100644
--- a/lib/puppet/ssl/host.rb
+++ b/lib/puppet/ssl/host.rb
@@ -1,3 +1,4 @@
+require 'puppet/indirector'
require 'puppet/ssl'
require 'puppet/ssl/key'
require 'puppet/ssl/certificate'
@@ -15,11 +16,17 @@ class Puppet::SSL::Host
CertificateRequest = Puppet::SSL::CertificateRequest
CertificateRevocationList = Puppet::SSL::CertificateRevocationList
+ extend Puppet::Indirector
+ indirects :certificate_status, :terminus_class => :file
+
attr_reader :name
attr_accessor :ca
attr_writer :key, :certificate, :certificate_request
+ # This accessor is used in instances for indirector requests to hold desired state
+ attr_accessor :desired_state
+
class << self
include Puppet::Util::Cacher
@@ -47,6 +54,13 @@ class Puppet::SSL::Host
CertificateRequest.indirection.terminus_class = terminus
CertificateRevocationList.indirection.terminus_class = terminus
+ host_map = {:ca => :file, :file => nil, :rest => :rest}
+ if term = host_map[terminus]
+ self.indirection.terminus_class = term
+ else
+ self.indirection.reset_terminus_class
+ end
+
if cache
# This is weird; we don't actually cache our keys, we
# use what would otherwise be the cache as our normal
@@ -85,30 +99,34 @@ class Puppet::SSL::Host
# Specify how we expect to interact with our certificate authority.
def self.ca_location=(mode)
- raise ArgumentError, "CA Mode can only be #{CA_MODES.collect { |m| m.to_s }.join(", ")}" unless CA_MODES.include?(mode)
+ modes = CA_MODES.collect { |m, vals| m.to_s }.join(", ")
+ raise ArgumentError, "CA Mode can only be one of: #{modes}" unless CA_MODES.include?(mode)
@ca_location = mode
configure_indirection(*CA_MODES[@ca_location])
end
- # Remove all traces of a given host
+ # Puppet::SSL::Host is actually indirected now so the original implementation
+ # has been moved into the certificate_status indirector. This method is in-use
+ # in `puppet cert -c <certname>`.
def self.destroy(name)
- [Key, Certificate, CertificateRequest].collect { |part| part.indirection.destroy(name) }.any? { |x| x }
+ indirection.destroy(name)
end
- # Search for more than one host, optionally only specifying
- # an interest in hosts with a given file type.
- # This just allows our non-indirected class to have one of
- # indirection methods.
- def self.search(options = {})
- classlist = [options[:for] || [Key, CertificateRequest, Certificate]].flatten
-
- # Collect the results from each class, flatten them, collect all of the names, make the name list unique,
- # then create a Host instance for each one.
- classlist.collect { |klass| klass.indirection.search }.flatten.collect { |r| r.name }.uniq.collect do |name|
- new(name)
+ def self.from_pson(pson)
+ instance = new(pson["name"])
+ if pson["desired_state"]
+ instance.desired_state = pson["desired_state"]
end
+ instance
+ end
+
+ # Puppet::SSL::Host is actually indirected now so the original implementation
+ # has been moved into the certificate_status indirector. This method does not
+ # appear to be in use in `puppet cert -l`.
+ def self.search(options = {})
+ indirection.search("*", options)
end
# Is this a ca host, meaning that all of its files go in the CA location?
@@ -221,6 +239,24 @@ class Puppet::SSL::Host
@ssl_store
end
+ def to_pson(*args)
+ my_cert = Puppet::SSL::Certificate.indirection.find(name)
+ pson_hash = { :name => name }
+
+ my_state = state
+
+ pson_hash[:state] = my_state
+ pson_hash[:desired_state] = desired_state if desired_state
+
+ if my_state == 'requested'
+ pson_hash[:fingerprint] = certificate_request.fingerprint
+ else
+ pson_hash[:fingerprint] = my_cert.fingerprint
+ end
+
+ pson_hash.to_pson(*args)
+ end
+
# Attempt to retrieve a cert, if we don't already have one.
def wait_for_cert(time)
begin
@@ -257,6 +293,20 @@ class Puppet::SSL::Host
end
end
end
+
+ def state
+ my_cert = Puppet::SSL::Certificate.indirection.find(name)
+ if certificate_request
+ return 'requested'
+ end
+
+ begin
+ Puppet::SSL::CertificateAuthority.new.verify(my_cert)
+ return 'signed'
+ rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError
+ return 'revoked'
+ end
+ end
end
require 'puppet/ssl/certificate_authority'
diff --git a/spec/unit/indirector/certificate_status/file_spec.rb b/spec/unit/indirector/certificate_status/file_spec.rb
new file mode 100644
index 000000000..6cc0bb547
--- /dev/null
+++ b/spec/unit/indirector/certificate_status/file_spec.rb
@@ -0,0 +1,188 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
+require 'puppet/ssl/host'
+require 'puppet/indirector/certificate_status'
+require 'tempfile'
+
+describe "Puppet::Indirector::CertificateStatus::File" do
+ include PuppetSpec::Files
+
+ before do
+ Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true
+ @terminus = Puppet::SSL::Host.indirection.terminus(:file)
+
+ @tmpdir = tmpdir("certificate_status_ca_testing")
+ Puppet[:confdir] = @tmpdir
+ Puppet[:vardir] = @tmpdir
+
+ # localcacert is where each client stores the CA certificate
+ # cacert is where the master stores the CA certificate
+ # Since we need to play the role of both for testing we need them to be the same and exist
+ Puppet[:cacert] = Puppet[:localcacert]
+ end
+
+ def generate_csr(host)
+ host.generate_key
+ csr = Puppet::SSL::CertificateRequest.new(host.name)
+ csr.generate(host.key.content)
+ Puppet::SSL::CertificateRequest.indirection.save(csr)
+ end
+
+ def sign_csr(host)
+ host.desired_state = "signed"
+ @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host))
+ end
+
+ def generate_signed_cert(host)
+ generate_csr(host)
+ sign_csr(host)
+
+ @terminus.find(Puppet::Indirector::Request.new(:certificate_status, :find, host.name, host))
+ end
+
+ def generate_revoked_cert(host)
+ generate_signed_cert(host)
+
+ host.desired_state = "revoked"
+
+ @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host))
+ end
+
+ it "should be a terminus on SSL::Host" do
+ @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::File)
+ end
+
+ it "should create a CA instance if none is present" do
+ @terminus.ca.should be_instance_of(Puppet::SSL::CertificateAuthority)
+ end
+
+ describe "when creating the CA" do
+ it "should fail if it is not a valid CA" do
+ Puppet::SSL::CertificateAuthority.expects(:ca?).returns false
+ lambda { @terminus.ca }.should raise_error(ArgumentError, "This process is not configured as a certificate authority")
+ end
+ end
+
+ it "should be indirected with the name 'certificate_status'" do
+ Puppet::SSL::Host.indirection.name.should == :certificate_status
+ end
+
+ describe "when finding" do
+ before do
+ @host = Puppet::SSL::Host.new("foo")
+ Puppet.settings.use(:main)
+ end
+
+ it "should return the Puppet::SSL::Host when a CSR exists for the host" do
+ generate_csr(@host)
+ request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+
+ retrieved_host = @terminus.find(request)
+
+ retrieved_host.name.should == @host.name
+ retrieved_host.certificate_request.content.to_s.chomp.should == @host.certificate_request.content.to_s.chomp
+ end
+
+ it "should return the Puppet::SSL::Host when a public key exist for the host" do
+ generate_signed_cert(@host)
+ request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+
+ retrieved_host = @terminus.find(request)
+
+ retrieved_host.name.should == @host.name
+ retrieved_host.certificate.content.to_s.chomp.should == @host.certificate.content.to_s.chomp
+ end
+
+ it "should return nil when neither a CSR nor public key exist for the host" do
+ request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+ @terminus.find(request).should == nil
+ end
+ end
+
+ describe "when saving" do
+ before do
+ @host = Puppet::SSL::Host.new("foobar")
+ Puppet.settings.use(:main)
+ end
+
+ describe "when signing a cert" do
+ before do
+ @host.desired_state = "signed"
+ @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host)
+ end
+
+ it "should fail if no CSR is on disk" do
+ lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /certificate request/)
+ end
+
+ it "should sign the on-disk CSR when it is present" do
+ signed_host = generate_signed_cert(@host)
+
+ signed_host.state.should == "signed"
+ Puppet::SSL::Certificate.indirection.find("foobar").should be_instance_of(Puppet::SSL::Certificate)
+ end
+ end
+
+ describe "when revoking a cert" do
+ before do
+ @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host)
+ end
+
+ it "should fail if no certificate is on disk" do
+ @host.desired_state = "revoked"
+ lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /Cannot revoke/)
+ end
+
+ it "should revoke the certificate when it is present" do
+ generate_revoked_cert(@host)
+
+ @host.state.should == 'revoked'
+ end
+ end
+ end
+
+ describe "when deleting" do
+ before do
+ Puppet.settings.use(:main)
+ end
+
+ it "should not delete anything if no certificate, request, or key is on disk" do
+ host = Puppet::SSL::Host.new("clean_me")
+ request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_me", host)
+ @terminus.destroy(request).should == "Nothing was deleted"
+ end
+
+ it "should clean certs, cert requests, keys" do
+ signed_host = Puppet::SSL::Host.new("clean_signed_cert")
+ generate_signed_cert(signed_host)
+ signed_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_signed_cert", signed_host)
+ @terminus.destroy(signed_request).should == "Deleted for clean_signed_cert: Puppet::SSL::Certificate, Puppet::SSL::Key"
+
+ requested_host = Puppet::SSL::Host.new("clean_csr")
+ generate_csr(requested_host)
+ csr_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_csr", requested_host)
+ @terminus.destroy(csr_request).should == "Deleted for clean_csr: Puppet::SSL::CertificateRequest, Puppet::SSL::Key"
+ end
+ end
+
+ describe "when searching" do
+ it "should return a list of all hosts with certificate requests, signed certs, or revoked certs" do
+ Puppet.settings.use(:main)
+
+ signed_host = Puppet::SSL::Host.new("signed_host")
+ generate_signed_cert(signed_host)
+
+ requested_host = Puppet::SSL::Host.new("requested_host")
+ generate_csr(requested_host)
+
+ revoked_host = Puppet::SSL::Host.new("revoked_host")
+ generate_revoked_cert(revoked_host)
+
+ retrieved_hosts = @terminus.search(Puppet::Indirector::Request.new(:certificate_status, :search, "all", signed_host))
+
+ results = retrieved_hosts.map {|h| [h.name, h.state]}.sort{ |h,i| h[0] <=> i[0] }
+ results.should == [["ca","signed"],["requested_host","requested"],["revoked_host","revoked"],["signed_host","signed"]]
+ end
+ end
+end
diff --git a/spec/unit/indirector/certificate_status/rest_spec.rb b/spec/unit/indirector/certificate_status/rest_spec.rb
new file mode 100644
index 000000000..f44eac671
--- /dev/null
+++ b/spec/unit/indirector/certificate_status/rest_spec.rb
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
+require 'puppet/ssl/host'
+require 'puppet/indirector/certificate_status'
+
+describe "Puppet::CertificateStatus::Rest" do
+ before do
+ @terminus = Puppet::SSL::Host.indirection.terminus(:rest)
+ end
+
+ it "should be a terminus on Puppet::SSL::Host" do
+ @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::Rest)
+ end
+end
diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb
index d8f15e738..885bd45e2 100755
--- a/spec/unit/ssl/host_spec.rb
+++ b/spec/unit/ssl/host_spec.rb
@@ -3,16 +3,19 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
require 'puppet/ssl/host'
+require 'puppet/sslcertificates'
+require 'puppet/sslcertificates/ca'
describe Puppet::SSL::Host do
before do
- @class = Puppet::SSL::Host
- @host = @class.new("myname")
+ Puppet::SSL::Host.indirection.terminus_class = :file
+ @host = Puppet::SSL::Host.new("myname")
end
after do
# Cleaned out any cached localhost instance.
Puppet::Util::Cacher.expire
+ Puppet::SSL::Host.ca_location = :none
end
it "should use any provided name as its name" do
@@ -140,13 +143,6 @@ describe Puppet::SSL::Host do
end
describe "when specifying the CA location" do
- before do
- [Puppet::SSL::Key, Puppet::SSL::Certificate, Puppet::SSL::CertificateRequest, Puppet::SSL::CertificateRevocationList].each do |klass|
- klass.indirection.stubs(:terminus_class=)
- klass.indirection.stubs(:cache_class=)
- end
- end
-
it "should support the location ':local'" do
lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error
end
@@ -168,80 +164,88 @@ describe Puppet::SSL::Host do
end
describe "as 'local'" do
- it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
- Puppet::SSL::Certificate.indirection.expects(:cache_class=).with :file
- Puppet::SSL::CertificateRequest.indirection.expects(:cache_class=).with :file
- Puppet::SSL::CertificateRevocationList.indirection.expects(:cache_class=).with :file
-
+ before do
Puppet::SSL::Host.ca_location = :local
end
- it "should set the terminus class for Key as :file" do
- Puppet::SSL::Key.indirection.expects(:terminus_class=).with :file
+ it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
+ Puppet::SSL::Certificate.indirection.cache_class.should == :file
+ Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file
+ Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file
+ end
- Puppet::SSL::Host.ca_location = :local
+ it "should set the terminus class for Key and Host as :file" do
+ Puppet::SSL::Key.indirection.terminus_class.should == :file
+ Puppet::SSL::Host.indirection.terminus_class.should == :file
end
it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do
- Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :ca
- Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :ca
- Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :ca
-
- Puppet::SSL::Host.ca_location = :local
+ Puppet::SSL::Certificate.indirection.terminus_class.should == :ca
+ Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca
+ Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca
end
end
describe "as 'remote'" do
- it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
- Puppet::SSL::Certificate.indirection.expects(:cache_class=).with :file
- Puppet::SSL::CertificateRequest.indirection.expects(:cache_class=).with :file
- Puppet::SSL::CertificateRevocationList.indirection.expects(:cache_class=).with :file
-
+ before do
Puppet::SSL::Host.ca_location = :remote
end
- it "should set the terminus class for Key as :file" do
- Puppet::SSL::Key.indirection.expects(:terminus_class=).with :file
-
- Puppet::SSL::Host.ca_location = :remote
+ it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
+ Puppet::SSL::Certificate.indirection.cache_class.should == :file
+ Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file
+ Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file
end
- it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :rest" do
- Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :rest
- Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :rest
- Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :rest
+ it "should set the terminus class for Key as :file" do
+ Puppet::SSL::Key.indirection.terminus_class.should == :file
+ end
- Puppet::SSL::Host.ca_location = :remote
+ it "should set the terminus class for Host, Certificate, CertificateRevocationList, and CertificateRequest as :rest" do
+ Puppet::SSL::Host.indirection.terminus_class.should == :rest
+ Puppet::SSL::Certificate.indirection.terminus_class.should == :rest
+ Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :rest
+ Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :rest
end
end
describe "as 'only'" do
- it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do
- Puppet::SSL::Key.indirection.expects(:terminus_class=).with :ca
- Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :ca
- Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :ca
- Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :ca
-
+ before do
Puppet::SSL::Host.ca_location = :only
end
- it "should reset the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do
- Puppet::SSL::Certificate.indirection.expects(:cache_class=).with nil
- Puppet::SSL::CertificateRequest.indirection.expects(:cache_class=).with nil
- Puppet::SSL::CertificateRevocationList.indirection.expects(:cache_class=).with nil
+ it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do
+ Puppet::SSL::Key.indirection.terminus_class.should == :ca
+ Puppet::SSL::Certificate.indirection.terminus_class.should == :ca
+ Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca
+ Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca
+ end
- Puppet::SSL::Host.ca_location = :only
+ it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do
+ Puppet::SSL::Certificate.indirection.cache_class.should be_nil
+ Puppet::SSL::CertificateRequest.indirection.cache_class.should be_nil
+ Puppet::SSL::CertificateRevocationList.indirection.cache_class.should be_nil
+ end
+
+ it "should set the terminus class for Host to :file" do
+ Puppet::SSL::Host.indirection.terminus_class.should == :file
end
end
describe "as 'none'" do
+ before do
+ Puppet::SSL::Host.ca_location = :none
+ end
+
it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do
- Puppet::SSL::Key.indirection.expects(:terminus_class=).with :file
- Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :file
- Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :file
- Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :file
+ Puppet::SSL::Key.indirection.terminus_class.should == :file
+ Puppet::SSL::Certificate.indirection.terminus_class.should == :file
+ Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :file
+ Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :file
+ end
- Puppet::SSL::Host.ca_location = :none
+ it "should set the terminus class for Host to 'none'" do
+ lambda { Puppet::SSL::Host.indirection.terminus_class }.should raise_error(Puppet::DevError)
end
end
end
@@ -271,8 +275,8 @@ describe Puppet::SSL::Host do
Puppet::SSL::Host.destroy("myhost").should be_true
end
- it "should return false if none of the classes returned true" do
- Puppet::SSL::Host.destroy("myhost").should be_false
+ it "should report that nothing was deleted if none of the classes returned true" do
+ Puppet::SSL::Host.destroy("myhost").should == "Nothing was deleted"
end
end
@@ -709,4 +713,84 @@ describe Puppet::SSL::Host do
@host.wait_for_cert(1)
end
end
+
+ describe "when handling PSON" do
+ include PuppetSpec::Files
+
+ before do
+ Puppet[:vardir] = tmpdir("ssl_test_vardir")
+ Puppet[:ssldir] = tmpdir("ssl_test_ssldir")
+ Puppet::SSLCertificates::CA.new.mkrootcert
+ # localcacert is where each client stores the CA certificate
+ # cacert is where the master stores the CA certificate
+ # Since we need to play the role of both for testing we need them to be the same and exist
+ Puppet[:cacert] = Puppet[:localcacert]
+
+ @ca=Puppet::SSL::CertificateAuthority.new
+ end
+
+ describe "when converting to PSON" do
+ it "should be able to identify a host with an unsigned certificate request" do
+ host = Puppet::SSL::Host.new("bazinga")
+ host.generate_certificate_request
+ pson_hash = {
+ "fingerprint" => host.certificate_request.fingerprint,
+ "desired_state" => 'requested',
+ "name" => host.name
+ }
+
+ result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
+ result["fingerprint"].should == pson_hash["fingerprint"]
+ result["name"].should == pson_hash["name"]
+ result["state"].should == pson_hash["desired_state"]
+ end
+
+ it "should be able to identify a host with a signed certificate" do
+ host = Puppet::SSL::Host.new("bazinga")
+ host.generate_certificate_request
+ @ca.sign(host.name)
+ pson_hash = {
+ "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint,
+ "desired_state" => 'signed',
+ "name" => host.name,
+ }
+
+ result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
+ result["fingerprint"].should == pson_hash["fingerprint"]
+ result["name"].should == pson_hash["name"]
+ result["state"].should == pson_hash["desired_state"]
+ end
+
+ it "should be able to identify a host with a revoked certificate" do
+ host = Puppet::SSL::Host.new("bazinga")
+ host.generate_certificate_request
+ @ca.sign(host.name)
+ @ca.revoke(host.name)
+ pson_hash = {
+ "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint,
+ "desired_state" => 'revoked',
+ "name" => host.name,
+ }
+
+ result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
+ result["fingerprint"].should == pson_hash["fingerprint"]
+ result["name"].should == pson_hash["name"]
+ result["state"].should == pson_hash["desired_state"]
+ end
+ end
+
+ describe "when converting from PSON" do
+ it "should return a Puppet::SSL::Host object with the specified desired state" do
+ host = Puppet::SSL::Host.new("bazinga")
+ host.desired_state="signed"
+ pson_hash = {
+ "name" => host.name,
+ "desired_state" => host.desired_state,
+ }
+ generated_host = Puppet::SSL::Host.from_pson(pson_hash)
+ generated_host.desired_state.should == host.desired_state
+ generated_host.name.should == host.name
+ end
+ end
+ end
end