Commit 8ebfe2664ab0d16dddc34a76af79461da9147acf

Authored by Victor Costa
2 parents eaa94bb4 553a5c2f

Merge branch 'login-captcha' into 'staging'

Login captcha

Merge previous repository with staging

See merge request !7
lib/noosfero/api/captcha_session_store.rb
... ... @@ -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
... ... @@ -22,7 +22,8 @@ require_relative '../../find_by_contents'
22 22  
23 23 def current_tmp_user
24 24 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
25   - @current_tmp_user = Noosfero::API::CaptchaSessionStore.get(private_token)
  25 + ## Get the "captcha" session store
  26 + @current_tmp_user = Noosfero::API::SessionStore.get("captcha##{private_token}")
26 27 @current_tmp_user
27 28 end
28 29  
... ... @@ -66,6 +67,19 @@ require_relative '../../find_by_contents'
66 67 include FindByContents
67 68  
68 69 ####################################################################
  70 + #### VOTE
  71 + ####################################################################
  72 + def do_vote(article, current_person, value)
  73 + begin
  74 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  75 + return vote.save!
  76 + rescue ActiveRecord::RecordInvalid => e
  77 + render_api_error!(e.message, 400)
  78 + return false
  79 + end
  80 + end
  81 +
  82 + ####################################################################
69 83 #### SEARCH
70 84 ####################################################################
71 85 def multiple_search?(searches=nil)
... ...
lib/noosfero/api/session.rb
... ... @@ -16,7 +16,11 @@ module Noosfero
16 16 # this return is just to improve the clarity of the execution path
17 17 return unless test_captcha(remote_ip, params, environment)
18 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 24 { "private_token" => "#{store.private_token}" }
21 25 end
22 26  
... ...
lib/noosfero/api/session_store.rb 0 → 100644
... ... @@ -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,19 @@ module Noosfero
149 149 # FIXME verify allowed values
150 150 render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
151 151 article = find_article(environment.articles, params[:id])
152   -
153   - begin
154   - 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)
  152 + ## If login with captcha
  153 + if @current_tmp_user
  154 + # Vote allowed only if data does not include this article
  155 + vote = (@current_tmp_user.data.include? article.id) ? false : true
  156 + if vote
  157 + @current_tmp_user.data << article.id
  158 + @current_tmp_user.store
  159 + {:vote => do_vote(article, current_person, value)}
  160 + else
  161 + {:vote => false}
  162 + end
  163 + else
  164 + {:vote => do_vote(article, current_person, value)}
159 165 end
160 166 end
161 167  
... ...
test/unit/api/articles_test.rb
... ... @@ -127,6 +127,37 @@ class ArticlesTest &lt; ActiveSupport::TestCase
127 127 assert_equal 1, json['total_followers']
128 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 400, last_response.status
  158 + end
  159 +
  160 +
130 161 should 'perform a vote in a article identified by id' do
131 162 article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
132 163 @params[:value] = 1
... ... @@ -136,6 +167,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
136 167  
137 168 assert_not_equal 401, last_response.status
138 169 assert_equal true, json['vote']
  170 +
139 171 end
140 172  
141 173 should 'not perform a vote in a archived article' do
... ...
test/unit/api/login_captcha_test.rb
... ... @@ -47,6 +47,26 @@ class LoginCaptchaTest &lt; ActiveSupport::TestCase
47 47 assert_equal true, json['vote']
48 48 end
49 49  
  50 + should 'not perform a vote twice in same article' do
  51 + login_with_captcha
  52 + article = create_article('Article 1')
  53 + params[:value] = 1
  54 +
  55 + ## Perform a vote twice in API should compute only one vote
  56 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  57 + json = JSON.parse(last_response.body)
  58 + assert_equal true, json['vote']
  59 +
  60 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  61 + json = JSON.parse(last_response.body)
  62 + ## Should not allow vote again
  63 + assert_equal false, json['vote']
  64 +
  65 + total = article.votes_total
  66 +
  67 + assert_equal 1, total
  68 + end
  69 +
50 70 should 'not follow any article' do
51 71 login_with_captcha
52 72 article = create_article('Article 1')
... ...
test/unit/api/session_store_test.rb 0 → 100644
... ... @@ -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 48 \ No newline at end of file
... ...