1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
|
#! /usr/bin/env ruby
require 'spec_helper'
require 'puppet/indirector/ssl_file'
describe Puppet::Indirector::SslFile do
include PuppetSpec::Files
before :all do
@indirection = stub 'indirection', :name => :testing, :model => @model
Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection)
module Testing; end
@file_class = class Testing::MyType < Puppet::Indirector::SslFile
self
end
end
before :each do
@model = mock 'model'
@setting = :certdir
@file_class.store_in @setting
@file_class.store_at nil
@file_class.store_ca_at nil
@path = make_absolute("/thisdoesntexist/my_directory")
Puppet[:noop] = false
Puppet[@setting] = @path
Puppet[:trace] = false
end
after :each do
@file_class.store_in nil
@file_class.store_at nil
@file_class.store_ca_at nil
end
it "should use :main and :ssl upon initialization" do
Puppet.settings.expects(:use).with(:main, :ssl)
@file_class.new
end
it "should return a nil collection directory if no directory setting has been provided" do
@file_class.store_in nil
expect(@file_class.collection_directory).to be_nil
end
it "should return a nil file location if no location has been provided" do
@file_class.store_at nil
expect(@file_class.file_location).to be_nil
end
it "should fail if no store directory or file location has been set" do
Puppet.settings.expects(:use).with(:main, :ssl)
@file_class.store_in nil
@file_class.store_at nil
expect {
@file_class.new
}.to raise_error(Puppet::DevError, /No file or directory setting provided/)
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")
@request = stub 'request', :key => @cert.name, :instance => @cert
end
it "should consider the file a ca file if the name is equal to what the SSL::Host class says is the CA name" do
Puppet::SSL::Host.expects(:ca_name).returns "amaca"
expect(@searcher).to be_ca("amaca")
end
describe "when choosing the location for certificates" do
it "should set them at the ca setting's path if a ca setting is available and the name resolves to the CA name" do
@file_class.store_in nil
@file_class.store_at :mysetting
@file_class.store_ca_at :cakey
Puppet[:cakey] = File.expand_path("/ca/file")
@searcher.expects(:ca?).with(@cert.name).returns true
expect(@searcher.path(@cert.name)).to eq(Puppet[:cakey])
end
it "should set them at the file location if a file setting is available" do
@file_class.store_in nil
@file_class.store_at :cacrl
Puppet[:cacrl] = File.expand_path("/some/file")
expect(@searcher.path(@cert.name)).to eq(Puppet[:cacrl])
end
it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do
expect(@searcher.path(@cert.name)).to eq(@certpath)
end
['../foo', '..\\foo', './../foo', '.\\..\\foo',
'/foo', '//foo', '\\foo', '\\\\goo',
"test\0/../bar", "test\0\\..\\bar",
"..\\/bar", "/tmp/bar", "/tmp\\bar", "tmp\\bar",
" / bar", " /../ bar", " \\..\\ bar",
"c:\\foo", "c:/foo", "\\\\?\\UNC\\bar", "\\\\foo\\bar",
"\\\\?\\c:\\foo", "//?/UNC/bar", "//foo/bar",
"//?/c:/foo",
].each do |input|
it "should resist directory traversal attacks (#{input.inspect})" do
expect { @searcher.path(input) }.to raise_error(ArgumentError, /invalid key/)
end
end
# REVISIT: Should probably test MS-DOS reserved names here, too, since
# they would represent a vulnerability on a Win32 system, should we ever
# support that path. Don't forget that 'CON.foo' == 'CON'
# --daniel 2011-09-24
end
describe "when finding certificates on disk" do
describe "and no certificate is present" do
it "should return nil" do
Puppet::FileSystem.expects(:exist?).with(@path).returns(true)
Dir.expects(:entries).with(@path).returns([])
Puppet::FileSystem.expects(:exist?).with(@certpath).returns(false)
expect(@searcher.find(@request)).to be_nil
end
end
describe "and a certificate is present" do
let(:cert) { mock 'cert' }
let(:model) { mock 'model' }
before(:each) do
@file_class.stubs(:model).returns model
end
context "is readable" do
it "should return an instance of the model, which it should use to read the certificate" do
Puppet::FileSystem.expects(:exist?).with(@certpath).returns true
model.expects(:new).with("myname").returns cert
cert.expects(:read).with(@certpath)
expect(@searcher.find(@request)).to equal(cert)
end
end
context "is unreadable" do
it "should raise an exception" do
Puppet::FileSystem.expects(:exist?).with(@certpath).returns(true)
model.expects(:new).with("myname").returns cert
cert.expects(:read).with(@certpath).raises(Errno::EACCES)
expect {
@searcher.find(@request)
}.to raise_error(Errno::EACCES)
end
end
end
describe "and a certificate is present but has uppercase letters" do
before do
@request = stub 'request', :key => "myhost"
end
# This is kind of more an integration test; it's for #1382, until
# the support for upper-case certs can be removed around mid-2009.
it "should rename the existing file to the lower-case path" do
@path = @searcher.path("myhost")
Puppet::FileSystem.expects(:exist?).with(@path).returns(false)
dir, file = File.split(@path)
Puppet::FileSystem.expects(:exist?).with(dir).returns true
Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase]
File.expects(:rename).with(File.join(dir, file.upcase), @path)
cert = mock 'cert'
model = mock 'model'
@searcher.stubs(:model).returns model
@searcher.model.expects(:new).with("myhost").returns cert
cert.expects(:read).with(@path)
@searcher.find(@request)
end
end
end
describe "when saving certificates to disk" do
before do
FileTest.stubs(:directory?).returns true
FileTest.stubs(:writable?).returns true
end
it "should fail if the directory is absent" do
FileTest.expects(:directory?).with(File.dirname(@certpath)).returns false
expect { @searcher.save(@request) }.to 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
expect { @searcher.save(@request) }.to raise_error(Puppet::Error)
end
it "should save to the path the output of converting the certificate to a string" do
fh = mock 'filehandle'
fh.expects(:print).with("mycert")
@searcher.stubs(:write).yields fh
@cert.expects(:to_s).returns "mycert"
@searcher.save(@request)
end
describe "and a directory setting is set" do
it "should use the Settings class to write the file" do
@searcher.class.store_in @setting
fh = mock 'filehandle'
fh.stubs :print
Puppet.settings.setting(@setting).expects(:open_file).with(@certpath, 'w').yields fh
@searcher.save(@request)
end
end
describe "and a file location is set" do
it "should use the filehandle provided by the Settings" do
@searcher.class.store_at @setting
fh = mock 'filehandle'
fh.stubs :print
Puppet.settings.setting(@setting).expects(:open).with('w').yields fh
@searcher.save(@request)
end
end
describe "and the name is the CA name and a ca setting is set" do
it "should use the filehandle provided by the Settings" do
@searcher.class.store_at @setting
@searcher.class.store_ca_at :cakey
Puppet[:cakey] = "castuff stub"
fh = mock 'filehandle'
fh.stubs :print
Puppet.settings.setting(:cakey).expects(:open).with('w').yields fh
@searcher.stubs(:ca?).returns true
@searcher.save(@request)
end
end
end
describe "when destroying certificates" do
describe "that do not exist" do
before do
Puppet::FileSystem.expects(:exist?).with(Puppet::FileSystem.pathname(@certpath)).returns false
end
it "should return false" do
expect(@searcher.destroy(@request)).to be_falsey
end
end
describe "that exist" do
it "should unlink the certificate file" do
path = Puppet::FileSystem.pathname(@certpath)
Puppet::FileSystem.expects(:exist?).with(path).returns true
Puppet::FileSystem.expects(:unlink).with(path)
@searcher.destroy(@request)
end
it "should log that is removing the file" do
Puppet::FileSystem.stubs(:exist?).returns true
Puppet::FileSystem.stubs(:unlink)
Puppet.expects(:notice)
@searcher.destroy(@request)
end
end
end
describe "when searching for certificates" do
let(:one) { stub 'one' }
let(:two) { stub 'two' }
let(:one_path) { File.join(@path, 'one.pem') }
let(:two_path) { File.join(@path, 'two.pem') }
let(:model) { mock 'model' }
before :each do
@file_class.stubs(:model).returns model
end
it "should return a certificate instance for all files that exist" do
Dir.expects(:entries).with(@path).returns(%w{. .. one.pem two.pem})
model.expects(:new).with("one").returns one
one.expects(:read).with(one_path)
model.expects(:new).with("two").returns two
two.expects(:read).with(two_path)
expect(@searcher.search(@request)).to eq([one, two])
end
it "should raise an exception if any file is unreadable" do
Dir.expects(:entries).with(@path).returns(%w{. .. one.pem two.pem})
model.expects(:new).with("one").returns(one)
one.expects(:read).with(one_path)
model.expects(:new).with("two").returns(two)
two.expects(:read).raises(Errno::EACCES)
expect {
@searcher.search(@request)
}.to raise_error(Errno::EACCES)
end
it "should skip any files that do not match /\.pem$/" do
Dir.expects(:entries).with(@path).returns(%w{. .. one two.notpem})
model.expects(:new).never
expect(@searcher.search(@request)).to eq([])
end
end
end
end
|