acme_plugin 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +33 -0
  4. data/app/controllers/acme_plugin/application_controller.rb +34 -0
  5. data/app/models/acme_plugin/challenge.rb +17 -0
  6. data/app/models/acme_plugin/setting.rb +10 -0
  7. data/config/routes.rb +3 -0
  8. data/db/migrate/20151206135029_create_acme_plugin_challenges.rb +9 -0
  9. data/db/migrate/20160412195212_create_acme_plugin_settings.rb +9 -0
  10. data/lib/acme_plugin.rb +146 -0
  11. data/lib/acme_plugin/certificate_output.rb +17 -0
  12. data/lib/acme_plugin/challenge_store.rb +18 -0
  13. data/lib/acme_plugin/configuration.rb +27 -0
  14. data/lib/acme_plugin/database_store.rb +11 -0
  15. data/lib/acme_plugin/engine.rb +5 -0
  16. data/lib/acme_plugin/file_output.rb +18 -0
  17. data/lib/acme_plugin/file_store.rb +16 -0
  18. data/lib/acme_plugin/heroku_output.rb +19 -0
  19. data/lib/acme_plugin/private_key_store.rb +24 -0
  20. data/lib/acme_plugin/version.rb +5 -0
  21. data/lib/tasks/acme_plugin_tasks.rake +15 -0
  22. data/test/acme_plugin_test.rb +351 -0
  23. data/test/controllers/application_controller_test.rb +26 -0
  24. data/test/dummy/README.rdoc +28 -0
  25. data/test/dummy/Rakefile +6 -0
  26. data/test/dummy/app/assets/javascripts/application.js +13 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  28. data/test/dummy/app/controllers/application_controller.rb +5 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  31. data/test/dummy/bin/bundle +3 -0
  32. data/test/dummy/bin/rails +4 -0
  33. data/test/dummy/bin/rake +4 -0
  34. data/test/dummy/bin/setup +29 -0
  35. data/test/dummy/config.ru +4 -0
  36. data/test/dummy/config/acme_plugin.yml +19 -0
  37. data/test/dummy/config/application.rb +23 -0
  38. data/test/dummy/config/boot.rb +5 -0
  39. data/test/dummy/config/database.yml +25 -0
  40. data/test/dummy/config/environment.rb +5 -0
  41. data/test/dummy/config/environments/development.rb +41 -0
  42. data/test/dummy/config/environments/production.rb +79 -0
  43. data/test/dummy/config/environments/test.rb +45 -0
  44. data/test/dummy/config/initializers/assets.rb +11 -0
  45. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  47. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/test/dummy/config/initializers/inflections.rb +16 -0
  49. data/test/dummy/config/initializers/mime_types.rb +4 -0
  50. data/test/dummy/config/initializers/session_store.rb +3 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/test/dummy/config/locales/en.yml +23 -0
  53. data/test/dummy/config/routes.rb +3 -0
  54. data/test/dummy/config/secrets.yml +22 -0
  55. data/test/dummy/db/schema.rb +27 -0
  56. data/test/dummy/db/test.sqlite3 +0 -0
  57. data/test/dummy/key/test_keyfile_1024.pem +15 -0
  58. data/test/dummy/key/test_keyfile_2048.pem +27 -0
  59. data/test/dummy/key/test_keyfile_4096.pem +51 -0
  60. data/test/dummy/key/test_keyfile_8192.pem +99 -0
  61. data/test/dummy/log/development.log +0 -0
  62. data/test/dummy/log/test.log +25582 -0
  63. data/test/dummy/public/404.html +67 -0
  64. data/test/dummy/public/422.html +67 -0
  65. data/test/dummy/public/500.html +66 -0
  66. data/test/dummy/public/favicon.ico +0 -0
  67. data/test/fixtures/acme_plugin/challenges.yml +7 -0
  68. data/test/fixtures/acme_plugin/settings.yml +56 -0
  69. data/test/lib/acme_plugin/configuration_test.rb +13 -0
  70. data/test/lib/acme_plugin/private_key_store_test.rb +30 -0
  71. data/test/models/acme_plugin/challenge_test.rb +6 -0
  72. data/test/test_helper.rb +23 -0
  73. metadata +309 -0
@@ -0,0 +1,24 @@
1
+ require 'openssl'
2
+
3
+ class PrivateKeyStore
4
+ def initialize(private_key)
5
+ # this should eventually be any one of many backends?
6
+ # these could then encapsulate the method of retrieving the
7
+ # RSA key string. see: http://hawkins.io/2013/10/implementing_the_repository_pattern/
8
+ @private_key = private_key
9
+ end
10
+
11
+ def retrieve
12
+ pk = OpenSSL::PKey::RSA.new(@private_key)
13
+ raise "Invalid key size: #{pk.n.num_bits}. Required size is between 2048 - 4096 bits" unless valid_key_size?(pk)
14
+ pk
15
+ rescue OpenSSL::PKey::RSAError
16
+ raise "#{pk} is not a valid private key identifier"
17
+ end
18
+
19
+ private
20
+
21
+ def valid_key_size?(key)
22
+ key.n.num_bits >= 2048 && key.n.num_bits <= 4096
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AcmePlugin
4
+ VERSION = '0.0.13'.freeze
5
+ end
@@ -0,0 +1,15 @@
1
+ require 'openssl'
2
+ require 'acme/client'
3
+
4
+ # Sets up logging - should only be called from other rake tasks
5
+ task setup_logger: :environment do
6
+ logger = Logger.new(STDOUT)
7
+ logger.level = Logger::INFO
8
+ Rails.logger = logger
9
+ end
10
+
11
+ desc "Generates SSL certificate using Let's Encrypt service"
12
+ task acme_plugin: :setup_logger do
13
+ cert_generator = AcmePlugin::CertGenerator.new(AcmePlugin.config.to_h)
14
+ cert_generator.generate_certificate
15
+ end
@@ -0,0 +1,351 @@
1
+ require 'test_helper'
2
+
3
+ class AcmePluginTest < ActiveSupport::TestCase
4
+ ACME_VERSION = 'v0.6.2'.freeze
5
+ ACME_USER_AGENT = "Acme::Client #{ACME_VERSION} (https://github.com/unixcharles/acme-client)".freeze
6
+ ENDPOINT_URL = 'https://acme-staging.api.letsencrypt.org'.freeze
7
+ API_URL = "#{ENDPOINT_URL}/acme".freeze
8
+ PRIVATE_KEY = %(
9
+ -----BEGIN RSA PRIVATE KEY-----
10
+ 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
59
+ -----END RSA PRIVATE KEY-----).freeze
60
+
61
+ def setup
62
+ WebMock.disable_net_connect!(allow_localhost: true)
63
+ end
64
+
65
+ def teardown
66
+ WebMock.allow_net_connect!
67
+ end
68
+
69
+ test 'is_valid_module' do
70
+ assert_kind_of Module, AcmePlugin
71
+ end
72
+
73
+ test 'if_fail_when_private_key_is_nil' do
74
+ exception = assert_raises RuntimeError do
75
+ cg = AcmePlugin::CertGenerator.new(private_key: nil)
76
+ cg.client
77
+ end
78
+ assert_equal 'Private key is not set, please check your config/acme_plugin.yml file!', exception.message
79
+ end
80
+
81
+ test 'if_fail_when_private_key_is_empty' do
82
+ exception = assert_raises RuntimeError do
83
+ cg = AcmePlugin::CertGenerator.new(private_key: '')
84
+ cg.client
85
+ end
86
+ assert_equal 'Private key is not set, please check your config/acme_plugin.yml file!', exception.message
87
+ end
88
+
89
+ test 'if_fail_when_private_key_is_directory' do
90
+ options = { private_key: 'public' }
91
+ exception = assert_raises RuntimeError do
92
+ cg = AcmePlugin::CertGenerator.new(options)
93
+ cg.client
94
+ end
95
+ assert_equal "Can not open private key: #{File.join(Rails.root, options[:private_key])}", exception.message
96
+ end
97
+
98
+ test 'if_keysize_smaller_than_2048_is_invalid' do
99
+ exception = assert_raises RuntimeError do
100
+ cg = AcmePlugin::CertGenerator.new(private_key: 'key/test_keyfile_1024.pem')
101
+ cg.client
102
+ end
103
+ assert_equal 'Invalid key size: 1024. Required size is between 2048 - 4096 bits', exception.message
104
+ end
105
+
106
+ test 'if_keysize_greater_than_4096_is_invalid' do
107
+ exception = assert_raises RuntimeError do
108
+ cg = AcmePlugin::CertGenerator.new(private_key: 'key/test_keyfile_8192.pem')
109
+ cg.client
110
+ end
111
+ assert_equal 'Invalid key size: 8192. Required size is between 2048 - 4096 bits', exception.message
112
+ end
113
+
114
+ test 'if_keysize_equal_4096_is_valid' do
115
+ assert_nothing_raised do
116
+ cg = AcmePlugin::CertGenerator.new(private_key: 'key/test_keyfile_4096.pem')
117
+ assert !cg.nil?
118
+ cg.client
119
+ end
120
+ end
121
+
122
+ test 'if_keysize_equal_2048_is_valid' do
123
+ assert_nothing_raised do
124
+ cg = AcmePlugin::CertGenerator.new(private_key: 'key/test_keyfile_2048.pem')
125
+ assert !cg.nil?
126
+ cg.client
127
+ end
128
+ end
129
+
130
+ test 'register with text based private key' do
131
+ cg = AcmePlugin::CertGenerator.new(private_key: PRIVATE_KEY,
132
+ endpoint: ENDPOINT_URL,
133
+ domain: 'example.com',
134
+ email: 'foobarbaz@example.com')
135
+ assert !cg.nil?
136
+
137
+ stub_request(:head, "#{API_URL}/new-reg")
138
+ .with(headers: { 'Accept' => '*/*' })
139
+ .to_return(status: 200, body: '', headers: {})
140
+
141
+ stub_request(:post, "#{API_URL}/new-reg")
142
+ .with(body: '{"protected":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eS'\
143
+ 'I6IlJTQSIsIm4iOiJxN0gtQ2txRHZ3ekx2OWRBZ05rSmQzM2FiVEpFa0ZHSjhXbGIxRnZ1Y1F6MEFYWX'\
144
+ 'JwWUx5ajdOYUNyQm90V1NaR2pFSnRQZ1k1M0xWWU1ET1BiOTktLTZEazNXVGhkT203U01JTlZYWlZ1Ym'\
145
+ 'hhNmtoY1pFWFA1NEdic0NzcFBmNm5OcUJCeEhDblV3V01GOElRcWkwTVdSNHFOeG1LZEVrcE56dGFCd0'\
146
+ 'xLU0ZWUHNRMHRGeXJHellhOEw0TkJqZWUyaWlEdWM2bHlMTkp6MnhfUUZMQWZneGw1cXdFa05FVGxNNz'\
147
+ 'JNQlRsTTNrR1k4UjN2RkVNVEhQSUtCM3VQcHdScE1qOEw2M1JLenpmcmM5Q0Y2TDRNaG01b05GUENndm'\
148
+ 'JDRWF6T0ctTEpwM2Q4bVlTZjBERE5RNlhoX0NnTG5TbXAzVWVmckpiQVFUZDBpSk4xa21TaV9fQmxOR1'\
149
+ 'JDS1dFZDgyZzA2aHgwbVRhOXVZZEQxMFNXaVF5UHZIbDRzWER3M0JMN0VpOVlPNHpoOUh6ZkpzUVhxY2'\
150
+ 'hzQnZVZ2dEVG9IZVJwMnhmS0hEcmFFZ2JPX0FxaEMxRkdkTUtPUnJMWnBMQm1oamw0MFVvMWNLUXZsbl'\
151
+ 'ZJZFJaQjU4QnoyZUhzNDVKN0ROYktsQm12cWZPQnJFN09nOHdEbFUzWFpIZ3dRZkt4TUhBV29Qc0d2RG'\
152
+ '1KRjRZdFRmZ0R4NmZWS2VNbng5cXpJQ0plRlZmYnkzaUc4dE9XUFNKWFZUcXZ2QlhiQnppT0h4bV9sOH'\
153
+ 'pma3UwUmk3RXYwWUU2VEJGWDZiNDRES0FpVW9adkUwTm9taXhhYll2ZDAwWFFPNndOMF9pdkN6Tmc0VV'\
154
+ 'ltaDlZUzJyM1E3YmloNFkyS3MyWllxdnNkazFnLWZiNGZRcyJ9LCJub25jZSI6bnVsbH0",'\
155
+ '"payload":"eyJyZXNvdXJjZSI6Im5ldy1yZWciLCJjb250YWN0IjpbIm1haWx0bzpmb29iYXJiYXpAZ'\
156
+ 'XhhbXBsZS5jb20iXX0",'\
157
+ '"signature":"aQ6T0XVo9jS_jXlvQ6bjAfqcrMYpQTPE9_CD7v1hDBUzKpSoygAJmrbb0kSumMkWf-r'\
158
+ 'acxG5i7tcD4ed32ap1OsWUoPGWhXkQifAqToYMdfBzjwaS_OfjPpFflPOZmUvOygtGt3LTsdhA27PCSC'\
159
+ 'saZDlozz2143b00QhZ-gVkFRJEwOMN22ByOsL-1_R_UEp7mqwQKzeZ_nsW7japvGWcru13bzqiD2b069'\
160
+ 'kwrfksnMIMSjFWnJPnxCLpY-DQn8kCBeJfljW-mOJyZFck1ko0KBiOEbY_k0IJ0aspwg9lGRLkVX-wWV'\
161
+ '8Q_e8gwoLjmaXouUxAl2E3wNInXRJkgDOMfEUSOHzxI6WFYLzhVKVl7ktP7zx21my2vL_J_nROTDaVIU'\
162
+ 'qJgdUgEZ40KR_Kjd_pRKPcB8kHyKtDnCopmrPXLu0QCuHYAn_M-cYgkFOyvplJNodaZfKamLuwChywPl'\
163
+ 'DC5tr1BQvdlUVp-cwr_C_KU-Kpkws924xI0ah8ksuwsHpkp2458aVEIeDEEw7FgHZXUPKXaJcBIHjQ1R'\
164
+ 'vnm062Ub_4LR25TrKTgftDCftK2YtN4LA-jtlfOrdM47xZatAzhmFy_vFIIu_-phh5he0nn8Mx2byjsZ'\
165
+ '1M-DeW0Wl9_zxhhOo1gtmFhtguCKw3W0mdcdDZmhRleNS-HtYA0w41LA"}',
166
+ headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
167
+ 'User-Agent' => ACME_USER_AGENT })
168
+ .to_return(status: 200, body: '', headers: {})
169
+
170
+ assert_nothing_raised do
171
+ cg.register
172
+ end
173
+ end
174
+
175
+ test 'register' do
176
+ cg = AcmePlugin::CertGenerator.new(private_key: 'key/test_keyfile_4096.pem',
177
+ endpoint: ENDPOINT_URL,
178
+ domain: 'example.com',
179
+ email: 'foobarbaz@example.com')
180
+ assert !cg.nil?
181
+
182
+ stub_request(:head, "#{API_URL}/new-reg")
183
+ .with(headers: { 'Accept' => '*/*' })
184
+ .to_return(status: 200, body: '', headers: {})
185
+
186
+ stub_request(:post, "#{API_URL}/new-reg")
187
+ .with(body: '{"protected":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eS'\
188
+ 'I6IlJTQSIsIm4iOiJxN0gtQ2txRHZ3ekx2OWRBZ05rSmQzM2FiVEpFa0ZHSjhXbGIxRnZ1Y1F6MEFYWX'\
189
+ 'JwWUx5ajdOYUNyQm90V1NaR2pFSnRQZ1k1M0xWWU1ET1BiOTktLTZEazNXVGhkT203U01JTlZYWlZ1Ym'\
190
+ 'hhNmtoY1pFWFA1NEdic0NzcFBmNm5OcUJCeEhDblV3V01GOElRcWkwTVdSNHFOeG1LZEVrcE56dGFCd0'\
191
+ 'xLU0ZWUHNRMHRGeXJHellhOEw0TkJqZWUyaWlEdWM2bHlMTkp6MnhfUUZMQWZneGw1cXdFa05FVGxNNz'\
192
+ 'JNQlRsTTNrR1k4UjN2RkVNVEhQSUtCM3VQcHdScE1qOEw2M1JLenpmcmM5Q0Y2TDRNaG01b05GUENndm'\
193
+ 'JDRWF6T0ctTEpwM2Q4bVlTZjBERE5RNlhoX0NnTG5TbXAzVWVmckpiQVFUZDBpSk4xa21TaV9fQmxOR1'\
194
+ 'JDS1dFZDgyZzA2aHgwbVRhOXVZZEQxMFNXaVF5UHZIbDRzWER3M0JMN0VpOVlPNHpoOUh6ZkpzUVhxY2'\
195
+ 'hzQnZVZ2dEVG9IZVJwMnhmS0hEcmFFZ2JPX0FxaEMxRkdkTUtPUnJMWnBMQm1oamw0MFVvMWNLUXZsbl'\
196
+ 'ZJZFJaQjU4QnoyZUhzNDVKN0ROYktsQm12cWZPQnJFN09nOHdEbFUzWFpIZ3dRZkt4TUhBV29Qc0d2RG'\
197
+ '1KRjRZdFRmZ0R4NmZWS2VNbng5cXpJQ0plRlZmYnkzaUc4dE9XUFNKWFZUcXZ2QlhiQnppT0h4bV9sOH'\
198
+ 'pma3UwUmk3RXYwWUU2VEJGWDZiNDRES0FpVW9adkUwTm9taXhhYll2ZDAwWFFPNndOMF9pdkN6Tmc0VV'\
199
+ 'ltaDlZUzJyM1E3YmloNFkyS3MyWllxdnNkazFnLWZiNGZRcyJ9LCJub25jZSI6bnVsbH0",'\
200
+ '"payload":"eyJyZXNvdXJjZSI6Im5ldy1yZWciLCJjb250YWN0IjpbIm1haWx0bzpmb29iYXJiYXpAZ'\
201
+ 'XhhbXBsZS5jb20iXX0",'\
202
+ '"signature":"aQ6T0XVo9jS_jXlvQ6bjAfqcrMYpQTPE9_CD7v1hDBUzKpSoygAJmrbb0kSumMkWf-r'\
203
+ 'acxG5i7tcD4ed32ap1OsWUoPGWhXkQifAqToYMdfBzjwaS_OfjPpFflPOZmUvOygtGt3LTsdhA27PCSC'\
204
+ 'saZDlozz2143b00QhZ-gVkFRJEwOMN22ByOsL-1_R_UEp7mqwQKzeZ_nsW7japvGWcru13bzqiD2b069'\
205
+ 'kwrfksnMIMSjFWnJPnxCLpY-DQn8kCBeJfljW-mOJyZFck1ko0KBiOEbY_k0IJ0aspwg9lGRLkVX-wWV'\
206
+ '8Q_e8gwoLjmaXouUxAl2E3wNInXRJkgDOMfEUSOHzxI6WFYLzhVKVl7ktP7zx21my2vL_J_nROTDaVIU'\
207
+ 'qJgdUgEZ40KR_Kjd_pRKPcB8kHyKtDnCopmrPXLu0QCuHYAn_M-cYgkFOyvplJNodaZfKamLuwChywPl'\
208
+ 'DC5tr1BQvdlUVp-cwr_C_KU-Kpkws924xI0ah8ksuwsHpkp2458aVEIeDEEw7FgHZXUPKXaJcBIHjQ1R'\
209
+ 'vnm062Ub_4LR25TrKTgftDCftK2YtN4LA-jtlfOrdM47xZatAzhmFy_vFIIu_-phh5he0nn8Mx2byjsZ'\
210
+ '1M-DeW0Wl9_zxhhOo1gtmFhtguCKw3W0mdcdDZmhRleNS-HtYA0w41LA"}',
211
+ headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
212
+ 'User-Agent' => ACME_USER_AGENT })
213
+ .to_return(status: 200, body: '', headers: {})
214
+
215
+ assert_nothing_raised do
216
+ cg.register
217
+ end
218
+ end
219
+
220
+ test 'register_with_privkey_in_db' do
221
+ cg = AcmePlugin::CertGenerator.new(private_key_in_db: true,
222
+ endpoint: ENDPOINT_URL,
223
+ domain: 'example.com',
224
+ email: 'foobarbaz@example.com')
225
+ assert !cg.nil?
226
+
227
+ stub_request(:head, "#{API_URL}/new-reg")
228
+ .with(headers: { 'Accept' => '*/*' })
229
+ .to_return(status: 200, body: '', headers: {})
230
+
231
+ stub_request(:post, "#{API_URL}/new-reg")
232
+ .with(body: '{"protected":"eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIiwibm9uY2UiOm51bGwsImp3ayI6eyJrdH'\
233
+ 'kiOiJSU0EiLCJlIjoiQVFBQiIsIm4iOiJxN0gtQ2txRHZ3ekx2OWRBZ05rSmQzM2FiVEpFa0ZHSjhXbG'\
234
+ 'IxRnZ1Y1F6MEFYWXJwWUx5ajdOYUNyQm90V1NaR2pFSnRQZ1k1M0xWWU1ET1BiOTktLTZEazNXVGhkT2'\
235
+ '03U01JTlZYWlZ1YmhhNmtoY1pFWFA1NEdic0NzcFBmNm5OcUJCeEhDblV3V01GOElRcWkwTVdSNHFOeG'\
236
+ '1LZEVrcE56dGFCd0xLU0ZWUHNRMHRGeXJHellhOEw0TkJqZWUyaWlEdWM2bHlMTkp6MnhfUUZMQWZneG'\
237
+ 'w1cXdFa05FVGxNNzJNQlRsTTNrR1k4UjN2RkVNVEhQSUtCM3VQcHdScE1qOEw2M1JLenpmcmM5Q0Y2TD'\
238
+ 'RNaG01b05GUENndmJDRWF6T0ctTEpwM2Q4bVlTZjBERE5RNlhoX0NnTG5TbXAzVWVmckpiQVFUZDBpSk'\
239
+ '4xa21TaV9fQmxOR1JDS1dFZDgyZzA2aHgwbVRhOXVZZEQxMFNXaVF5UHZIbDRzWER3M0JMN0VpOVlPNH'\
240
+ 'poOUh6ZkpzUVhxY2hzQnZVZ2dEVG9IZVJwMnhmS0hEcmFFZ2JPX0FxaEMxRkdkTUtPUnJMWnBMQm1oam'\
241
+ 'w0MFVvMWNLUXZsblZJZFJaQjU4QnoyZUhzNDVKN0ROYktsQm12cWZPQnJFN09nOHdEbFUzWFpIZ3dRZk'\
242
+ 't4TUhBV29Qc0d2RG1KRjRZdFRmZ0R4NmZWS2VNbng5cXpJQ0plRlZmYnkzaUc4dE9XUFNKWFZUcXZ2Ql'\
243
+ 'hiQnppT0h4bV9sOHpma3UwUmk3RXYwWUU2VEJGWDZiNDRES0FpVW9adkUwTm9taXhhYll2ZDAwWFFPNn'\
244
+ 'dOMF9pdkN6Tmc0VVltaDlZUzJyM1E3YmloNFkyS3MyWllxdnNkazFnLWZiNGZRcyIsImtpZCI6IlFwSG'\
245
+ 'dDOWxCZ0ZEX1A0dV9aejBNd2x0V1dUN1g2QlRZZnVPNUlob1VrUDAifX0",'\
246
+ '"payload":"eyJyZXNvdXJjZSI6Im5ldy1yZWciLCJjb250YWN0IjpbIm1haWx0bzpmb29iYXJiYXpAZ'\
247
+ 'XhhbXBsZS5jb20iXX0",'\
248
+ '"signature":"T4aKq85rcACJdyEEx_Dw90lLypyPba6GSyG1YlFjCMwM7qdRlCsb3YIJE_Eia-A9IVl'\
249
+ 'cXKc2rzvVOPvGB2bYmPCxep5o7uy1mkC7hKJhSEw97mT_sidrLamEKIrYtnVrW0KSO3EKEY-rUSMIeOA'\
250
+ 'qLuZosSXLCEDlQTLqPUJPrhm8pDBRLzEqUO1HIMLfOEH5bWwXHofxL_QqUQjzMqIh_VWqy08MxuWEThD'\
251
+ 'UyKPewBx1E_Y0H-vsM_D39SJA04s9fnIGyOlXISL1LZ9pFZ_W5rF25_P9vwks9m7E8iZFo-rFc073DsM'\
252
+ 'zc_-5VbUgAoAqDZA69q8FTkgUkA6pu-vZsxeIb6OUNS6wxt6U4dqYS1aBXnhLeKvbIb7NqFmY8erzvcG'\
253
+ 'XBvab1F0ndg6sw5PmrDZyOzcCKdA9eQk6LqCnfh8TcoywmJiqm2Ck49BUUe4t98AjvMNhaHel4Vn4HUX'\
254
+ 'GX5xOEmt7gtsIE6YJP04ZoRKeWCrbYwjYIfLNlsbXpZj5oXPzBxOvwBuS6KL2HweeUGObiBsxRLsc1Bo'\
255
+ 'A_Na8Y2HtkWeZyUQPY1G3eSTPbBKR94T20LhcDSdNInzIFIhE5qI-WWCgHws6jB1K4Iip58zMgeTjGsD'\
256
+ '5Rc8ttBJwfprac8hBqMST_rzo-gn70b-iT3PJkasUbm32UwOxXtavJ5g"}',
257
+ headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
258
+ 'User-Agent' => ACME_USER_AGENT })
259
+ .to_return(status: 200, body: '', headers: {})
260
+
261
+ assert_nothing_raised do
262
+ cg.register
263
+ end
264
+ end
265
+
266
+ test 'register_and_authorize' do
267
+ cg = AcmePlugin::CertGenerator.new(private_key: 'key/test_keyfile_4096.pem',
268
+ endpoint: ENDPOINT_URL,
269
+ domain: 'example.com',
270
+ email: 'foobarbaz@example.com')
271
+ assert !cg.nil?
272
+
273
+ stub_request(:head, "#{API_URL}/new-reg")
274
+ .with(headers: { 'Accept' => '*/*' })
275
+ .to_return(status: 200, body: '', headers: {})
276
+
277
+ protected_field = '"protected":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eS'\
278
+ 'I6IlJTQSIsIm4iOiJxN0gtQ2txRHZ3ekx2OWRBZ05rSmQzM2FiVEpFa0ZHSjhXbGIxRnZ1Y1F6MEFYWX'\
279
+ 'JwWUx5ajdOYUNyQm90V1NaR2pFSnRQZ1k1M0xWWU1ET1BiOTktLTZEazNXVGhkT203U01JTlZYWlZ1Ym'\
280
+ 'hhNmtoY1pFWFA1NEdic0NzcFBmNm5OcUJCeEhDblV3V01GOElRcWkwTVdSNHFOeG1LZEVrcE56dGFCd0'\
281
+ 'xLU0ZWUHNRMHRGeXJHellhOEw0TkJqZWUyaWlEdWM2bHlMTkp6MnhfUUZMQWZneGw1cXdFa05FVGxNNz'\
282
+ 'JNQlRsTTNrR1k4UjN2RkVNVEhQSUtCM3VQcHdScE1qOEw2M1JLenpmcmM5Q0Y2TDRNaG01b05GUENndm'\
283
+ 'JDRWF6T0ctTEpwM2Q4bVlTZjBERE5RNlhoX0NnTG5TbXAzVWVmckpiQVFUZDBpSk4xa21TaV9fQmxOR1'\
284
+ 'JDS1dFZDgyZzA2aHgwbVRhOXVZZEQxMFNXaVF5UHZIbDRzWER3M0JMN0VpOVlPNHpoOUh6ZkpzUVhxY2'\
285
+ 'hzQnZVZ2dEVG9IZVJwMnhmS0hEcmFFZ2JPX0FxaEMxRkdkTUtPUnJMWnBMQm1oamw0MFVvMWNLUXZsbl'\
286
+ 'ZJZFJaQjU4QnoyZUhzNDVKN0ROYktsQm12cWZPQnJFN09nOHdEbFUzWFpIZ3dRZkt4TUhBV29Qc0d2RG'\
287
+ '1KRjRZdFRmZ0R4NmZWS2VNbng5cXpJQ0plRlZmYnkzaUc4dE9XUFNKWFZUcXZ2QlhiQnppT0h4bV9sOH'\
288
+ 'pma3UwUmk3RXYwWUU2VEJGWDZiNDRES0FpVW9adkUwTm9taXhhYll2ZDAwWFFPNndOMF9pdkN6Tmc0VV'\
289
+ 'ltaDlZUzJyM1E3YmloNFkyS3MyWllxdnNkazFnLWZiNGZRcyJ9LCJub25jZSI6bnVsbH0"'
290
+ stub_request(:post, "#{API_URL}/new-reg")
291
+ .with(body: "{#{protected_field},"\
292
+ '"payload":"eyJyZXNvdXJjZSI6Im5ldy1yZWciLCJjb250YWN0IjpbIm1haWx0bzpmb29iYXJiYXpAZ'\
293
+ 'XhhbXBsZS5jb20iXX0",'\
294
+ '"signature":"aQ6T0XVo9jS_jXlvQ6bjAfqcrMYpQTPE9_CD7v1hDBUzKpSoygAJmrbb0kSumMkWf-r'\
295
+ 'acxG5i7tcD4ed32ap1OsWUoPGWhXkQifAqToYMdfBzjwaS_OfjPpFflPOZmUvOygtGt3LTsdhA27PCSC'\
296
+ 'saZDlozz2143b00QhZ-gVkFRJEwOMN22ByOsL-1_R_UEp7mqwQKzeZ_nsW7japvGWcru13bzqiD2b069'\
297
+ 'kwrfksnMIMSjFWnJPnxCLpY-DQn8kCBeJfljW-mOJyZFck1ko0KBiOEbY_k0IJ0aspwg9lGRLkVX-wWV'\
298
+ '8Q_e8gwoLjmaXouUxAl2E3wNInXRJkgDOMfEUSOHzxI6WFYLzhVKVl7ktP7zx21my2vL_J_nROTDaVIU'\
299
+ 'qJgdUgEZ40KR_Kjd_pRKPcB8kHyKtDnCopmrPXLu0QCuHYAn_M-cYgkFOyvplJNodaZfKamLuwChywPl'\
300
+ 'DC5tr1BQvdlUVp-cwr_C_KU-Kpkws924xI0ah8ksuwsHpkp2458aVEIeDEEw7FgHZXUPKXaJcBIHjQ1R'\
301
+ 'vnm062Ub_4LR25TrKTgftDCftK2YtN4LA-jtlfOrdM47xZatAzhmFy_vFIIu_-phh5he0nn8Mx2byjsZ'\
302
+ '1M-DeW0Wl9_zxhhOo1gtmFhtguCKw3W0mdcdDZmhRleNS-HtYA0w41LA"}',
303
+ headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
304
+ 'User-Agent' => ACME_USER_AGENT })
305
+ .to_return(status: 200, body: '', headers: {})
306
+
307
+ stub_request(:post, "#{API_URL}/new-authz")
308
+ .with(body: "{#{protected_field},"\
309
+ '"payload":"eyJyZXNvdXJjZSI6Im5ldy1hdXRoeiIsImlkZW50aWZpZXIiOnsidHlwZSI6ImRucyIsI'\
310
+ 'nZhbHVlIjoiZXhhbXBsZS5jb20ifX0",'\
311
+ '"signature":"NIo2YSDVmY-ILSdUXu4r1sBi31YYEHGvAz0t9IskSjCDl3A2C7fKXXejxLEU9aHvjR8'\
312
+ '9ZwrQtdDjhSVvZ5tCOkN8UhZ_jo2Y9iDIoNijd7t11jCxiv9epgQ83cUXtqgYLQnfpNNFlxd24yyLNNe'\
313
+ 'K6NM4mIhf5fyji6skTOrv4TPg4ZZzx107dmPmGsKQpSo2uRb1bV6H63qOwqjfp4LYM_HrHTH-L_HQTgt'\
314
+ '1fV4pmlTYlmaK__OLgxBXdVFgSN2yAUXPl3b9xYsGq3yEdXlGFv_f2iNbfhhiFHl_ZbRsjbLY5QU5TRa'\
315
+ 'Y6w5LjVkMwNLuusAeR5SwkOjGYQw-PQ303xuCx2WhlcIVlz06Lex9zrYH7TVc1Tfl5aiaqw4lABJoFu8'\
316
+ 'YLWx83gQZbeyBJJ612e_oNqsbmd0RWytXzS-qydLmpjl2KS7U_877JIoZPePwqzbuNoUmeVfqQcCGcpM'\
317
+ 't1atBAoBVgOKW5cWlEhZD4BRY_q-dX2hcR-7T1suLdMpEc1b_JQr0EVxgz0rZWcE3xAtlQLYZtC7n_nW'\
318
+ 'B4EiQzRiiP1mgPRwdmASmAAMp7ToW53lYh-vLxpHX7CzLdug2AuvFkG6ajaGFMzZaJ4dyxf0GbznSNyV'\
319
+ 'ulEJlxSt81MVthCX3JjUtRDcJHZx6tQFR1Gm1RUDE7Sob0Xe6jZKfqRE"}',
320
+ headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
321
+ 'User-Agent' => ACME_USER_AGENT })
322
+ .to_return(status: 200,
323
+ body: '{'\
324
+ ' "status": "valid",'\
325
+ ' "expires": "2020-12-31T23:59:59+01:00",'\
326
+ ' "identifier":'\
327
+ ' {'\
328
+ ' "type": "dns", "value": "example.org"'\
329
+ ' },'\
330
+ ' "challenges":'\
331
+ ' ['\
332
+ ' {'\
333
+ ' "type": "http-01",'\
334
+ ' "status": "pending",'\
335
+ " \"uri\": \"#{API_URL}/challenge/JmusiS7mAL_OZ1tIbwQxpYadpM9E3Azbywa6KSveuEk/93\","\
336
+ ' "token":"-Jbrff2stnTiZXFFKJHXtYrZof2dqlQaegRbeG1t6BY"'\
337
+ ' }'\
338
+ ' ],'\
339
+ ' "combinations":[[0]]'\
340
+ '}',
341
+ headers: { 'Content-Type' => 'application/json',
342
+ 'Link' => "<#{API_URL}/new-cert>;rel=\"next\"",
343
+ 'Location' => "#{API_URL}/authz/JmusiS7mAL_OZ1tIbwQxpYadpM9E3Azbywa6KSveuEk",
344
+ 'Replay-Nonce' => 'Hdhg8ovViG7ipkQrQRW2dsDbK5vxhGd_4T9HDqg3u4c' })
345
+
346
+ assert_nothing_raised do
347
+ cg.register
348
+ cg.authorize
349
+ end
350
+ end
351
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ module AcmePlugin
4
+ class ApplicationControllerTest < ActionController::TestCase
5
+ setup do
6
+ @routes = AcmePlugin::Engine.routes
7
+ end
8
+
9
+ test 'if challenge request is invalid when is smaller than 128 bits' do
10
+ get :index, params: { challenge: 'dG9rZW4=' }
11
+ assert_response :bad_request
12
+ assert_match('Challenge failed - Request has invalid length!', response.body)
13
+ end
14
+
15
+ test 'if challenge request is invalid if it is larger than 256 bytes' do
16
+ get :index, params: { challenge: 'a' * 257 }
17
+ assert_response :bad_request
18
+ assert_match('Challenge failed - Request has invalid length!', response.body)
19
+ end
20
+
21
+ test 'if challenge is valid' do
22
+ get :index, params: { challenge: 'rpzxDjD-8xrr5I1G_JBTEToVMYgjNjfSs-XZ62tRtgs' }
23
+ assert_response :ok
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.