Commit dd84c4adaa38b9655d76673d5018c63e186b3c27

Authored by Evandro Junior
2 parents af9a4c2b 88a604ed

Merge branch 'api_captcha_with_frontend_console_feedback' into api

lib/noosfero/api/helpers.rb
  1 +require 'grape'
  2 +
1 module Noosfero; 3 module Noosfero;
2 4
3 module API 5 module API
@@ -37,6 +39,10 @@ @@ -37,6 +39,10 @@
37 @environment 39 @environment
38 end 40 end
39 41
  42 + def logger
  43 + Noosfero::API::API.logger
  44 + end
  45 +
40 def limit 46 def limit
41 limit = params[:limit].to_i 47 limit = params[:limit].to_i
42 limit = default_limit if limit <= 0 48 limit = default_limit if limit <= 0
@@ -202,20 +208,6 @@ @@ -202,20 +208,6 @@
202 attrs 208 attrs
203 end 209 end
204 210
205 - def verify_recaptcha_v2(remote_ip, g_recaptcha_response, private_key, api_recaptcha_verify_uri)  
206 - verify_hash = {  
207 - "secret" => private_key,  
208 - "remoteip" => remote_ip,  
209 - "response" => g_recaptcha_response  
210 - }  
211 - uri = URI(api_recaptcha_verify_uri)  
212 - https = Net::HTTP.new(uri.host, uri.port)  
213 - https.use_ssl = true  
214 - request = Net::HTTP::Post.new(uri.path)  
215 - request.set_form_data(verify_hash)  
216 - JSON.parse(https.request(request).body)  
217 - end  
218 -  
219 ########################################## 211 ##########################################
220 # error helpers # 212 # error helpers #
221 ########################################## 213 ##########################################
@@ -251,8 +243,19 @@ @@ -251,8 +243,19 @@
251 render_api_error!(_('Method Not Allowed'), 405) 243 render_api_error!(_('Method Not Allowed'), 405)
252 end 244 end
253 245
254 - def render_api_error!(message, status)  
255 - error!({'message' => message, :code => status}, status) 246 + # javascript_console_message is supposed to be executed as console.log()
  247 + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil)
  248 + message_hash = {'message' => user_message, :code => status}
  249 + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present?
  250 + log_msg = "#{status}, User message: #{user_message}"
  251 + log_msg = "#{log_message}, #{log_msg}" if log_message.present?
  252 + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present?
  253 + logger.error log_msg
  254 + if javascript_console_message.present?
  255 + error!(message_hash, status)
  256 + else
  257 + error!(user_message, status)
  258 + end
256 end 259 end
257 260
258 def render_api_errors!(messages) 261 def render_api_errors!(messages)
@@ -319,10 +322,13 @@ @@ -319,10 +322,13 @@
319 def test_captcha(remote_ip, params, environment) 322 def test_captcha(remote_ip, params, environment)
320 d = environment.api_captcha_settings 323 d = environment.api_captcha_settings
321 return true unless d[:enabled] == true 324 return true unless d[:enabled] == true
  325 + msg_icve = _('Internal captcha validation error')
  326 + msg_eacs = 'Environment api_captcha_settings'
  327 + s = 500
322 328
323 if d[:provider] == 'google' 329 if d[:provider] == 'google'
324 - raise ArgumentError, "Environment api_captcha_settings private_key not defined" if d[:private_key].nil?  
325 - raise ArgumentError, "Environment api_captcha_settings version not defined" unless d[:version] == 1 || d[:version] == 2 330 + return render_api_error!(msg_icve, s, nil, "#{msg_eacs} private_key not defined") if d[:private_key].nil?
  331 + return render_api_error!(msg_icve, s, nil, "#{msg_eacs} version not defined") unless d[:version] == 1 || d[:version] == 2
326 if d[:version] == 1 332 if d[:version] == 1
327 d[:verify_uri] ||= 'https://www.google.com/recaptcha/api/verify' 333 d[:verify_uri] ||= 'https://www.google.com/recaptcha/api/verify'
328 return verify_recaptcha_v1(remote_ip, d[:private_key], d[:verify_uri], params[:recaptcha_challenge_field], params[:recaptcha_response_field]) 334 return verify_recaptcha_v1(remote_ip, d[:private_key], d[:verify_uri], params[:recaptcha_challenge_field], params[:recaptcha_response_field])
@@ -333,15 +339,15 @@ @@ -333,15 +339,15 @@
333 end 339 end
334 end 340 end
335 if d[:provider] == 'serpro' 341 if d[:provider] == 'serpro'
336 - raise ArgumentError, "Environment api_captcha_settings verify_uri not defined" if d[:verify_uri].nil? 342 + return render_api_error!(msg_icve, s, nil, "#{msg_eacs} verify_uri not defined") if d[:verify_uri].nil?
337 return verify_serpro_captcha(d[:serpro_client_id], params[:txtToken_captcha_serpro_gov_br], params[:captcha_text], d[:verify_uri]) 343 return verify_serpro_captcha(d[:serpro_client_id], params[:txtToken_captcha_serpro_gov_br], params[:captcha_text], d[:verify_uri])
338 end 344 end
339 - raise ArgumentError, "Environment api_captcha_settings provider not defined" 345 + return render_api_error!(msg_icve, s, nil, "#{msg_eacs} provider not defined")
340 end 346 end
341 347
342 def verify_recaptcha_v1(remote_ip, private_key, api_recaptcha_verify_uri, recaptcha_challenge_field, recaptcha_response_field) 348 def verify_recaptcha_v1(remote_ip, private_key, api_recaptcha_verify_uri, recaptcha_challenge_field, recaptcha_response_field)
343 if recaptcha_challenge_field == nil || recaptcha_response_field == nil 349 if recaptcha_challenge_field == nil || recaptcha_response_field == nil
344 - return _('Missing captcha data') 350 + return render_api_error!(_('Captcha validation error'), 500, nil, _('Missing captcha data'))
345 end 351 end
346 352
347 verify_hash = { 353 verify_hash = {
@@ -358,19 +364,14 @@ @@ -358,19 +364,14 @@
358 begin 364 begin
359 body = https.request(request).body 365 body = https.request(request).body
360 rescue Exception => e 366 rescue Exception => e
361 - logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))  
362 - logger.error e  
363 - return _("Google recaptcha error: #{e.message}") 367 + return render_api_error!(_('Internal captcha validation error'), 500, nil, "recaptcha error: #{e.message}")
364 end 368 end
365 body = JSON.parse(body) 369 body = JSON.parse(body)
366 body == "true\nsuccess" ? true : body 370 body == "true\nsuccess" ? true : body
367 end 371 end
368 372
369 def verify_recaptcha_v2(remote_ip, private_key, api_recaptcha_verify_uri, g_recaptcha_response) 373 def verify_recaptcha_v2(remote_ip, private_key, api_recaptcha_verify_uri, g_recaptcha_response)
370 - if g_recaptcha_response == nil  
371 - return _('Missing captcha data')  
372 - end  
373 - 374 + return render_api_error!(_('Captcha validation error'), 500, nil, _('Missing captcha data')) if g_recaptcha_response == nil
374 verify_hash = { 375 verify_hash = {
375 "secret" => private_key, 376 "secret" => private_key,
376 "remoteip" => remote_ip, 377 "remoteip" => remote_ip,
@@ -384,17 +385,15 @@ @@ -384,17 +385,15 @@
384 begin 385 begin
385 body = https.request(request).body 386 body = https.request(request).body
386 rescue Exception => e 387 rescue Exception => e
387 - logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))  
388 - logger.error e  
389 - return _("Google recaptcha error: #{e.message}") 388 + return render_api_error!(_('Internal captcha validation error'), 500, nil, "recaptcha error: #{e.message}")
390 end 389 end
391 captcha_result = JSON.parse(body) 390 captcha_result = JSON.parse(body)
392 captcha_result["success"] ? true : captcha_result 391 captcha_result["success"] ? true : captcha_result
393 end 392 end
394 393
395 def verify_serpro_captcha(client_id, token, captcha_text, verify_uri) 394 def verify_serpro_captcha(client_id, token, captcha_text, verify_uri)
396 - return _('Missing Serpro Captcha token') if token == nil  
397 - return _('Captcha text has not been filled') if captcha_text == nil 395 + return render_api_error!(_("Error processing token validation"), 500, nil, "Missing Serpro's Captcha token") unless token
  396 + return render_api_error!(_('Captcha text has not been filled'), 403) unless captcha_text
398 uri = URI(verify_uri) 397 uri = URI(verify_uri)
399 http = Net::HTTP.new(uri.host, uri.port) 398 http = Net::HTTP.new(uri.host, uri.port)
400 request = Net::HTTP::Post.new(uri.path) 399 request = Net::HTTP::Post.new(uri.path)
@@ -403,13 +402,15 @@ @@ -403,13 +402,15 @@
403 begin 402 begin
404 body = http.request(request).body 403 body = http.request(request).body
405 rescue Exception => e 404 rescue Exception => e
406 - logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))  
407 - logger.error e  
408 - return _("Serpro captcha error: #{e.message}") 405 + return render_api_error!(_('Internal captcha validation error'), 500, nil, "Serpro captcha error: #{e.message}")
409 end 406 end
410 - return _("Wrong captcha text, please try again") if body == 0  
411 - return _("Token not found") if body == 2  
412 - body == '1' ? true : body 407 + return true if body == '1'
  408 + return render_api_error!(_("Internal captcha validation error"), 500, body, "Unable to reach Serpro's Captcha validation service") if body == "Activity timed out"
  409 + return render_api_error!(_("Wrong captcha text, please try again"), 403) if body == 0
  410 + return render_api_error!(_("Serpro's captcha token not found"), 500) if body == 2
  411 + return render_api_error!(_("No data sent to validation server or other serious problem"), 500) if body == -1
  412 + #Catches all errors at the end
  413 + return render_api_error!(_("Internal captcha validation error"), 500, nil, "Error validating Serpro's captcha #{body}")
413 end 414 end
414 415
415 end 416 end
lib/noosfero/api/session.rb
@@ -2,7 +2,6 @@ require &quot;uri&quot; @@ -2,7 +2,6 @@ require &quot;uri&quot;
2 2
3 module Noosfero 3 module Noosfero
4 module API 4 module API
5 -  
6 class Session < Grape::API 5 class Session < Grape::API
7 6
8 # Login to get token 7 # Login to get token
@@ -40,13 +39,9 @@ module Noosfero @@ -40,13 +39,9 @@ module Noosfero
40 unique_attributes! User, [:email, :login] 39 unique_attributes! User, [:email, :login]
41 attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields 40 attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields
42 remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) 41 remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
43 -  
44 - result = test_captcha(remote_ip, params, environment)  
45 - unless result == true  
46 - render_api_error!(result, 401)  
47 - return  
48 - end  
49 - 42 + # test_captcha will render_api_error! and exit in case of some problem
  43 + # this return is only improve the clarity of the execution path
  44 + return unless test_captcha(remote_ip, params, environment)
50 user = User.new(attrs) 45 user = User.new(attrs)
51 if user.save 46 if user.save
52 user.generate_private_token! if user.activated? 47 user.generate_private_token! if user.activated?
test/unit/api/helpers_test.rb
@@ -113,7 +113,6 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -113,7 +113,6 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
113 p = fast_create(Profile) 113 p = fast_create(Profile)
114 a = fast_create(Article, :published => false, :profile_id => p.id) 114 a = fast_create(Article, :published => false, :profile_id => p.id)
115 fast_create(Article, :profile_id => p.id) 115 fast_create(Article, :profile_id => p.id)
116 -  
117 user.generate_private_token! 116 user.generate_private_token!
118 User.expects(:find_by_private_token).returns(user) 117 User.expects(:find_by_private_token).returns(user)
119 assert_equal 403, find_article(p.articles, a.id).last 118 assert_equal 403, find_article(p.articles, a.id).last
@@ -162,57 +161,6 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -162,57 +161,6 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
162 assert_nil make_conditions_with_parameter[:type] 161 assert_nil make_conditions_with_parameter[:type]
163 end 162 end
164 163
165 - should 'do not test captcha when there are no settings' do  
166 - environment = Environment.new  
167 - assert test_captcha("127.0.0.1", {}, environment)  
168 - end  
169 -  
170 - should 'do not test captcha when captcha is disabled on settings' do  
171 - environment = Environment.new  
172 - environment.api_captcha_settings = {  
173 - enabled: false,  
174 - }  
175 - assert test_captcha("127.0.0.1", {}, environment)  
176 - end  
177 -  
178 - should 'fail display recaptcha v1' do  
179 - environment = Environment.new  
180 - environment.api_captcha_settings = {  
181 - enabled: true,  
182 - provider: 'google',  
183 - version: 1,  
184 - private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY',  
185 - public_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-',  
186 - verify_uri: 'https://www.google.com/recaptcha/api/verify',  
187 - }  
188 - assert_equal test_captcha("127.0.0.1", {}, environment), "Missing captcha data"  
189 - end  
190 -  
191 - should 'fail display recaptcha v2' do  
192 - environment = Environment.new  
193 - environment.api_captcha_settings = {  
194 - enabled: true,  
195 - provider: 'google',  
196 - version: 2,  
197 - private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY',  
198 - public_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-',  
199 - verify_uri: 'https://www.google.com/recaptcha/api/siteverify',  
200 - }  
201 - assert_equal test_captcha("127.0.0.1", {}, environment), "Missing captcha data"  
202 - end  
203 -  
204 - should 'fail display Serpro captcha' do  
205 - environment = Environment.new  
206 - environment.api_captcha_settings = {  
207 - enabled: true,  
208 - provider: 'serpro',  
209 - serpro_client_id: '0000000000000000',  
210 - verify_uri: 'http://localhost/api/verify',  
211 - }  
212 - params = {}  
213 - params[:txtToken_captcha_serpro_gov_br] = '4324343'  
214 - assert_equal test_captcha("127.0.0.1", params, environment), _('Captcha text has not been filled')  
215 - end  
216 164
217 should 'render not_found if endpoint is unavailable' do 165 should 'render not_found if endpoint is unavailable' do
218 Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true) 166 Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true)
@@ -232,11 +180,79 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -232,11 +180,79 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
232 #assert_equal 403, find_article(p.articles, a.id).last 180 #assert_equal 403, find_article(p.articles, a.id).last
233 181
234 #assert_equals [article1, article2], present_articles 182 #assert_equals [article1, article2], present_articles
  183 + end
235 184
  185 +###### Captcha tests ######
236 186
237 - end 187 +should 'do not test captcha when there are no settings' do
  188 + environment = Environment.new
  189 + assert test_captcha("127.0.0.1", {}, environment)
  190 +end
  191 +
  192 +should 'do not test captcha when captcha is disabled on settings' do
  193 + environment = Environment.new
  194 + environment.api_captcha_settings = {
  195 + enabled: false,
  196 + }
  197 + assert test_captcha("127.0.0.1", {}, environment)
  198 +end
  199 +
  200 +should 'fail display recaptcha v1' do
  201 + environment = Environment.new
  202 + environment.api_captcha_settings = {
  203 + enabled: true,
  204 + provider: 'google',
  205 + version: 1,
  206 + private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY',
  207 + public_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-',
  208 + verify_uri: 'https://www.google.com/recaptcha/api/verify',
  209 + }
  210 + r = test_captcha('127.0.0.1', params, environment)
  211 + assert_equal(_("Missing captcha data"), r[0][:javascript_console_message])
  212 +end
238 213
239 - should 'captcha serpro say name or service not known' do 214 +should 'fail display recaptcha v2' do
  215 + environment = Environment.new
  216 + environment.api_captcha_settings = {
  217 + enabled: true,
  218 + provider: 'google',
  219 + version: 2,
  220 + private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY',
  221 + public_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-',
  222 + verify_uri: 'https://www.google.com/recaptcha/api/siteverify',
  223 + }
  224 + r = test_captcha('127.0.0.1', params, environment)
  225 + assert_equal(_("Missing captcha data"), r[0][:javascript_console_message])
  226 +end
  227 +
  228 +should 'verify if user filled Serpro\' captcha text' do
  229 + environment = Environment.new
  230 + environment.api_captcha_settings = {
  231 + enabled: true,
  232 + provider: 'serpro',
  233 + serpro_client_id: '0000000000000000',
  234 + verify_uri: 'http://localhost/api/verify',
  235 + }
  236 + params = {}
  237 + params[:txtToken_captcha_serpro_gov_br] = '4324343'
  238 + assert_equal(_('Captcha text has not been filled'), test_captcha('127.0.0.1', params, environment)[0])
  239 +end
  240 +
  241 +should 'verify if Serpro\' captcha token has been sent' do
  242 + environment = Environment.new
  243 + environment.api_captcha_settings = {
  244 + enabled: true,
  245 + provider: 'serpro',
  246 + serpro_client_id: '0000000000000000',
  247 + verify_uri: 'http://localhost/api/verify',
  248 + }
  249 + params = {}
  250 + params[:captcha_text] = '4324343'
  251 + r = test_captcha('127.0.0.1', params, environment)
  252 + assert_equal(_("Missing Serpro's Captcha token"), r[0][:javascript_console_message])
  253 +end
  254 +
  255 +should 'captcha serpro say name or service not known' do
240 environment = Environment.new 256 environment = Environment.new
241 environment.api_captcha_settings = { 257 environment.api_captcha_settings = {
242 enabled: true, 258 enabled: true,
@@ -247,11 +263,11 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -247,11 +263,11 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
247 params = {} 263 params = {}
248 params[:txtToken_captcha_serpro_gov_br] = '4324343' 264 params[:txtToken_captcha_serpro_gov_br] = '4324343'
249 params[:captcha_text] = '4324343' 265 params[:captcha_text] = '4324343'
250 - logger = Logger.new(File.join(Rails.root, 'log', 'test_api.log'))  
251 - stubs(:logger).returns(logger)  
252 - assert_equal test_captcha('127.0.0.1', params, environment), 'Serpro captcha error: getaddrinfo: Name or service not known'  
253 - end 266 + r = test_captcha('127.0.0.1', params, environment)
  267 + assert_equal(_("Serpro captcha error: getaddrinfo: Name or service not known"), r[0][:javascript_console_message])
  268 +end
254 269
  270 +###### END Captcha tests ######
255 271
256 protected 272 protected
257 273
test/unit/api/session_test.rb
@@ -52,7 +52,7 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -52,7 +52,7 @@ class SessionTest &lt; ActiveSupport::TestCase
52 assert_equal 400, last_response.status 52 assert_equal 400, last_response.status
53 end 53 end
54 54
55 - should 'detected error, Name or service not known, for Serpro Captcha communication' do 55 + should 'detected error, Name or service not known, for Serpro captcha communication' do
56 environment = Environment.default 56 environment = Environment.default
57 environment.api_captcha_settings = { 57 environment.api_captcha_settings = {
58 enabled: true, 58 enabled: true,
@@ -64,7 +64,8 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -64,7 +64,8 @@ class SessionTest &lt; ActiveSupport::TestCase
64 params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com", 64 params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com",
65 :txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'} 65 :txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'}
66 post "/api/v1/register?#{params.to_query}" 66 post "/api/v1/register?#{params.to_query}"
67 - assert_equal "Serpro captcha error: getaddrinfo: Name or service not known", JSON.parse(last_response.body)["message"] 67 + message = JSON.parse(last_response.body)['javascript_console_message']
  68 + assert_equal "Serpro captcha error: getaddrinfo: Name or service not known", message
68 end 69 end
69 70
70 # TODO: Add another test cases to check register situations 71 # TODO: Add another test cases to check register situations