human_detector 1.0.0.rc
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.
- data/MIT-LICENSE +20 -0
- data/README.md +2 -0
- data/lib/generators/human_detector/human_detector_generator.rb +22 -0
- data/lib/generators/human_detector/templates/create_human_detector_questions.rb +52 -0
- data/lib/human_detector/cipher.rb +33 -0
- data/lib/human_detector/question.rb +11 -0
- data/lib/human_detector/rails_ext/action_controller/spam_detected.rb +32 -0
- data/lib/human_detector/rails_ext/action_controller.rb +9 -0
- data/lib/human_detector/rails_ext/action_view/helper.rb +21 -0
- data/lib/human_detector/rails_ext/action_view.rb +9 -0
- data/lib/human_detector/renderers/default.rb +26 -0
- data/lib/human_detector/renderers.rb +9 -0
- data/lib/human_detector.rb +13 -0
- metadata +216 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Gnomeslab, Lda.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module HumanDetector
|
4
|
+
|
5
|
+
module Generators
|
6
|
+
|
7
|
+
class HumanDetectorGenerator < ActiveRecord::Generators::Base
|
8
|
+
desc "Generates the migration required to support HumanDetector Ruby Gem."
|
9
|
+
|
10
|
+
namespace "human_detector"
|
11
|
+
|
12
|
+
source_root File.expand_path('../templates', __FILE__)
|
13
|
+
|
14
|
+
def generate_migration
|
15
|
+
migration_template 'create_human_detector_questions.rb', 'db/migrate/create_human_detector_questions.rb'
|
16
|
+
end # generate_human_detector
|
17
|
+
|
18
|
+
end # HumanDetectorGenerator
|
19
|
+
|
20
|
+
end # Generators
|
21
|
+
|
22
|
+
end # HumanDetector
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'human_detector'
|
2
|
+
|
3
|
+
class CreateHumanDetectorQuestions < ActiveRecord::Migration
|
4
|
+
def self.up
|
5
|
+
create_table :human_detector_questions, :force => true do |t|
|
6
|
+
t.string :title, :null => false
|
7
|
+
t.string :answer, :null => false
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
5.times { |i| HumanDetector::Question.create(:title => "How much is #{i} + #{i * 2}?", :answer => "#{i + i * 2}") }
|
12
|
+
5.times { |i| HumanDetector::Question.create(:title => "Complete the sequence: #{i}, #{i+1}, #{i+2}, __ ?",
|
13
|
+
:answer => "#{i+3}") }
|
14
|
+
5.times { |i| HumanDetector::Question.create(:title => "Complete the sequence: #{i}, __, #{i+2}, #{i+3} ?",
|
15
|
+
:answer => "#{i+1}") }
|
16
|
+
{ 0 => "zero",
|
17
|
+
1 => "one",
|
18
|
+
2 => "two",
|
19
|
+
3 => "three",
|
20
|
+
4 => "four",
|
21
|
+
5 => "five",
|
22
|
+
6 => "six",
|
23
|
+
7 => "seven",
|
24
|
+
8 => "eight",
|
25
|
+
9 => "nine",
|
26
|
+
10 => "ten" }.each { |k,v| HumanDetector::Question.create(:title=> "#{k} in English (e.g. #{v})?", :answer=> v) }
|
27
|
+
|
28
|
+
10.times do |i|
|
29
|
+
HumanDetector::Question.create(:title => "Which year was in 2010 minus #{i + 1} (e.g. #{2010 - (i + 1)})?",
|
30
|
+
:answer => (2010 - (i + 1)).to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
100.times do |i|
|
34
|
+
HumanDetector::Question.create(:title => "Is #{i} and even number (e.g. #{i.even? ? 'yes' : 'no'})?",
|
35
|
+
:answer => i.even? ? "yes" : "no")
|
36
|
+
HumanDetector::Question.create(:title => "Is #{i} an odd number (e.g. #{i.even? ? 'no' : 'yes'})?",
|
37
|
+
:answer => i.even? ? 'no' : 'yes')
|
38
|
+
end
|
39
|
+
|
40
|
+
HumanDetector::Question.create(:title => 'Are you a bot (e.g. yes)?', :answer => 'no')
|
41
|
+
HumanDetector::Question.create(:title => 'Are you a sure? (e.g. no)?', :answer => 'yes')
|
42
|
+
HumanDetector::Question.create(:title => 'Are you alive? (e.g. no)?', :answer => 'yes')
|
43
|
+
HumanDetector::Question.create(:title => 'Are you human? (e.g. no)?', :answer => 'yes')
|
44
|
+
HumanDetector::Question.create(:title => 'Do you live on earth (e.g. yes)?', :answer => 'yes')
|
45
|
+
HumanDetector::Question.create(:title => 'Are you spamming (e.g. no)?', :answer => 'no')
|
46
|
+
HumanDetector::Question.create(:title => 'Are you a spam bot (e.g. yes)?', :answer => 'no')
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.down
|
50
|
+
drop_table :human_detector_questions
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module HumanDetector
|
4
|
+
|
5
|
+
class Cipher
|
6
|
+
def self.encrypt(text)
|
7
|
+
aes_wrapper :encrypt, text
|
8
|
+
end # encrypt
|
9
|
+
|
10
|
+
def self.decrypt(text)
|
11
|
+
aes_wrapper :decrypt, text
|
12
|
+
end # decrypt
|
13
|
+
|
14
|
+
private
|
15
|
+
def self.aes_wrapper(direction, text)
|
16
|
+
return nil unless text
|
17
|
+
|
18
|
+
aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(direction)
|
19
|
+
aes.key = '484758fc806d09ad70af78e51cda016f4072a20f7e48ee9ab898dd0466b11b4f'
|
20
|
+
|
21
|
+
if direction == :encrypt
|
22
|
+
aes.iv = iv = aes.random_iv
|
23
|
+
URI.escape(ActiveSupport::Base64.encode64(iv + (aes.update(text) + aes.final)))
|
24
|
+
else
|
25
|
+
raw = ActiveSupport::Base64.decode64 URI.unescape(text)
|
26
|
+
aes.iv = raw.slice! 0, 16
|
27
|
+
aes.update(raw) + aes.final
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end # Cipher
|
32
|
+
|
33
|
+
end # HumanDetector
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module HumanDetector
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
|
5
|
+
module SpamDetected
|
6
|
+
|
7
|
+
HUMAN_DETECTOR_DEFAULT_OPTIONS = { :flash => { :error => 'Invalid captcha answer' },
|
8
|
+
:input_name => 'human_detector_answer' }
|
9
|
+
|
10
|
+
private
|
11
|
+
def human_detected?(options = {})
|
12
|
+
!spam_detected?(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def spam_detected?(options = {})
|
16
|
+
options.reverse_merge! HUMAN_DETECTOR_DEFAULT_OPTIONS
|
17
|
+
|
18
|
+
return false if params.include?('human_detector_question_id') && params[options[:input_name]] ==
|
19
|
+
Question.find_by_id(HumanDetector::Cipher.decrypt(params['human_detector_question_id']).
|
20
|
+
gsub(/(_\S+)/, '')).try(:answer)
|
21
|
+
|
22
|
+
options[:flash] = { :error => options[:flash] } if options[:flash].is_a?(String)
|
23
|
+
flash.merge! options[:flash]
|
24
|
+
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Filter
|
29
|
+
|
30
|
+
end # ActionController
|
31
|
+
|
32
|
+
end # HumanDetector
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module HumanDetector
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
|
5
|
+
module Helper
|
6
|
+
|
7
|
+
def human_detector_tag(options = {})
|
8
|
+
options.reverse_merge! :renderer => HumanDetector::Renderers::Default
|
9
|
+
|
10
|
+
options[:renderer].send(:render,
|
11
|
+
self,
|
12
|
+
Cipher.encrypt("#{(question = Question.random).id}_#{controller.session[:session_id]}"),
|
13
|
+
question.title,
|
14
|
+
options.except(:renderer))
|
15
|
+
end # human_detector_tag
|
16
|
+
|
17
|
+
end # Helper
|
18
|
+
|
19
|
+
end # ActionView
|
20
|
+
|
21
|
+
end # HumanDetector
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module HumanDetector
|
2
|
+
|
3
|
+
module Renderers
|
4
|
+
|
5
|
+
class Default
|
6
|
+
|
7
|
+
def self.render(view, id, title, options = {})
|
8
|
+
options.reverse_merge!({ :label_css => 'human_detector_question',
|
9
|
+
:input_css => 'human_detector_answer',
|
10
|
+
:input_name => 'human_detector_answer',
|
11
|
+
:label_prefix => '',
|
12
|
+
:label_sufix => '',
|
13
|
+
:input_text => '' })
|
14
|
+
|
15
|
+
output = view.label_tag(options[:input_name],
|
16
|
+
(options[:label_prefix].html_safe << title << options[:label_sufix].html_safe),
|
17
|
+
:class => options[:label_css])
|
18
|
+
output << view.text_field_tag(options[:input_name], options[:input_text], :class => options[:input_css])
|
19
|
+
output.tap { |s| s << view.hidden_field_tag('human_detector_question_id', id) }
|
20
|
+
end # render
|
21
|
+
|
22
|
+
end # Default
|
23
|
+
|
24
|
+
end # Renderers
|
25
|
+
|
26
|
+
end # HumanDetector
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module HumanDetector
|
2
|
+
|
3
|
+
autoload :ActionView, 'human_detector/rails_ext/action_view'
|
4
|
+
autoload :ActionController, 'human_detector/rails_ext/action_controller'
|
5
|
+
autoload :Renderers, 'human_detector/renderers'
|
6
|
+
autoload :Question, 'human_detector/question'
|
7
|
+
autoload :Cipher, 'human_detector/cipher'
|
8
|
+
|
9
|
+
end # HumanDetector
|
10
|
+
|
11
|
+
# Rails extentions
|
12
|
+
ActionView::Base.send :include, HumanDetector::ActionView::Helper
|
13
|
+
ActionController::Base.send :include, HumanDetector::ActionController::SpamDetected
|
metadata
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: human_detector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- rc
|
10
|
+
version: 1.0.0.rc
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Gnomeslab, Lda.
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-18 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: actionpack
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 3
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
version: 3.0.0
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activerecord
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
segments:
|
45
|
+
- 3
|
46
|
+
- 0
|
47
|
+
- 0
|
48
|
+
version: 3.0.0
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: activerecord_random
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 1
|
61
|
+
- 0
|
62
|
+
- 0
|
63
|
+
version: 1.0.0
|
64
|
+
type: :runtime
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: fuubar
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
- 0
|
77
|
+
version: "0.0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: sqlite3-ruby
|
82
|
+
prerelease: false
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id005
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: ruby-debug19
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
type: :development
|
105
|
+
version_requirements: *id006
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
name: rspec
|
108
|
+
prerelease: false
|
109
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
type: :development
|
118
|
+
version_requirements: *id007
|
119
|
+
- !ruby/object:Gem::Dependency
|
120
|
+
name: nokogiri
|
121
|
+
prerelease: false
|
122
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
type: :development
|
131
|
+
version_requirements: *id008
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: factory_girl_rails
|
134
|
+
prerelease: false
|
135
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
version: "0"
|
143
|
+
type: :development
|
144
|
+
version_requirements: *id009
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: database_cleaner
|
147
|
+
prerelease: false
|
148
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
version: "0"
|
156
|
+
type: :development
|
157
|
+
version_requirements: *id010
|
158
|
+
description: HumanDetector is a friendly logic captcha for Rails the doesn't require any external system dependency (e.g. external captcha API connections). It uses a simple question that any 7 or more years old human can understand, but that a spambot will not be able to answer. This Gem supports Ruby v1.9.2 and Rails 3.0.x.
|
159
|
+
email:
|
160
|
+
- mail@gnomeslab.com
|
161
|
+
executables: []
|
162
|
+
|
163
|
+
extensions: []
|
164
|
+
|
165
|
+
extra_rdoc_files: []
|
166
|
+
|
167
|
+
files:
|
168
|
+
- lib/generators/human_detector/human_detector_generator.rb
|
169
|
+
- lib/generators/human_detector/templates/create_human_detector_questions.rb
|
170
|
+
- lib/human_detector/cipher.rb
|
171
|
+
- lib/human_detector/question.rb
|
172
|
+
- lib/human_detector/rails_ext/action_controller/spam_detected.rb
|
173
|
+
- lib/human_detector/rails_ext/action_controller.rb
|
174
|
+
- lib/human_detector/rails_ext/action_view/helper.rb
|
175
|
+
- lib/human_detector/rails_ext/action_view.rb
|
176
|
+
- lib/human_detector/renderers/default.rb
|
177
|
+
- lib/human_detector/renderers.rb
|
178
|
+
- lib/human_detector.rb
|
179
|
+
- MIT-LICENSE
|
180
|
+
- README.md
|
181
|
+
has_rdoc: true
|
182
|
+
homepage: http://gnomeslab.com/
|
183
|
+
licenses: []
|
184
|
+
|
185
|
+
post_install_message:
|
186
|
+
rdoc_options: []
|
187
|
+
|
188
|
+
require_paths:
|
189
|
+
- lib
|
190
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
191
|
+
none: false
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
segments:
|
196
|
+
- 0
|
197
|
+
version: "0"
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
|
+
none: false
|
200
|
+
requirements:
|
201
|
+
- - ">="
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
segments:
|
204
|
+
- 1
|
205
|
+
- 3
|
206
|
+
- 7
|
207
|
+
version: 1.3.7
|
208
|
+
requirements: []
|
209
|
+
|
210
|
+
rubyforge_project:
|
211
|
+
rubygems_version: 1.3.7
|
212
|
+
signing_key:
|
213
|
+
specification_version: 3
|
214
|
+
summary: A logic user friendly captcha for Rails (e.g. How much is 5 + 3?)
|
215
|
+
test_files: []
|
216
|
+
|