Commit 920f13a937341eb52a0b26d4924cf399fc994b52
1 parent
05dbd36f
Exists in
master
and in
29 other branches
Force minimum signup time to block bots. Also add captcha for fast submitters
ActionItem2556
Showing
5 changed files
with
88 additions
and
15 deletions
Show diff stats
app/controllers/public/account_controller.rb
| ... | ... | @@ -56,8 +56,8 @@ class AccountController < ApplicationController |
| 56 | 56 | end |
| 57 | 57 | |
| 58 | 58 | def signup_time |
| 59 | - set_signup_time_for_now | |
| 60 | - render :text => {:ok=>true}.to_json | |
| 59 | + key = set_signup_start_time_for_now | |
| 60 | + render :text => { :ok=>true, :key=>key }.to_json | |
| 61 | 61 | end |
| 62 | 62 | |
| 63 | 63 | # action to register an user to the application |
| ... | ... | @@ -83,12 +83,12 @@ class AccountController < ApplicationController |
| 83 | 83 | @person.environment = @user.environment |
| 84 | 84 | if request.post? |
| 85 | 85 | if may_be_a_bot |
| 86 | - set_signup_time_for_now | |
| 86 | + set_signup_start_time_for_now | |
| 87 | 87 | @block_bot = true |
| 88 | 88 | session[:may_be_a_bot] = true |
| 89 | 89 | else |
| 90 | 90 | if session[:may_be_a_bot] |
| 91 | - return false unless verify_recaptcha :model=>@user, :message=>_('bota o recaptcha manuel!') | |
| 91 | + return false unless verify_recaptcha :model=>@user, :message=>_('Captcha (the human test)') | |
| 92 | 92 | end |
| 93 | 93 | @user.signup! |
| 94 | 94 | owner_role = Role.find_by_name('owner') |
| ... | ... | @@ -112,6 +112,7 @@ class AccountController < ApplicationController |
| 112 | 112 | @person.errors.delete(:user_id) |
| 113 | 113 | render :action => 'signup' |
| 114 | 114 | end |
| 115 | + clear_signup_start_time | |
| 115 | 116 | end |
| 116 | 117 | |
| 117 | 118 | # action to perform logout from the application |
| ... | ... | @@ -287,13 +288,33 @@ class AccountController < ApplicationController |
| 287 | 288 | @cannot_redirect = true |
| 288 | 289 | end |
| 289 | 290 | |
| 290 | - def set_signup_time_for_now | |
| 291 | - session[:signup_time] = Time.now | |
| 291 | + def set_signup_start_time_for_now | |
| 292 | + key = 'signup_start_time_' + rand.to_s.split('.')[1] | |
| 293 | + Rails.cache.write key, Time.now | |
| 294 | + key | |
| 295 | + end | |
| 296 | + | |
| 297 | + def get_signup_start_time | |
| 298 | + Rails.cache.read params[:signup_time_key] | |
| 299 | + end | |
| 300 | + | |
| 301 | + def clear_signup_start_time | |
| 302 | + Rails.cache.delete params[:signup_time_key] | |
| 292 | 303 | end |
| 293 | 304 | |
| 294 | 305 | def may_be_a_bot |
| 295 | - return true if session[:signup_time].nil? | |
| 296 | - session[:signup_time] > ( Time.now - 15.seconds ) | |
| 306 | + # No minimum signup delay, no bot test. | |
| 307 | + return false if environment.min_signup_delay == 0 | |
| 308 | + | |
| 309 | + # answering captcha, may be human! | |
| 310 | + return false if params[:recaptcha_response_field] | |
| 311 | + | |
| 312 | + # never set signup_time, hi wget! | |
| 313 | + signup_start_time = get_signup_start_time | |
| 314 | + return true if signup_start_time.nil? | |
| 315 | + | |
| 316 | + # so fast, so bot. | |
| 317 | + signup_start_time > ( Time.now - environment.min_signup_delay.seconds ) | |
| 297 | 318 | end |
| 298 | 319 | |
| 299 | 320 | def check_answer | ... | ... |
app/models/environment.rb
| ... | ... | @@ -233,6 +233,7 @@ class Environment < ActiveRecord::Base |
| 233 | 233 | settings[:message_for_member_invitation] || InviteMember.mail_template |
| 234 | 234 | end |
| 235 | 235 | |
| 236 | + settings_items :min_signup_delay, :type => Integer, :default => 25 #seconds | |
| 236 | 237 | settings_items :activation_blocked_text, :type => String |
| 237 | 238 | settings_items :message_for_disabled_enterprise, :type => String, |
| 238 | 239 | :default => _('This enterprise needs to be enabled.') | ... | ... |
app/views/account/_signup_form.rhtml
| 1 | -<script type="text/javascript"> | |
| 2 | - jQuery.post("<%= url_for :controller=>'account', :action=>'signup_time' %>"); | |
| 3 | -</script> | |
| 4 | - | |
| 5 | 1 | <% if @block_bot %> |
| 6 | - <div class="bot-notice"> | |
| 7 | - <%=_('<strong>How Fast!</strong> Looks like you are a bot.')%> | |
| 2 | + <div id="bot-notice"> | |
| 3 | + <strong><%=_('How Fast!')%></strong> | |
| 4 | + <p><%=_('Looks like you are a robot. Please, prove that you are human.')%></p> | |
| 8 | 5 | </div> |
| 9 | 6 | <% end %> |
| 10 | 7 | |
| ... | ... | @@ -14,6 +11,18 @@ |
| 14 | 11 | |
| 15 | 12 | <% labelled_form_for :user, @user, :html => { :multipart => true, :id => 'signup-form' } do |f| %> |
| 16 | 13 | |
| 14 | +<input type="hidden" id="signup_time_key" name="signup_time_key" /> | |
| 15 | +<script type="text/javascript"> | |
| 16 | + jQuery.ajax({ | |
| 17 | + type: "POST", | |
| 18 | + url: "<%= url_for :controller=>'account', :action=>'signup_time' %>", | |
| 19 | + dataType: 'json', | |
| 20 | + success: function(data) { | |
| 21 | + if (data.ok) jQuery('#signup_time_key').val(data.key); | |
| 22 | + } | |
| 23 | + }); | |
| 24 | +</script> | |
| 25 | + | |
| 17 | 26 | <%= hidden_field_tag :invitation_code, @invitation_code %> |
| 18 | 27 | |
| 19 | 28 | <div id='signup-form-header'> | ... | ... |
public/stylesheets/application.css
| ... | ... | @@ -5636,6 +5636,16 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img { |
| 5636 | 5636 | |
| 5637 | 5637 | /* Signup interface {{{ */ |
| 5638 | 5638 | |
| 5639 | +#bot-notice { | |
| 5640 | + border: 3px solid #000; | |
| 5641 | + background: #FE0; | |
| 5642 | + padding: 5px 10px; | |
| 5643 | + font-size: 150%; | |
| 5644 | +} | |
| 5645 | +#bot-notice p { | |
| 5646 | + margin: 0px; | |
| 5647 | +} | |
| 5648 | + | |
| 5639 | 5649 | #url-check { |
| 5640 | 5650 | margin: 0 0 -5px 0; |
| 5641 | 5651 | width: 100%; | ... | ... |
test/integration/signup_test.rb
| ... | ... | @@ -7,8 +7,40 @@ class SignupTest < ActionController::IntegrationTest |
| 7 | 7 | ActionController::Integration::Session.any_instance.stubs(:https?).returns(true) |
| 8 | 8 | end |
| 9 | 9 | |
| 10 | + # helper | |
| 11 | + def registering_with_bot_test(min_signup_delay, sleep_secs) | |
| 12 | + env = Environment.default | |
| 13 | + env.min_signup_delay = min_signup_delay | |
| 14 | + env.save! | |
| 15 | + get '/account/signup' | |
| 16 | + assert_response :success | |
| 17 | + get '/account/signup_time' | |
| 18 | + assert_response :success | |
| 19 | + data = ActiveSupport::JSON.decode response.body | |
| 20 | + sleep sleep_secs | |
| 21 | + post '/account/signup', :user => { :login => 'someone', :password => 'test', :password_confirmation => 'test', :email => 'someone@example.com' }, :signup_time_key => data['key'] | |
| 22 | + assert_response :success | |
| 23 | + end | |
| 24 | + | |
| 25 | + def test_signup_form_submition_must_be_blocked_for_fast_bots | |
| 26 | + count = User.count | |
| 27 | + registering_with_bot_test 5, 1 | |
| 28 | + assert_template 'signup' | |
| 29 | + assert_equal count, User.count | |
| 30 | + assert_match /you are a robot/, response.body | |
| 31 | + end | |
| 32 | + | |
| 33 | + def test_signup_form_submition_must_not_block_after_min_signup_delay | |
| 34 | + count = User.count | |
| 35 | + registering_with_bot_test 1, 2 | |
| 36 | + assert_equal count+1, User.count | |
| 37 | + end | |
| 38 | + | |
| 10 | 39 | def test_should_require_acceptance_of_terms_for_signup |
| 11 | - Environment.default.update_attributes(:terms_of_use => 'You agree to not be annoying.') | |
| 40 | + env = Environment.default | |
| 41 | + env.update_attributes(:terms_of_use => 'You agree to not be annoying.') | |
| 42 | + env.min_signup_delay = 0 | |
| 43 | + env.save! | |
| 12 | 44 | |
| 13 | 45 | count = User.count |
| 14 | 46 | mail_count = ActionMailer::Base.deliveries.count | ... | ... |