Commit f753c8b73b39f0e1b9f9b284333c11077b3495fd
1 parent
0d2ac9fb
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Implemented loging with captcha with mencache
Showing
6 changed files
with
125 additions
and
38 deletions
Show diff stats
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
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,8 +23,7 @@ require 'grape' | @@ -23,8 +23,7 @@ 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 ||= User.find_by_private_token(private_token) | ||
27 | - @current_tmp_user = nil if !@current_tmp_user.nil? && @current_tmp_user.private_token_expired? | 26 | + @current_tmp_user = Noosfero::API::CaptchaSessionStore.get(private_token) |
28 | @current_tmp_user | 27 | @current_tmp_user |
29 | end | 28 | end |
30 | 29 | ||
@@ -244,6 +243,13 @@ require 'grape' | @@ -244,6 +243,13 @@ require 'grape' | ||
244 | unauthorized! unless current_user | 243 | unauthorized! unless current_user |
245 | end | 244 | end |
246 | 245 | ||
246 | + # Allows the anonymous captcha user authentication | ||
247 | + # to pass the check. Used by the articles/vote to allow | ||
248 | + # the vote without login | ||
249 | + def authenticate_allow_captcha! | ||
250 | + unauthorized! unless current_tmp_user || current_user | ||
251 | + end | ||
252 | + | ||
247 | # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash | 253 | # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash |
248 | # or a Bad Request error is invoked. | 254 | # or a Bad Request error is invoked. |
249 | # | 255 | # |
lib/noosfero/api/session.rb
@@ -15,13 +15,9 @@ module Noosfero | @@ -15,13 +15,9 @@ module Noosfero | ||
15 | # test_captcha will render_api_error! and exit in case of any problem | 15 | # test_captcha will render_api_error! and exit in case of any problem |
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 | - | ||
19 | - name = "tmp_user_#{remote_ip}" | ||
20 | - user = User.new(:name => name) | ||
21 | - user.generate_private_token! | ||
22 | - | ||
23 | - @current_tmp_user = user | ||
24 | - {:private_token => user.private_token} | 18 | + ## Creates and caches a captcha session store |
19 | + store = Noosfero::API::CaptchaSessionStore.create | ||
20 | + { "private_token" => "#{store.private_token}" } | ||
25 | end | 21 | end |
26 | 22 | ||
27 | # Login to get token | 23 | # Login to get token |
lib/noosfero/api/v1/articles.rb
@@ -133,7 +133,8 @@ module Noosfero | @@ -133,7 +133,8 @@ module Noosfero | ||
133 | named 'ArticleVote' | 133 | named 'ArticleVote' |
134 | end | 134 | end |
135 | post ':id/vote' do | 135 | post ':id/vote' do |
136 | - authenticate! | 136 | + ## The vote api should allow regular login or with captcha |
137 | + authenticate_allow_captcha! | ||
137 | value = (params[:value] || 1).to_i | 138 | value = (params[:value] || 1).to_i |
138 | # FIXME verify allowed values | 139 | # FIXME verify allowed values |
139 | render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) | 140 | render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) |
test/unit/api/login_captcha_test.rb
@@ -2,50 +2,72 @@ require File.dirname(__FILE__) + '/test_helper' | @@ -2,50 +2,72 @@ require File.dirname(__FILE__) + '/test_helper' | ||
2 | 2 | ||
3 | class LoginCaptchaTest < ActiveSupport::TestCase | 3 | class LoginCaptchaTest < ActiveSupport::TestCase |
4 | 4 | ||
5 | - url = "/api/v1/login-captcha?" | ||
6 | - | ||
7 | def setup() | 5 | def setup() |
8 | - environment = Environment.default | ||
9 | - environment.api_captcha_settings = { | 6 | + @environment = Environment.default |
7 | + @environment.api_captcha_settings = { | ||
10 | enabled: true, | 8 | enabled: true, |
11 | provider: 'serpro', | 9 | provider: 'serpro', |
12 | serpro_client_id: '0000000000000000', | 10 | serpro_client_id: '0000000000000000', |
13 | verify_uri: 'http://captcha.serpro.gov.br/validate', | 11 | verify_uri: 'http://captcha.serpro.gov.br/validate', |
14 | } | 12 | } |
15 | - environment.save! | ||
16 | - | 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) | ||
20 | + end | ||
21 | + | ||
22 | + should 'not perform a vote without authentication' do | ||
23 | + article = create_article('Article 1') | ||
24 | + params = {} | ||
25 | + params[:value] = 1 | ||
26 | + | ||
27 | + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" | ||
28 | + json = JSON.parse(last_response.body) | ||
29 | + assert_equal 401, last_response.status | ||
30 | + end | ||
31 | + | ||
32 | + should 'perform login from helpers' do | ||
33 | + login_with_captcha | ||
34 | + assert_not_nil @private_token | ||
35 | + end | ||
36 | + | ||
37 | + | ||
38 | + should 'perform a vote in an article identified by id' do | ||
39 | + login_with_captcha | ||
40 | + article = create_article('Article 1') | ||
41 | + params[:value] = 1 | ||
42 | + | ||
43 | + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" | ||
44 | + json = JSON.parse(last_response.body) | ||
45 | + | ||
46 | + assert_not_equal 401, last_response.status | ||
47 | + assert_equal true, json['vote'] | ||
48 | + end | ||
49 | + | ||
50 | + should 'not follow any article' do | ||
51 | + login_with_captcha | ||
52 | + article = create_article('Article 1') | ||
53 | + post "/api/v1/articles/#{article.id}/follow?#{params.to_query}" | ||
54 | + json = JSON.parse(last_response.body) | ||
55 | + | ||
56 | + assert_equal 401, last_response.status | ||
17 | end | 57 | end |
18 | 58 | ||
19 | should 'not generate private token when login without captcha' do | 59 | should 'not generate private token when login without captcha' do |
20 | params = {} | 60 | params = {} |
21 | - post "#{url}#{params.to_query}" | 61 | + post "#{@url}#{params.to_query}" |
22 | json = JSON.parse(last_response.body) | 62 | json = JSON.parse(last_response.body) |
23 | - puts "JSon1: #{json}" | ||
24 | assert json["private_token"].blank? | 63 | assert json["private_token"].blank? |
25 | end | 64 | end |
26 | 65 | ||
27 | should 'generate private token when login with captcha' do | 66 | should 'generate private token when login with captcha' do |
28 | - #request = mock() | ||
29 | - stub_request(:post, "http://captcha.serpro.gov.br/validate"). | ||
30 | - with(:body => "0000000000000000&4324343&4030320", | ||
31 | - :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). | ||
32 | - to_return(:status => 200, :body => "1", :headers => {}) | ||
33 | - | ||
34 | - # Mock the user to check the private_token | ||
35 | - token = "private_token@1234" | ||
36 | - user = mock | ||
37 | - User.expects(:new).returns(user) | ||
38 | - user.expects(:generate_private_token!).returns(token) | ||
39 | - # To store the user session helpers.rb call the private_token method | ||
40 | - user.expects(:private_token).times(2).returns(token) | ||
41 | - | ||
42 | - params = {:txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'} | ||
43 | - post "#{url}#{params.to_query}" | ||
44 | - json = JSON.parse(last_response.body) | ||
45 | - puts "JSon2: #{json}" | ||
46 | - assert !json["private_token"].blank? | 67 | + json = login_with_captcha |
47 | ret = json["private_token"] | 68 | ret = json["private_token"] |
48 | - assert ret == token | 69 | + assert !ret.blank? |
70 | + assert ret == @private_token | ||
49 | end | 71 | end |
50 | 72 | ||
51 | end | 73 | end |
52 | \ No newline at end of file | 74 | \ No newline at end of file |
test/unit/api/test_helper.rb
@@ -8,6 +8,38 @@ class ActiveSupport::TestCase | @@ -8,6 +8,38 @@ class ActiveSupport::TestCase | ||
8 | Noosfero::API::API | 8 | Noosfero::API::API |
9 | end | 9 | end |
10 | 10 | ||
11 | + def login_with_captcha | ||
12 | + json = do_login_captcha_from_api | ||
13 | + @private_token = json["private_token"] | ||
14 | + @params = { "private_token" => @private_token} | ||
15 | + json | ||
16 | + end | ||
17 | + | ||
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 | ||
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) | ||
40 | + json | ||
41 | + end | ||
42 | + | ||
11 | def login_api | 43 | def login_api |
12 | @environment = Environment.default | 44 | @environment = Environment.default |
13 | @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment) | 45 | @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment) |