r509-ca-http 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 (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