Commit 8f098f7c6d32adc350a1d21419a86f93b89438fe
0 parents
Exists in
master
Plugin initial commit
Showing
10 changed files
with
330 additions
and
0 deletions
Show diff stats
| 1 | +++ a/README.md | ||
| @@ -0,0 +1,6 @@ | @@ -0,0 +1,6 @@ | ||
| 1 | +Sample config values: | ||
| 2 | + | ||
| 3 | +verify_uri 'http://captcha.servicoscorporativos.serpro.gov.br/captchavalidar/1.0.0/validar' | ||
| 4 | +serpro_client_id 'fdbcdc7a0b754ee7ae9d865fda740f17' | ||
| 5 | + | ||
| 6 | +See http://stdcs.supst.serpro/manual/html/#captcha/page/introducao.html for more details |
| 1 | +++ a/controllers/serpro_captcha_plugin_admin_controller.rb | ||
| @@ -0,0 +1,17 @@ | @@ -0,0 +1,17 @@ | ||
| 1 | +class SerproCaptchaPluginAdminController < PluginAdminController | ||
| 2 | + | ||
| 3 | + append_view_path File.join(File.dirname(__FILE__) + '/../views') | ||
| 4 | + | ||
| 5 | + def index | ||
| 6 | + end | ||
| 7 | + | ||
| 8 | + def update | ||
| 9 | + if @environment.update_attributes(params[:environment]) | ||
| 10 | + session[:notice] = _('Captcha configuration updated successfully.') | ||
| 11 | + else | ||
| 12 | + session[:notice] = _('Captcha configuration could not be saved.') | ||
| 13 | + end | ||
| 14 | + render :action => 'index' | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | +end |
| 1 | +++ a/lib/ext/environment.rb | ||
| @@ -0,0 +1,35 @@ | @@ -0,0 +1,35 @@ | ||
| 1 | +require_dependency 'environment' | ||
| 2 | + | ||
| 3 | +class Environment | ||
| 4 | + | ||
| 5 | + #Captcha settings | ||
| 6 | + settings_items :serpro_captcha_plugin, :type => ActiveSupport::HashWithIndifferentAccess, :default => {} | ||
| 7 | + | ||
| 8 | +# settings_items :verify_uri, :type => :string, :default => 'http://captcha.servicoscorporativos.serpro.gov.br/captchavalidar/1.0.0/validar' | ||
| 9 | +# settings_items :serpro_client_id, :type => :string, :default => 'fdbcdc7a0b754ee7ae9d865fda740f17' | ||
| 10 | + | ||
| 11 | + attr_accessible :serpro_captcha_plugin_attributes, :serpro_captcha_verify_uri, :serpro_captcha_client_id | ||
| 12 | + | ||
| 13 | + def serpro_captcha_plugin_attributes | ||
| 14 | + self.serpro_captcha_plugin || {} | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + def serpro_captcha_verify_uri= verify_uri | ||
| 18 | + self.serpro_captcha_plugin = {} if self.serpro_captcha_plugin.blank? | ||
| 19 | + self.serpro_captcha_plugin['serpro_captcha_verify_uri'] = verify_uri | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + def serpro_captcha_verify_uri | ||
| 23 | + self.serpro_captcha_plugin['serpro_captcha_verify_uri'] | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + def serpro_captcha_client_id= client_id | ||
| 27 | + self.serpro_captcha_plugin = {} if self.serpro_captcha_plugin.blank? | ||
| 28 | + self.serpro_captcha_plugin['serpro_captcha_client_id'] = client_id | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + def serpro_captcha_client_id | ||
| 32 | + self.serpro_captcha_plugin['serpro_captcha_client_id'] | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | +end |
| 1 | +++ a/lib/serpro_captcha_plugin.rb | ||
| @@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
| 1 | +class SerproCaptchaPlugin < Noosfero::Plugin | ||
| 2 | + | ||
| 3 | + def self.plugin_name | ||
| 4 | + _('Serpro\'s captcha plugin') | ||
| 5 | + end | ||
| 6 | + | ||
| 7 | + def self.plugin_description | ||
| 8 | + _("Provides a plugin to Serpro's captcha infrastructure.") | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def self.api_mount_points | ||
| 12 | + [SerproCaptchaPlugin::API ] | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + def test_captcha(remote_ip, params, environment) | ||
| 16 | + scv = SerproCaptchaVerification.new | ||
| 17 | + return scv.verify_serpro_captcha(environment.serpro_captcha_client_id, params[:txtToken_captcha_serpro_gov_br], params[:captcha_text], environment.serpro_captcha_verify_uri) | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | +end |
| 1 | +++ a/lib/serpro_captcha_verification.rb | ||
| @@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
| 1 | +class SerproCaptchaVerification | ||
| 2 | + | ||
| 3 | + # return true or a hash with the error | ||
| 4 | + # :user_message, :status, :log_message, :javascript_console_message | ||
| 5 | + def verify_serpro_captcha(client_id, token, captcha_text, verify_uri) | ||
| 6 | + msg_icve = _('Internal captcha validation error') | ||
| 7 | + msg_esca = 'Environment serpro_captcha_plugin_attributes' | ||
| 8 | + return hash_error(msg_icve, 500, nil, "#{msg_esca} verify_uri not defined") if verify_uri.nil? | ||
| 9 | + return hash_error(msg_icve, 500, nil, "#{msg_esca} client_id not defined") if client_id.nil? | ||
| 10 | + return hash_error(_("Error processing token validation"), 500, nil, _("Missing Serpro's Captcha token")) unless token | ||
| 11 | + return hash_error(_('Captcha text has not been filled'), 403) unless captcha_text | ||
| 12 | + uri = URI(verify_uri) | ||
| 13 | + http = Net::HTTP.new(uri.host, uri.port) | ||
| 14 | + request = Net::HTTP::Post.new(uri.path) | ||
| 15 | + verify_string = "#{client_id}&#{token}&#{captcha_text}" | ||
| 16 | + request.body = verify_string | ||
| 17 | + body = http.request(request).body | ||
| 18 | + return true if body == '1' | ||
| 19 | + return hash_error(_("Internal captcha validation error"), 500, body, "Unable to reach Serpro's Captcha validation service") if body == "Activity timed out" | ||
| 20 | + return hash_error(_("Wrong captcha text, please try again"), 403) if body == '0' | ||
| 21 | + return hash_error(_("Serpro's captcha token not found"), 500) if body == '2' | ||
| 22 | + return hash_error(_("No data sent to validation server or other serious problem"), 500) if body == -1 | ||
| 23 | + #Catches all errors at the end | ||
| 24 | + return hash_error(_("Internal captcha validation error"), 500, nil, "Error validating Serpro's captcha service returned: #{body}") | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + def hash_error(user_message, status, log_message=nil, javascript_console_message=nil) | ||
| 28 | + {user_message: user_message, status: status, log_message: log_message, javascript_console_message: javascript_console_message} | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | +end |
| 1 | +++ a/test/functional/account_controller_plugin_test.rb | ||
| @@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +# Re-raise errors caught by the controller. | ||
| 4 | +class AccountController; def rescue_action(e) raise e end; end | ||
| 5 | + | ||
| 6 | +class AccountControllerPluginTest < ActionController::TestCase | ||
| 7 | + | ||
| 8 | + def setup | ||
| 9 | + @controller = AccountController.new | ||
| 10 | + @request = ActionController::TestRequest.new | ||
| 11 | + @response = ActionController::TestResponse.new | ||
| 12 | + | ||
| 13 | + @environment = Environment.default | ||
| 14 | + @environment.enabled_plugins = ['SerproCaptchaPlugin'] | ||
| 15 | + @environment.save! | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | +end |
| 1 | +++ a/test/test_helper.rb | ||
| @@ -0,0 +1,65 @@ | @@ -0,0 +1,65 @@ | ||
| 1 | +require "#{Rails.root}/lib/noosfero/api/helpers" | ||
| 2 | + | ||
| 3 | +class ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + include Rack::Test::Methods | ||
| 6 | + | ||
| 7 | + def app | ||
| 8 | + Noosfero::API::API | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def pass_captcha(mocked_url, captcha_verification_body) | ||
| 12 | + stub_request(:post, mocked_url). | ||
| 13 | + with(:body => captcha_verification_body, | ||
| 14 | + :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). | ||
| 15 | + to_return(:status => 200, :body => "1", :headers => {'Content-Length' => 1}) | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def fail_captcha_text(mocked_url, captcha_verification_body) | ||
| 19 | + stub_request(:post, mocked_url). | ||
| 20 | + with(:body => captcha_verification_body, | ||
| 21 | + :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). | ||
| 22 | + to_return(:status => 200, :body => "0", :headers => {'Content-Length' => 1}) | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + def login_with_captcha | ||
| 26 | + json = do_login_captcha_from_api | ||
| 27 | + @private_token = json["private_token"] | ||
| 28 | + @params = { "private_token" => @private_token} | ||
| 29 | + json | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + ## Performs a login using the session.rb but mocking the | ||
| 33 | + ## real HTTP request to validate the captcha. | ||
| 34 | + def do_login_captcha_from_api | ||
| 35 | + post "/api/v1/login-captcha" | ||
| 36 | + json = JSON.parse(last_response.body) | ||
| 37 | + json | ||
| 38 | + end | ||
| 39 | + | ||
| 40 | + def login_api | ||
| 41 | + @environment = Environment.default | ||
| 42 | + @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment) | ||
| 43 | + @user.activate | ||
| 44 | + @person = @user.person | ||
| 45 | + | ||
| 46 | + post "/api/v1/login?login=testapi&password=testapi" | ||
| 47 | + json = JSON.parse(last_response.body) | ||
| 48 | + @private_token = json["private_token"] | ||
| 49 | + unless @private_token | ||
| 50 | + @user.generate_private_token! | ||
| 51 | + @private_token = @user.private_token | ||
| 52 | + end | ||
| 53 | + | ||
| 54 | + @params = {:private_token => @private_token} | ||
| 55 | + end | ||
| 56 | + attr_accessor :private_token, :user, :person, :params, :environment | ||
| 57 | + | ||
| 58 | + private | ||
| 59 | + | ||
| 60 | + def json_response_ids(kind) | ||
| 61 | + json = JSON.parse(last_response.body) | ||
| 62 | + json[kind.to_s].map {|c| c['id']} | ||
| 63 | + end | ||
| 64 | + | ||
| 65 | +end |
| 1 | +++ a/test/unit/serpro_captcha_verification_test.rb | ||
| @@ -0,0 +1,109 @@ | @@ -0,0 +1,109 @@ | ||
| 1 | +require 'webmock' | ||
| 2 | +include WebMock::API | ||
| 3 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
| 4 | +require_relative '../test_helper' | ||
| 5 | + | ||
| 6 | +class SerproCaptchaVerificationTest < ActiveSupport::TestCase | ||
| 7 | + | ||
| 8 | + def setup | ||
| 9 | + @environment = Environment.default | ||
| 10 | + @environment.enabled_plugins = ['SerproCaptchaPlugin'] | ||
| 11 | + @environment.serpro_captcha_verify_uri="http://www.somecompany.com:443/validate" | ||
| 12 | + @environment.serpro_captcha_client_id='323232' | ||
| 13 | + @environment.save! | ||
| 14 | + @captcha_token = "642646" | ||
| 15 | + @captcha_text = "44641441" | ||
| 16 | + @captcha_verification_body = "#{@environment.serpro_captcha_client_id}&#{@captcha_token}&#{@captcha_text}" | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + def login_with_captcha | ||
| 20 | + store = Noosfero::API::SessionStore.create("captcha") | ||
| 21 | + ## Initialize the data for the session store | ||
| 22 | + store.data = [] | ||
| 23 | + ## Put it back in cache | ||
| 24 | + store.store | ||
| 25 | + { "private_token" => "#{store.private_token}" } | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + def create_article(name) | ||
| 29 | + person = fast_create(Person, :environment_id => @environment.id) | ||
| 30 | + fast_create(Article, :profile_id => person.id, :name => name) | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + should 'register a user when there are no enabled captcha pluging' do | ||
| 34 | + @environment.enabled_plugins = [] | ||
| 35 | + @environment.save! | ||
| 36 | + Environment.default.enable('skip_new_user_email_confirmation') | ||
| 37 | + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } | ||
| 38 | + post "/api/v1/register?#{params.to_query}" | ||
| 39 | + assert_equal 201, last_response.status | ||
| 40 | + json = JSON.parse(last_response.body) | ||
| 41 | + assert User['newuserapi'].activated? | ||
| 42 | + assert json['user']['private_token'].present? | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | + should 'not register a user if captcha fails' do | ||
| 46 | + fail_captcha_text @environment.serpro_captcha_verify_uri, @captcha_verification_body | ||
| 47 | + Environment.default.enable('skip_new_user_email_confirmation') | ||
| 48 | + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com", :txtToken_captcha_serpro_gov_br => @captcha_token, :captcha_text => @captcha_text} | ||
| 49 | + post "/api/v1/register?#{params.to_query}" | ||
| 50 | + assert_equal 403, last_response.status | ||
| 51 | + json = JSON.parse(last_response.body) | ||
| 52 | + assert_equal json["message"], _("Wrong captcha text, please try again") | ||
| 53 | + end | ||
| 54 | + | ||
| 55 | + should 'verify_serpro_captcha' do | ||
| 56 | + pass_captcha @environment.serpro_captcha_verify_uri, @captcha_verification_body | ||
| 57 | + scv = SerproCaptchaVerification.new | ||
| 58 | + assert scv.verify_serpro_captcha(@environment.serpro_captcha_client_id, @captcha_token, @captcha_text, @environment.serpro_captcha_verify_uri) | ||
| 59 | + end | ||
| 60 | + | ||
| 61 | + should 'fail captcha if user has not filled Serpro\' captcha text' do | ||
| 62 | + pass_captcha @environment.serpro_captcha_verify_uri, @captcha_verification_body | ||
| 63 | + scv = SerproCaptchaVerification.new | ||
| 64 | + hash = scv.verify_serpro_captcha(@environment.serpro_captcha_client_id, @captcha_token, nil, @environment.serpro_captcha_verify_uri) | ||
| 65 | + assert hash[:user_message], _('Captcha text has not been filled') | ||
| 66 | + end | ||
| 67 | + | ||
| 68 | + should 'fail captcha if Serpro\' captcha token has not been sent' do | ||
| 69 | + pass_captcha @environment.serpro_captcha_verify_uri, @captcha_verification_body | ||
| 70 | + scv = SerproCaptchaVerification.new | ||
| 71 | + hash = scv.verify_serpro_captcha(@environment.serpro_captcha_client_id, nil, @captcha_text, @environment.serpro_captcha_verify_uri) | ||
| 72 | + assert hash[:javascript_console_message], _("Missing Serpro's Captcha token") | ||
| 73 | + end | ||
| 74 | + | ||
| 75 | + should 'fail captcha text' do | ||
| 76 | + fail_captcha_text @environment.serpro_captcha_verify_uri, @captcha_verification_body | ||
| 77 | + scv = SerproCaptchaVerification.new | ||
| 78 | + hash = scv.verify_serpro_captcha(@environment.serpro_captcha_client_id, nil, @captcha_text, @environment.serpro_captcha_verify_uri) | ||
| 79 | + assert hash[:javascript_console_message], _("Wrong captcha text, please try again") | ||
| 80 | + end | ||
| 81 | + | ||
| 82 | + should 'not perform a vote without authentication' do | ||
| 83 | + article = create_article('Article 1') | ||
| 84 | + params = {} | ||
| 85 | + params[:value] = 1 | ||
| 86 | + | ||
| 87 | + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" | ||
| 88 | + json = JSON.parse(last_response.body) | ||
| 89 | + assert_equal 401, last_response.status | ||
| 90 | + end | ||
| 91 | + | ||
| 92 | + should 'perform a vote on an article identified by id' do | ||
| 93 | + pass_captcha @environment.serpro_captcha_verify_uri, @captcha_verification_body | ||
| 94 | + params = {} | ||
| 95 | + params[:txtToken_captcha_serpro_gov_br]= @captcha_token | ||
| 96 | + params[:captcha_text]= @captcha_text | ||
| 97 | + post "/api/v1/login-captcha?#{params.to_query}" | ||
| 98 | + json = JSON.parse(last_response.body) | ||
| 99 | + article = create_article('Article 1') | ||
| 100 | + params = {} | ||
| 101 | + params[:private_token] = json['private_token'] | ||
| 102 | + params[:value] = 1 | ||
| 103 | + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" | ||
| 104 | + json = JSON.parse(last_response.body) | ||
| 105 | + assert_not_equal 401, last_response.status | ||
| 106 | + assert_equal true, json['vote'] | ||
| 107 | + end | ||
| 108 | + | ||
| 109 | +end |
| 1 | +++ a/views/serpro_captcha_plugin_admin/index.html.erb | ||
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +<h1><%= _("Serpro's Captcha Management") %> </h1> | ||
| 2 | + | ||
| 3 | +<%= labelled_form_for(:environment, :url => {:action => 'update'}) do |f| %> | ||
| 4 | + | ||
| 5 | +<table> | ||
| 6 | + <tr> | ||
| 7 | + <th><%= c_('Configuration') %></th> | ||
| 8 | + <th><%= _('Value') %></th> | ||
| 9 | + </tr> | ||
| 10 | + <tr> | ||
| 11 | + <td><%= _('Verify URI') %></td> | ||
| 12 | + <td><%= text_field :environment, :serpro_captcha_verify_uri %></td> | ||
| 13 | + </tr> | ||
| 14 | + <tr> | ||
| 15 | + <td><%= _('Client Id') %></td> | ||
| 16 | + <td><%= text_field :environment, :serpro_captcha_client_id %></td> | ||
| 17 | + </tr> | ||
| 18 | +</table> | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +<div> | ||
| 22 | + <% button_bar do %> | ||
| 23 | + <%= submit_button('save', c_('Save changes')) %> | ||
| 24 | + <%= button :back, _('Back to plugins administration panel'), :controller => 'plugins' %> | ||
| 25 | + <% end %> | ||
| 26 | +</div> | ||
| 27 | + | ||
| 28 | +<% end %> |