certmeister 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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