diff --git a/lib/noosfero/api/captcha_session_store.rb b/lib/noosfero/api/captcha_session_store.rb new file mode 100644 index 0000000..2617129 --- /dev/null +++ b/lib/noosfero/api/captcha_session_store.rb @@ -0,0 +1,30 @@ +class Noosfero::API::CaptchaSessionStore + + attr_accessor :data + attr_reader :private_token + + def self.create + key = SecureRandom.hex + store = Noosfero::API::CaptchaSessionStore.new(key) + Rails.cache.write(store.private_token, store, expires_in: 300) + return store + end + + def initialize(key) + @private_token = key + end + + def self.get(key) + Rails.cache.fetch(key) + end + + def store + Rails.cache.write(@private_token, self) + end + + def destroy + Rails.cache.delete(@private_token) + end + + +end diff --git a/lib/noosfero/api/helpers.rb b/lib/noosfero/api/helpers.rb index c48bc13..e3cea4f 100644 --- a/lib/noosfero/api/helpers.rb +++ b/lib/noosfero/api/helpers.rb @@ -23,8 +23,7 @@ require 'grape' def current_tmp_user private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s - @current_tmp_user ||= User.find_by_private_token(private_token) - @current_tmp_user = nil if !@current_tmp_user.nil? && @current_tmp_user.private_token_expired? + @current_tmp_user = Noosfero::API::CaptchaSessionStore.get(private_token) @current_tmp_user end @@ -244,6 +243,13 @@ require 'grape' unauthorized! unless current_user end + # Allows the anonymous captcha user authentication + # to pass the check. Used by the articles/vote to allow + # the vote without login + def authenticate_allow_captcha! + unauthorized! unless current_tmp_user || current_user + end + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash # or a Bad Request error is invoked. # diff --git a/lib/noosfero/api/session.rb b/lib/noosfero/api/session.rb index ba56997..fa5824a 100644 --- a/lib/noosfero/api/session.rb +++ b/lib/noosfero/api/session.rb @@ -15,13 +15,9 @@ module Noosfero # test_captcha will render_api_error! and exit in case of any problem # this return is just to improve the clarity of the execution path return unless test_captcha(remote_ip, params, environment) - - name = "tmp_user_#{remote_ip}" - user = User.new(:name => name) - user.generate_private_token! - - @current_tmp_user = user - {:private_token => user.private_token} + ## Creates and caches a captcha session store + store = Noosfero::API::CaptchaSessionStore.create + { "private_token" => "#{store.private_token}" } end # Login to get token diff --git a/lib/noosfero/api/v1/articles.rb b/lib/noosfero/api/v1/articles.rb index fec1944..518b5cf 100644 --- a/lib/noosfero/api/v1/articles.rb +++ b/lib/noosfero/api/v1/articles.rb @@ -133,7 +133,8 @@ module Noosfero named 'ArticleVote' end post ':id/vote' do - authenticate! + ## The vote api should allow regular login or with captcha + authenticate_allow_captcha! value = (params[:value] || 1).to_i # FIXME verify allowed values render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) diff --git a/test/unit/api/login_captcha_test.rb b/test/unit/api/login_captcha_test.rb index 165ed56..8864e61 100644 --- a/test/unit/api/login_captcha_test.rb +++ b/test/unit/api/login_captcha_test.rb @@ -2,50 +2,72 @@ require File.dirname(__FILE__) + '/test_helper' class LoginCaptchaTest < ActiveSupport::TestCase - url = "/api/v1/login-captcha?" - def setup() - environment = Environment.default - environment.api_captcha_settings = { + @environment = Environment.default + @environment.api_captcha_settings = { enabled: true, provider: 'serpro', serpro_client_id: '0000000000000000', verify_uri: 'http://captcha.serpro.gov.br/validate', } - environment.save! - + @environment.save! + @url = "/api/v1/login-captcha?" + end + + def create_article(name) + person = fast_create(Person, :environment_id => @environment.id) + fast_create(Article, :profile_id => person.id, :name => name) + end + + should 'not perform a vote without authentication' do + article = create_article('Article 1') + params = {} + params[:value] = 1 + + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 401, last_response.status + end + + should 'perform login from helpers' do + login_with_captcha + assert_not_nil @private_token + end + + + should 'perform a vote in an article identified by id' do + login_with_captcha + article = create_article('Article 1') + params[:value] = 1 + + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" + json = JSON.parse(last_response.body) + + assert_not_equal 401, last_response.status + assert_equal true, json['vote'] + end + + should 'not follow any article' do + login_with_captcha + article = create_article('Article 1') + post "/api/v1/articles/#{article.id}/follow?#{params.to_query}" + json = JSON.parse(last_response.body) + + assert_equal 401, last_response.status end should 'not generate private token when login without captcha' do params = {} - post "#{url}#{params.to_query}" + post "#{@url}#{params.to_query}" json = JSON.parse(last_response.body) - puts "JSon1: #{json}" assert json["private_token"].blank? end should 'generate private token when login with captcha' do - #request = mock() - stub_request(:post, "http://captcha.serpro.gov.br/validate"). - with(:body => "0000000000000000&4324343&4030320", - :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). - to_return(:status => 200, :body => "1", :headers => {}) - - # Mock the user to check the private_token - token = "private_token@1234" - user = mock - User.expects(:new).returns(user) - user.expects(:generate_private_token!).returns(token) - # To store the user session helpers.rb call the private_token method - user.expects(:private_token).times(2).returns(token) - - params = {:txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'} - post "#{url}#{params.to_query}" - json = JSON.parse(last_response.body) - puts "JSon2: #{json}" - assert !json["private_token"].blank? + json = login_with_captcha ret = json["private_token"] - assert ret == token + assert !ret.blank? + assert ret == @private_token end end \ No newline at end of file diff --git a/test/unit/api/test_helper.rb b/test/unit/api/test_helper.rb index c0bc921..3e1c761 100644 --- a/test/unit/api/test_helper.rb +++ b/test/unit/api/test_helper.rb @@ -8,6 +8,38 @@ class ActiveSupport::TestCase Noosfero::API::API end + def login_with_captcha + json = do_login_captcha_from_api + @private_token = json["private_token"] + @params = { "private_token" => @private_token} + json + end + + ## Performs a login using the session.rb but mocking the + ## real HTTP request to validate the captcha. + def do_login_captcha_from_api + # Request mocking + #Net::HTTP::Post Mock + request = mock + #Net::HTTP Mock + http = mock + uri = URI(environment.api_captcha_settings[:verify_uri]) + Net::HTTP.expects(:new).with(uri.host, uri.port).returns(http) + Net::HTTP::Post.expects(:new).with(uri.path).returns(request) + + # Captcha required codes + request.stubs(:body=).with("0000000000000000&4324343&4030320") + http.stubs(:request).with(request).returns(http) + + # Captcha validation success !! + http.stubs(:body).returns("1") + + params = {:txtToken_captcha_serpro_gov_br => '4324343', :captcha_text => '4030320'} + post "#{@url}#{params.to_query}" + json = JSON.parse(last_response.body) + json + end + def login_api @environment = Environment.default @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment) -- libgit2 0.21.2