summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-03-10 10:37:57 -0700
committerLuke Kanies <luke@madstop.com>2008-04-15 21:34:04 -0500
commitec5bdf3b2d089d53f1f8fd986df83768564e79ac (patch)
treef2246b87471efe1f4b2689afb2a4abc1d2319843
parentbb87464f75cf3ea2e17bb660e7d1880bc36e141f (diff)
downloadpuppet-ec5bdf3b2d089d53f1f8fd986df83768564e79ac.tar.gz
puppet-ec5bdf3b2d089d53f1f8fd986df83768564e79ac.tar.xz
puppet-ec5bdf3b2d089d53f1f8fd986df83768564e79ac.zip
The basics for the certificate and certificate request
indirection terminii are done. I need to move most of the test code to a shared behaviour now.
-rw-r--r--lib/puppet/indirector/certificate/file.rb8
-rw-r--r--lib/puppet/indirector/key/file.rb11
-rw-r--r--lib/puppet/indirector/ssl_file.rb66
-rw-r--r--lib/puppet/ssl/base.rb40
-rw-r--r--lib/puppet/ssl/certificate.rb14
-rw-r--r--lib/puppet/ssl/certificate_request.rb13
-rw-r--r--lib/puppet/ssl/key.rb13
-rwxr-xr-xspec/unit/indirector/certificate/file.rb103
-rwxr-xr-xspec/unit/ssl/certificate.rb25
-rwxr-xr-xspec/unit/ssl/certificate_request.rb33
-rwxr-xr-xspec/unit/ssl/key.rb19
11 files changed, 304 insertions, 41 deletions
diff --git a/lib/puppet/indirector/certificate/file.rb b/lib/puppet/indirector/certificate/file.rb
new file mode 100644
index 000000000..9e2e8ed99
--- /dev/null
+++ b/lib/puppet/indirector/certificate/file.rb
@@ -0,0 +1,8 @@
+require 'puppet/indirector/ssl_file'
+require 'puppet/ssl/certificate'
+
+class Puppet::SSL::Certificate::File < Puppet::Indirector::SslFile
+ desc "Manage SSL certificates on disk."
+
+ store_in :certdir
+end
diff --git a/lib/puppet/indirector/key/file.rb b/lib/puppet/indirector/key/file.rb
index 47152ee08..d4f39ac2e 100644
--- a/lib/puppet/indirector/key/file.rb
+++ b/lib/puppet/indirector/key/file.rb
@@ -4,11 +4,6 @@ require 'puppet/ssl/key'
class Puppet::SSL::Key::File < Puppet::Indirector::File
desc "Manage SSL private and public keys on disk."
- # Is this key a CA key?
- def ca_key?(key)
- key.name == :ca
- end
-
def path(name)
if name == :ca
Puppet.settings[:cakey]
@@ -26,8 +21,6 @@ class Puppet::SSL::Key::File < Puppet::Indirector::File
end
def save(key)
- return save_ca_key(key) if ca_key?(key)
-
# Save the private key
File.open(path(key.name), "w") { |f| f.print key.to_pem }
@@ -36,15 +29,11 @@ class Puppet::SSL::Key::File < Puppet::Indirector::File
end
def find(name)
- return find_ca_key(key) if ca_key?(key)
-
return nil unless FileTest.exist?(path(name))
OpenSSL::PKey::RSA.new(File.read(path(name)))
end
def destroy(name)
- return find_ca_key(key) if ca_key?(key)
-
return nil unless FileTest.exist?(path(name))
File.unlink(path(name)) and true
end
diff --git a/lib/puppet/indirector/ssl_file.rb b/lib/puppet/indirector/ssl_file.rb
new file mode 100644
index 000000000..50e9eb8df
--- /dev/null
+++ b/lib/puppet/indirector/ssl_file.rb
@@ -0,0 +1,66 @@
+require 'puppet/indirector/file'
+
+class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus
+ def self.store_in(setting)
+ @directory_setting = setting
+ end
+
+ class << self
+ attr_reader :directory_setting
+ end
+
+ def self.collection_directory
+ raise(Puppet::DevError, "No setting defined for %s" % self) unless @directory_setting
+ Puppet.settings[@directory_setting]
+ end
+
+ def initialize
+ Puppet.settings.use(:ssl)
+ end
+
+ # Use a setting to determine our path.
+ def path(name)
+ File.join(collection_directory, name.to_s + ".pem")
+ end
+
+ def destroy(file)
+ path = path(file.name)
+ raise Puppet::Error.new("File %s does not exist; cannot destroy" % [file]) unless FileTest.exist?(path)
+
+ begin
+ File.unlink(path)
+ rescue => detail
+ raise Puppet::Error, "Could not remove %s: %s" % [file, detail]
+ end
+ end
+
+ def find(name)
+ path = path(name)
+
+ return nil unless FileTest.exist?(path)
+
+ result = model.new(name)
+ result.read(path)
+ result
+ end
+
+ def save(file)
+ path = path(file.name)
+ dir = File.dirname(path)
+
+ raise Puppet::Error.new("Cannot save %s; parent directory %s does not exist" % [file, dir]) unless FileTest.directory?(dir)
+ raise Puppet::Error.new("Cannot save %s; parent directory %s does not exist" % [file, dir]) unless FileTest.writable?(dir)
+
+ begin
+ File.open(path, "w") { |f| f.print file.to_s }
+ rescue => detail
+ raise Puppet::Error, "Could not write %s: %s" % [file, detail]
+ end
+ end
+
+ private
+
+ def collection_directory
+ self.class.collection_directory
+ end
+end
diff --git a/lib/puppet/ssl/base.rb b/lib/puppet/ssl/base.rb
new file mode 100644
index 000000000..87cbea4b5
--- /dev/null
+++ b/lib/puppet/ssl/base.rb
@@ -0,0 +1,40 @@
+require 'puppet/ssl'
+
+# The base class for wrapping SSL instances.
+class Puppet::SSL::Base
+ def self.wraps(klass)
+ @wrapped_class = klass
+ end
+
+ def self.wrapped_class
+ raise(Puppet::DevError, "%s has not declared what class it wraps" % self) unless defined?(@wrapped_class)
+ @wrapped_class
+ end
+
+ attr_accessor :name, :content
+
+ def generate
+ raise Puppet::DevError, "%s did not override 'generate'" % self.class
+ end
+
+ def initialize(name)
+ @name = name
+ end
+
+ # Read content from disk appropriately.
+ def read(path)
+ @content = wrapped_class.new(File.read(path))
+ end
+
+ # Convert our thing to pem.
+ def to_s
+ return "" unless content
+ content.to_pem
+ end
+
+ private
+
+ def wrapped_class
+ self.class.wrapped_class
+ end
+end
diff --git a/lib/puppet/ssl/certificate.rb b/lib/puppet/ssl/certificate.rb
index 7a5f97452..9a89b4530 100644
--- a/lib/puppet/ssl/certificate.rb
+++ b/lib/puppet/ssl/certificate.rb
@@ -1,19 +1,15 @@
-require 'puppet/ssl'
+require 'puppet/ssl/base'
# The class that manages all aspects of our SSL certificates --
# private keys, public keys, requests, etc.
-class Puppet::SSL::Certificate
- extend Puppet::Indirector
+class Puppet::SSL::Certificate < Puppet::SSL::Base
+ # This is defined from the base class
+ wraps OpenSSL::X509::Certificate
+ extend Puppet::Indirector
indirects :certificate #, :terminus_class => :file
- attr_accessor :name, :content
-
def generate
raise Puppet::DevError, "Cannot generate certificates directly; they must be generated during signing"
end
-
- def initialize(name)
- @name = name
- end
end
diff --git a/lib/puppet/ssl/certificate_request.rb b/lib/puppet/ssl/certificate_request.rb
index ce3aff477..67f0f23c6 100644
--- a/lib/puppet/ssl/certificate_request.rb
+++ b/lib/puppet/ssl/certificate_request.rb
@@ -1,13 +1,12 @@
-require 'puppet/ssl'
+require 'puppet/ssl/base'
# This constant just exists for us to use for adding our request terminii.
-class Puppet::SSL::CertificateRequest # :nodoc:
- extend Puppet::Indirector
+class Puppet::SSL::CertificateRequest < Puppet::SSL::Base
+ wraps OpenSSL::X509::Request
+ extend Puppet::Indirector
indirects :certificate_request #, :terminus_class => :file
- attr_reader :name, :content
-
# How to create a certificate request with our system defaults.
def generate(key)
Puppet.info "Creating a new SSL certificate request for %s" % name
@@ -20,8 +19,4 @@ class Puppet::SSL::CertificateRequest # :nodoc:
@content = csr
end
-
- def initialize(name)
- @name = name
- end
end
diff --git a/lib/puppet/ssl/key.rb b/lib/puppet/ssl/key.rb
index 0a207f320..18bf2a4cb 100644
--- a/lib/puppet/ssl/key.rb
+++ b/lib/puppet/ssl/key.rb
@@ -1,21 +1,16 @@
-require 'puppet/ssl'
+require 'puppet/ssl/base'
require 'puppet/indirector'
# Manage private and public keys as a pair.
-class Puppet::SSL::Key
- extend Puppet::Indirector
+class Puppet::SSL::Key < Puppet::SSL::Base
+ wraps OpenSSL::PKey::RSA
+ extend Puppet::Indirector
indirects :key #, :terminus_class => :file
- attr_accessor :name, :content
-
# Knows how to create keys with our system defaults.
def generate
Puppet.info "Creating a new SSL key for %s" % name
@content = OpenSSL::PKey::RSA.new(Puppet[:keylength])
end
-
- def initialize(name)
- @name = name
- end
end
diff --git a/spec/unit/indirector/certificate/file.rb b/spec/unit/indirector/certificate/file.rb
new file mode 100755
index 000000000..159a87606
--- /dev/null
+++ b/spec/unit/indirector/certificate/file.rb
@@ -0,0 +1,103 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-7.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/certificate/file'
+
+describe Puppet::SSL::Certificate::File do
+ before do
+ @file = Puppet::SSL::Certificate::File.new
+ @cert = Puppet::SSL::Certificate.new("myname")
+ Puppet.settings.stubs(:value).with(@file.class.directory_setting).returns "/test/dir"
+ @path = "/test/dir/myname.pem"
+ end
+
+ it "should have documentation" do
+ Puppet::SSL::Certificate::File.doc.should be_instance_of(String)
+ end
+
+ describe "when choosing the location for certificates" do
+ it "should set them in the :certdir, with the certificate name plus '.pem'" do
+ @file.path(@cert.name).should == @path
+ end
+ end
+
+ describe "when finding certificates on disk" do
+ describe "and no certificate is present" do
+ before do
+ FileTest.expects(:exist?).with(@path).returns false
+ end
+
+ it "should return nil" do
+ @file.find(@cert.name).should be_nil
+ end
+ end
+
+ describe "and a certificate is present" do
+ before do
+ FileTest.expects(:exist?).with(@path).returns true
+ end
+
+ it "should return an instance of the model, which it should use to read the certificate" do
+ cert = mock 'cert'
+ Puppet::SSL::Certificate.expects(:new).with("myname").returns cert
+ cert.expects(:read).with(@path)
+ @file.find("myname").should equal(cert)
+ end
+ end
+ end
+
+ describe "when saving certificates to disk" do
+ it "should fail if the directory is absent" do
+ FileTest.expects(:directory?).with(File.dirname(@path)).returns false
+ lambda { @file.save(@cert) }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail if the directory is not writeable" do
+ FileTest.stubs(:directory?).returns true
+ FileTest.expects(:writable?).with(File.dirname(@path)).returns false
+ lambda { @file.save(@cert) }.should raise_error(Puppet::Error)
+ end
+
+ it "should save to the path the output of converting the certificate to a string" do
+ FileTest.stubs(:directory?).returns true
+ FileTest.stubs(:writable?).returns true
+
+ fh = mock 'filehandle'
+ File.expects(:open).with(@path, "w").yields(fh)
+
+ @cert.expects(:to_s).returns "mycert"
+
+ fh.expects(:print).with("mycert")
+
+ @file.save(@cert)
+
+ end
+ end
+
+ describe "when destroying certificates" do
+ describe "that do not exist" do
+ before do
+ FileTest.expects(:exist?).with(@path).returns false
+ end
+
+ it "should fail" do
+ lambda { @file.destroy(@cert) }.should raise_error(Puppet::Error)
+ end
+ end
+
+ describe "that exist" do
+ before do
+ FileTest.expects(:exist?).with(@path).returns true
+ end
+
+ it "should unlink the certificate file" do
+ File.expects(:unlink).with(@path)
+ @file.destroy(@cert)
+ end
+ end
+ end
+end
diff --git a/spec/unit/ssl/certificate.rb b/spec/unit/ssl/certificate.rb
index d3ae1b949..69f4e1fa1 100755
--- a/spec/unit/ssl/certificate.rb
+++ b/spec/unit/ssl/certificate.rb
@@ -19,15 +19,34 @@ describe Puppet::SSL::Certificate do
describe "when managing instances" do
before do
- @cert = @class.new("myname")
+ @certificate = @class.new("myname")
end
it "should have a name attribute" do
- @cert.name.should == "myname"
+ @certificate.name.should == "myname"
end
it "should have a content attribute" do
- @cert.should respond_to(:content)
+ @certificate.should respond_to(:content)
+ end
+
+ it "should be able to read certificates from disk" do
+ path = "/my/path"
+ File.expects(:read).with(path).returns("my certificate")
+ certificate = mock 'certificate'
+ OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(certificate)
+ @certificate.read(path).should equal(certificate)
+ @certificate.content.should equal(certificate)
+ end
+
+ it "should return an empty string when converted to a string with no certificate" do
+ @certificate.to_s.should == ""
+ end
+
+ it "should convert the certificate to pem format when converted to a string" do
+ certificate = mock 'certificate', :to_pem => "pem"
+ @certificate.content = certificate
+ @certificate.to_s.should == "pem"
end
end
diff --git a/spec/unit/ssl/certificate_request.rb b/spec/unit/ssl/certificate_request.rb
index 4bcec5567..48755a614 100755
--- a/spec/unit/ssl/certificate_request.rb
+++ b/spec/unit/ssl/certificate_request.rb
@@ -22,6 +22,39 @@ describe Puppet::SSL::CertificateRequest do
@class.new("myname").name.should == "myname"
end
+ describe "when managing instances" do
+ before do
+ @request = @class.new("myname")
+ end
+
+ it "should have a name attribute" do
+ @request.name.should == "myname"
+ end
+
+ it "should have a content attribute" do
+ @request.should respond_to(:content)
+ end
+
+ it "should be able to read requests from disk" do
+ path = "/my/path"
+ File.expects(:read).with(path).returns("my request")
+ request = mock 'request'
+ OpenSSL::X509::Request.expects(:new).with("my request").returns(request)
+ @request.read(path).should equal(request)
+ @request.content.should equal(request)
+ end
+
+ it "should return an empty string when converted to a string with no request" do
+ @request.to_s.should == ""
+ end
+
+ it "should convert the request to pem format when converted to a string" do
+ request = mock 'request', :to_pem => "pem"
+ @request.content = request
+ @request.to_s.should == "pem"
+ end
+ end
+
describe "when generating" do
before do
@instance = @class.new("myname")
diff --git a/spec/unit/ssl/key.rb b/spec/unit/ssl/key.rb
index f8a74dcf1..e580bbc55 100755
--- a/spec/unit/ssl/key.rb
+++ b/spec/unit/ssl/key.rb
@@ -28,6 +28,25 @@ describe Puppet::SSL::Key do
it "should have a content attribute" do
@key.should respond_to(:content)
end
+
+ it "should be able to read keys from disk" do
+ path = "/my/path"
+ File.expects(:read).with(path).returns("my key")
+ key = mock 'key'
+ OpenSSL::PKey::RSA.expects(:new).with("my key").returns(key)
+ @key.read(path).should equal(key)
+ @key.content.should equal(key)
+ end
+
+ it "should return an empty string when converted to a string with no key" do
+ @key.to_s.should == ""
+ end
+
+ it "should convert the key to pem format when converted to a string" do
+ key = mock 'key', :to_pem => "pem"
+ @key.content = key
+ @key.to_s.should == "pem"
+ end
end
describe "when generating the private key" do