summaryrefslogtreecommitdiffstats
path: root/spec/unit/indirector/ssl_file_spec.rb
blob: 8ee19c8c8b9f735259f808034499ed06a93a2e00 (plain)
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
#!/usr/bin/env rspec
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
    @path = make_absolute("/thisdoesntexist/my_directory")
    Puppet[:noop] = false
    Puppet[@setting] = @path
    Puppet[:trace] = false
  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
    @file_class.collection_directory.should be_nil
  end

  it "should return a nil file location if no location has been provided" do
    @file_class.store_at nil
    @file_class.file_location.should be_nil
  end

  it "should fail if no store directory or file location has been set" do
    @file_class.store_in nil
    @file_class.store_at nil
    FileTest.expects(:exists?).with(File.dirname(@path)).at_least(0).returns(true)
    Dir.stubs(:mkdir).with(@path)
    lambda { @file_class.new }.should 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"
      @searcher.should 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 :casetting

        Puppet.settings.stubs(:value).with(:casetting).returns "/ca/file"

        @searcher.expects(:ca?).with(@cert.name).returns true
        @searcher.path(@cert.name).should == "/ca/file"
      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 :mysetting

        Puppet.settings.stubs(:value).with(:mysetting).returns "/some/file"

        @searcher.path(@cert.name).should == "/some/file"
      end

      it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" 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
          # Stub things so the case management bits work.
          FileTest.stubs(:exist?).with(File.dirname(@certpath)).returns false
          FileTest.expects(:exist?).with(@certpath).returns false
        end

        it "should return nil" do
          @searcher.find(@request).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(@request).should equal(cert)
        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")
          FileTest.expects(:exist?).with(@path).returns(false)
          dir, file = File.split(@path)
          FileTest.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
        lambda { @searcher.save(@request) }.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(@request) }.should 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.expects(:writesub).with(@setting, @certpath).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.expects(:write).with(@setting).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 :castuff
          Puppet.settings.stubs(:value).with(:castuff).returns "castuff stub"

          fh = mock 'filehandle'
          fh.stubs :print
          Puppet.settings.expects(:write).with(:castuff).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
          FileTest.expects(:exist?).with(@certpath).returns false
        end

        it "should return false" do
          @searcher.destroy(@request).should be_false
        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(@request)
        end

        it "should log that is removing the file" do
          File.stubs(:exist?).returns true
          File.stubs(:unlink)
          Puppet.expects(:notice)
          @searcher.destroy(@request)
        end
      end
    end

    describe "when searching for certificates" do
      before do
        @model = mock 'model'
        @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}

        one = stub 'one', :read => nil
        two = stub 'two', :read => nil

        @model.expects(:new).with("one").returns one
        @model.expects(:new).with("two").returns two

        @searcher.search(@request).should == [one, two]
      end

      it "should read each certificate in using the model's :read method" do
        Dir.expects(:entries).with(@path).returns %w{one.pem}

        one = stub 'one'
        one.expects(:read).with(File.join(@path, "one.pem"))

        @model.expects(:new).with("one").returns one

        @searcher.search(@request)
      end

      it "should skip any files that do not match /\.pem$/" do
        Dir.expects(:entries).with(@path).returns %w{. .. one.pem}

        one = stub 'one', :read => nil

        @model.expects(:new).with("one").returns one

        @searcher.search(@request)
      end
    end
  end
end