certmeister 0.0.2 → 0.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2abf1804931548fc418f67e0ff9ef45b166ea05d
4
- data.tar.gz: 0393170797e50b7bd2e94d9186ad4fd7f4aeef51
3
+ metadata.gz: 45294a93b1babeea90937ee91c345bddefa60b25
4
+ data.tar.gz: 1c0f12426788ad03d9952e2e3532128951d9575e
5
5
  SHA512:
6
- metadata.gz: 734c2e011504db4f4722122351b667e0a9402f5767fcf01beed9dd1666c201bd3c561630a4135bea44f20ff638008fbecfee891aaeaf04e06d60c0f317348e20
7
- data.tar.gz: c6078762403f470caafd18e58ba3516885e16ef4821b644aa57039c1ae4505c9efd35943e84d042fb9520c4f7e90fce480e9d9e45a9c7b0dc59cf37f32cd660a
6
+ metadata.gz: 763ed829a24c1002c00e3732b41db17d013195ad61686f6552d07424ed4ba101f5ff18545c47a1caa50c81228cb46961dac32048227b2ec9fcc164b69aab91cb
7
+ data.tar.gz: a953b468dddfdf2a27f381b1c9b316926ee7397011b9e28cbb9fd9834cfd439073ccf7a8c52fdeb98985785479ebe4b510f0ead3a791872aa31128239a05d463
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- certmeister (0.0.2)
5
- certmeister-redis (0.0.2)
6
- certmeister (= 0.0.2)
4
+ certmeister (0.1.0)
5
+ certmeister-redis (0.1.0)
6
+ certmeister (= 0.1.0)
7
7
  redis-sentinel (~> 1.4)
8
8
 
9
9
  GEM
data/README.md CHANGED
@@ -26,7 +26,23 @@ To hit the service:
26
26
  ```
27
27
  $ curl -L \
28
28
  -d "psk=secretkey" \
29
- -d "csr=$(perl -MURI::Escape -e 'print uri_escape(join("", <STDIN>));' < request/client.csr)" \
30
- http://certmeister.hetzner.co.za/certificate/$(hostname --fqdn) > request/client.crt
29
+ -d "csr=$(perl -MURI::Escape -e 'print uri_escape(join("", <STDIN>));' < fixtures/client.csr)" \
30
+ http://certmeister.hetzner.co.za/certificate/axl.starjuice.net
31
31
  ```
32
32
 
33
+ ## Testing
34
+
35
+ Because we test both certmeister and certmeister-redis with `rake spec`, you need redis up if you want to run the tests. It's easy:
36
+
37
+ * Install redis-2.8.4 or later.
38
+ * Start redis.
39
+ * Run tests.
40
+ * Stop redis.
41
+
42
+ ```
43
+ sudo yum install -y ansible
44
+ sudo ansible-playbook -i contrib/hosts contrib/redis.yml
45
+ redis-server --logfile /dev/null &
46
+ rake spec
47
+ kill %1; wait %1
48
+ ```
@@ -0,0 +1 @@
1
+ certmeister-contrib
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p247
data/contrib/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org/"
2
+
3
+ gem "certmeister", path: '..'
4
+ gem "certmeister-redis", path: '..'
5
+ gem "redis"
6
+ gem "rack"
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ certmeister (0.0.2)
5
+ certmeister-redis (0.0.2)
6
+ certmeister (= 0.0.2)
7
+ redis-sentinel (~> 1.4)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ rack (1.5.2)
13
+ redis (3.0.7)
14
+ redis-sentinel (1.4.2)
15
+ redis
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ certmeister!
22
+ certmeister-redis!
23
+ rack
24
+ redis
data/contrib/config.ru ADDED
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'rack'
3
+
4
+ require 'certmeister'
5
+ require 'certmeister/redis/store'
6
+ require 'redis'
7
+
8
+ allow = Certmeister::Policy::Noop.new
9
+
10
+ ca = Certmeister.new(
11
+ Certmeister::Config.new(
12
+ sign_policy: allow,
13
+ fetch_policy: allow,
14
+ remove_policy: allow,
15
+ store: Certmeister::Redis::Store.new(Redis.new, "development"),
16
+ ca_cert: File.read("../fixtures/ca.crt"),
17
+ ca_key: File.read("../fixtures/ca.key"),
18
+ )
19
+ )
20
+
21
+ sign_action = ->(params) do
22
+ response = ca.sign(params)
23
+ if response.error?
24
+ [500, {'Content-Type' => 'text/plain'}, ["500 Internal Server Error (#{response.error})"]]
25
+ elsif response.denied?
26
+ [403, {'Content-Type' => 'text/plain'}, ["403 Forbidden (#{response.error})"]]
27
+ else
28
+ [303, {'Content-Type' => 'text/plain',
29
+ 'Location' => "/certificate/#{params[:cn]}"}, ["303 See Other"]]
30
+ end
31
+ end
32
+
33
+ fetch_action = ->(params) do
34
+ response = ca.fetch(params)
35
+ if response.error?
36
+ [500, {'Content-Type' => 'text/plain'}, ["500 Internal Server Error (#{response.error})"]]
37
+ elsif response.denied?
38
+ [403, {'Content-Type' => 'text/plain'}, ["403 Forbidden (#{response.error})"]]
39
+ elsif response.miss?
40
+ [404, {'Content-Type' => 'text/plain'}, ["404 Not Found"]]
41
+ else
42
+ [200, {'Content-Type' => 'application/x-pem-file'}, [response.pem]]
43
+ end
44
+ end
45
+
46
+ remove_action = ->(params) do
47
+ response = ca.remove(params)
48
+ if response.error?
49
+ [500, {'Content-Type' => 'text/plain'}, ["500 Internal Server Error (#{response.error})"]]
50
+ elsif response.denied?
51
+ [403, {'Content-Type' => 'text/plain'}, ["403 Forbidden (#{response.error})"]]
52
+ elsif response.miss?
53
+ [404, {'Content-Type' => 'text/plain'}, ["404 Not Found"]]
54
+ else
55
+ [200, {'Content-Type' => 'text/plain'}, ["200 OK"]]
56
+ end
57
+ end
58
+
59
+ router = ->(env) do
60
+ req = Rack::Request.new(env)
61
+ if req.path_info =~ /^\/certificate\/(.+)/
62
+ params = req.params.tap do |p|
63
+ p[:cn] = $1
64
+ p[:ip] = req.ip
65
+ end
66
+ case req.request_method
67
+ when 'POST' then sign_action.call(params)
68
+ when 'GET' then fetch_action.call(params)
69
+ when 'DELETE' then remove_action.call(params)
70
+ else [405, {'Content-Type' => 'text-plain'}, ["405 Method Not Allowed"]]
71
+ end
72
+ else
73
+ [501, {'Content-Type' => 'text-plain'}, ["501 Not Implemented"]]
74
+ end
75
+ end
76
+
77
+ run router
data/contrib/hosts ADDED
@@ -0,0 +1,3 @@
1
+ [local]
2
+ localhost
3
+
data/contrib/redis.yml ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ - hosts: local
3
+ connection: local
4
+ vars:
5
+ version: 2.8.4
6
+ tasks:
7
+ - name: Download redis source
8
+ get_url: dest=/usr/src/redis-{{version}}.tar.gz url=http://download.redis.io/releases/redis-{{version}}.tar.gz
9
+ - name: Unpack redis source
10
+ command: tar -C /usr/src -xzf /usr/src/redis-{{version}}.tar.gz creates=/usr/src/redis-{{version}}
11
+ - name: Build redis from source
12
+ command: chdir=/usr/src/redis-{{version}} make creates=/usr/src/redis-{{version}}/src/redis-server
13
+ - name: Install redis from source
14
+ command: chdir=/usr/src/redis-{{version}} make install creates=/usr/local/bin/redis-server
@@ -15,7 +15,8 @@ module Certmeister
15
15
  @store = config.store
16
16
  @openssl_digest = config.openssl_digest
17
17
  else
18
- raise RuntimeError.new("invalid config")
18
+ reasons = config.errors.map { |kv| kv.join(' ') }
19
+ raise RuntimeError.new("invalid config: #{reasons.join('; ')}")
19
20
  end
20
21
  end
21
22
 
@@ -39,13 +40,21 @@ module Certmeister
39
40
 
40
41
  def fetch(request)
41
42
  subject_to_policy(@fetch_policy, request) do |request|
42
- Certmeister::Response.new(@store.fetch(request[:cn]), nil)
43
+ if pem = @store.fetch(request[:cn])
44
+ Certmeister::Response.hit(pem)
45
+ else
46
+ Certmeister::Response.miss
47
+ end
43
48
  end
44
49
  end
45
50
 
46
51
  def remove(request)
47
52
  subject_to_policy(@remove_policy, request) do |request|
48
- Certmeister::Response.new(!!@store.remove(request[:cn]), nil)
53
+ if @store.remove(request[:cn])
54
+ Certmeister::Response.hit
55
+ else
56
+ Certmeister::Response.miss
57
+ end
49
58
  end
50
59
  end
51
60
 
@@ -2,12 +2,12 @@ module Certmeister
2
2
 
3
3
  class Response
4
4
 
5
- def initialize(pem, error)
5
+ private_class_method :new
6
+
7
+ def initialize(type, pem, error)
8
+ @type = type
6
9
  @pem = pem
7
10
  @error = error
8
- if @pem and @error
9
- raise ArgumentError.new("pem and error are mutually exclusive")
10
- end
11
11
  end
12
12
 
13
13
  def pem
@@ -19,27 +19,35 @@ module Certmeister
19
19
  end
20
20
 
21
21
  def hit?
22
- !!@pem
22
+ @type == :hit
23
23
  end
24
24
 
25
25
  def miss?
26
- !(hit? or error?)
26
+ @type == :miss
27
+ end
28
+
29
+ def denied?
30
+ @type == :denied
27
31
  end
28
32
 
29
33
  def error?
30
- !!@error
34
+ @type == :error
31
35
  end
32
36
 
33
- def self.hit(pem)
34
- self.new(pem, nil)
37
+ def self.hit(pem = :none)
38
+ new(:hit, pem, nil)
35
39
  end
36
40
 
37
41
  def self.miss
38
- self.new(nil, nil)
42
+ new(:miss, nil, nil)
43
+ end
44
+
45
+ def self.denied(message)
46
+ new(:denied, nil, message)
39
47
  end
40
48
 
41
49
  def self.error(message)
42
- self.new(nil, message)
50
+ new(:error, nil, message)
43
51
  end
44
52
 
45
53
  end
@@ -1,5 +1,5 @@
1
1
  module Certmeister
2
2
 
3
- VERSION = '0.0.2' unless defined?(VERSION)
3
+ VERSION = '0.1.0' unless defined?(VERSION)
4
4
 
5
5
  end
@@ -44,7 +44,7 @@ describe Certmeister do
44
44
  expect(response.error).to eql "invalid CSR (not enough data)"
45
45
  end
46
46
 
47
- it "refuses to sign a CSR if the subject does not agree with the request CN" do
47
+ it "(XXX move this into the policy) refuses to sign a CSR if the subject does not agree with the request CN" do
48
48
  request = valid_request.tap { |r| r[:cn] = "monkeyface.example.com" }
49
49
  ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
50
50
  response = ca.sign(request)
@@ -6,13 +6,13 @@ describe Certmeister::Response do
6
6
 
7
7
  let(:pem) { File.read('fixtures/client.crt') }
8
8
 
9
- it "cannot be created with pem and error" do
10
- expect { Certmeister::Response.new(pem, "silly") }.to raise_error(ArgumentError)
9
+ it "must be instantiated via the factory methods only" do
10
+ expect { Certmeister::Response.new(pem, nil, nil) }.to raise_error(NoMethodError, /private/)
11
11
  end
12
12
 
13
13
  describe "on error" do
14
14
 
15
- subject { Certmeister::Response.new(nil, "something went wrong") }
15
+ subject { Certmeister::Response.error("something went wrong") }
16
16
 
17
17
  it "provides the error" do
18
18
  expect(subject.error).to eql "something went wrong"
@@ -25,14 +25,36 @@ describe Certmeister::Response do
25
25
  it "offers appropriate boolean flags" do
26
26
  expect(subject.hit?).to be_false
27
27
  expect(subject.miss?).to be_false
28
+ expect(subject.denied?).to be_false
28
29
  expect(subject.error?).to be_true
29
30
  end
30
31
 
31
32
  end
32
33
 
34
+ describe "on denial" do
35
+
36
+ subject { Certmeister::Response.denied("bad client, no cookie") }
37
+
38
+ it "provides the reason" do
39
+ expect(subject.error).to eql "bad client, no cookie"
40
+ end
41
+
42
+ it "doesn't provide the PEM-encoded X509 certificate as a string" do
43
+ expect(subject.pem).to be_nil
44
+ end
45
+
46
+ it "offers appropriate boolean flags" do
47
+ expect(subject.hit?).to be_false
48
+ expect(subject.miss?).to be_false
49
+ expect(subject.denied?).to be_true
50
+ expect(subject.error?).to be_false
51
+ end
52
+
53
+ end
54
+
33
55
  describe "on miss (i.e. not found)" do
34
56
 
35
- subject { Certmeister::Response.new(nil, nil) }
57
+ subject { Certmeister::Response.miss }
36
58
 
37
59
  it "has no error" do
38
60
  expect(subject.error).to be_nil
@@ -45,6 +67,7 @@ describe Certmeister::Response do
45
67
  it "offers appropriate boolean flags" do
46
68
  expect(subject.hit?).to be_false
47
69
  expect(subject.miss?).to be_true
70
+ expect(subject.denied?).to be_false
48
71
  expect(subject.error?).to be_false
49
72
  end
50
73
 
@@ -52,19 +75,21 @@ describe Certmeister::Response do
52
75
 
53
76
  describe "on hit (success)" do
54
77
 
55
- subject { Certmeister::Response.new(pem, nil) }
78
+ subject { Certmeister::Response.hit(pem) }
56
79
 
57
80
  it "has no error" do
58
81
  expect(subject.error).to be_nil
59
82
  end
60
83
 
61
- it "provides the PEM-encoded X509 certificate as a string" do
84
+ it "provides the PEM-encoded X509 certificate as a string (or :none)" do
85
+ # some hits, like the one for a remove(), don't have a pem, returning :none
62
86
  expect(subject.pem).to eql pem
63
87
  end
64
88
 
65
89
  it "offers appropriate boolean flags" do
66
90
  expect(subject.hit?).to be_true
67
91
  expect(subject.miss?).to be_false
92
+ expect(subject.denied?).to be_false
68
93
  expect(subject.error?).to be_false
69
94
  end
70
95
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: certmeister
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sheldon Hearn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-30 00:00:00.000000000 Z
11
+ date: 2014-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -73,6 +73,13 @@ files:
73
73
  - Rakefile
74
74
  - certmeister-redis.gemspec
75
75
  - certmeister.gemspec
76
+ - contrib/.ruby-gemset
77
+ - contrib/.ruby-version
78
+ - contrib/Gemfile
79
+ - contrib/Gemfile.lock
80
+ - contrib/config.ru
81
+ - contrib/hosts
82
+ - contrib/redis.yml
76
83
  - fixtures/ca.crt
77
84
  - fixtures/ca.csr
78
85
  - fixtures/ca.key