diff options
| author | Luke Kanies <luke@madstop.com> | 2008-03-10 10:37:57 -0700 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2008-04-15 21:34:04 -0500 |
| commit | ec5bdf3b2d089d53f1f8fd986df83768564e79ac (patch) | |
| tree | f2246b87471efe1f4b2689afb2a4abc1d2319843 | |
| parent | bb87464f75cf3ea2e17bb660e7d1880bc36e141f (diff) | |
| download | puppet-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.rb | 8 | ||||
| -rw-r--r-- | lib/puppet/indirector/key/file.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/indirector/ssl_file.rb | 66 | ||||
| -rw-r--r-- | lib/puppet/ssl/base.rb | 40 | ||||
| -rw-r--r-- | lib/puppet/ssl/certificate.rb | 14 | ||||
| -rw-r--r-- | lib/puppet/ssl/certificate_request.rb | 13 | ||||
| -rw-r--r-- | lib/puppet/ssl/key.rb | 13 | ||||
| -rwxr-xr-x | spec/unit/indirector/certificate/file.rb | 103 | ||||
| -rwxr-xr-x | spec/unit/ssl/certificate.rb | 25 | ||||
| -rwxr-xr-x | spec/unit/ssl/certificate_request.rb | 33 | ||||
| -rwxr-xr-x | spec/unit/ssl/key.rb | 19 |
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 |
