cure-recaptcha 0.4.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5f9b067367260a289398dd9afc46f91c49163d38
4
+ data.tar.gz: bf5581b12a643b557a916daaf4c9c61eafecd500
5
+ SHA512:
6
+ metadata.gz: 40d1386d493d31c6f8781b19c9509220b2823c9fdb93e5966d87385807382edc03c746fb3987451e9ab7659fb0408d076c1ed89aab495b23705c00e532d6cbc0
7
+ data.tar.gz: ba4b195f4c3b123dbe8e7126fa03d80f20c97ec5fcd15ad2d302ff64d44f1a8522c7509193b010c95db469fbeec8704d5f2a2fa2abf0252202e563e89c198ca5
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ rdoc
2
+ pkg
3
+ .ruby-gemset
4
+ .ruby-version
5
+ .bundle
6
+
7
+ .gems
8
+ .rbenv-gemsets
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
data/CHANGELOG ADDED
@@ -0,0 +1,41 @@
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
+
9
+ == 0.3.5 / 2012-05-02
10
+
11
+ * I18n for error messages
12
+ * Rails: delete flash keys if unused
13
+
14
+ == 0.3.4 / 2011-12-13
15
+
16
+ * Rails 3
17
+ * Remove jeweler
18
+
19
+ == 0.2.2 / 2009-09-14
20
+
21
+ * Add a timeout to the validator
22
+ * Give the documentation some love
23
+
24
+ == 0.2.1 / 2009-09-14
25
+
26
+ * Removed Ambethia namespace, and restructured classes a bit
27
+ * Added an example rails app in the example-rails branch
28
+
29
+ == 0.2.0 / 2009-09-12
30
+
31
+ * RecaptchaOptions AJAX API Fix
32
+ * Added 'cucumber' as a test environment to skip
33
+ * Ruby 1.9 compat fixes
34
+ * Added option :message => 'Custom error message' to verify_recaptcha
35
+ * Removed dependency on ActiveRecord constant
36
+ * Add I18n
37
+
38
+ == 0.1.0 / 2008-2-8
39
+
40
+ * 1 major enhancement
41
+ * Initial Gem Release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cure-recaptcha (0.4.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (4.1.8)
10
+ i18n (~> 0.6, >= 0.6.9)
11
+ json (~> 1.7, >= 1.7.7)
12
+ minitest (~> 5.1)
13
+ thread_safe (~> 0.1)
14
+ tzinfo (~> 1.1)
15
+ byebug (3.5.1)
16
+ columnize (~> 0.8)
17
+ debugger-linecache (~> 1.2)
18
+ slop (~> 3.6)
19
+ coderay (1.1.0)
20
+ columnize (0.8.9)
21
+ debugger-linecache (1.2.0)
22
+ i18n (0.6.11)
23
+ json (1.8.1)
24
+ metaclass (0.0.4)
25
+ method_source (0.8.2)
26
+ minitest (5.4.3)
27
+ mocha (1.1.0)
28
+ metaclass (~> 0.0.1)
29
+ pry (0.10.1)
30
+ coderay (~> 1.1.0)
31
+ method_source (~> 0.8.1)
32
+ slop (~> 3.4)
33
+ pry-byebug (2.0.0)
34
+ byebug (~> 3.4)
35
+ pry (~> 0.10)
36
+ rake (10.3.2)
37
+ slop (3.6.0)
38
+ thread_safe (0.3.4)
39
+ tzinfo (1.2.2)
40
+ thread_safe (~> 0.1)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ activesupport
47
+ i18n
48
+ minitest (~> 5.0)
49
+ mocha
50
+ pry-byebug
51
+ rake
52
+ recaptcha!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007 Jason L Perry
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,151 @@
1
+ = reCAPTCHA
2
+
3
+ Author:: Jason L Perry (http://ambethia.com)
4
+ Copyright:: Copyright (c) 2007-2013 Jason L Perry
5
+ License:: {MIT}[http://creativecommons.org/licenses/MIT/]
6
+ Info:: http://github.com/ambethia/recaptcha
7
+ Bugs:: http://github.com/ambethia/recaptcha/issues
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
+ reCAPTCHA for Rails > 3.0, add this to your Gemfile:
21
+
22
+ gem "recaptcha", :require => "recaptcha/rails"
23
+
24
+ Rails apps below 3.0 are no longer supported, but you can install an older
25
+ release and view it's README.
26
+
27
+ == Setting up your API Keys
28
+
29
+ There are multiple ways to setup your reCAPTCHA API key once you
30
+ {obtain}[https://www.google.com/recaptcha/admin] a pair.
31
+
32
+ === Recaptcha.configure
33
+
34
+ You may use the block style configuration. The following code could be placed
35
+ into a +config/initializers/recaptcha.rb+ when used in a Rails project.
36
+
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 newer version of the API,
43
+ # only works for versions >= 0.3.7:
44
+ # config.api_version = 'v2'
45
+ end
46
+
47
+ This way, you may also set additional options to fit recaptcha into your
48
+ deployment environment.
49
+
50
+ == Recaptcha#with_configuration
51
+
52
+ If you want to temporarily overwrite the configuration you set with `Recaptcha.configure` (when testing, for example), you can use a `Recaptcha#with_configuration` block:
53
+
54
+ Recaptcha.with_configuration(:public_key => '12345') do
55
+ # Do stuff with the overwritten public_key.
56
+ end
57
+
58
+ === Heroku & Shell environment
59
+
60
+ Or, you can keep your keys out of your code base by exporting the following
61
+ environment variables. You might do this in the .profile/rc, or equivalent for
62
+ the user running your application. This would also be the preffered method
63
+ in an Heroku deployment.
64
+
65
+ export RECAPTCHA_PUBLIC_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
66
+ export RECAPTCHA_PRIVATE_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
67
+
68
+ === Per call
69
+
70
+ You can also pass in your keys as options at runtime, for example:
71
+
72
+ recaptcha_tags :public_key => '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
73
+
74
+ and later,
75
+
76
+ verify_recaptcha :private_key => '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
77
+
78
+ This option might be useful, if the same code base is used for multiple
79
+ reCAPTCHA setups.
80
+
81
+ == To use 'recaptcha'
82
+
83
+ Add +recaptcha_tags+ to each form you want to protect. Place it where you want the recaptcha widget to appear.
84
+
85
+ Example:
86
+
87
+ <%= form_for @foo do |f| %>
88
+ # ... additional lines truncated for brevity ...
89
+ <%= recaptcha_tags %>
90
+ # ... additional lines truncated for brevity ...
91
+ <% end %>
92
+
93
+ And, add +verify_recaptcha+ logic to each form action that you've protected.
94
+
95
+ === +recaptcha_tags+
96
+
97
+ Some of the options available:
98
+
99
+ <tt>:ssl</tt>:: Uses secure http for captcha widget (default +false+, but can be changed by setting +config.use_ssl_by_default+)
100
+ <tt>:noscript</tt>:: Include <noscript> content (default +true+)
101
+ <tt>:display</tt>:: Takes a hash containing the +theme+ and +tabindex+ options per the API. (default +nil+), options: 'red', 'white', 'blackglass', 'clean', 'custom'
102
+ <tt>:ajax</tt>:: Render the dynamic AJAX captcha per the API. (default +false+)
103
+ <tt>:public_key</tt>:: Your public API key, takes precedence over the ENV variable (default +nil+)
104
+ <tt>:error</tt>:: Override the error code returned from the reCAPTCHA API (default +nil+)
105
+
106
+ You can also override the html attributes for the sizes of the generated +textarea+ and +iframe+
107
+ elements, if CSS isn't your thing. Inspect the source of +recaptcha_tags+ to see these options.
108
+
109
+ === +verify_recaptcha+
110
+
111
+ This method returns +true+ or +false+ after processing the parameters from the reCAPTCHA widget. Why
112
+ isn't this a model validation? Because that violates MVC. You can use it like this, or how ever you
113
+ like. Passing in the ActiveRecord object is optional, if you do--and the captcha fails to verify--an
114
+ error will be added to the object for you to use.
115
+
116
+ Some of the options available:
117
+
118
+ <tt>:model</tt>:: Model to set errors
119
+ <tt>:attribute</tt>:: Model attribute to receive errors (default :base)
120
+ <tt>:message</tt>:: Custom error message
121
+ <tt>:private_key</tt>:: Your private API key, takes precedence over the ENV variable (default +nil+).
122
+ <tt>:timeout</tt>:: The number of seconds to wait for reCAPTCHA servers before give up. (default +3+)
123
+
124
+ respond_to do |format|
125
+ if verify_recaptcha(:model => @post, :message => "Oh! It's error with reCAPTCHA!") && @post.save
126
+ # ...
127
+ else
128
+ # ...
129
+ end
130
+ end
131
+
132
+ == I18n support
133
+ reCAPTCHA passes two types of error explanation to a linked model. It will use the I18n gem
134
+ to translate the default error message if I18n is available. To customize the messages to your locale,
135
+ add these keys to your I18n backend:
136
+
137
+ <tt>recaptcha.errors.verification_failed</tt>:: error message displayed if the captcha words didn't match
138
+ <tt>recaptcha.errors.recaptcha_unreachable</tt>:: displayed if a timeout error occured while attempting to verify the captcha
139
+
140
+ Also you can translate API response errors to human friendly by adding translations to the locale (+config/locales/en.yml+):
141
+
142
+ en:
143
+ recaptcha:
144
+ errors:
145
+ incorrect-captcha-sol: 'Fail'
146
+
147
+ == TODO
148
+ * Remove Rails/ActionController dependencies
149
+ * Framework agnostic
150
+ * Add some helpers to use in before_filter and what not
151
+ * Better documentation
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList['test/*_test.rb']
6
+ end
7
+
8
+ task :default => :test
data/init.rb ADDED
@@ -0,0 +1,5 @@
1
+ # Rails plugin initialization.
2
+ # You can also install it as a gem:
3
+ # config.gem "ambethia-recaptcha", :lib => "recaptcha/rails", :source => "http://gems.github.com"
4
+
5
+ require 'recaptcha/rails'
data/lib/recaptcha.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'recaptcha/configuration'
2
+ require 'recaptcha/client_helper'
3
+ require 'recaptcha/verify'
4
+
5
+ module Recaptcha
6
+ CONFIG =
7
+ {
8
+ 'v1' => {
9
+ 'server_url' => '//www.google.com/recaptcha/api',
10
+ 'secure_server_url' => 'https://www.google.com/recaptcha/api',
11
+ 'verify_url' => 'http://www.google.com/recaptcha/api/verify'
12
+ },
13
+
14
+ 'v2' => {
15
+ 'server_url' => '//www.google.com/recaptcha/api.js',
16
+ 'secure_server_url' => 'https://www.google.com/recaptcha/api.js',
17
+ 'verify_url' => 'https://www.google.com/recaptcha/api/siteverify'
18
+ }
19
+ }
20
+
21
+ RECAPTCHA_API_VERSION = 'v2'
22
+ USE_SSL_BY_DEFAULT = false
23
+ HANDLE_TIMEOUTS_GRACEFULLY = true
24
+ SKIP_VERIFY_ENV = ['test', 'cucumber']
25
+
26
+ # Gives access to the current Configuration.
27
+ def self.configuration
28
+ @configuration ||= Configuration.new
29
+ end
30
+
31
+ # Allows easy setting of multiple configuration options. See Configuration
32
+ # for all available options.
33
+ #--
34
+ # The temp assignment is only used to get a nicer rdoc. Feel free to remove
35
+ # this hack.
36
+ #++
37
+ def self.configure
38
+ config = configuration
39
+ yield(config)
40
+ end
41
+
42
+ def self.with_configuration(config)
43
+ original_config = {}
44
+
45
+ config.each do |key, value|
46
+ original_config[key] = configuration.send(key)
47
+ configuration.send("#{key}=", value)
48
+ end
49
+
50
+ result = yield if block_given?
51
+
52
+ original_config.each { |key, value| configuration.send("#{key}=", value) }
53
+ result
54
+ end
55
+
56
+ class RecaptchaError < StandardError
57
+ end
58
+
59
+ class VerifyError < RecaptchaError
60
+ end
61
+
62
+ end
63
+
64
+ if defined?(Rails)
65
+ require 'recaptcha/rails'
66
+ end
@@ -0,0 +1,121 @@
1
+ module Recaptcha
2
+ module ClientHelper
3
+ # Your public API can be specified in the +options+ hash or preferably
4
+ # using the Configuration.
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)
11
+ # Default options
12
+ key = options[:public_key] ||= Recaptcha.configuration.public_key
13
+ raise RecaptchaError, "No public key specified." unless key
14
+ error = options[:error] ||= ((defined? flash) ? flash[:recaptcha_error] : "")
15
+ uri = Recaptcha.configuration.api_server_url(options[:ssl])
16
+ lang = options[:display] && options[:display][:lang] ? options[:display][:lang].to_sym : ""
17
+ html = ""
18
+ if options[:display]
19
+ html << %{<script type="text/javascript">\n}
20
+ html << %{ var RecaptchaOptions = #{hash_to_json(options[:display])};\n}
21
+ html << %{</script>\n}
22
+ end
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
32
+ html << <<-EOS
33
+ <script type="text/javascript">
34
+ var rc_script_tag = document.createElement('script'),
35
+ rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("#{widget}")#{',RecaptchaOptions' if options[:display]});}
36
+ rc_script_tag.src = "#{uri}/js/recaptcha_ajax.js";
37
+ rc_script_tag.type = 'text/javascript';
38
+ rc_script_tag.onload = function(){rc_init_func.call();};
39
+ rc_script_tag.onreadystatechange = function(){
40
+ if (rc_script_tag.readyState == 'loaded' || rc_script_tag.readyState == 'complete') {rc_init_func.call();}
41
+ };
42
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(rc_script_tag);
43
+ </script>
44
+ EOS
45
+ else
46
+ html << %{<script type="text/javascript" src="#{uri}/challenge?k=#{key}}
47
+ html << %{#{error ? "&amp;error=#{CGI::escape(error)}" : ""}}
48
+ html << %{#{lang ? "&amp;lang=#{lang}" : ""}"></script>\n}
49
+ unless options[:noscript] == false
50
+ html << %{<noscript>\n }
51
+ html << %{<iframe src="#{uri}/noscript?k=#{key}" }
52
+ html << %{height="#{options[:iframe_height] ||= 300}" }
53
+ html << %{width="#{options[:iframe_width] ||= 500}" }
54
+ html << %{style="border:none;"></iframe><br/>\n }
55
+ html << %{<textarea name="recaptcha_challenge_field" }
56
+ html << %{rows="#{options[:textarea_rows] ||= 3}" }
57
+ html << %{cols="#{options[:textarea_cols] ||= 40}"></textarea>\n }
58
+ html << %{<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>}
59
+ html << %{</noscript>\n}
60
+ end
61
+ end
62
+ return (html.respond_to?(:html_safe) && html.html_safe) || html
63
+ end
64
+
65
+ def v2_tags(options)
66
+ key = options[:public_key] ||= Recaptcha.configuration.public_key
67
+ raise RecaptchaError, "No public key specified." unless key
68
+ error = options[:error] ||= ((defined? flash) ? flash[:recaptcha_error] : "")
69
+ uri = Recaptcha.configuration.api_server_url(options[:ssl])
70
+ uri += "?hl=#{options[:hl]}" unless options[:hl].blank?
71
+
72
+ v2_options = options.slice(:theme, :type, :callback).map {|k,v| %{data-#{k}="#{v}"} }.join(" ")
73
+
74
+ html = ""
75
+ html << %{<script src="#{uri}" async defer></script>\n}
76
+ html << %{<div class="g-recaptcha" data-sitekey="#{key}" #{v2_options}></div>\n}
77
+
78
+ unless options[:noscript] == false
79
+ fallback_uri = "#{uri.chomp('.js')}/fallback?k=#{key}"
80
+ html << %{<noscript>}
81
+ html << %{<div style="width: 302px; height: 352px;">}
82
+ html << %{ <div style="width: 302px; height: 352px; position: relative;">}
83
+ html << %{ <div style="width: 302px; height: 352px; position: absolute;">}
84
+ html << %{ <iframe src="#{fallback_uri}"}
85
+ html << %{ frameborder="0" scrolling="no"}
86
+ html << %{ style="width: 302px; height:352px; border-style: none;">}
87
+ html << %{ </iframe>}
88
+ html << %{ </div>}
89
+ html << %{ <div style="width: 250px; height: 80px; position: absolute; border-style: none; }
90
+ html << %{ bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">}
91
+ html << %{ <textarea id="g-recaptcha-response" name="g-recaptcha-response" }
92
+ html << %{ class="g-recaptcha-response" }
93
+ html << %{ style="width: 250px; height: 80px; border: 1px solid #c1c1c1; }
94
+ html << %{ margin: 0px; padding: 0px; resize: none;" value=""> }
95
+ html << %{ </textarea>}
96
+ html << %{ </div>}
97
+ html << %{ </div>}
98
+ html << %{ </div>}
99
+ html << %{</noscript>}
100
+ end
101
+
102
+ return (html.respond_to?(:html_safe) && html.html_safe) || html
103
+ end
104
+
105
+ private
106
+
107
+ def hash_to_json(hash)
108
+ result = "{"
109
+ result << hash.map do |k, v|
110
+ if v.is_a?(Hash)
111
+ "\"#{k}\": #{hash_to_json(v)}"
112
+ elsif ! v.is_a?(String) || k.to_s == "callback"
113
+ "\"#{k}\": #{v}"
114
+ else
115
+ "\"#{k}\": \"#{v}\""
116
+ end
117
+ end.join(", ")
118
+ result << "}"
119
+ end
120
+ end # ClientHelper
121
+ end # Recaptcha
@@ -0,0 +1,74 @@
1
+ module Recaptcha
2
+ # This class enables detailed configuration of the recaptcha services.
3
+ #
4
+ # By calling
5
+ #
6
+ # Recaptcha.configuration # => instance of Recaptcha::Configuration
7
+ #
8
+ # or
9
+ # Recaptcha.configure do |config|
10
+ # config # => instance of Recaptcha::Configuration
11
+ # end
12
+ #
13
+ # you are able to perform configuration updates.
14
+ #
15
+ # Your are able to customize all attributes listed below. All values have
16
+ # sensitive default and will very likely not need to be changed.
17
+ #
18
+ # Please note that the public and private key for the reCAPTCHA API Access
19
+ # have no useful default value. The keys may be set via the Shell enviroment
20
+ # or using this configuration. Settings within this configuration always take
21
+ # precedence.
22
+ #
23
+ # Setting the keys with this Configuration
24
+ #
25
+ # Recaptcha.configure do |config|
26
+ # config.public_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
27
+ # config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
28
+ # end
29
+ #
30
+ class Configuration
31
+ attr_accessor :api_version,
32
+ :skip_verify_env,
33
+ :private_key,
34
+ :public_key,
35
+ :proxy,
36
+ :handle_timeouts_gracefully,
37
+ :use_ssl_by_default
38
+
39
+ def initialize #:nodoc:
40
+ @api_version = RECAPTCHA_API_VERSION
41
+ @skip_verify_env = SKIP_VERIFY_ENV
42
+ @handle_timeouts_gracefully = HANDLE_TIMEOUTS_GRACEFULLY
43
+ @use_ssl_by_default = USE_SSL_BY_DEFAULT
44
+
45
+ @private_key = ENV['RECAPTCHA_PRIVATE_KEY']
46
+ @public_key = ENV['RECAPTCHA_PUBLIC_KEY']
47
+ end
48
+
49
+ def api_server_url(ssl = nil) #:nodoc:
50
+ ssl = use_ssl_by_default if ssl.nil?
51
+ ssl ? ssl_api_server_url : nonssl_api_server_url
52
+ end
53
+
54
+ def nonssl_api_server_url
55
+ CONFIG[@api_version]['server_url']
56
+ end
57
+
58
+ def ssl_api_server_url
59
+ CONFIG[@api_version]['secure_server_url']
60
+ end
61
+
62
+ def verify_url
63
+ CONFIG[@api_version]['verify_url']
64
+ end
65
+
66
+ def v1?
67
+ @api_version == 'v1'
68
+ end
69
+
70
+ def v2?
71
+ @api_version == 'v2'
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+ require 'recaptcha'
2
+
3
+ Merb::GlobalHelpers.send(:include, Recaptcha::ClientHelper)
4
+ Merb::Controller.send(:include, Recaptcha::Verify)
@@ -0,0 +1,5 @@
1
+ require 'net/http'
2
+ require 'recaptcha'
3
+
4
+ ActionView::Base.send(:include, Recaptcha::ClientHelper)
5
+ ActionController::Base.send(:include, Recaptcha::Verify)
@@ -0,0 +1,15 @@
1
+ require 'net/http'
2
+ require 'recaptcha'
3
+ module Rails
4
+ module Recaptcha
5
+ class Railtie < Rails::Railtie
6
+ initializer "setup config" do
7
+ begin
8
+ ActionView::Base.send(:include, ::Recaptcha::ClientHelper)
9
+ ActionController::Base.send(:include, ::Recaptcha::Verify)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,117 @@
1
+ require "uri"
2
+ module Recaptcha
3
+ module Verify
4
+ # Your private API can be specified in the +options+ hash or preferably
5
+ # using the Configuration.
6
+ def verify_recaptcha(options = {})
7
+ options = {:model => options} unless options.is_a? Hash
8
+
9
+ env_options = options[:env] || ENV['RAILS_ENV']
10
+ return true if Recaptcha.configuration.skip_verify_env.include? env_options
11
+ model = options[:model]
12
+ attribute = options[:attribute] || :base
13
+ private_key = options[:private_key] || Recaptcha.configuration.private_key
14
+ raise RecaptchaError, "No private key specified." unless private_key
15
+
16
+ begin
17
+ recaptcha = nil
18
+ if(Recaptcha.configuration.proxy)
19
+ proxy_server = URI.parse(Recaptcha.configuration.proxy)
20
+ http = Net::HTTP::Proxy(proxy_server.host, proxy_server.port, proxy_server.user, proxy_server.password)
21
+ else
22
+ http = Net::HTTP
23
+ end
24
+
25
+ # env['REMOTE_ADDR'] to retrieve IP for Grape API
26
+ remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
27
+ if Recaptcha.configuration.v1?
28
+ verify_hash = {
29
+ "privatekey" => private_key,
30
+ "remoteip" => remote_ip,
31
+ "challenge" => params[:recaptcha_challenge_field],
32
+ "response" => params[:recaptcha_response_field]
33
+ }
34
+ Timeout::timeout(options[:timeout] || 3) do
35
+ recaptcha = http.post_form(URI.parse(Recaptcha.configuration.verify_url), verify_hash)
36
+ end
37
+ answer, error = recaptcha.body.split.map { |s| s.chomp }
38
+ end
39
+
40
+ if Recaptcha.configuration.v2?
41
+ verify_hash = {
42
+ "secret" => private_key,
43
+ "remoteip" => remote_ip,
44
+ "response" => params['g-recaptcha-response']
45
+ }
46
+
47
+ Timeout::timeout(options[:timeout] || 3) do
48
+ uri = URI.parse(Recaptcha.configuration.verify_url + '?' + verify_hash.to_query)
49
+ http_instance = http.new(uri.host, uri.port)
50
+ if uri.port==443
51
+ http_instance.use_ssl =
52
+ http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
53
+ end
54
+ request = Net::HTTP::Get.new(uri.request_uri)
55
+ recaptcha = http_instance.request(request)
56
+ end
57
+ #answer, error = JSON.parse(recaptcha.body).values
58
+ answer = JSON.parse(recaptcha.body)['success']
59
+ if answer.to_s != 'true'
60
+ error = true
61
+ else
62
+ error = false
63
+ end
64
+ end
65
+
66
+ unless answer.to_s == 'true'
67
+ error = 'verification_failed' if error && Recaptcha.configuration.v2?
68
+ if request_in_html_format?
69
+ flash[:recaptcha_error] = if defined?(I18n)
70
+ I18n.translate("recaptcha.errors.#{error}", {:default => error})
71
+ else
72
+ error
73
+ end
74
+ end
75
+
76
+ if model
77
+ message = "Word verification response is incorrect, please try again."
78
+ message = I18n.translate('recaptcha.errors.verification_failed', {:default => message}) if defined?(I18n)
79
+ model.errors.add attribute, options[:message] || message
80
+ end
81
+ return false
82
+ else
83
+ flash.delete(:recaptcha_error) if request_in_html_format?
84
+ return true
85
+ end
86
+ rescue Timeout::Error
87
+ if Recaptcha.configuration.handle_timeouts_gracefully
88
+ if request_in_html_format?
89
+ flash[:recaptcha_error] = if defined?(I18n)
90
+ I18n.translate('recaptcha.errors.recaptcha_unreachable', {:default => 'Recaptcha unreachable.'})
91
+ else
92
+ 'Recaptcha unreachable.'
93
+ end
94
+ end
95
+
96
+ if model
97
+ message = "Oops, we failed to validate your word verification response. Please try again."
98
+ message = I18n.translate('recaptcha.errors.recaptcha_unreachable', :default => message) if defined?(I18n)
99
+ model.errors.add attribute, options[:message] || message
100
+ end
101
+ return false
102
+ else
103
+ raise RecaptchaError, "Recaptcha unreachable."
104
+ end
105
+ rescue Exception => e
106
+ raise RecaptchaError, e.message, e.backtrace
107
+ end
108
+ end # verify_recaptcha
109
+
110
+ def request_in_html_format?
111
+ request.respond_to?(:format) && request.format == :html && respond_to?(:flash)
112
+ end
113
+ def verify_recaptcha!(options = {})
114
+ verify_recaptcha(options) or raise VerifyError
115
+ end #verify_recaptcha!
116
+ end # Verify
117
+ end # Recaptcha
@@ -0,0 +1,3 @@
1
+ module Recaptcha
2
+ VERSION = "0.4.1"
3
+ end
data/recaptcha.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "recaptcha/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cure-recaptcha"
7
+ s.version = Recaptcha::VERSION
8
+ s.authors = ["Ward Vandewege"]
9
+ s.email = ["ward@curoverse.com"]
10
+ s.homepage = "http://github.com/cure/recaptcha"
11
+ s.summary = %q{Helpers for the reCAPTCHA API}
12
+ s.description = %q{This plugin adds helpers for the reCAPTCHA API}
13
+
14
+ s.rubyforge_project = "cure-recaptcha"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_development_dependency "mocha"
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "activesupport"
23
+ s.add_development_dependency "i18n"
24
+ s.add_development_dependency "minitest", "~> 5.0"
25
+ s.add_development_dependency "pry-byebug"
26
+ end
@@ -0,0 +1,44 @@
1
+ require 'minitest/autorun'
2
+ require 'cgi'
3
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha'
4
+
5
+ class RecaptchaConfigurationTest < Minitest::Test
6
+ include Recaptcha
7
+ include Recaptcha::ClientHelper
8
+ include Recaptcha::Verify
9
+
10
+ attr_accessor :session
11
+
12
+ def setup
13
+ @session = {}
14
+ @nonssl_api_server_url = Regexp.new(Regexp.quote(Recaptcha.configuration.nonssl_api_server_url) + '(.*)')
15
+ @ssl_api_server_url = Regexp.new(Regexp.quote(Recaptcha.configuration.ssl_api_server_url) + '(.*)')
16
+ Recaptcha.configure do |config|
17
+ config.public_key = '0000000000000000000000000000000000000000'
18
+ config.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
19
+ config.api_version = 'v2'
20
+ end
21
+ end
22
+
23
+ def test_recaptcha_api_version_default
24
+ assert_equal(Recaptcha.configuration.api_version, Recaptcha::RECAPTCHA_API_VERSION)
25
+ end
26
+
27
+ def test_v2_with_v2_api?
28
+ assert Recaptcha.configuration.v2?
29
+ refute Recaptcha.configuration.v1?
30
+ end
31
+
32
+ def test_different_configuration_within_with_configuration_block
33
+ key = Recaptcha.with_configuration(:public_key => '12345') do
34
+ Recaptcha.configuration.public_key
35
+ end
36
+
37
+ assert_equal('12345', key)
38
+ end
39
+
40
+ def test_reset_configuration_after_with_configuration_block
41
+ Recaptcha.with_configuration(:public_key => '12345')
42
+ assert_equal('0000000000000000000000000000000000000000', Recaptcha.configuration.public_key)
43
+ end
44
+ end
@@ -0,0 +1,54 @@
1
+ require 'minitest/autorun'
2
+ require 'cgi'
3
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha'
4
+
5
+ class RecaptchaClientHelperTest < Minitest::Test
6
+ include Recaptcha
7
+ include Recaptcha::ClientHelper
8
+ include Recaptcha::Verify
9
+
10
+ attr_accessor :session
11
+
12
+ def setup
13
+ @session = {}
14
+ @nonssl_api_server_url = Regexp.new(Regexp.quote(Recaptcha.configuration.nonssl_api_server_url) + '(.*)')
15
+ @ssl_api_server_url = Regexp.new(Regexp.quote(Recaptcha.configuration.ssl_api_server_url) + '(.*)')
16
+ Recaptcha.configure do |config|
17
+ config.public_key = '0000000000000000000000000000000000000000'
18
+ config.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
19
+ end
20
+ end
21
+
22
+ def test_recaptcha_tags_v2
23
+ Recaptcha.configuration.api_version = 'v2'
24
+ # match a v2 only tag
25
+ assert_match /data-sitekey/, recaptcha_tags
26
+ # refute a v1 only tag
27
+ refute_match /\/challenge\?/, recaptcha_tags
28
+ end
29
+
30
+ def test_ssl_by_default
31
+ Recaptcha.configuration.use_ssl_by_default = true
32
+ assert_match @ssl_api_server_url, recaptcha_tags
33
+ end
34
+
35
+ def test_relative_protocol_by_default_without_ssl
36
+ Recaptcha.configuration.use_ssl_by_default = false
37
+ assert_match @nonssl_api_server_url, recaptcha_tags(:ssl => false)
38
+ end
39
+
40
+ def test_recaptcha_tags_with_ssl
41
+ assert_match @ssl_api_server_url, recaptcha_tags(:ssl => true)
42
+ end
43
+
44
+ def test_recaptcha_tags_without_noscript
45
+ refute_match /noscript/, recaptcha_tags(:noscript => false)
46
+ end
47
+
48
+ def test_should_raise_exception_without_public_key
49
+ assert_raises RecaptchaError do
50
+ Recaptcha.configuration.public_key = nil
51
+ recaptcha_tags
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ require 'minitest/autorun'
2
+ require 'cgi'
3
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha'
4
+
5
+ class RecaptchaV1Test < Minitest::Test
6
+ include Recaptcha
7
+ include Recaptcha::ClientHelper
8
+ include Recaptcha::Verify
9
+
10
+ attr_accessor :session
11
+
12
+ def setup
13
+ @session = {}
14
+ @nonssl_api_server_url = Regexp.new(Regexp.quote(Recaptcha.configuration.nonssl_api_server_url) + '(.*)')
15
+ @ssl_api_server_url = Regexp.new(Regexp.quote(Recaptcha.configuration.ssl_api_server_url) + '(.*)')
16
+ Recaptcha.configure do |config|
17
+ config.public_key = '0000000000000000000000000000000000000000'
18
+ config.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
19
+ config.api_version = 'v1'
20
+ end
21
+ end
22
+
23
+ def test_v1_with_v1_api?
24
+ assert Recaptcha.configuration.v1?
25
+ refute Recaptcha.configuration.v2?
26
+ end
27
+
28
+ def test_recaptcah_tags_v1
29
+ Recaptcha.configuration.api_version = 'v1'
30
+ # match a v1 only tag
31
+ assert_match /\/challenge\?/, recaptcha_tags
32
+ # refute a v2 only tag
33
+ refute_match /data-sitekey/, recaptcha_tags
34
+ end
35
+ end
@@ -0,0 +1,191 @@
1
+ # coding: utf-8
2
+
3
+ require 'minitest/autorun'
4
+ require 'rubygems'
5
+ require 'active_support'
6
+ require 'active_support/core_ext/string'
7
+ require 'mocha/setup'
8
+ require 'i18n'
9
+ require 'net/http'
10
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha'
11
+
12
+ class RecaptchaVerifyTest < Minitest::Test
13
+ def setup
14
+ Recaptcha.configuration.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
15
+ @controller = TestController.new
16
+ @controller.request = stub(:remote_ip => "1.1.1.1", format: :html)
17
+
18
+ @expected_post_data = {}
19
+ @expected_post_data["remoteip"] = @controller.request.remote_ip
20
+ @expected_post_data["response"] = "response"
21
+
22
+ if Recaptcha.configuration.v1?
23
+ @controller.params = {:recaptcha_challenge_field => "challenge", :recaptcha_response_field => "response"}
24
+ @expected_post_data["privatekey"] = Recaptcha.configuration.private_key
25
+ @expected_post_data["challenge"] = "challenge"
26
+ end
27
+
28
+ if Recaptcha.configuration.v2?
29
+ @controller.params = {:recaptcha_response_field => "response"}
30
+ @expected_post_data["secret"] = Recaptcha.configuration.private_key
31
+ end
32
+
33
+ @expected_uri = URI.parse(Recaptcha.configuration.verify_url)
34
+ end
35
+
36
+ def test_should_raise_exception_when_calling_bang_method
37
+ @controller.expects(:verify_recaptcha).returns(false)
38
+
39
+ assert_raises Recaptcha::VerifyError do
40
+ @controller.verify_recaptcha!
41
+ end
42
+ end
43
+
44
+ def test_should_return_whatever_verify_method_returns_when_using_bang_method
45
+ @controller.expects(:verify_recaptcha).returns(:foo)
46
+
47
+ assert_equal :foo, @controller.verify_recaptcha!
48
+ end
49
+
50
+ def test_should_raise_exception_without_private_key
51
+ skip
52
+ assert_raises Recaptcha::RecaptchaError do
53
+ Recaptcha.configuration.private_key = nil
54
+ @controller.verify_recaptcha
55
+ end
56
+ end
57
+
58
+ def test_should_return_false_when_key_is_invalid
59
+ skip
60
+ expect_http_post(response_with_body("false\ninvalid-site-private-key"))
61
+
62
+ assert !@controller.verify_recaptcha
63
+ assert_equal "invalid-site-private-key", @controller.flash[:recaptcha_error]
64
+ end
65
+
66
+ def test_returns_true_on_success
67
+ skip
68
+ @controller.flash[:recaptcha_error] = "previous error that should be cleared"
69
+ expect_http_post(response_with_body("true\n"))
70
+
71
+ assert @controller.verify_recaptcha
72
+ assert_nil @controller.flash[:recaptcha_error]
73
+ end
74
+
75
+ def test_errors_should_be_added_to_model
76
+ skip
77
+ expect_http_post(response_with_body("false\nbad-news"))
78
+
79
+ errors = mock
80
+ errors.expects(:add).with(:base, "Word verification response is incorrect, please try again.")
81
+ model = mock(:errors => errors)
82
+
83
+ assert !@controller.verify_recaptcha(:model => model)
84
+ assert_equal "bad-news", @controller.flash[:recaptcha_error]
85
+ end
86
+
87
+ def test_returns_true_on_success_with_optional_key
88
+ skip
89
+ @controller.flash[:recaptcha_error] = "previous error that should be cleared"
90
+ # reset private key
91
+ @expected_post_data["privatekey"] = 'ADIFFERENTPRIVATEKEYXXXXXXXXXXXXXX'
92
+ expect_http_post(response_with_body("true\n"))
93
+
94
+ assert @controller.verify_recaptcha(:private_key => 'ADIFFERENTPRIVATEKEYXXXXXXXXXXXXXX')
95
+ assert_nil @controller.flash[:recaptcha_error]
96
+ end
97
+
98
+ def test_timeout
99
+ skip
100
+ expect_http_post(Timeout::Error, :exception => true)
101
+ assert !@controller.verify_recaptcha()
102
+ assert_equal "Recaptcha unreachable.", @controller.flash[:recaptcha_error]
103
+ end
104
+
105
+ def test_timeout_when_handle_timeouts_gracefully_disabled
106
+ skip
107
+ Recaptcha.with_configuration(:handle_timeouts_gracefully => false) do
108
+ expect_http_post(Timeout::Error, :exception => true)
109
+ assert_raises Recaptcha::RecaptchaError, "Recaptcha unreachable." do
110
+ assert @controller.verify_recaptcha()
111
+ end
112
+ assert_nil @controller.flash[:recaptcha_error]
113
+ end
114
+ end
115
+
116
+ def test_message_should_use_i18n
117
+ skip
118
+ I18n.locale = :de
119
+ verification_failed_translated = "Sicherheitscode konnte nicht verifiziert werden."
120
+ verification_failed_default = "Word verification response is incorrect, please try again."
121
+ recaptcha_unreachable_translated = "Netzwerkfehler, bitte versuchen Sie es später erneut."
122
+ recaptcha_unreachable_default = "Oops, we failed to validate your word verification response. Please try again."
123
+
124
+ I18n.expects(:translate).with('recaptcha.errors.bad-news', {:default => 'bad-news'})
125
+ I18n.expects(:translate).with('recaptcha.errors.recaptcha_unreachable', {:default => 'Recaptcha unreachable.'})
126
+
127
+ I18n.expects(:translate).with('recaptcha.errors.verification_failed', :default => verification_failed_default).returns(verification_failed_translated)
128
+ I18n.expects(:translate).with('recaptcha.errors.recaptcha_unreachable', :default => recaptcha_unreachable_default).returns(recaptcha_unreachable_translated)
129
+
130
+ errors = mock
131
+ errors.expects(:add).with(:base, verification_failed_translated)
132
+ errors.expects(:add).with(:base, recaptcha_unreachable_translated)
133
+ model = mock; model.stubs(:errors => errors)
134
+
135
+ expect_http_post(response_with_body("false\nbad-news"))
136
+ @controller.verify_recaptcha(:model => model)
137
+
138
+ expect_http_post(Timeout::Error, :exception => true)
139
+ @controller.verify_recaptcha(:model => model)
140
+
141
+ end
142
+
143
+ def test_it_translates_api_response_with_i18n
144
+ skip
145
+ api_error_translated = "Bad news, body :("
146
+ expect_http_post(response_with_body("false\nbad-news"))
147
+ I18n.expects(:translate).with('recaptcha.errors.bad-news', :default => 'bad-news').returns(api_error_translated)
148
+
149
+ assert !@controller.verify_recaptcha
150
+ assert_equal api_error_translated, @controller.flash[:recaptcha_error]
151
+ end
152
+
153
+ def test_it_fallback_to_api_response_if_i18n_translation_is_missing
154
+ skip
155
+ expect_http_post(response_with_body("false\nbad-news"))
156
+
157
+ assert !@controller.verify_recaptcha
158
+ assert_equal 'bad-news', @controller.flash[:recaptcha_error]
159
+ end
160
+
161
+ def test_not_flashing_error_if_request_format_not_in_html
162
+ skip
163
+ @controller.request = stub(:remote_ip => "1.1.1.1", format: :json)
164
+ expect_http_post(response_with_body("false\nbad-news"))
165
+ assert !@controller.verify_recaptcha
166
+ assert_nil @controller.flash[:recaptcha_error]
167
+ end
168
+
169
+ private
170
+
171
+ class TestController
172
+ include Recaptcha::Verify
173
+ attr_accessor :request, :params, :flash
174
+
175
+ def initialize
176
+ @flash = {}
177
+ end
178
+ end
179
+
180
+ def expect_http_post(response, options = {})
181
+ unless options[:exception]
182
+ Net::HTTP.expects(:post_form).with(@expected_uri, @expected_post_data).returns(response)
183
+ else
184
+ Net::HTTP.expects(:post_form).raises response
185
+ end
186
+ end
187
+
188
+ def response_with_body(body)
189
+ stub(:body => body)
190
+ end
191
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cure-recaptcha
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ platform: ruby
6
+ authors:
7
+ - Ward Vandewege
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mocha
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: i18n
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: This plugin adds helpers for the reCAPTCHA API
98
+ email:
99
+ - ward@curoverse.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - CHANGELOG
107
+ - Gemfile
108
+ - Gemfile.lock
109
+ - LICENSE
110
+ - README.rdoc
111
+ - Rakefile
112
+ - init.rb
113
+ - lib/recaptcha.rb
114
+ - lib/recaptcha/client_helper.rb
115
+ - lib/recaptcha/configuration.rb
116
+ - lib/recaptcha/merb.rb
117
+ - lib/recaptcha/rails.rb
118
+ - lib/recaptcha/railtie.rb
119
+ - lib/recaptcha/verify.rb
120
+ - lib/recaptcha/version.rb
121
+ - recaptcha.gemspec
122
+ - test/recaptcha_configuration_test.rb
123
+ - test/recaptcha_test.rb
124
+ - test/recaptcha_v1_test.rb
125
+ - test/verify_recaptcha_test.rb
126
+ homepage: http://github.com/cure/recaptcha
127
+ licenses: []
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project: cure-recaptcha
145
+ rubygems_version: 2.4.8
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Helpers for the reCAPTCHA API
149
+ test_files: []