r509-ca-http 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.md +122 -0
  2. data/Rakefile +38 -0
  3. data/doc/R509.html +117 -0
  4. data/doc/R509/CertificateAuthority.html +117 -0
  5. data/doc/R509/CertificateAuthority/Http.html +131 -0
  6. data/doc/R509/CertificateAuthority/Http/Factory.html +115 -0
  7. data/doc/R509/CertificateAuthority/Http/Factory/CsrFactory.html +189 -0
  8. data/doc/R509/CertificateAuthority/Http/Factory/SpkiFactory.html +189 -0
  9. data/doc/R509/CertificateAuthority/Http/Server.html +133 -0
  10. data/doc/R509/CertificateAuthority/Http/SubjectParser.html +265 -0
  11. data/doc/R509/CertificateAuthority/Http/ValidityPeriodConverter.html +207 -0
  12. data/doc/_index.html +206 -0
  13. data/doc/class_list.html +53 -0
  14. data/doc/css/common.css +1 -0
  15. data/doc/css/full_list.css +57 -0
  16. data/doc/css/style.css +328 -0
  17. data/doc/file.README.html +209 -0
  18. data/doc/file_list.html +55 -0
  19. data/doc/frames.html +28 -0
  20. data/doc/index.html +209 -0
  21. data/doc/js/app.js +214 -0
  22. data/doc/js/full_list.js +173 -0
  23. data/doc/js/jquery.js +4 -0
  24. data/doc/method_list.html +92 -0
  25. data/doc/top-level-namespace.html +112 -0
  26. data/lib/r509/certificateauthority/http/factory.rb +15 -0
  27. data/lib/r509/certificateauthority/http/server.rb +237 -0
  28. data/lib/r509/certificateauthority/http/subjectparser.rb +33 -0
  29. data/lib/r509/certificateauthority/http/validityperiodconverter.rb +16 -0
  30. data/lib/r509/certificateauthority/http/version.rb +7 -0
  31. data/lib/r509/certificateauthority/http/views/test_issue.erb +85 -0
  32. data/lib/r509/certificateauthority/http/views/test_revoke.erb +31 -0
  33. data/lib/r509/certificateauthority/http/views/test_unrevoke.erb +26 -0
  34. data/spec/fixtures/test_ca.cer +22 -0
  35. data/spec/fixtures/test_ca.key +28 -0
  36. data/spec/fixtures/test_config.yaml +18 -0
  37. data/spec/http_spec.rb +250 -0
  38. data/spec/spec_helper.rb +22 -0
  39. data/spec/subject_parser_spec.rb +51 -0
  40. data/spec/validity_period_converter_spec.rb +79 -0
  41. metadata +165 -0
@@ -0,0 +1,31 @@
1
+ <html>
2
+ <head>
3
+ <title>Revoke</title>
4
+ </head>
5
+ <body>
6
+
7
+ <h1>Revoke a certificate</h1>
8
+
9
+ <form method="post" action="/1/certificate/revoke/">
10
+ <p>
11
+ CA:
12
+ <br />
13
+ <input type="text" name="ca" />
14
+ </p>
15
+ <p>
16
+ Serial:
17
+ <br />
18
+ <input type="text" name="serial" />
19
+ </p>
20
+ <p>
21
+ Reason:
22
+ <br />
23
+ <input type="text" name="reason" value="0" />
24
+ </p>
25
+ <p>
26
+ <input type="submit" value="Revoke" />
27
+ </p>
28
+ </form>
29
+
30
+ </body>
31
+ </html>
@@ -0,0 +1,26 @@
1
+ <html>
2
+ <head>
3
+ <title>Unrevoke</title>
4
+ </head>
5
+ <body>
6
+
7
+ <h1>Unrevoke a certificate</h1>
8
+
9
+ <form method="post" action="/1/certificate/unrevoke/">
10
+ <p>
11
+ CA:
12
+ <br />
13
+ <input type="text" name="ca" />
14
+ </p>
15
+ <p>
16
+ Serial:
17
+ <br />
18
+ <input type="text" name="serial" />
19
+ </p>
20
+ <p>
21
+ <input type="submit" value="Unrevoke" />
22
+ </p>
23
+ </form>
24
+
25
+ </body>
26
+ </html>
@@ -0,0 +1,22 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDjzCCAnegAwIBAgIJAP/ZxwuHN9GUMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV
3
+ BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEYMBYG
4
+ A1UECgwPUnVieSBDQSBQcm9qZWN0MRAwDgYDVQQDDAdUZXN0IENBMB4XDTExMDIy
5
+ MDIwNDkxMloXDTMwMDQyMTIwNDkxMlowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM
6
+ CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRgwFgYDVQQKDA9SdWJ5IENBIFBy
7
+ b2plY3QxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
8
+ ggEKAoIBAQCot8/z3jxARkwyRk0csM84dlWs91W95wPpunx3P3otqUxCJaOAyQl/
9
+ uY8deg0xDmsime2JQ6aG6m76y3LfT4J1tlkhtmiGKhNJir1kgL8sL0wTXYXvwZEw
10
+ qEEYD5mKi7Na9eo4R0ydqd9KAquVIhKywXvV1+Y4RDTx+f1WXmMCBaYZ76/rXmIe
11
+ oE0avriRiihOtlgto+VNJw7VRnvq+cEd81BT62wRk1fG1lcpCqTEfEtKEI6PqqZh
12
+ E2f+6lNmhZZ3Tj7NeNgYQLd4q+L1y030Vj4onpAIJrwfQq3dxTmrLDqnN2WOFsbo
13
+ 0qGh3s4yqUaOYd2wIBgCp7fEf5X/yh53AgMBAAGjUDBOMB0GA1UdDgQWBBR5dbuE
14
+ Osss3noJvjEbQ7wcKk1TWDAfBgNVHSMEGDAWgBR5dbuEOsss3noJvjEbQ7wcKk1T
15
+ WDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAp/Fh+WWjN7x/SZzMm
16
+ H1INAS4loufHXzkqI+3bClAzlhzpBVbY7dHwD6H8oKh06EtppbN0Bw3iqXSRyoNd
17
+ +lDXccPij5dCuTFQ97DOrv7kGlhiM95XVkx4mvnd+i6jKwgOpllgDE/nKOwC42bq
18
+ lIikcp7XLQUPt7amyX9UeJ8lxQ/niSkT868ls2IZQkF3XEhCi+5VlefEoaiMQZmD
19
+ RyEd/vlsSnpXI5LLpkFq+NFGvgdI7+UADNIyDplyivD4VyQ6+zisX3r1ojroa6Y+
20
+ WyXxLx+AVJVnGjngr2fDKr4ggsYWlJEs8lJ0r90jliYrSPA6rIldTbs3k9AbUp8G
21
+ kS6X
22
+ -----END CERTIFICATE-----
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCot8/z3jxARkwy
3
+ Rk0csM84dlWs91W95wPpunx3P3otqUxCJaOAyQl/uY8deg0xDmsime2JQ6aG6m76
4
+ y3LfT4J1tlkhtmiGKhNJir1kgL8sL0wTXYXvwZEwqEEYD5mKi7Na9eo4R0ydqd9K
5
+ AquVIhKywXvV1+Y4RDTx+f1WXmMCBaYZ76/rXmIeoE0avriRiihOtlgto+VNJw7V
6
+ Rnvq+cEd81BT62wRk1fG1lcpCqTEfEtKEI6PqqZhE2f+6lNmhZZ3Tj7NeNgYQLd4
7
+ q+L1y030Vj4onpAIJrwfQq3dxTmrLDqnN2WOFsbo0qGh3s4yqUaOYd2wIBgCp7fE
8
+ f5X/yh53AgMBAAECggEAMlRS5m6fDpVp2X17N1nPFwrF2AkYPMQTOL/2rSP0cHaW
9
+ Vw0fTyWpfb5+4M4t7Tpd3z6Hy3Cw1oJMhOf35oGzayXwRMxDNfKLOl72zGpTnPym
10
+ 9wfpEnJtu1QVxvWwWdH+uN2u9wbd5hJsl4lgYeZ+KXDqXgo/lP1TxfNLDV6urkVA
11
+ wsR5URtG2ddOTOUjhUvizWPt1QCVE6Z5YVe9haTtQ4EtLjPywd8LB01AXRkvLIlL
12
+ 2XpL/EeuzF8GLd3GRMjEwgnT7WWBLHe7zIZqsRVx318Pu+WoWRFXUbKxrwH05tUV
13
+ kx9Gh//L01uNwGx7qHSyJxTmYibjJXQAyQqVpPY4oQKBgQDgTbRVX65AEj5x8J3A
14
+ 1UEBDZE6ZQL/AnM+yPZGFSTfM04ebLUKyeIDvoSUxrbWMSf8JJwU1OCA+zJiXSld
15
+ 0BNBwd/irr9aglEuOBx6ITJypaKtehR5TtZGWsrvJT9f7IZ5Tc29gC0wiKNOqQ09
16
+ O4J7/tvk2j4viAawGHSlRvJDnwKBgQDAj0X1kLwaH+QlVHU9QyRbK8qJsouot8Km
17
+ BEjkZQ4D+7BQ/3mfFszJ/AG6tMA6FgikskyEics7XrtNYPZOGZ1xxKpi1an6o8cm
18
+ GKUavZnkR5eHNwUEl2c9V5lNMbtAbCbIz30sCBFluicw37M2O8n38xTDSnXcRTbN
19
+ n3rqpu52KQKBgE3MRc8Sx7JrYYNNjLnUfZ5q4UNaw8ZFSEmvlFPMg6Ry/BZraAPc
20
+ 7+qSixO7NLFoDVFUNVq4V0IFXn1liLKEOBmnsArEx5QR/SxFxALMPt4q+ximbjGB
21
+ Gar/VMHLroaL2Dx8su6WZZYe3l2rHu9tE54EUKq407bSvFcZtGObDu5LAoGAfxmS
22
+ ueYQ4sWOF73JrOg2hR9AjucVHAY/KsnFO0wglix5Ut1ub73i6qe2lIBeKXkFt4Ag
23
+ 1ZMGXGfJBegsa5youcFwHdCeY9vaxaCayi2/+FfxAsUkQMWW1XyOqc9bo8g/SWj7
24
+ XCbvJNBcsfvWFMQeKdV/LPBnHz9oTw0nWt9YoxECgYAA9f2l6btaIV2JuDEzR8LK
25
+ 8MjXZMwmg9pEiUT0DsiiVIkJDsHdb2i54DhkfEKxi+ZDTO8AoFpqc4GTwobQHgPk
26
+ N6Qa+wvig8SAWTS0dbPq2usvmx8cOiuqbn88VVl9jmFT6WTRJzNVRgVMM8xwl3uS
27
+ odNmWsnOyWhM8h5LMVNb5w==
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,18 @@
1
+ certificate_authorities: {
2
+ test_ca: {
3
+ ca_cert: {
4
+ cert: "test_ca.cer",
5
+ key: "test_ca.key"
6
+ },
7
+ cdp_location: 'URI:http://crl.domain.com/test_ca.crl',
8
+ message_digest: 'SHA1', #SHA1, SHA256, SHA512 supported. MD5 too, but you really shouldn't use that unless you have a good reason
9
+ profiles: {
10
+ server: {
11
+ basic_constraints: "CA:FALSE",
12
+ key_usage: [digitalSignature,keyEncipherment],
13
+ extended_key_usage: [serverAuth],
14
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.1", "CPS.1=http://example.com/cps"] ]
15
+ }
16
+ }
17
+ }
18
+ }
data/spec/http_spec.rb ADDED
@@ -0,0 +1,250 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require "openssl"
3
+
4
+ describe R509::CertificateAuthority::Http::Server do
5
+ before :all do
6
+ #config_pool registry is in spec_helper because we need to register it
7
+ #BEFORE we include r509-ca-http
8
+ Dependo::Registry[:log] = Logger.new(nil)
9
+ end
10
+
11
+ before :each do
12
+ @crls = { "test_ca" => double("crl") }
13
+ @certificate_authorities = { "test_ca" => double("test_ca") }
14
+ @subject_parser = double("subject parser")
15
+ @validity_period_converter = double("validity period converter")
16
+ @csr_factory = double("csr factory")
17
+ @spki_factory = double("spki factory")
18
+ end
19
+
20
+ def app
21
+ @app ||= R509::CertificateAuthority::Http::Server
22
+ @app.send(:set, :crls, @crls)
23
+ @app.send(:set, :certificate_authorities, @certificate_authorities)
24
+ @app.send(:set, :subject_parser, @subject_parser)
25
+ @app.send(:set, :validity_period_converter, @validity_period_converter)
26
+ @app.send(:set, :csr_factory, @csr_factory)
27
+ @app.send(:set, :spki_factory, @spki_factory)
28
+ end
29
+
30
+ context "get CRL" do
31
+ it "gets the CRL" do
32
+ @crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
33
+ get "/1/crl/test_ca/get"
34
+ last_response.should be_ok
35
+ last_response.content_type.should match /text\/plain/
36
+ last_response.body.should == "generated crl"
37
+ end
38
+ it "when CA is not found" do
39
+ get "/1/crl/bogus/get/"
40
+ last_response.status.should == 500
41
+ last_response.body.should == "#<ArgumentError: CA not found>"
42
+ end
43
+ end
44
+
45
+ context "generate CRL" do
46
+ it "generates the CRL" do
47
+ @crls["test_ca"].should_receive(:generate_crl).and_return("generated crl")
48
+ get "/1/crl/test_ca/generate"
49
+ last_response.should be_ok
50
+ last_response.body.should == "generated crl"
51
+ end
52
+ it "when CA is not found" do
53
+ get "/1/crl/bogus/generate/"
54
+ last_response.status.should == 500
55
+ last_response.body.should == "#<ArgumentError: CA not found>"
56
+ end
57
+ end
58
+
59
+ context "issue certificate" do
60
+ it "when no parameters are given" do
61
+ post "/1/certificate/issue"
62
+ last_response.should_not be_ok
63
+ last_response.body.should == "#<ArgumentError: Must provide a CA>"
64
+ end
65
+ it "when there's a profile, subject, CSR, validity period, but no ca" do
66
+ post "/1/certificate/issue", "profile" => "my profile", "subject" => "subject", "csr" => "my csr", "validityPeriod" => 365
67
+ last_response.should_not be_ok
68
+ last_response.body.should == "#<ArgumentError: Must provide a CA>"
69
+ end
70
+ it "when there's a ca, profile, subject, CSR, but no validity period" do
71
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "my profile", "subject" => "subject", "csr" => "my csr"
72
+ last_response.should_not be_ok
73
+ last_response.body.should == "#<ArgumentError: Must provide a validity period>"
74
+ end
75
+ it "when there's a ca, profile, subject, validity period, but no CSR" do
76
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "my profile", "subject" => "subject", "validityPeriod" => 365
77
+ last_response.should_not be_ok
78
+ last_response.body.should == "#<ArgumentError: Must provide a CSR or SPKI>"
79
+ end
80
+ it "when there's a ca, profile, CSR, validity period, but no subject" do
81
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(R509::Subject.new)
82
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "validityPeriod" => 365, "csr" => "csr"
83
+ last_response.should_not be_ok
84
+ last_response.body.should == "#<ArgumentError: Must provide a subject>"
85
+ end
86
+ it "when there's a ca, subject, CSR, validity period, but no profile" do
87
+ post "/1/certificate/issue", "ca" => "test_ca", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr"
88
+ last_response.should_not be_ok
89
+ last_response.body.should == "#<ArgumentError: Must provide a CA profile>"
90
+ end
91
+ it "when the given CA is not found" do
92
+ post "/1/certificate/issue", "ca" => "some bogus CA"
93
+ last_response.should_not be_ok
94
+ last_response.body.should == "#<ArgumentError: CA not found>"
95
+ end
96
+ it "fails to issue" do
97
+ csr = double("csr")
98
+ @csr_factory.should_receive(:build).with({:csr => "csr"}).and_return(csr)
99
+ @validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
100
+ subject = R509::Subject.new [["CN", "domain.com"]]
101
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
102
+ @certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => []}, :not_before => 1, :not_after => 2).and_raise(R509::R509Error.new("failed to issue because of: good reason"))
103
+
104
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr"
105
+ last_response.should_not be_ok
106
+ last_response.body.should == "#<R509::R509Error: failed to issue because of: good reason>"
107
+ end
108
+ it "issues a CSR with no SAN extensions" do
109
+ csr = double("csr")
110
+ @csr_factory.should_receive(:build).with(:csr => "csr").and_return(csr)
111
+ @validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
112
+ subject = R509::Subject.new [["CN", "domain.com"]]
113
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
114
+ cert = double("cert")
115
+ @certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => []}, :not_before => 1, :not_after => 2).and_return(cert)
116
+ cert.should_receive(:to_pem).and_return("signed cert")
117
+
118
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr"
119
+ last_response.should be_ok
120
+ last_response.body.should == "signed cert"
121
+ end
122
+ it "issues a CSR with SAN extensions" do
123
+ csr = double("csr")
124
+ @csr_factory.should_receive(:build).with(:csr => "csr").and_return(csr)
125
+ @validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
126
+ subject = R509::Subject.new [["CN", "domain.com"]]
127
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
128
+ cert = double("cert")
129
+ @certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => ["domain1.com", "domain2.com"]}, :not_before => 1, :not_after => 2).and_return(cert)
130
+ cert.should_receive(:to_pem).and_return("signed cert")
131
+
132
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr", "extensions[subjectAlternativeName][]" => ["domain1.com","domain2.com"]
133
+ last_response.should be_ok
134
+ last_response.body.should == "signed cert"
135
+ end
136
+ it "issues an SPKI without SAN extensions" do
137
+ @validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
138
+ subject = R509::Subject.new [["CN", "domain.com"]]
139
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
140
+ spki = double("spki")
141
+ @spki_factory.should_receive(:build).with(:spki => "spki", :subject => subject).and_return(spki)
142
+ cert = double("cert")
143
+ @certificate_authorities["test_ca"].should_receive(:sign).with(:spki => spki, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => []}, :not_before => 1, :not_after => 2).and_return(cert)
144
+ cert.should_receive(:to_pem).and_return("signed cert")
145
+
146
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "spki" => "spki"
147
+ last_response.should be_ok
148
+ last_response.body.should == "signed cert"
149
+ end
150
+ it "issues an SPKI with SAN extensions" do
151
+ @validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
152
+ subject = R509::Subject.new [["CN", "domain.com"]]
153
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
154
+ spki = double("spki")
155
+ @spki_factory.should_receive(:build).with(:spki => "spki", :subject => subject).and_return(spki)
156
+ cert = double("cert")
157
+ @certificate_authorities["test_ca"].should_receive(:sign).with(:spki => spki, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => ["domain1.com", "domain2.com"]}, :not_before => 1, :not_after => 2).and_return(cert)
158
+ cert.should_receive(:to_pem).and_return("signed cert")
159
+
160
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "spki" => "spki", "extensions[subjectAlternativeName][]" => ["domain1.com","domain2.com"]
161
+ last_response.should be_ok
162
+ last_response.body.should == "signed cert"
163
+ end
164
+ it "when there are empty SAN names" do
165
+ csr = double("csr")
166
+ @csr_factory.should_receive(:build).with(:csr => "csr").and_return(csr)
167
+ @validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
168
+ subject = R509::Subject.new [["CN", "domain.com"]]
169
+ @subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
170
+ cert = double("cert")
171
+ @certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => ["domain1.com", "domain2.com"]}, :not_before => 1, :not_after => 2).and_return(cert)
172
+ cert.should_receive(:to_pem).and_return("signed cert")
173
+
174
+ post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr", "extensions[subjectAlternativeName][]" => ["domain1.com","domain2.com","",""]
175
+ last_response.should be_ok
176
+ last_response.body.should == "signed cert"
177
+ end
178
+ end
179
+
180
+ context "revoke certificate" do
181
+ it "when no CA is given" do
182
+ post "/1/certificate/revoke", "serial" => "foo"
183
+ last_response.status.should == 500
184
+ last_response.body.should == "#<ArgumentError: CA must be provided>"
185
+ end
186
+ it "when CA is not found" do
187
+ post "/1/certificate/revoke", "ca" => "bogus ca name", "serial" => "foo"
188
+ last_response.status.should == 500
189
+ last_response.body.should == "#<ArgumentError: CA not found>"
190
+ end
191
+ it "when no serial is given" do
192
+ post "/1/certificate/revoke", "ca" => "test_ca"
193
+ last_response.should_not be_ok
194
+ last_response.body.should == "#<ArgumentError: Serial must be provided>"
195
+ end
196
+ it "when serial is given but not reason" do
197
+ @crls["test_ca"].should_receive(:revoke_cert).with(12345, 0).and_return(nil)
198
+ @crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
199
+ post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "12345"
200
+ last_response.should be_ok
201
+ last_response.body.should == "generated crl"
202
+ end
203
+ it "when serial and reason are given" do
204
+ @crls["test_ca"].should_receive(:revoke_cert).with(12345, 1).and_return(nil)
205
+ @crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
206
+ post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "12345", "reason" => "1"
207
+ last_response.should be_ok
208
+ last_response.body.should == "generated crl"
209
+ end
210
+ it "when serial is not an integer" do
211
+ @crls["test_ca"].should_receive(:revoke_cert).with(0, 0).and_raise(R509::R509Error.new("some r509 error"))
212
+ post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "foo"
213
+ last_response.should_not be_ok
214
+ last_response.body.should == "#<R509::R509Error: some r509 error>"
215
+ end
216
+ it "when reason is not an integer" do
217
+ @crls["test_ca"].should_receive(:revoke_cert).with(12345, 0).and_return(nil)
218
+ @crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
219
+ post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "12345", "reason" => "foo"
220
+ last_response.should be_ok
221
+ last_response.body.should == "generated crl"
222
+ end
223
+ end
224
+
225
+ context "unrevoke certificate" do
226
+ it "when no CA is given" do
227
+ post "/1/certificate/unrevoke", "serial" => "foo"
228
+ last_response.status.should == 500
229
+ last_response.body.should == "#<ArgumentError: CA must be provided>"
230
+ end
231
+ it "when CA is not found" do
232
+ post "/1/certificate/unrevoke", "ca" => "bogus ca", "serial" => "foo"
233
+ last_response.status.should == 500
234
+ last_response.body.should == "#<ArgumentError: CA not found>"
235
+ end
236
+ it "when no serial is given" do
237
+ post "/1/certificate/unrevoke", "ca" => "test_ca"
238
+ last_response.should_not be_ok
239
+ last_response.body.should == "#<ArgumentError: Serial must be provided>"
240
+ end
241
+ it "when serial is given" do
242
+ @crls["test_ca"].should_receive(:unrevoke_cert).with(12345).and_return(nil)
243
+ @crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
244
+ post "/1/certificate/unrevoke", "ca" => "test_ca", "serial" => "12345"
245
+ last_response.should be_ok
246
+ last_response.body.should == "generated crl"
247
+ end
248
+ end
249
+
250
+ end
@@ -0,0 +1,22 @@
1
+ if (RUBY_VERSION.split('.')[1].to_i > 8)
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ end
5
+
6
+ $:.unshift File.expand_path("../../lib", __FILE__)
7
+ $:.unshift File.expand_path("../", __FILE__)
8
+ require 'rubygems'
9
+ #require 'fixtures'
10
+ require 'rspec'
11
+ require 'rack/test'
12
+ require 'r509'
13
+ require 'dependo'
14
+ require 'logger'
15
+
16
+ Dependo::Registry[:config_pool] = R509::Config::CaConfigPool.from_yaml("certificate_authorities", File.read(File.dirname(__FILE__)+"/fixtures/test_config.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
17
+
18
+ require 'r509/certificateauthority/http/server'
19
+
20
+ RSpec.configure do |conf|
21
+ conf.include Rack::Test::Methods
22
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe R509::CertificateAuthority::Http::SubjectParser do
4
+ before :all do
5
+ @parser = R509::CertificateAuthority::Http::SubjectParser.new
6
+ end
7
+
8
+ it "when the query string is nil" do
9
+ expect { @parser.parse(nil) }.to raise_error(ArgumentError, "Must provide a query string")
10
+ end
11
+ it "when the query string is empty" do
12
+ subject = @parser.parse("")
13
+ subject.empty?.should == true
14
+ end
15
+ it "when the query string doesn't contain any subject data" do
16
+ subject = @parser.parse("validityPeriod=1095&data=blahblah")
17
+ subject.empty?.should == true
18
+ end
19
+ it "when there is one subject component" do
20
+ subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&data=blahblah")
21
+ subject.empty?.should == false
22
+ subject["CN"].should == "domain.com"
23
+ end
24
+ it "when there are three subject components should maintain order" do
25
+ subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[O]=org&subject[L]=locality&data=blahblah")
26
+ subject.empty?.should == false
27
+ subject["CN"].should == "domain.com"
28
+ subject["O"].should == "org"
29
+ subject["L"].should == "locality"
30
+ subject.to_s.should == "/CN=domain.com/O=org/L=locality"
31
+ end
32
+ it "when one of the subject components has an unknown key" do
33
+ expect { subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[NOTATHING]=org&subject[L]=locality&data=blahblah") }.to raise_error(OpenSSL::X509::NameError)
34
+ end
35
+ it "when one of the subject components is just an OID" do
36
+ subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[1.3.6.1.4.1.311.60.2.1.300]=org&subject[L]=locality&data=blahblah")
37
+ subject.empty?.should == false
38
+ subject["CN"].should == "domain.com"
39
+ subject["1.3.6.1.4.1.311.60.2.1.300"].should == "org"
40
+ subject["L"].should == "locality"
41
+ subject.to_s.should == "/CN=domain.com/1.3.6.1.4.1.311.60.2.1.300=org/L=locality"
42
+ end
43
+ it "when one of the subject components is an empty string" do
44
+ subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[O]=&subject[L]=locality&data=blahblah")
45
+ subject.empty?.should == false
46
+ subject["CN"].should == "domain.com"
47
+ subject["O"].should == nil
48
+ subject["L"].should == "locality"
49
+ subject.to_s.should == "/CN=domain.com/L=locality"
50
+ end
51
+ end