certmeister 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +27 -0
  8. data/LICENSE +20 -0
  9. data/README.md +32 -0
  10. data/Rakefile +6 -0
  11. data/certmeister.gemspec +26 -0
  12. data/fixtures/ca.crt +15 -0
  13. data/fixtures/ca.csr +12 -0
  14. data/fixtures/ca.key +15 -0
  15. data/fixtures/client.crt +15 -0
  16. data/fixtures/client.csr +12 -0
  17. data/fixtures/client.key +15 -0
  18. data/lib/certmeister.rb +14 -0
  19. data/lib/certmeister/base.rb +92 -0
  20. data/lib/certmeister/config.rb +129 -0
  21. data/lib/certmeister/in_memory_store.rb +43 -0
  22. data/lib/certmeister/policy.rb +21 -0
  23. data/lib/certmeister/policy/blackhole.rb +15 -0
  24. data/lib/certmeister/policy/chain_all.rb +36 -0
  25. data/lib/certmeister/policy/domain.rb +37 -0
  26. data/lib/certmeister/policy/existing.rb +32 -0
  27. data/lib/certmeister/policy/fcrdns.rb +40 -0
  28. data/lib/certmeister/policy/noop.rb +15 -0
  29. data/lib/certmeister/policy/psk.rb +37 -0
  30. data/lib/certmeister/policy/response.rb +24 -0
  31. data/lib/certmeister/response.rb +47 -0
  32. data/lib/certmeister/store_error.rb +6 -0
  33. data/lib/certmeister/test/memory_store_interface.rb +54 -0
  34. data/lib/certmeister/version.rb +5 -0
  35. data/signit.rb +39 -0
  36. data/spec/certmeister/base_spec.rb +205 -0
  37. data/spec/certmeister/config_spec.rb +170 -0
  38. data/spec/certmeister/in_memory_store_spec.rb +40 -0
  39. data/spec/certmeister/policy/blackhole_spec.rb +19 -0
  40. data/spec/certmeister/policy/chain_all_spec.rb +40 -0
  41. data/spec/certmeister/policy/domain_spec.rb +38 -0
  42. data/spec/certmeister/policy/existing_spec.rb +39 -0
  43. data/spec/certmeister/policy/fcrdns_spec.rb +45 -0
  44. data/spec/certmeister/policy/noop_spec.rb +17 -0
  45. data/spec/certmeister/policy/psk_spec.rb +38 -0
  46. data/spec/certmeister/policy/response_spec.rb +35 -0
  47. data/spec/certmeister/response_spec.rb +73 -0
  48. data/spec/helpers/certmeister_config_helper.rb +21 -0
  49. data/spec/helpers/certmeister_fetching_request_helper.rb +9 -0
  50. data/spec/helpers/certmeister_policy_helper.rb +14 -0
  51. data/spec/helpers/certmeister_removing_request_helper.rb +9 -0
  52. data/spec/helpers/certmeister_signing_request_helper.rb +10 -0
  53. data/spec/spec_helper.rb +20 -0
  54. metadata +159 -0
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/domain'
4
+
5
+ describe Certmeister::Policy::Domain do
6
+
7
+ subject { Certmeister::Policy::Domain.new(['hetzner.africa']) }
8
+
9
+ it "must be configured with a list of domains" do
10
+ expected_error = "enumerable collection of domains required"
11
+ expect { Certmeister::Policy::Domain.new }.to raise_error(ArgumentError)
12
+ expect { Certmeister::Policy::Domain.new('example.com') }.to raise_error(ArgumentError, expected_error)
13
+ expect { Certmeister::Policy::Domain.new([]) }.to raise_error(ArgumentError, expected_error)
14
+ end
15
+
16
+ it "demands a request" do
17
+ expect { subject.authenticate }.to raise_error(ArgumentError)
18
+ end
19
+
20
+ it "refuses to authenticate a request with a missing cn" do
21
+ response = subject.authenticate({anything: 'something'})
22
+ expect(response).to_not be_authenticated
23
+ expect(response.error).to eql "missing cn"
24
+ end
25
+
26
+ it "refuses to authenticate a request with a cn in an unknown domain" do
27
+ response = subject.authenticate({anything: 'something', cn: 'axl.starjuice.net'})
28
+ expect(response).to_not be_authenticated
29
+ expect(response.error).to eql "cn in unknown domain"
30
+ end
31
+
32
+ it "authenticates any request with a cn in a known domain" do
33
+ response = subject.authenticate({anything: 'something', cn: 'axl.hetzner.africa'})
34
+ expect(response).to be_authenticated
35
+ end
36
+
37
+
38
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/existing'
4
+ require 'certmeister/in_memory_store'
5
+
6
+ describe Certmeister::Policy::Existing do
7
+
8
+ subject { Certmeister::Policy::Existing.new(Certmeister::InMemoryStore.new) }
9
+
10
+ it "must be configured with access to the store" do
11
+ expect { subject.class.new }.to raise_error(ArgumentError)
12
+ expect { subject.class.new(Object.new) }.to raise_error(ArgumentError)
13
+ expect { subject }.to_not raise_error
14
+ end
15
+
16
+ it "demands a request" do
17
+ expect { subject.authenticate }.to raise_error(ArgumentError)
18
+ end
19
+
20
+ context "when the store contains a cert for axl.hetzner.africa" do
21
+
22
+ subject { Certmeister::Policy::Existing.new(Certmeister::InMemoryStore.new({"axl.hetzner.africa" => "...cert..."})) }
23
+
24
+ it "refuses to authenticate a request for axl.hetzner.africa" do
25
+ response = subject.authenticate(cn: 'axl.hetzner.africa')
26
+ expect(response).to_not be_authenticated
27
+ expect(response.error).to match /exists/
28
+ end
29
+
30
+ it "authenticates requests for other common names" do
31
+ response = subject.authenticate(cn: 'bob.example.com')
32
+ expect(response).to be_authenticated
33
+ end
34
+
35
+ end
36
+
37
+
38
+
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/fcrdns'
4
+
5
+ describe Certmeister::Policy::Fcrdns do
6
+
7
+ it "demands a request" do
8
+ expect { subject.authenticate }.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it "refuses to authenticate a request with a missing cn" do
12
+ response = subject.authenticate({ip: '127.0.0.1'})
13
+ expect(response).to_not be_authenticated
14
+ expect(response.error).to eql "missing cn"
15
+ end
16
+
17
+ it "refuses to authenticate a request with a missing ip" do
18
+ response = subject.authenticate({cn: 'localhost'})
19
+ expect(response).to_not be_authenticated
20
+ expect(response.error).to eql "missing ip"
21
+ end
22
+
23
+ it "refuses to authenticate a request with an ip that does not have fcrdns that matches the cn" do
24
+ response = subject.authenticate({cn: 'bad.example.com', ip: '127.0.0.1'})
25
+ expect(response).to_not be_authenticated
26
+ expect(response.error).to eql "cn in unknown domain"
27
+ end
28
+
29
+ it "authenticates any request with an ip that does not have fcrdns that matches the cn" do
30
+ response = subject.authenticate({cn: 'localhost', ip: '127.0.0.1'})
31
+ expect(response).to be_authenticated
32
+ end
33
+
34
+ describe "error handling" do
35
+
36
+ it "refuses to authenticate a request when a DNS failure occurs" do
37
+ Resolv::DNS.any_instance.stub(:getnames).with('nonsense').and_raise(Resolv::ResolvError.new("cannot interpret as address: nonsense"))
38
+ response = subject.authenticate({cn: 'localhost', ip: 'nonsense'})
39
+ expect(response).to_not be_authenticated
40
+ expect(response.error).to eql "DNS error (cannot interpret as address: nonsense)"
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/noop'
4
+
5
+ describe Certmeister::Policy::Noop do
6
+
7
+ it "demands a request" do
8
+ expect { subject.authenticate }.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it "authenticates any non-empty request" do
12
+ response = subject.authenticate(anything: 'something')
13
+ expect(response).to be_authenticated
14
+ expect(response.error).to be_nil
15
+ end
16
+
17
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/psk'
4
+
5
+ describe Certmeister::Policy::Psk do
6
+
7
+ subject { Certmeister::Policy::Psk.new(['secret']) }
8
+
9
+ it "must be configured with a list of psks" do
10
+ expected_error = "enumerable collection of psks required"
11
+ expect { Certmeister::Policy::Psk.new }.to raise_error(ArgumentError)
12
+ expect { Certmeister::Policy::Psk.new('secret') }.to raise_error(ArgumentError, expected_error)
13
+ expect { Certmeister::Policy::Psk.new([]) }.to raise_error(ArgumentError, expected_error)
14
+ end
15
+
16
+ it "demands a request" do
17
+ expect { subject.authenticate }.to raise_error(ArgumentError)
18
+ end
19
+
20
+ it "refuses to authenticate a request with a missing psk" do
21
+ response = subject.authenticate({anything: 'something'})
22
+ expect(response).to_not be_authenticated
23
+ expect(response.error).to eql "missing psk"
24
+ end
25
+
26
+ it "refuses to authenticate a request with an unknown psk" do
27
+ response = subject.authenticate({anything: 'something', psk: 'wrong'})
28
+ expect(response).to_not be_authenticated
29
+ expect(response.error).to eql "unknown psk"
30
+ end
31
+
32
+ it "authenticates any request with a known psk" do
33
+ response = subject.authenticate({anything: 'something', psk: 'secret'})
34
+ expect(response).to be_authenticated
35
+ end
36
+
37
+
38
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/response'
4
+
5
+ describe Certmeister::Policy::Response do
6
+
7
+ describe "on error" do
8
+
9
+ it "is not authenticated" do
10
+ response = Certmeister::Policy::Response.new(nil, "you smell wrong")
11
+ expect(response).to_not be_authenticated
12
+ end
13
+
14
+ it "provides the error" do
15
+ response = Certmeister::Policy::Response.new(nil, "you smell wrong")
16
+ expect(response.error).to eql "you smell wrong"
17
+ end
18
+
19
+ end
20
+
21
+ describe "on success" do
22
+
23
+ it "is authenticated" do
24
+ response = Certmeister::Policy::Response.new(true, nil)
25
+ expect(response).to be_authenticated
26
+ end
27
+
28
+ it "provides no error" do
29
+ response = Certmeister::Policy::Response.new(true, nil)
30
+ expect(response.error).to be_nil
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/response'
4
+
5
+ describe Certmeister::Response do
6
+
7
+ let(:pem) { File.read('fixtures/client.crt') }
8
+
9
+ it "cannot be created with pem and error" do
10
+ expect { Certmeister::Response.new(pem, "silly") }.to raise_error(ArgumentError)
11
+ end
12
+
13
+ describe "on error" do
14
+
15
+ subject { Certmeister::Response.new(nil, "something went wrong") }
16
+
17
+ it "provides the error" do
18
+ expect(subject.error).to eql "something went wrong"
19
+ end
20
+
21
+ it "doesn't provide the PEM-encoded X509 certificate as a string" do
22
+ expect(subject.pem).to be_nil
23
+ end
24
+
25
+ it "offers appropriate boolean flags" do
26
+ expect(subject.hit?).to be_false
27
+ expect(subject.miss?).to be_false
28
+ expect(subject.error?).to be_true
29
+ end
30
+
31
+ end
32
+
33
+ describe "on miss (i.e. not found)" do
34
+
35
+ subject { Certmeister::Response.new(nil, nil) }
36
+
37
+ it "has no error" do
38
+ expect(subject.error).to be_nil
39
+ end
40
+
41
+ it "doesn't provide the PEM-encoded X509 certificate as a string" do
42
+ expect(subject.pem).to be_nil
43
+ end
44
+
45
+ it "offers appropriate boolean flags" do
46
+ expect(subject.hit?).to be_false
47
+ expect(subject.miss?).to be_true
48
+ expect(subject.error?).to be_false
49
+ end
50
+
51
+ end
52
+
53
+ describe "on hit (success)" do
54
+
55
+ subject { Certmeister::Response.new(pem, nil) }
56
+
57
+ it "has no error" do
58
+ expect(subject.error).to be_nil
59
+ end
60
+
61
+ it "provides the PEM-encoded X509 certificate as a string" do
62
+ expect(subject.pem).to eql pem
63
+ end
64
+
65
+ it "offers appropriate boolean flags" do
66
+ expect(subject.hit?).to be_true
67
+ expect(subject.miss?).to be_false
68
+ expect(subject.error?).to be_false
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,21 @@
1
+ require 'certmeister/in_memory_store'
2
+ require 'certmeister/policy/noop'
3
+
4
+ module CertmeisterConfigHelper
5
+
6
+ def self.valid_config_options
7
+ ca_cert = File.read('fixtures/ca.crt')
8
+ ca_key = File.read('fixtures/ca.key')
9
+ { ca_cert: ca_cert,
10
+ ca_key: ca_key,
11
+ store: Certmeister::InMemoryStore.new,
12
+ sign_policy: Certmeister::Policy::Noop.new,
13
+ fetch_policy: Certmeister::Policy::Noop.new,
14
+ remove_policy: Certmeister::Policy::Noop.new }
15
+ end
16
+
17
+ def self.valid_config
18
+ Certmeister::Config.new(valid_config_options)
19
+ end
20
+
21
+ end
@@ -0,0 +1,9 @@
1
+ module CertmeisterFetchingRequestHelper
2
+
3
+ def self.valid_request
4
+ { cn: 'axl.hetzner.africa',
5
+ ip: '127.0.0.1',
6
+ psk: 'secret' }
7
+ end
8
+
9
+ end
@@ -0,0 +1,14 @@
1
+ # This policy is broken because it violates the rule that an empty request must always be refused.
2
+
3
+ module CertmeisterPolicyHelper
4
+
5
+ class BrokenPolicy
6
+
7
+ def authenticate(request)
8
+ :white_elephant
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,9 @@
1
+ module CertmeisterRemovingRequestHelper
2
+
3
+ def self.valid_request
4
+ { cn: 'axl.hetzner.africa',
5
+ ip: '127.0.0.1',
6
+ psk: 'secret' }
7
+ end
8
+
9
+ end
@@ -0,0 +1,10 @@
1
+ module CertmeisterSigningRequestHelper
2
+
3
+ def self.valid_request
4
+ { cn: 'axl.hetzner.africa',
5
+ ip: '127.0.0.1',
6
+ csr: File.read('fixtures/client.csr'),
7
+ psk: 'secret' }
8
+ end
9
+
10
+ end
@@ -0,0 +1,20 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
18
+
19
+ $LOAD_PATH.unshift File.dirname(__FILE__)
20
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: certmeister
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sheldon Hearn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ description: Certificate authority that can be configured to make decisions about
56
+ whether to autosign certificate signing requests for clients. This gem provides
57
+ the protocol-agnostic library, which is expected to be used within something like
58
+ an HTTP REST service.
59
+ email:
60
+ - sheldonh@starjuice.net
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - .ruby-gemset
68
+ - .ruby-version
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - LICENSE
72
+ - README.md
73
+ - Rakefile
74
+ - certmeister.gemspec
75
+ - fixtures/ca.crt
76
+ - fixtures/ca.csr
77
+ - fixtures/ca.key
78
+ - fixtures/client.crt
79
+ - fixtures/client.csr
80
+ - fixtures/client.key
81
+ - lib/certmeister.rb
82
+ - lib/certmeister/base.rb
83
+ - lib/certmeister/config.rb
84
+ - lib/certmeister/in_memory_store.rb
85
+ - lib/certmeister/policy.rb
86
+ - lib/certmeister/policy/blackhole.rb
87
+ - lib/certmeister/policy/chain_all.rb
88
+ - lib/certmeister/policy/domain.rb
89
+ - lib/certmeister/policy/existing.rb
90
+ - lib/certmeister/policy/fcrdns.rb
91
+ - lib/certmeister/policy/noop.rb
92
+ - lib/certmeister/policy/psk.rb
93
+ - lib/certmeister/policy/response.rb
94
+ - lib/certmeister/response.rb
95
+ - lib/certmeister/store_error.rb
96
+ - lib/certmeister/test/memory_store_interface.rb
97
+ - lib/certmeister/version.rb
98
+ - signit.rb
99
+ - spec/certmeister/base_spec.rb
100
+ - spec/certmeister/config_spec.rb
101
+ - spec/certmeister/in_memory_store_spec.rb
102
+ - spec/certmeister/policy/blackhole_spec.rb
103
+ - spec/certmeister/policy/chain_all_spec.rb
104
+ - spec/certmeister/policy/domain_spec.rb
105
+ - spec/certmeister/policy/existing_spec.rb
106
+ - spec/certmeister/policy/fcrdns_spec.rb
107
+ - spec/certmeister/policy/noop_spec.rb
108
+ - spec/certmeister/policy/psk_spec.rb
109
+ - spec/certmeister/policy/response_spec.rb
110
+ - spec/certmeister/response_spec.rb
111
+ - spec/helpers/certmeister_config_helper.rb
112
+ - spec/helpers/certmeister_fetching_request_helper.rb
113
+ - spec/helpers/certmeister_policy_helper.rb
114
+ - spec/helpers/certmeister_removing_request_helper.rb
115
+ - spec/helpers/certmeister_signing_request_helper.rb
116
+ - spec/spec_helper.rb
117
+ homepage: https://github.com/sheldonh/certmeister
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.2.1
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Conditionally autosigning certificate authority.
141
+ test_files:
142
+ - spec/certmeister/base_spec.rb
143
+ - spec/certmeister/config_spec.rb
144
+ - spec/certmeister/in_memory_store_spec.rb
145
+ - spec/certmeister/policy/blackhole_spec.rb
146
+ - spec/certmeister/policy/chain_all_spec.rb
147
+ - spec/certmeister/policy/domain_spec.rb
148
+ - spec/certmeister/policy/existing_spec.rb
149
+ - spec/certmeister/policy/fcrdns_spec.rb
150
+ - spec/certmeister/policy/noop_spec.rb
151
+ - spec/certmeister/policy/psk_spec.rb
152
+ - spec/certmeister/policy/response_spec.rb
153
+ - spec/certmeister/response_spec.rb
154
+ - spec/helpers/certmeister_config_helper.rb
155
+ - spec/helpers/certmeister_fetching_request_helper.rb
156
+ - spec/helpers/certmeister_policy_helper.rb
157
+ - spec/helpers/certmeister_removing_request_helper.rb
158
+ - spec/helpers/certmeister_signing_request_helper.rb
159
+ - spec/spec_helper.rb