recaptcha 0.3.6 → 0.6.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: a2ab60ab04802c7dc8a205ffa010071d2edd9f47
4
- data.tar.gz: 7155b2dcb7ee6c183b3a06b6768562daf6e3e2da
3
+ metadata.gz: 15a8d9b4ffc50fb479df2f03065310e3b85be5f4
4
+ data.tar.gz: 1d2412b3ce72619d131388e77e9b82a4b1e9de46
5
5
  SHA512:
6
- metadata.gz: b2dcd0d13ae9dcbb8f9c96a7c65e0231f058a7979696bd181fe101640f5c3778d02b8dbb39d898f2bcd2a788b1ab062dbb0b027619d71a6c55af9e2e8a0cf151
7
- data.tar.gz: c55e6b3701db18b477ea056b289ec710e550daa71dfe3d03c3540ebdd8e22adef22969fac2013bc0b8586cb65ec89acef72200984fa1765d119b83568676d9aa
6
+ metadata.gz: f1a53822196216dc231d60369edcce9719e724e4e66a35b57e35b7b57ca8104b2d5211a07231616fad499c13420daa3eb584b5337e87dd47631d40e61d801721
7
+ data.tar.gz: e3fef919669e86e48de71216c3def51609e1d525d1fe7cebc5ada112f5f9613d96783625c0d675cddfeafce97d7783006665371db42be17f027248945784ebc6
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.3.6 / 2012-01-07
2
+
3
+ * Many documentation changes
4
+ * Fixed deprecations in dependencies
5
+ * Protocol relative JS includes
6
+ * Fixes for options hash
7
+ * Fixes for failing tests
8
+
1
9
  == 0.3.5 / 2012-05-02
2
10
 
3
11
  * I18n for error messages
data/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # reCAPTCHA
2
+
3
+ Author: Jason L Perry (http://ambethia.com)<br/>
4
+ Copyright: Copyright (c) 2007-2013 Jason L Perry<br/>
5
+ License: [MIT](http://creativecommons.org/licenses/MIT/)<br/>
6
+ Info: https://github.com/ambethia/recaptcha<br/>
7
+ Bugs: https://github.com/ambethia/recaptcha/issues<br/>
8
+
9
+ This plugin adds helpers for the [reCAPTCHA API](https://www.google.com/recaptcha). In your
10
+ views you can use the `recaptcha_tags` method to embed the needed javascript,
11
+ and you can validate in your controllers with `verify_recaptcha` or `verify_recaptcha!`,
12
+ which throws an error on failiure.
13
+
14
+ Beforehand you need to configure Recaptcha with your custom private and public
15
+ key. You may find detailed examples below. Exceptions will be raised if you
16
+ call these methods and the keys can't be found.
17
+
18
+ ## Rails Installation
19
+
20
+ ```Ruby
21
+ gem "recaptcha", require: "recaptcha/rails"
22
+ ```
23
+
24
+ (Rails < 3.0: install an older release and view it's README)
25
+
26
+ ## Setting up your API Keys
27
+
28
+ There are multiple ways to setup your reCAPTCHA API key once you
29
+ [obtain a pair](https://www.google.com/recaptcha/admin).
30
+
31
+ ### Recaptcha.configure
32
+
33
+ You may use the block style configuration. The following code could be placed
34
+ into a `config/initializers/recaptcha.rb` when used in a Rails project.
35
+
36
+ ```Ruby
37
+ Recaptcha.configure do |config|
38
+ config.public_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
39
+ config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
40
+ # Uncomment the following line if you are using a proxy server:
41
+ # config.proxy = 'http://myproxy.com.au:8080'
42
+ # Uncomment if you want to use the older version of the API,
43
+ # only works for versions >= 0.3.7, default value is 'v2':
44
+ # config.api_version = 'v1'
45
+ end
46
+ ```
47
+
48
+ This way, you may also set additional options to fit recaptcha into your
49
+ deployment environment.
50
+
51
+ ### Recaptcha.with_configuration
52
+
53
+ If you want to temporarily overwrite the configuration you set with
54
+ `Recaptcha.configure` (when testing, for example), you can use a
55
+ `Recaptcha#with_configuration` block:
56
+
57
+ ```Ruby
58
+ Recaptcha.with_configuration(public_key: '12345') do
59
+ # Do stuff with the overwritten public_key.
60
+ end
61
+ ```
62
+
63
+ ### Heroku & Shell environment
64
+
65
+ Or, you can keep your keys out of your code base by exporting the following
66
+ environment variables. You might do this in the .profile/rc, or equivalent for
67
+ the user running your application. This would also be the preffered method
68
+ in an Heroku deployment.
69
+
70
+ ```
71
+ export RECAPTCHA_PUBLIC_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
72
+ export RECAPTCHA_PRIVATE_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
73
+ ```
74
+
75
+ ### Per call
76
+
77
+ You can also pass in your keys as options at runtime, for example:
78
+
79
+ ```Ruby
80
+ recaptcha_tags public_key: '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
81
+ ```
82
+
83
+ and later,
84
+
85
+ ```Ruby
86
+ verify_recaptcha private_key: '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
87
+ ```
88
+
89
+ This option might be useful, if the same code base is used for multiple
90
+ reCAPTCHA setups.
91
+
92
+ ## To use 'recaptcha'
93
+
94
+ Add `recaptcha_tags` to each form you want to protect.
95
+ Place it where you want the recaptcha widget to appear.
96
+
97
+ Example:
98
+
99
+ ```Erb
100
+ <%= form_for @foo do |f| %>
101
+ # ... additional lines truncated for brevity ...
102
+ <%= recaptcha_tags %>
103
+ # ... additional lines truncated for brevity ...
104
+ <% end %>
105
+ ```
106
+
107
+ And, add `verify_recaptcha` logic to each form action that you've protected.
108
+
109
+ ### recaptcha_tags
110
+
111
+ Some of the options available:
112
+
113
+ | Option | Description |
114
+ |-------------|-------------|
115
+ | :ssl | Uses secure http for captcha widget (default `false`, but can be changed by setting `config.use_ssl_by_default`)|
116
+ | :noscript | Include <noscript> content (default `true`)|
117
+ | :display | Takes a hash containing the `theme` and `tabindex` options per the API. (default `nil`), options: 'red', 'white', 'blackglass', 'clean', 'custom'|
118
+ | :ajax | Render the dynamic AJAX captcha per the API. (default `false`)|
119
+ | :public_key | Your public API key, takes precedence over the ENV variable (default `nil`)|
120
+ | :error | Override the error code returned from the reCAPTCHA API (default `nil`)|
121
+ | :stoken | Include in reCAPTCHA API v2 the security token (default `true`)|
122
+ | :size | Specify a size (default `nil`)|
123
+
124
+
125
+ You can also override the html attributes for the sizes of the generated `textarea` and `iframe`
126
+ elements, if CSS isn't your thing. Inspect the source of `recaptcha_tags` to see these options.
127
+
128
+ ### verify_recaptcha
129
+
130
+ This method returns `true` or `false` after processing the parameters from the reCAPTCHA widget. Why
131
+ isn't this a model validation? Because that violates MVC. You can use it like this, or how ever you
132
+ like. Passing in the ActiveRecord object is optional, if you do--and the captcha fails to verify--an
133
+ error will be added to the object for you to use.
134
+
135
+ Some of the options available:
136
+
137
+ | Option | Description |
138
+ |--------------|-------------|
139
+ | :model | Model to set errors
140
+ | :attribute | Model attribute to receive errors (default :base)
141
+ | :message | Custom error message
142
+ | :private_key | Your private API key, takes precedence over the ENV variable (default `nil`).
143
+ | :timeout | The number of seconds to wait for reCAPTCHA servers before give up. (default `3`)
144
+
145
+ ```Ruby
146
+ respond_to do |format|
147
+ if verify_recaptcha(model @post, message: "Oh! It's error with reCAPTCHA!") && @post.save
148
+ # ...
149
+ else
150
+ # ...
151
+ end
152
+ end
153
+ ```
154
+
155
+ ### Add multiple widgets to the same page
156
+
157
+ This is an example taken from the [official google documentation](https://developers.google.com/recaptcha/docs/display).
158
+
159
+ Add a script tag for a callback
160
+
161
+ ```Html
162
+ <script type="text/javascript">
163
+ var verifyCallback = function(response) {
164
+ alert(response);
165
+ };
166
+ var widgetId1;
167
+ var widgetId2;
168
+ var onloadCallback = function() {
169
+ // Renders the HTML element with id 'example1' as a reCAPTCHA widget.
170
+ // The id of the reCAPTCHA widget is assigned to 'widgetId1'.
171
+ widgetId1 = grecaptcha.render('example1', {
172
+ 'sitekey' : "<%= Recaptcha.configuration.public_key %>",
173
+ 'theme' : 'light'
174
+ });
175
+ widgetId2 = grecaptcha.render(document.getElementById('example2'), {
176
+ 'sitekey' : "<%= Recaptcha.configuration.public_key %>"
177
+ });
178
+ grecaptcha.render('example3', {
179
+ 'sitekey' : "<%= Recaptcha.configuration.public_key %>",
180
+ 'callback' : verifyCallback,
181
+ 'theme' : 'dark'
182
+ });
183
+ };
184
+ </script>
185
+ ```
186
+
187
+ In the callback you will have the `sitekey` generated by this gem with `<%= Recaptcha.configuration.public_key %>`
188
+
189
+ Next you need to have some elements with an id matching those specified in the callback
190
+
191
+ ```Erb
192
+ <%= form_for @foo do |f| %>
193
+ # ... additional lines truncated for brevity ...
194
+ <div id="example1"></div>
195
+ # ... additional lines truncated for brevity ...
196
+ <% end %>
197
+ <%= form_for @foo2 do |f| %>
198
+ # ... additional lines truncated for brevity ...
199
+ <div id="example2"></div>
200
+ # ... additional lines truncated for brevity ...
201
+ <% end %>
202
+ <%= form_for @foo3 do |f| %>
203
+ # ... additional lines truncated for brevity ...
204
+ <div id="example3"></div>
205
+ # ... additional lines truncated for brevity ...
206
+ <% end %>
207
+ ```
208
+
209
+ And finally you need a script tag that gets the reCAPTCHA code from google and tells it that your code is explicit and gives it the callback.
210
+
211
+ ```Html
212
+ <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
213
+ ```
214
+
215
+ Now all together:
216
+
217
+ ```Html
218
+ <html>
219
+ <head>
220
+ <title>reCAPTCHA demo: Explicit render for multiple widgets</title>
221
+ <script type="text/javascript">
222
+ var verifyCallback = function(response) {
223
+ alert(response);
224
+ };
225
+ var widgetId1;
226
+ var widgetId2;
227
+ var onloadCallback = function() {
228
+ // Renders the HTML element with id 'example1' as a reCAPTCHA widget.
229
+ // The id of the reCAPTCHA widget is assigned to 'widgetId1'.
230
+ widgetId1 = grecaptcha.render('example1', {
231
+ 'sitekey' : "<%= Recaptcha.configuration.public_key %>",
232
+ 'theme' : 'light'
233
+ });
234
+ widgetId2 = grecaptcha.render(document.getElementById('example2'), {
235
+ 'sitekey' : "<%= Recaptcha.configuration.public_key %>"
236
+ });
237
+ grecaptcha.render('example3', {
238
+ 'sitekey' : "<%= Recaptcha.configuration.public_key %>",
239
+ 'callback' : verifyCallback,
240
+ 'theme' : 'dark'
241
+ });
242
+ };
243
+ </script>
244
+ </head>
245
+ <body>
246
+ <%= form_for @foo do |f| %>
247
+ # ... additional lines truncated for brevity ...
248
+ <div id="example1"></div>
249
+ # ... additional lines truncated for brevity ...
250
+ <% end %>
251
+ <%= form_for @foo2 do |f| %>
252
+ # ... additional lines truncated for brevity ...
253
+ <div id="example2"></div>
254
+ # ... additional lines truncated for brevity ...
255
+ <% end %>
256
+ <%= form_for @foo3 do |f| %>
257
+ # ... additional lines truncated for brevity ...
258
+ <div id="example3"></div>
259
+ # ... additional lines truncated for brevity ...
260
+ <% end %>
261
+ <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
262
+ </body>
263
+ </html>
264
+ ```
265
+
266
+ The only real difference between this example and the google example is you will use the `<%= Recaptcha.configuration.public_key %>` for the `sitekey`
267
+
268
+ If your callback has to live on another file (maybe a layout), then you would set the callback on window `window.onloadCallback = function() {...}`
269
+
270
+ Then on the backend, you will still use the `verify_recaptcha` as explained in this readme.
271
+
272
+ ## I18n support
273
+ reCAPTCHA passes two types of error explanation to a linked model. It will use the I18n gem
274
+ to translate the default error message if I18n is available. To customize the messages to your locale,
275
+ add these keys to your I18n backend:
276
+
277
+ `recaptcha.errors.verification_failed` error message displayed if the captcha words didn't match
278
+ `recaptcha.errors.recaptcha_unreachable` displayed if a timeout error occured while attempting to verify the captcha
279
+
280
+ Also you can translate API response errors to human friendly by adding translations to the locale (`config/locales/en.yml`):
281
+
282
+ ```Yaml
283
+ en:
284
+ recaptcha:
285
+ errors:
286
+ incorrect-captcha-sol: 'Fail'
287
+ ```
288
+
289
+ ## Testing
290
+ By default, reCAPTCHA is skipped on "test" env, so if you need to make sure it's working properly, just remove the "test" entry of the skip_environment inside Recapcha class, look:
291
+
292
+
293
+ ```Ruby
294
+ Recaptcha.configuration.skip_verify_env.delete("test")
295
+ ```
data/lib/recaptcha.rb CHANGED
@@ -1,13 +1,26 @@
1
1
  require 'recaptcha/configuration'
2
2
  require 'recaptcha/client_helper'
3
3
  require 'recaptcha/verify'
4
+ require 'recaptcha/token'
4
5
 
5
6
  module Recaptcha
6
- RECAPTCHA_API_SERVER_URL = '//www.google.com/recaptcha/api'
7
- RECAPTCHA_API_SECURE_SERVER_URL = 'https://www.google.com/recaptcha/api'
8
- RECAPTCHA_VERIFY_URL = 'http://www.google.com/recaptcha/api/verify'
7
+ CONFIG =
8
+ {
9
+ 'v1' => {
10
+ 'server_url' => '//www.google.com/recaptcha/api',
11
+ 'secure_server_url' => 'https://www.google.com/recaptcha/api',
12
+ 'verify_url' => 'http://www.google.com/recaptcha/api/verify'
13
+ },
14
+
15
+ 'v2' => {
16
+ 'server_url' => '//www.google.com/recaptcha/api.js',
17
+ 'secure_server_url' => 'https://www.google.com/recaptcha/api.js',
18
+ 'verify_url' => 'https://www.google.com/recaptcha/api/siteverify'
19
+ }
20
+ }
21
+
22
+ RECAPTCHA_API_VERSION = 'v2'
9
23
  USE_SSL_BY_DEFAULT = false
10
-
11
24
  HANDLE_TIMEOUTS_GRACEFULLY = true
12
25
  SKIP_VERIFY_ENV = ['test', 'cucumber']
13
26
 
@@ -43,8 +56,7 @@ module Recaptcha
43
56
 
44
57
  class RecaptchaError < StandardError
45
58
  end
46
- end
47
59
 
48
- if defined?(Rails)
49
- require 'recaptcha/rails'
60
+ class VerifyError < RecaptchaError
61
+ end
50
62
  end
@@ -3,6 +3,11 @@ module Recaptcha
3
3
  # Your public API can be specified in the +options+ hash or preferably
4
4
  # using the Configuration.
5
5
  def recaptcha_tags(options = {})
6
+ return v1_tags(options) if Recaptcha.configuration.v1?
7
+ return v2_tags(options) if Recaptcha.configuration.v2?
8
+ end # recaptcha_tags
9
+
10
+ def v1_tags(options)
6
11
  # Default options
7
12
  key = options[:public_key] ||= Recaptcha.configuration.public_key
8
13
  raise RecaptchaError, "No public key specified." unless key
@@ -16,11 +21,18 @@ module Recaptcha
16
21
  html << %{</script>\n}
17
22
  end
18
23
  if options[:ajax]
24
+ if options[:display] && options[:display][:custom_theme_widget]
25
+ widget = options[:display][:custom_theme_widget]
26
+ else
27
+ widget = "dynamic_recaptcha"
28
+ html << <<-EOS
29
+ <div id="#{widget}"></div>
30
+ EOS
31
+ end
19
32
  html << <<-EOS
20
- <div id="dynamic_recaptcha"></div>
21
33
  <script type="text/javascript">
22
34
  var rc_script_tag = document.createElement('script'),
23
- rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("dynamic_recaptcha")#{',RecaptchaOptions' if options[:display]});}
35
+ rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("#{widget}")#{',RecaptchaOptions' if options[:display]});}
24
36
  rc_script_tag.src = "#{uri}/js/recaptcha_ajax.js";
25
37
  rc_script_tag.type = 'text/javascript';
26
38
  rc_script_tag.onload = function(){rc_init_func.call();};
@@ -48,7 +60,57 @@ module Recaptcha
48
60
  end
49
61
  end
50
62
  return (html.respond_to?(:html_safe) && html.html_safe) || html
51
- end # recaptcha_tags
63
+ end
64
+
65
+ def v2_tags(options)
66
+ public_key = options[:public_key] ||= Recaptcha.configuration.public_key
67
+ raise RecaptchaError, "No public key specified." unless public_key
68
+ private_key = options[:private_key] ||= Recaptcha.configuration.private_key
69
+ raise RecaptchaError, "No private key specified." unless private_key
70
+ error = options[:error] ||= ((defined? flash) ? flash[:recaptcha_error] : "") # TODO not being used !?
71
+ uri = Recaptcha.configuration.api_server_url(options[:ssl])
72
+ uri += "?hl=#{options[:hl]}" unless options[:hl].blank?
73
+
74
+ html = ""
75
+ html << %{<script src="#{uri}" async defer></script>\n}
76
+
77
+ data_attributes = options.slice(:theme, :type, :callback, :expired_callback, :size)
78
+ data_attributes[:sitekey] = public_key
79
+
80
+ if options[:stoken] != false
81
+ data_attributes[:stoken] = Recaptcha::Token.secure_token
82
+ end
83
+
84
+ data_attributes = data_attributes.map {|k,v| %{data-#{k.to_s.gsub(/_/,'-')}="#{v}"} }.join(" ")
85
+
86
+ html << %{<div class="g-recaptcha" #{data_attributes}></div>\n}
87
+
88
+ unless options[:noscript] == false
89
+ fallback_uri = "#{uri.chomp('.js')}/fallback?k=#{public_key}"
90
+ html << %{<noscript>}
91
+ html << %{<div style="width: 302px; height: 352px;">}
92
+ html << %{ <div style="width: 302px; height: 352px; position: relative;">}
93
+ html << %{ <div style="width: 302px; height: 352px; position: absolute;">}
94
+ html << %{ <iframe src="#{fallback_uri}"}
95
+ html << %{ frameborder="0" scrolling="no"}
96
+ html << %{ style="width: 302px; height:352px; border-style: none;">}
97
+ html << %{ </iframe>}
98
+ html << %{ </div>}
99
+ html << %{ <div style="width: 250px; height: 80px; position: absolute; border-style: none; }
100
+ html << %{ bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">}
101
+ html << %{ <textarea id="g-recaptcha-response" name="g-recaptcha-response" }
102
+ html << %{ class="g-recaptcha-response" }
103
+ html << %{ style="width: 250px; height: 80px; border: 1px solid #c1c1c1; }
104
+ html << %{ margin: 0px; padding: 0px; resize: none;" value=""> }
105
+ html << %{ </textarea>}
106
+ html << %{ </div>}
107
+ html << %{ </div>}
108
+ html << %{ </div>}
109
+ html << %{</noscript>}
110
+ end
111
+
112
+ (html.respond_to?(:html_safe) && html.html_safe) || html
113
+ end
52
114
 
53
115
  private
54
116
 
@@ -57,7 +119,7 @@ module Recaptcha
57
119
  result << hash.map do |k, v|
58
120
  if v.is_a?(Hash)
59
121
  "\"#{k}\": #{hash_to_json(v)}"
60
- elsif ! v.is_a?(String) || k.to_s == "callback"
122
+ elsif ! v.is_a?(String) || k.to_s =~ %r{(callback|expired-callback)}
61
123
  "\"#{k}\": #{v}"
62
124
  else
63
125
  "\"#{k}\": \"#{v}\""
@@ -65,5 +127,5 @@ module Recaptcha
65
127
  end.join(", ")
66
128
  result << "}"
67
129
  end
68
- end # ClientHelper
69
- end # Recaptcha
130
+ end
131
+ end