Commit 920f13a937341eb52a0b26d4924cf399fc994b52

Authored by Aurélio A. Heckert
1 parent 05dbd36f

Force minimum signup time to block bots. Also add captcha for fast submitters

ActionItem2556
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 &lt; 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
... ...