Commit 796cdd6ed65b204d356da54d202b17de93844b14

Authored by Evandro Junior
Committed by Victor Costa
1 parent 7e2363ea

api: refactoring captcha

app/models/environment.rb
@@ -348,9 +348,6 @@ class Environment < ActiveRecord::Base @@ -348,9 +348,6 @@ class Environment < ActiveRecord::Base
348 348
349 settings_items :signup_welcome_screen_body, :type => String 349 settings_items :signup_welcome_screen_body, :type => String
350 350
351 - #Captcha settings  
352 - settings_items :api_captcha_settings, :type => ActiveSupport::HashWithIndifferentAccess, :default => {}  
353 -  
354 def has_custom_welcome_screen? 351 def has_custom_welcome_screen?
355 settings[:signup_welcome_screen_body].present? 352 settings[:signup_welcome_screen_body].present?
356 end 353 end
lib/noosfero/api/captcha_session_store.rb
@@ -1,30 +0,0 @@ @@ -1,30 +0,0 @@
1 -class Noosfero::API::CaptchaSessionStore  
2 -  
3 - attr_accessor :data  
4 - attr_reader :private_token  
5 -  
6 - def self.create  
7 - key = SecureRandom.hex  
8 - store = Noosfero::API::CaptchaSessionStore.new(key)  
9 - Rails.cache.write(store.private_token, store, expires_in: 300)  
10 - return store  
11 - end  
12 -  
13 - def initialize(key)  
14 - @private_token = key  
15 - end  
16 -  
17 - def self.get(key)  
18 - Rails.cache.fetch(key)  
19 - end  
20 -  
21 - def store  
22 - Rails.cache.write(@private_token, self)  
23 - end  
24 -  
25 - def destroy  
26 - Rails.cache.delete(@private_token)  
27 - end  
28 -  
29 -  
30 -end  
lib/noosfero/api/helpers.rb
@@ -23,13 +23,14 @@ require 'grape' @@ -23,13 +23,14 @@ require 'grape'
23 23
24 def current_tmp_user 24 def current_tmp_user
25 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s 25 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
26 - @current_tmp_user = Noosfero::API::CaptchaSessionStore.get(private_token) 26 + ## Get the "captcha" session store
  27 + @current_tmp_user = Noosfero::API::SessionStore.get("captcha##{private_token}")
27 @current_tmp_user 28 @current_tmp_user
28 end 29 end
29 30
30 def logout_tmp_user 31 def logout_tmp_user
31 @current_tmp_user = nil 32 @current_tmp_user = nil
32 - end 33 + end
33 34
34 def current_user 35 def current_user
35 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s 36 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
@@ -273,7 +274,7 @@ require 'grape' @@ -273,7 +274,7 @@ require 'grape'
273 unauthorized! unless current_user 274 unauthorized! unless current_user
274 end 275 end
275 276
276 - # Allows the anonymous captcha user authentication 277 + # Allows the anonymous captcha user authentication
277 # to pass the check. Used by the articles/vote to allow 278 # to pass the check. Used by the articles/vote to allow
278 # the vote without login 279 # the vote without login
279 def authenticate_allow_captcha! 280 def authenticate_allow_captcha!
@@ -411,99 +412,14 @@ require 'grape' @@ -411,99 +412,14 @@ require 'grape'
411 ########################################## 412 ##########################################
412 413
413 def test_captcha(remote_ip, params, environment) 414 def test_captcha(remote_ip, params, environment)
414 - d = environment.api_captcha_settings  
415 - return true unless d[:enabled] == true  
416 - msg_icve = _('Internal captcha validation error')  
417 - msg_eacs = 'Environment api_captcha_settings'  
418 - s = 500  
419 -  
420 - if d[:provider] == 'google'  
421 - return render_api_error!(msg_icve, s, nil, "#{msg_eacs} private_key not defined") if d[:private_key].nil?  
422 - return render_api_error!(msg_icve, s, nil, "#{msg_eacs} version not defined") unless d[:version] == 1 || d[:version] == 2  
423 - if d[:version] == 1  
424 - d[:verify_uri] ||= 'https://www.google.com/recaptcha/api/verify'  
425 - return verify_recaptcha_v1(remote_ip, d[:private_key], d[:verify_uri], params[:recaptcha_challenge_field], params[:recaptcha_response_field])  
426 - end  
427 - if d[:version] == 2  
428 - d[:verify_uri] ||= 'https://www.google.com/recaptcha/api/siteverify'  
429 - return verify_recaptcha_v2(remote_ip, d[:private_key], d[:verify_uri], params[:g_recaptcha_response])  
430 - end  
431 - end  
432 - if d[:provider] == 'serpro'  
433 - return render_api_error!(msg_icve, s, nil, "#{msg_eacs} verify_uri not defined") if d[:verify_uri].nil?  
434 - return verify_serpro_captcha(d[:serpro_client_id], params[:txtToken_captcha_serpro_gov_br], params[:captcha_text], d[:verify_uri])  
435 - end  
436 - return render_api_error!(msg_icve, s, nil, "#{msg_eacs} provider not defined")  
437 - end  
438 -  
439 - def verify_recaptcha_v1(remote_ip, private_key, api_recaptcha_verify_uri, recaptcha_challenge_field, recaptcha_response_field)  
440 - if recaptcha_challenge_field == nil || recaptcha_response_field == nil  
441 - return render_api_error!(_('Captcha validation error'), 500, nil, _('Missing captcha data'))  
442 - end  
443 -  
444 - verify_hash = {  
445 - "privatekey" => private_key,  
446 - "remoteip" => remote_ip,  
447 - "challenge" => recaptcha_challenge_field,  
448 - "response" => recaptcha_response_field  
449 - }  
450 - uri = URI(api_recaptcha_verify_uri)  
451 - https = Net::HTTP.new(uri.host, uri.port)  
452 - https.use_ssl = true  
453 - request = Net::HTTP::Post.new(uri.path)  
454 - request.set_form_data(verify_hash)  
455 - begin  
456 - result = https.request(request).body.split("\n")  
457 - rescue Exception => e  
458 - return render_api_error!(_('Internal captcha validation error'), 500, nil, "Error validating Googles' recaptcha version 1: #{e.message}")  
459 - end  
460 - return true if result[0] == "true"  
461 - return render_api_error!(_("Wrong captcha text, please try again"), 403, nil, "Error validating Googles' recaptcha version 1: #{result[1]}") if result[1] == "incorrect-captcha-sol"  
462 - #Catches all errors at the end  
463 - return render_api_error!(_("Internal recaptcha validation error"), 500, nil, "Error validating Googles' recaptcha version 1: #{result[1]}")  
464 - end  
465 -  
466 - def verify_recaptcha_v2(remote_ip, private_key, api_recaptcha_verify_uri, g_recaptcha_response)  
467 - return render_api_error!(_('Captcha validation error'), 500, nil, _('Missing captcha data')) if g_recaptcha_response == nil  
468 - verify_hash = {  
469 - "secret" => private_key,  
470 - "remoteip" => remote_ip,  
471 - "response" => g_recaptcha_response  
472 - }  
473 - uri = URI(api_recaptcha_verify_uri)  
474 - https = Net::HTTP.new(uri.host, uri.port)  
475 - https.use_ssl = true  
476 - request = Net::HTTP::Post.new(uri.path)  
477 - request.set_form_data(verify_hash)  
478 - begin  
479 - body = https.request(request).body  
480 - rescue Exception => e  
481 - return render_api_error!(_('Internal captcha validation error'), 500, nil, "recaptcha error: #{e.message}")  
482 - end  
483 - captcha_result = JSON.parse(body)  
484 - captcha_result["success"] ? true : captcha_result  
485 - end  
486 -  
487 - def verify_serpro_captcha(client_id, token, captcha_text, verify_uri)  
488 - return render_api_error!(_("Error processing token validation"), 500, nil, "Missing Serpro's Captcha token") unless token  
489 - return render_api_error!(_('Captcha text has not been filled'), 403) unless captcha_text  
490 - uri = URI(verify_uri)  
491 - http = Net::HTTP.new(uri.host, uri.port)  
492 - request = Net::HTTP::Post.new(uri.path)  
493 - verify_string = "#{client_id}&#{token}&#{captcha_text}"  
494 - request.body = verify_string  
495 - begin  
496 - body = http.request(request).body  
497 - rescue Exception => e  
498 - return render_api_error!(_('Internal captcha validation error'), 500, nil, "Serpro captcha error: #{e.message}") 415 + captcha_plugin_enabled = @plugins.dispatch(:test_captcha, remote_ip, params, environment).map {|p| p if ! ( p===nil ) }
  416 + return true if captcha_plugin_enabled.size == 0
  417 + if captcha_plugin_enabled.size > 1
  418 + return render_api_error!(_("Error processing Captcha"), 500, nil, "More than one captcha plugin enabled")
499 end 419 end
500 - return true if body == '1'  
501 - return render_api_error!(_("Internal captcha validation error"), 500, body, "Unable to reach Serpro's Captcha validation service") if body == "Activity timed out"  
502 - return render_api_error!(_("Wrong captcha text, please try again"), 403) if body == 0  
503 - return render_api_error!(_("Serpro's captcha token not found"), 500) if body == 2  
504 - return render_api_error!(_("No data sent to validation server or other serious problem"), 500) if body == -1  
505 - #Catches all errors at the end  
506 - return render_api_error!(_("Internal captcha validation error"), 500, nil, "Error validating Serpro's captcha #{body}") 420 + test_result = captcha_plugin_enabled[0]
  421 + return true if test_result === true
  422 + render_api_error!(test_result[:user_message], test_result[:status], test_result[:log_message], test_result[:javascript_console_message])
507 end 423 end
508 424
509 end 425 end
lib/noosfero/api/session.rb
@@ -16,7 +16,11 @@ module Noosfero @@ -16,7 +16,11 @@ module Noosfero
16 # this return is just to improve the clarity of the execution path 16 # this return is just to improve the clarity of the execution path
17 return unless test_captcha(remote_ip, params, environment) 17 return unless test_captcha(remote_ip, params, environment)
18 ## Creates and caches a captcha session store 18 ## Creates and caches a captcha session store
19 - store = Noosfero::API::CaptchaSessionStore.create 19 + store = Noosfero::API::SessionStore.create("captcha")
  20 + ## Initialize the data for the session store
  21 + store.data = []
  22 + ## Put it back in cache
  23 + store.store
20 { "private_token" => "#{store.private_token}" } 24 { "private_token" => "#{store.private_token}" }
21 end 25 end
22 26
lib/noosfero/api/session_store.rb 0 → 100644
@@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
  1 +## A session store for the API. It can store
  2 +## generic data on the Rails Cache to simulate
  3 +## a stateful session for API methods
  4 +class Noosfero::API::SessionStore
  5 +
  6 + ## A generic data value to allow storing any
  7 + ## value within this SessionStore
  8 + attr_accessor :data
  9 + ## The user private_token associated with this SessionStore
  10 + attr_reader :private_token
  11 + ## The key for this SessionStore in the Rails Cache
  12 + attr_reader :key
  13 +
  14 + ## Call this method to create and store a SessionStore
  15 + ## in Rails Cache. The SessionStore is returned. The
  16 + ## client_key parameter, if used, will uniquely identify
  17 + ## this SessionStore in Rails Cache, along with the user
  18 + ## private_token in the form: client_key#private_token
  19 + def self.create(client_key = nil)
  20 + private_token = SecureRandom.hex
  21 + store = Noosfero::API::SessionStore.new(client_key, private_token)
  22 + Rails.cache.write(store.key, store, expires_in: 300)
  23 + return store
  24 + end
  25 +
  26 + ## Creates a new SessionStore. Do not use directly in cliente code.
  27 + ## Please use the self.create method instead
  28 + def initialize(client_key, private_token)
  29 + ## Creates the key to store this object in Rails Cache
  30 + key = "#{client_key}#" if client_key
  31 + key = "#{key}#{private_token}"
  32 + @key = key
  33 + @private_token = private_token
  34 + end
  35 +
  36 + ## Returns the SessionStore in Rails Cache associated
  37 + ## with the given key
  38 + def self.get(key)
  39 + Rails.cache.fetch(key)
  40 + end
  41 +
  42 + ## Stores this SessionStore in Rails Cache using the
  43 + ## key attribute as the unique identifier
  44 + def store
  45 + Rails.cache.write(@key, self)
  46 + end
  47 +
  48 + ## Remove this session store from Rails Cache
  49 + def destroy
  50 + Rails.cache.delete(@key)
  51 + end
  52 +
  53 +end
lib/noosfero/api/v1/articles.rb
@@ -149,13 +149,24 @@ module Noosfero @@ -149,13 +149,24 @@ module Noosfero
149 # FIXME verify allowed values 149 # FIXME verify allowed values
150 render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) 150 render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
151 article = find_article(environment.articles, params[:id]) 151 article = find_article(environment.articles, params[:id])
152 -  
153 - begin 152 + ## If login with captcha
  153 + if @current_tmp_user
  154 + vote = (@current_tmp_user.data.include? article.id) ? false : true
  155 + if vote
  156 + @current_tmp_user.data << article.id
  157 + @current_tmp_user.store
  158 + begin
  159 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  160 + {:vote => vote.save}
  161 + rescue ActiveRecord::RecordInvalid => e
  162 + render_api_error!(e.message, 400)
  163 + end
  164 + else
  165 + {:vote => false}
  166 + end
  167 + else
154 vote = Vote.new(:voteable => article, :voter => current_person, :vote => value) 168 vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
155 - saved = vote.save!  
156 - {:vote => saved}  
157 - rescue ActiveRecord::RecordInvalid => e  
158 - render_api_error!(e.message, 400) 169 + {:vote => vote.save}
159 end 170 end
160 end 171 end
161 172
lib/noosfero/plugin.rb
@@ -671,6 +671,11 @@ class Noosfero::Plugin @@ -671,6 +671,11 @@ class Noosfero::Plugin
671 nil 671 nil
672 end 672 end
673 673
  674 + #By default will return nil that will mean not implented by the plugin
  675 + def test_captcha(*args)
  676 + nil
  677 + end
  678 +
674 # -> Adds additional blocks to profiles and environments. 679 # -> Adds additional blocks to profiles and environments.
675 # Your plugin must implements a class method called 'extra_blocks' 680 # Your plugin must implements a class method called 'extra_blocks'
676 # that returns a hash with the following syntax. 681 # that returns a hash with the following syntax.
plugins/serpro_captcha 0 → 160000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +Subproject commit 6bc22cb64bf23a142d934b7e1818ce02d073b578
test/unit/api/articles_test.rb
@@ -127,6 +127,37 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -127,6 +127,37 @@ class ArticlesTest &lt; ActiveSupport::TestCase
127 assert_equal 1, json['total_followers'] 127 assert_equal 1, json['total_followers']
128 end 128 end
129 129
  130 + should 'not perform a vote twice in same article' do
  131 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  132 + @params[:value] = 1
  133 + ## Perform a vote twice in API should compute only one vote
  134 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  135 + json = JSON.parse(last_response.body)
  136 +
  137 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  138 + json = JSON.parse(last_response.body)
  139 +
  140 + total = article.votes_total
  141 +
  142 + assert_equal 1, total
  143 + end
  144 +
  145 + should 'not perform a vote in favor and against a proposal' do
  146 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  147 + @params[:value] = 1
  148 + ## Perform a vote in favor a proposal
  149 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  150 + json = JSON.parse(last_response.body)
  151 + assert_equal 201, last_response.status
  152 + ## Perform a vote against a proposal
  153 + @params[:value] = -1
  154 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  155 + json = JSON.parse(last_response.body)
  156 + ## The api should not allow to save this vote
  157 + assert_equal false, json['vote']
  158 + end
  159 +
  160 +
130 should 'perform a vote in a article identified by id' do 161 should 'perform a vote in a article identified by id' do
131 article = fast_create(Article, :profile_id => @person.id, :name => "Some thing") 162 article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
132 @params[:value] = 1 163 @params[:value] = 1
@@ -136,6 +167,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -136,6 +167,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
136 167
137 assert_not_equal 401, last_response.status 168 assert_not_equal 401, last_response.status
138 assert_equal true, json['vote'] 169 assert_equal true, json['vote']
  170 +
139 end 171 end
140 172
141 should 'not perform a vote in a archived article' do 173 should 'not perform a vote in a archived article' do
test/unit/api/helpers_test.rb
1 require File.dirname(__FILE__) + '/test_helper'; 1 require File.dirname(__FILE__) + '/test_helper';
2 2
  3 +
3 require File.expand_path(File.dirname(__FILE__) + "/../../../lib/noosfero/api/helpers") 4 require File.expand_path(File.dirname(__FILE__) + "/../../../lib/noosfero/api/helpers")
4 5
5 class APIHelpersTest < ActiveSupport::TestCase 6 class APIHelpersTest < ActiveSupport::TestCase
@@ -216,88 +217,15 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -216,88 +217,15 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
216 217
217 ###### Captcha tests ###### 218 ###### Captcha tests ######
218 219
219 -should 'do not test captcha when there are no settings' do  
220 - environment = Environment.new  
221 - assert test_captcha("127.0.0.1", {}, environment)  
222 -end  
223 -  
224 -should 'do not test captcha when captcha is disabled on settings' do  
225 - environment = Environment.new  
226 - environment.api_captcha_settings = {  
227 - enabled: false,  
228 - }  
229 - assert test_captcha("127.0.0.1", {}, environment)  
230 -end  
231 -  
232 -should 'fail display recaptcha v1' do  
233 - environment = Environment.new  
234 - environment.api_captcha_settings = {  
235 - enabled: true,  
236 - provider: 'google',  
237 - version: 1,  
238 - private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY',  
239 - public_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-',  
240 - verify_uri: 'https://www.google.com/recaptcha/api/verify',  
241 - }  
242 - r = test_captcha('127.0.0.1', params, environment)  
243 - assert_equal(_("Missing captcha data"), r[0][:javascript_console_message])  
244 -end  
245 -  
246 -should 'fail display recaptcha v2' do  
247 - environment = Environment.new  
248 - environment.api_captcha_settings = {  
249 - enabled: true,  
250 - provider: 'google',  
251 - version: 2,  
252 - private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY',  
253 - public_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-',  
254 - verify_uri: 'https://www.google.com/recaptcha/api/siteverify',  
255 - }  
256 - r = test_captcha('127.0.0.1', params, environment)  
257 - assert_equal(_("Missing captcha data"), r[0][:javascript_console_message])  
258 -end  
259 -  
260 -should 'verify if user filled Serpro\' captcha text' do  
261 - environment = Environment.new  
262 - environment.api_captcha_settings = {  
263 - enabled: true,  
264 - provider: 'serpro',  
265 - serpro_client_id: '0000000000000000',  
266 - verify_uri: 'http://localhost/api/verify',  
267 - }  
268 - params = {}  
269 - params[:txtToken_captcha_serpro_gov_br] = '4324343'  
270 - assert_equal(_('Captcha text has not been filled'), test_captcha('127.0.0.1', params, environment)[0]['message'])  
271 -end  
272 -  
273 -should 'verify if Serpro\' captcha token has been sent' do  
274 - environment = Environment.new  
275 - environment.api_captcha_settings = {  
276 - enabled: true,  
277 - provider: 'serpro',  
278 - serpro_client_id: '0000000000000000',  
279 - verify_uri: 'http://localhost/api/verify',  
280 - }  
281 - params = {}  
282 - params[:captcha_text] = '4324343'  
283 - r = test_captcha('127.0.0.1', params, environment)  
284 - assert_equal(_("Missing Serpro's Captcha token"), r[0][:javascript_console_message])  
285 -end  
286 -  
287 -should 'captcha serpro say name or service not known' do  
288 - environment = Environment.new  
289 - environment.api_captcha_settings = {  
290 - enabled: true,  
291 - provider: 'serpro',  
292 - serpro_client_id: '0000000000000000',  
293 - verify_uri: 'http://someserverthatdoesnotexist.mycompanythatdoesnotexist.com/validate',  
294 - }  
295 - params = {}  
296 - params[:txtToken_captcha_serpro_gov_br] = '4324343'  
297 - params[:captcha_text] = '4324343'  
298 - r = test_captcha('127.0.0.1', params, environment)  
299 - assert (r[0][:javascript_console_message]).starts_with?("Serpro captcha error: getaddrinfo")  
300 -end 220 +# def plugins
  221 +# environment = Environment.default
  222 +# Noosfero::Plugin::Manager.new(environment, self)
  223 +# end
  224 +#
  225 +# should 'do not test captcha when there is no captcha plugin enabled' do
  226 +# environment = Environment.new
  227 +# assert test_captcha("127.0.0.1", {}, environment)
  228 +# end
301 229
302 ###### END Captcha tests ###### 230 ###### END Captcha tests ######
303 231
test/unit/api/login_captcha_test.rb
@@ -3,20 +3,8 @@ require File.dirname(__FILE__) + &#39;/test_helper&#39; @@ -3,20 +3,8 @@ require File.dirname(__FILE__) + &#39;/test_helper&#39;
3 class LoginCaptchaTest < ActiveSupport::TestCase 3 class LoginCaptchaTest < ActiveSupport::TestCase
4 4
5 def setup() 5 def setup()
6 - @environment = Environment.default  
7 - @environment.api_captcha_settings = {  
8 - enabled: true,  
9 - provider: 'serpro',  
10 - serpro_client_id: '0000000000000000',  
11 - verify_uri: 'http://captcha.serpro.gov.br/validate',  
12 - }  
13 - @environment.save!  
14 - @url = "/api/v1/login-captcha?"  
15 - end  
16 -  
17 - def create_article(name)  
18 - person = fast_create(Person, :environment_id => @environment.id)  
19 - fast_create(Article, :profile_id => person.id, :name => name) 6 + @url = "/api/v1/login-captcha"
  7 + OutcomeCaptcha.outcome_captcha_test = true
20 end 8 end
21 9
22 should 'not perform a vote without authentication' do 10 should 'not perform a vote without authentication' do
@@ -34,7 +22,6 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase @@ -34,7 +22,6 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase
34 assert_not_nil @private_token 22 assert_not_nil @private_token
35 end 23 end
36 24
37 -  
38 should 'perform a vote in an article identified by id' do 25 should 'perform a vote in an article identified by id' do
39 login_with_captcha 26 login_with_captcha
40 article = create_article('Article 1') 27 article = create_article('Article 1')
@@ -42,11 +29,31 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase @@ -42,11 +29,31 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase
42 29
43 post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" 30 post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
44 json = JSON.parse(last_response.body) 31 json = JSON.parse(last_response.body)
45 - 32 +
46 assert_not_equal 401, last_response.status 33 assert_not_equal 401, last_response.status
47 assert_equal true, json['vote'] 34 assert_equal true, json['vote']
48 end 35 end
49 36
  37 + should 'not perform a vote twice in same article' do
  38 + login_with_captcha
  39 + article = create_article('Article 1')
  40 + params[:value] = 1
  41 +
  42 + ## Perform a vote twice in API should compute only one vote
  43 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  44 + json = JSON.parse(last_response.body)
  45 + assert_equal true, json['vote']
  46 +
  47 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  48 + json = JSON.parse(last_response.body)
  49 + ## Should not allow vote again
  50 + assert_equal false, json['vote']
  51 +
  52 + total = article.votes_total
  53 +
  54 + assert_equal 1, total
  55 + end
  56 +
50 should 'not follow any article' do 57 should 'not follow any article' do
51 login_with_captcha 58 login_with_captcha
52 article = create_article('Article 1') 59 article = create_article('Article 1')
@@ -57,9 +64,11 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase @@ -57,9 +64,11 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase
57 end 64 end
58 65
59 should 'not generate private token when login without captcha' do 66 should 'not generate private token when login without captcha' do
  67 + OutcomeCaptcha.outcome_captcha_test = false
60 params = {} 68 params = {}
61 post "#{@url}#{params.to_query}" 69 post "#{@url}#{params.to_query}"
62 json = JSON.parse(last_response.body) 70 json = JSON.parse(last_response.body)
  71 + assert_equal last_response.status, 403
63 assert json["private_token"].blank? 72 assert json["private_token"].blank?
64 end 73 end
65 74
@@ -70,4 +79,8 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase @@ -70,4 +79,8 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase
70 assert ret == @private_token 79 assert ret == @private_token
71 end 80 end
72 81
73 -end  
74 \ No newline at end of file 82 \ No newline at end of file
  83 + should 'do login captcha from api' do
  84 + do_login_captcha_from_api
  85 + end
  86 +
  87 +end
test/unit/api/session_store_test.rb 0 → 100644
@@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
  1 +require File.dirname(__FILE__) + '/test_helper'
  2 +
  3 +class SessionStoreTest < ActiveSupport::TestCase
  4 +
  5 + should 'create a session store without client key' do
  6 + store = Noosfero::API::SessionStore.create
  7 + assert_not_nil store
  8 + private_token = store.private_token
  9 + assert_not_nil private_token
  10 + key = store.key
  11 + assert_not_nil key
  12 + assert_equal key, private_token
  13 + end
  14 +
  15 + should 'create a session store with client key' do
  16 + store = Noosfero::API::SessionStore.create("mykey")
  17 + assert_not_nil store
  18 + private_token = store.private_token
  19 + assert_not_nil private_token
  20 + key = store.key
  21 + assert_not_nil key
  22 + assert_equal key, "mykey##{private_token}"
  23 + end
  24 +
  25 + should 'get a session store with client key' do
  26 + store = Noosfero::API::SessionStore.create("mykey")
  27 + retrieved = Noosfero::API::SessionStore.get(store.key)
  28 + assert_not_nil retrieved
  29 + end
  30 +
  31 + should 'not get a destroyed session store with client key' do
  32 + store = Noosfero::API::SessionStore.create("mykey")
  33 + store.destroy
  34 + retrieved = Noosfero::API::SessionStore.get(store.key)
  35 + assert_nil retrieved
  36 + end
  37 +
  38 + should 'store data in session store' do
  39 + store = Noosfero::API::SessionStore.create("mykey")
  40 + store.data = [1, 2]
  41 + ## Put it back in cache
  42 + store.store
  43 + retrieved = Noosfero::API::SessionStore.get(store.key)
  44 + assert_equal [1,2], retrieved.data
  45 + end
  46 +
  47 +end
0 \ No newline at end of file 48 \ No newline at end of file
test/unit/api/session_test.rb
@@ -4,6 +4,7 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -4,6 +4,7 @@ class SessionTest &lt; ActiveSupport::TestCase
4 4
5 def setup 5 def setup
6 login_api 6 login_api
  7 + OutcomeCaptcha.outcome_captcha_test = true
7 end 8 end
8 9
9 should 'generate private token when login' do 10 should 'generate private token when login' do
@@ -89,22 +90,6 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -89,22 +90,6 @@ class SessionTest &lt; ActiveSupport::TestCase
89 json = JSON.parse(last_response.body) 90 json = JSON.parse(last_response.body)
90 end 91 end
91 92
92 - should 'detected error, Name or service not known, for Serpro captcha communication' do  
93 - environment = Environment.default  
94 - environment.api_captcha_settings = {  
95 - enabled: true,  
96 - provider: 'serpro',  
97 - serpro_client_id: '0000000000000000',  
98 - verify_uri: 'http://someserverthatdoesnotexist.mycompanythatdoesnotexist.com/validate',  
99 - }  
100 - environment.save!  
101 - params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com",  
102 - :txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'}  
103 - post "/api/v1/register?#{params.to_query}"  
104 - message = JSON.parse(last_response.body)['javascript_console_message']  
105 - assert_equal "Serpro captcha error: getaddrinfo: Name or service not known", message  
106 - end  
107 -  
108 # TODO: Add another test cases to check register situations 93 # TODO: Add another test cases to check register situations
109 should 'activate a user' do 94 should 'activate a user' do
110 params = { 95 params = {
@@ -185,7 +170,7 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -185,7 +170,7 @@ class SessionTest &lt; ActiveSupport::TestCase
185 170
186 should 'do not change user password when password confirmation is wrong' do 171 should 'do not change user password when password confirmation is wrong' do
187 user = create_user 172 user = create_user
188 - user.activate 173 + user.activate
189 task = ChangePassword.create!(:requestor => user.person) 174 task = ChangePassword.create!(:requestor => user.person)
190 params = {:code => task.code, :password => 'secret', :password_confirmation => 's3cret'} 175 params = {:code => task.code, :password => 'secret', :password_confirmation => 's3cret'}
191 patch "/api/v1/new_password?#{params.to_query}" 176 patch "/api/v1/new_password?#{params.to_query}"
@@ -200,6 +185,14 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -200,6 +185,14 @@ class SessionTest &lt; ActiveSupport::TestCase
200 assert_equal 404, last_response.status 185 assert_equal 404, last_response.status
201 end 186 end
202 187
  188 + should 'do not register a user if captcha fails' do
  189 + OutcomeCaptcha.outcome_captcha_test = false
  190 + Environment.default.enable('skip_new_user_email_confirmation')
  191 + params = {:login => "newuserapi_ewa ", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
  192 + post "/api/v1/register?#{params.to_query}"
  193 + assert_equal 403, last_response.status
  194 + end
  195 +
203 should 'not return private token when the registered user is inactive' do 196 should 'not return private token when the registered user is inactive' do
204 params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } 197 params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
205 post "/api/v1/register?#{params.to_query}" 198 post "/api/v1/register?#{params.to_query}"
test/unit/api/test_helper.rb
1 require File.dirname(__FILE__) + '/../../test_helper' 1 require File.dirname(__FILE__) + '/../../test_helper'
  2 +require File.join(Rails.root, '/lib/noosfero/api/helpers.rb')
  3 +
  4 +class OutcomeCaptcha
  5 + class << self
  6 + attr_accessor :outcome_captcha_test
  7 + end
  8 + @outcome_captcha_test = true
  9 +end
  10 +
  11 +module Noosfero
  12 + module API
  13 + module APIHelpers
  14 + def test_captcha(*args)
  15 + return true if OutcomeCaptcha.outcome_captcha_test
  16 + render_api_error!("Error testing captcha", 403)
  17 + end
  18 + end
  19 + end
  20 +end
2 21
3 class ActiveSupport::TestCase 22 class ActiveSupport::TestCase
4 23
5 include Rack::Test::Methods 24 include Rack::Test::Methods
  25 + include Noosfero::API::APIHelpers
6 26
7 def app 27 def app
8 Noosfero::API::API 28 Noosfero::API::API
@@ -15,31 +35,18 @@ class ActiveSupport::TestCase @@ -15,31 +35,18 @@ class ActiveSupport::TestCase
15 json 35 json
16 end 36 end
17 37
18 - ## Performs a login using the session.rb but mocking the  
19 - ## real HTTP request to validate the captcha.  
20 def do_login_captcha_from_api 38 def do_login_captcha_from_api
21 - # Request mocking  
22 - #Net::HTTP::Post Mock  
23 - request = mock  
24 - #Net::HTTP Mock  
25 - http = mock  
26 - uri = URI(environment.api_captcha_settings[:verify_uri])  
27 - Net::HTTP.expects(:new).with(uri.host, uri.port).returns(http)  
28 - Net::HTTP::Post.expects(:new).with(uri.path).returns(request)  
29 -  
30 - # Captcha required codes  
31 - request.stubs(:body=).with("0000000000000000&4324343&4030320")  
32 - http.stubs(:request).with(request).returns(http)  
33 -  
34 - # Captcha validation success !!  
35 - http.stubs(:body).returns("1")  
36 -  
37 - params = {:txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'}  
38 - post "#{@url}#{params.to_query}"  
39 - json = JSON.parse(last_response.body) 39 + post "/api/v1/login-captcha"
  40 + json = JSON.parse(last_response.body)
40 json 41 json
41 end 42 end
42 43
  44 + def create_article(name)
  45 + @environment = Environment.default
  46 + person = fast_create(Person, :environment_id => @environment.id)
  47 + fast_create(Article, :profile_id => person.id, :name => name)
  48 + end
  49 +
43 def login_api 50 def login_api
44 @environment = Environment.default 51 @environment = Environment.default
45 @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment) 52 @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment)