Commit 8ebfe2664ab0d16dddc34a76af79461da9147acf
Exists in
theme-brasil-digital-from-staging
and in
3 other branches
Merge branch 'login-captcha' into 'staging'
Login captcha Merge previous repository with staging See merge request !7
Showing
8 changed files
with
185 additions
and
39 deletions
Show diff stats
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
@@ -22,7 +22,8 @@ require_relative '../../find_by_contents' | @@ -22,7 +22,8 @@ require_relative '../../find_by_contents' | ||
22 | 22 | ||
23 | def current_tmp_user | 23 | def current_tmp_user |
24 | private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s | 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 | @current_tmp_user | 27 | @current_tmp_user |
27 | end | 28 | end |
28 | 29 | ||
@@ -66,6 +67,19 @@ require_relative '../../find_by_contents' | @@ -66,6 +67,19 @@ require_relative '../../find_by_contents' | ||
66 | include FindByContents | 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 | #### SEARCH | 83 | #### SEARCH |
70 | #################################################################### | 84 | #################################################################### |
71 | def multiple_search?(searches=nil) | 85 | def multiple_search?(searches=nil) |
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,19 @@ module Noosfero | @@ -149,13 +149,19 @@ 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 | ||
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 | end | 165 | end |
160 | end | 166 | end |
161 | 167 |
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 400, last_response.status | ||
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/login_captcha_test.rb
@@ -47,6 +47,26 @@ class LoginCaptchaTest < ActiveSupport::TestCase | @@ -47,6 +47,26 @@ class LoginCaptchaTest < ActiveSupport::TestCase | ||
47 | assert_equal true, json['vote'] | 47 | assert_equal true, json['vote'] |
48 | end | 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 | should 'not follow any article' do | 70 | should 'not follow any article' do |
51 | login_with_captcha | 71 | login_with_captcha |
52 | article = create_article('Article 1') | 72 | article = create_article('Article 1') |
@@ -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 |