diff options
-rw-r--r-- | lib/puppet/indirector/key/file.rb | 48 | ||||
-rw-r--r-- | lib/puppet/indirector/ssl_file.rb | 5 | ||||
-rw-r--r-- | lib/puppet/ssl.rb | 3 | ||||
-rwxr-xr-x | spec/unit/indirector/certificate/file.rb | 90 | ||||
-rwxr-xr-x | spec/unit/indirector/key/file.rb | 167 | ||||
-rwxr-xr-x | spec/unit/indirector/ssl_file.rb | 130 |
6 files changed, 201 insertions, 242 deletions
diff --git a/lib/puppet/indirector/key/file.rb b/lib/puppet/indirector/key/file.rb index d4f39ac2e..9efcd1a31 100644 --- a/lib/puppet/indirector/key/file.rb +++ b/lib/puppet/indirector/key/file.rb @@ -1,40 +1,36 @@ -require 'puppet/indirector/file' +require 'puppet/indirector/ssl_file' require 'puppet/ssl/key' -class Puppet::SSL::Key::File < Puppet::Indirector::File +class Puppet::SSL::Key::File < Puppet::Indirector::SslFile desc "Manage SSL private and public keys on disk." - def path(name) - if name == :ca - Puppet.settings[:cakey] - else - File.join(Puppet.settings[:privatekeydir], name.to_s + ".pem") - end - end + store_in :privatekeydir def public_key_path(name) - if name == :ca - Puppet.settings[:capub] - else - File.join(Puppet.settings[:publickeydir], name.to_s + ".pem") - end + File.join(Puppet[:publickeydir], name.to_s + ".pem") end - def save(key) - # Save the private key - File.open(path(key.name), "w") { |f| f.print key.to_pem } + # Remove the public key, in addition to the private key + def destroy(key) + super - # Now save the public key - File.open(public_key_path(name), "w") { |f| f.print key.to_pem } - end + return unless FileTest.exist?(public_key_path(key.name)) - def find(name) - return nil unless FileTest.exist?(path(name)) - OpenSSL::PKey::RSA.new(File.read(path(name))) + begin + File.unlink(public_key_path(key.name)) + rescue => detail + raise Puppet::Error, "Could not remove %s public key: %s" % [key.name, detail] + end end - def destroy(name) - return nil unless FileTest.exist?(path(name)) - File.unlink(path(name)) and true + # Save the public key, in addition to the private key. + def save(key) + super + + begin + File.open(public_key_path(key.name), "w") { |f| f.print key.content.public_key.to_pem } + rescue => detail + raise Puppet::Error, "Could not write %s: %s" % [key, detail] + end end end diff --git a/lib/puppet/indirector/ssl_file.rb b/lib/puppet/indirector/ssl_file.rb index 50e9eb8df..6125d46e4 100644 --- a/lib/puppet/indirector/ssl_file.rb +++ b/lib/puppet/indirector/ssl_file.rb @@ -9,6 +9,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus attr_reader :directory_setting end + # The full path to where we should store our files. def self.collection_directory raise(Puppet::DevError, "No setting defined for %s" % self) unless @directory_setting Puppet.settings[@directory_setting] @@ -23,6 +24,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus File.join(collection_directory, name.to_s + ".pem") end + # Remove our file. def destroy(file) path = path(file.name) raise Puppet::Error.new("File %s does not exist; cannot destroy" % [file]) unless FileTest.exist?(path) @@ -34,6 +36,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus end end + # Find the file on disk, returning an instance of the model. def find(name) path = path(name) @@ -44,6 +47,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus result end + # Save our file to disk. def save(file) path = path(file.name) dir = File.dirname(path) @@ -60,6 +64,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus private + # A demeterish pointer to the collection directory. def collection_directory self.class.collection_directory end diff --git a/lib/puppet/ssl.rb b/lib/puppet/ssl.rb index ae8f0abea..68c65ca80 100644 --- a/lib/puppet/ssl.rb +++ b/lib/puppet/ssl.rb @@ -1,3 +1,6 @@ # Just to make the constants work out. +require 'puppet' +require 'openssl' + module Puppet::SSL # :nodoc: end diff --git a/spec/unit/indirector/certificate/file.rb b/spec/unit/indirector/certificate/file.rb index 159a87606..18fe9a1c3 100755 --- a/spec/unit/indirector/certificate/file.rb +++ b/spec/unit/indirector/certificate/file.rb @@ -8,96 +8,12 @@ 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 + it "should use the :certdir as the collection directory" do + Puppet.settings.expects(:value).with(:certdir).returns "/cert/dir" + Puppet::SSL::Certificate::File.collection_directory.should == "/cert/dir" end end diff --git a/spec/unit/indirector/key/file.rb b/spec/unit/indirector/key/file.rb index b9e9e2b11..add80b26c 100755 --- a/spec/unit/indirector/key/file.rb +++ b/spec/unit/indirector/key/file.rb @@ -8,158 +8,67 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/key/file' describe Puppet::SSL::Key::File do - it "should be a subclass of the File terminus class" do - Puppet::SSL::Key::File.superclass.should equal(Puppet::Indirector::File) - end - it "should have documentation" do Puppet::SSL::Key::File.doc.should be_instance_of(String) end - describe "when managing keys on disk" do - before do - @file = Puppet::SSL::Key::File.new - end - - describe "and choosing the location" do - describe "for certificate authority keys" do - before do - @private_key = "/path/to/private/ca/key" - @public_key = "/path/to/public/ca/key" - Puppet.settings.stubs(:value).with(:cakey).returns @private_key - Puppet.settings.stubs(:value).with(:capub).returns @public_key - end - - it "should use the :cakey as the private key location and :capub for the public key location" do - File.expects(:open).with(@private_key, "w") - File.expects(:open).with(@private_key, "w") - - key = stub 'key', :name => :ca - - @file.save(key) - end - end - - describe "for normal keys" do - before do - @name = "myhost" - end - - it "should save private key to the :privatekeydir with the file named after the key name plus '.pem'" - - it "should save the public key to the :publickeydir with the file named after the key name plus '.pem'" - end - end - - it "should be able to find keys saved to disk" - - it "should convert found keys to instances of OpenSSL::PKey::RSA" - - it "should be able to save keys to disk" - - it "should save keys in pem format" - - it "should save both public and private keys" - - it "should be able to remove keys stored on disk" - - it "should remove both public and private keys when the key is destroyed" - - it "should fail when attempting to remove missing keys" + it "should use the :privatekeydir as the collection directory" do + Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" + Puppet::SSL::Key::File.collection_directory.should == "/key/dir" end - describe "when choosing a path for a ca key" do + describe "when managing private keys" do + before do + @private = "/private/key/dir" + @public = "/public/key/dir" + Puppet.settings.stubs(:value).with(:privatekeydir).returns @private + Puppet.settings.stubs(:value).with(:publickeydir).returns @public + Puppet.settings.stubs(:use) - it "should use the cadir" do - pending "eh" - Puppet.settings.stubs(:value).with(:cadir).returns("/dir") - @file.path(@name).should =~ /^\/dir/ - end + @searcher = Puppet::SSL::Key::File.new - it "should use 'ca_key.pem' as the file name" do - pending "eh" - @file.path(@name).should =~ /ca_key\.pem$/ - end - end + @privatekey = File.join(@private, "myname" + ".pem") + @publickey = File.join(@public, "myname" + ".pem") - describe "when choosing a path for a non-ca key" do - before do - @name = :publickey - end + @public_key = stub 'public_key' + @real_key = stub 'sslkey', :public_key => @public_key - it "should use the privatekeydir" do - pending "eh" - Puppet.settings.stubs(:value).with(:publickeydir).returns("/dir") - @file.path(@name).should =~ /^\/dir/ + @key = stub 'key', :name => "myname", :content => @real_key end - it "should use the key name with the pem file extension" do - pending "eh" - @file.path(@name).should =~ /#{@name}\.pem$/ - end - end + it "should save the public key when saving the private key" do + FileTest.stubs(:directory?).returns true + FileTest.stubs(:writable?).returns true - describe "when saving" do - before do - Puppet.settings.stubs(:value).with(:publickeydir).returns("/dir") - @key = stub "key", :name => "foo" - end + File.stubs(:open).with(@privatekey, "w") - it "should store the private key to disk in pem format in the privatekey directory" do - pending "eh" - @key.expects(:to_pem).returns(:data) - @path = "/dir/foo.pem" - filehandle = mock "filehandle" - File.expects(:open).with(@path, "w").yields(filehandle) - filehandle.expects(:print).with(:data) - @file.save(@key) - end + fh = mock 'filehandle' - it "should store the public key to disk in pem format in the publickey directory" - end + File.expects(:open).with(@publickey, "w").yields fh + @public_key.expects(:to_pem).returns "my pem" - describe "when finding a key by name" do - before do - Puppet.settings.stubs(:value).with(:publickeydir).returns("/dir") - @name = "foo" - end + fh.expects(:print).with "my pem" - it "should return the key as a key object on success" do - pending "eh" - @path = "/dir/foo.pem" - FileTest.stubs(:exists?).with(@path).returns(true) - File.stubs(:read).with(@path).returns(:data) - OpenSSL::PKey::RSA.expects(:new).with(:data).returns(:mykey) - @file.find(@name).should == :mykey + @searcher.save(@key) end - it "should return 'nil' on failure" do - pending "eh" - @path = "/dir/foo.pem" - FileTest.stubs(:exists?).with(@path).returns(false) - @file.find(@name).should == nil - end - end + it "should destroy the public key when destroying the private key" do + File.stubs(:unlink).with(@privatekey) + FileTest.stubs(:exist?).with(@privatekey).returns true + FileTest.expects(:exist?).with(@publickey).returns true + File.expects(:unlink).with(@publickey) - describe "when removing a key" do - before do - Puppet.settings.stubs(:value).with(:publickeydir).returns("/dir") - @name = "foo" + @searcher.destroy(@key) end - it "should remove the key from disk and return true" do - pending "eh" - @path = "/dir/foo.pem" - FileTest.stubs(:exists?).with(@path).returns(true) - File.stubs(:unlink).with(@path).returns(true) - @file.destroy(@name).should == true - end + it "should not fail if the public key does not exist when deleting the private key" do + File.stubs(:unlink).with(@privatekey) + + FileTest.stubs(:exist?).with(@privatekey).returns true + FileTest.expects(:exist?).with(@publickey).returns false + File.expects(:unlink).with(@publickey).never - it "should return an exception on failure" do - pending "eh" - @path = "/dir/foo.pem" - FileTest.stubs(:exists?).with(@path).returns(false) - @file.destroy(@name).should == nil + @searcher.destroy(@key) end end end diff --git a/spec/unit/indirector/ssl_file.rb b/spec/unit/indirector/ssl_file.rb new file mode 100755 index 000000000..5c148a967 --- /dev/null +++ b/spec/unit/indirector/ssl_file.rb @@ -0,0 +1,130 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-10. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/ssl_file' + +describe Puppet::Indirector::SslFile do + before do + @indirection = stub 'indirection', :name => :testing + Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) + @file_class = Class.new(Puppet::Indirector::SslFile) do + def self.to_s + "Testing::Mytype" + end + end + + @setting = :mydir + @file_class.store_in @setting + @path = "/my/directory" + Puppet.settings.stubs(:[]).with(@setting).returns(@path) + end + + it "should use ssl upon initialization" do + Puppet.settings.expects(:use).with(:ssl) + @file_class.new + end + + it "should fail if no store directory has been set" do + @file_class.store_in nil + lambda { @file_class.collection_directory }.should raise_error(Puppet::DevError) + end + + describe "when managing ssl files" do + before do + Puppet.settings.stubs(:use) + @searcher = @file_class.new + + @cert = stub 'certificate', :name => "myname" + @certpath = File.join(@path, "myname" + ".pem") + end + + describe "when choosing the location for certificates" do + it "should set them in the setting directory, with the certificate name plus '.pem'" do + @searcher.path(@cert.name).should == @certpath + end + end + + describe "when finding certificates on disk" do + describe "and no certificate is present" do + before do + FileTest.expects(:exist?).with(@certpath).returns false + end + + it "should return nil" do + @searcher.find(@cert.name).should be_nil + end + end + + describe "and a certificate is present" do + before do + FileTest.expects(:exist?).with(@certpath).returns true + end + + it "should return an instance of the model, which it should use to read the certificate" do + cert = mock 'cert' + model = mock 'model' + @file_class.stubs(:model).returns model + + model.expects(:new).with("myname").returns cert + cert.expects(:read).with(@certpath) + @searcher.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(@certpath)).returns false + lambda { @searcher.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(@certpath)).returns false + lambda { @searcher.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(@certpath, "w").yields(fh) + + @cert.expects(:to_s).returns "mycert" + + fh.expects(:print).with("mycert") + + @searcher.save(@cert) + + end + end + + describe "when destroying certificates" do + describe "that do not exist" do + before do + FileTest.expects(:exist?).with(@certpath).returns false + end + + it "should fail" do + lambda { @searcher.destroy(@cert) }.should raise_error(Puppet::Error) + end + end + + describe "that exist" do + before do + FileTest.expects(:exist?).with(@certpath).returns true + end + + it "should unlink the certificate file" do + File.expects(:unlink).with(@certpath) + @searcher.destroy(@cert) + end + end + end + end +end |