Commit 796cdd6ed65b204d356da54d202b17de93844b14
Committed by
Victor Costa
1 parent
7e2363ea
Exists in
captcha_serpro_plugin
api: refactoring captcha
Showing
14 changed files
with
249 additions
and
272 deletions
Show diff stats
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 |
@@ -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. |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +Subproject commit 6bc22cb64bf23a142d934b7e1818ce02d073b578 |
test/unit/api/articles_test.rb
@@ -127,6 +127,37 @@ class ArticlesTest < ActiveSupport::TestCase | @@ -127,6 +127,37 @@ class ArticlesTest < 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 < ActiveSupport::TestCase | @@ -136,6 +167,7 @@ class ArticlesTest < 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 < ActiveSupport::TestCase | @@ -216,88 +217,15 @@ class APIHelpersTest < 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__) + '/test_helper' | @@ -3,20 +3,8 @@ require File.dirname(__FILE__) + '/test_helper' | ||
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 < ActiveSupport::TestCase | @@ -34,7 +22,6 @@ class LoginCaptchaTest < 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 < ActiveSupport::TestCase | @@ -42,11 +29,31 @@ class LoginCaptchaTest < 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 < ActiveSupport::TestCase | @@ -57,9 +64,11 @@ class LoginCaptchaTest < 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 < ActiveSupport::TestCase | @@ -70,4 +79,8 @@ class LoginCaptchaTest < 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 |
@@ -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 < ActiveSupport::TestCase | @@ -4,6 +4,7 @@ class SessionTest < 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 < ActiveSupport::TestCase | @@ -89,22 +90,6 @@ class SessionTest < 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 < ActiveSupport::TestCase | @@ -185,7 +170,7 @@ class SessionTest < 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 < ActiveSupport::TestCase | @@ -200,6 +185,14 @@ class SessionTest < 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) |