colaboraciones anonimas #75
This commit is contained in:
parent
6713821dd5
commit
583405f9ff
9 changed files with 237 additions and 16 deletions
1
Gemfile
1
Gemfile
|
@ -96,4 +96,5 @@ end
|
|||
group :test do
|
||||
gem 'database_cleaner'
|
||||
gem 'factory_bot_rails'
|
||||
gem 'timecop'
|
||||
end
|
||||
|
|
|
@ -246,7 +246,7 @@ GEM
|
|||
nio4r (~> 2.0)
|
||||
pundit (2.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
rack (2.0.8)
|
||||
rack (2.2.2)
|
||||
rack-proxy (0.6.5)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
|
@ -367,6 +367,7 @@ GEM
|
|||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.10)
|
||||
timecop (0.9.1)
|
||||
turbolinks (5.2.1)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
|
@ -452,6 +453,7 @@ DEPENDENCIES
|
|||
sqlite3
|
||||
sucker_punch
|
||||
terminal-table
|
||||
timecop
|
||||
turbolinks (~> 5)
|
||||
uglifier (>= 1.3.0)
|
||||
validates_hostname
|
||||
|
|
|
@ -28,7 +28,7 @@ module Api
|
|||
expires = 30.minutes
|
||||
cookies.encrypted[site] = {
|
||||
httponly: true,
|
||||
secure: true,
|
||||
secure: !Rails.env.test?,
|
||||
expires: expires,
|
||||
same_site: :none,
|
||||
value: {
|
||||
|
|
|
@ -26,8 +26,15 @@ module Api
|
|||
# No procesar nada más si ya se aplicaron todos los filtros
|
||||
return if performed?
|
||||
|
||||
usuarie = Site::Author.new name: 'Anon', email: "anon@#{site.hostname}"
|
||||
service = PostService.new(params: params,
|
||||
site: site,
|
||||
usuarie: usuarie)
|
||||
|
||||
service.create_anonymous
|
||||
|
||||
# Redirigir a la URL de agradecimiento
|
||||
redirect_to params[:redirect_to]
|
||||
redirect_to params[:redirect_to] || site.url
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -39,7 +46,7 @@ module Api
|
|||
def cookie_is_valid?
|
||||
unless cookies.encrypted[site_id] &&
|
||||
cookies.encrypted[site_id]['expires'] > Time.now.to_i
|
||||
render html: nil, status: :no_content
|
||||
render html: 'cookie_invalid', status: :no_content
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -50,9 +57,11 @@ module Api
|
|||
# TODO: Pensar una forma de redirigir al origen sin vaciar el
|
||||
# formulario para que le usuarie recargue la cookie.
|
||||
def valid_authenticity_token_in_cookie?
|
||||
unless valid_authenticity_token? session, cookies.encrypted[site_id]
|
||||
render html: nil, status: :no_content
|
||||
if valid_authenticity_token? session, cookies.encrypted[site_id]['csrf']
|
||||
return
|
||||
end
|
||||
|
||||
render html: 'token_invalid', status: :no_content
|
||||
end
|
||||
|
||||
# El sitio existe y soporta colaboracion anónima
|
||||
|
@ -62,7 +71,7 @@ module Api
|
|||
def site_exists_and_is_anonymous?
|
||||
_, anon = site_anon_pair
|
||||
|
||||
render html: nil, status: :no_content
|
||||
render html: 'site_not_anon', status: :no_content unless anon
|
||||
end
|
||||
|
||||
# El navegador envía la URL del sitio en el encabezado Origin,
|
||||
|
@ -70,9 +79,9 @@ module Api
|
|||
def site_is_origin?
|
||||
site, = site_anon_pair
|
||||
|
||||
unless "https://#{site}" === request.headers['Origin']
|
||||
render html: nil, status: :no_content
|
||||
end
|
||||
return if request.headers['Origin'] == "https://#{site}"
|
||||
|
||||
render html: 'site_not_origin', status: :no_content
|
||||
end
|
||||
|
||||
# Solo soy un atajo
|
||||
|
|
5
app/models/site/author.rb
Normal file
5
app/models/site/author.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Site
|
||||
Author = Struct.new :email, :name, keyword_init: true
|
||||
end
|
|
@ -8,7 +8,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
#
|
||||
# @return Post
|
||||
def create
|
||||
self.post = site.posts(lang: params[:post][:lang] || I18n.locale)
|
||||
self.post = site.posts(lang: lang)
|
||||
.build(layout: params[:post][:layout])
|
||||
post.usuaries << usuarie
|
||||
params[:post][:draft] = true if site.invitade? usuarie
|
||||
|
@ -20,6 +20,19 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
post
|
||||
end
|
||||
|
||||
# Crear un post anónimo, con opciones más limitadas
|
||||
def create_anonymous
|
||||
# XXX: Confiamos en el parámetro de idioma porque estamos
|
||||
# verificándolos en Site#posts
|
||||
self.post = site.posts(lang: lang)
|
||||
.build(layout: params[:post][:layout])
|
||||
# Los artículos anónimos siempre son borradores
|
||||
params[:post][:draft] = true
|
||||
|
||||
commit(action: :created) if post.update(anon_post_params)
|
||||
post
|
||||
end
|
||||
|
||||
def update
|
||||
post.usuaries << usuarie
|
||||
params[:post][:draft] = true if site.invitade? usuarie
|
||||
|
@ -82,6 +95,13 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
params.require(:post).permit(post.params)
|
||||
end
|
||||
|
||||
# Eliminar metadatos internos
|
||||
def anon_post_params
|
||||
post_params.delete_if do |k, _|
|
||||
%w[date slug order uuid].include? k
|
||||
end
|
||||
end
|
||||
|
||||
def lang
|
||||
params[:post][:lang] || I18n.locale
|
||||
end
|
||||
|
|
12
db/seeds.rb
12
db/seeds.rb
|
@ -16,10 +16,12 @@ licencias.each do |l|
|
|||
licencia.update l
|
||||
end
|
||||
|
||||
YAML.safe_load(File.read('db/seeds/sites.yml')).each do |site|
|
||||
site = Site.find_or_create_by name: site['name']
|
||||
unless Rails.env.test?
|
||||
YAML.safe_load(File.read('db/seeds/sites.yml')).each do |site|
|
||||
site = Site.find_or_create_by name: site['name']
|
||||
|
||||
site.update licencia: Licencia.first, design: Design.first,
|
||||
title: site.name, description: 'x' * 50,
|
||||
deploys: [DeployLocal.new]
|
||||
site.update licencia: Licencia.first, design: Design.first,
|
||||
title: site.name, description: 'x' * 50,
|
||||
deploys: [DeployLocal.new]
|
||||
end
|
||||
end
|
||||
|
|
46
test/controllers/api/v1/invitades_controller_test.rb
Normal file
46
test/controllers/api/v1/invitades_controller_test.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@rol = create :rol
|
||||
@site = @rol.site
|
||||
@usuarie = @rol.usuarie
|
||||
|
||||
@site.update_attribute :colaboracion_anonima, true
|
||||
end
|
||||
|
||||
teardown do
|
||||
@site.destroy
|
||||
end
|
||||
|
||||
test 'primero hay que pedir una cookie' do
|
||||
get v1_site_invitades_cookie_url(@site)
|
||||
|
||||
assert cookies[@site.name]
|
||||
assert cookies['_sutty_session']
|
||||
end
|
||||
|
||||
test 'solo si el sitio existe' do
|
||||
site = SecureRandom.hex
|
||||
|
||||
get v1_site_invitades_cookie_url(site_id: site)
|
||||
|
||||
assert_not cookies[site]
|
||||
assert_not cookies['_sutty_session']
|
||||
end
|
||||
|
||||
test 'solo si el sitio tiene colaboracion anonima' do
|
||||
@site.update_attribute :colaboracion_anonima, false
|
||||
|
||||
get v1_site_invitades_cookie_url(@site)
|
||||
|
||||
assert_not cookies[@site.name]
|
||||
assert_not cookies['_sutty_session']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
136
test/controllers/api/v1/posts_controller_test.rb
Normal file
136
test/controllers/api/v1/posts_controller_test.rb
Normal file
|
@ -0,0 +1,136 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@rol = create :rol
|
||||
@site = @rol.site
|
||||
@usuarie = @rol.usuarie
|
||||
|
||||
@site.update_attribute :colaboracion_anonima, true
|
||||
end
|
||||
|
||||
teardown do
|
||||
@site.destroy
|
||||
end
|
||||
|
||||
test 'no se pueden enviar sin cookie' do
|
||||
post v1_site_posts_url(@site), params: {
|
||||
post: {
|
||||
title: SecureRandom.hex,
|
||||
description: SecureRandom.hex
|
||||
}
|
||||
}
|
||||
|
||||
posts = @site.posts.size
|
||||
@site = Site.find(@site.id)
|
||||
|
||||
assert_equal posts, @site.posts.size
|
||||
assert_response :no_content
|
||||
end
|
||||
|
||||
test 'no se pueden enviar a sitios que no existen' do
|
||||
site = SecureRandom.hex
|
||||
|
||||
get v1_site_invitades_cookie_url(site_id: site)
|
||||
|
||||
post v1_site_posts_url(site_id: site),
|
||||
headers: { cookies: cookies },
|
||||
params: {
|
||||
post: {
|
||||
title: SecureRandom.hex,
|
||||
description: SecureRandom.hex
|
||||
}
|
||||
}
|
||||
|
||||
assert_response :no_content
|
||||
end
|
||||
|
||||
test 'antes hay que pedir una cookie' do
|
||||
assert_equal 2, @site.posts.size
|
||||
|
||||
get v1_site_invitades_cookie_url(@site)
|
||||
|
||||
post v1_site_posts_url(@site),
|
||||
headers: {
|
||||
cookies: cookies,
|
||||
origin: "https://#{@site.name}"
|
||||
},
|
||||
params: {
|
||||
post: {
|
||||
title: SecureRandom.hex,
|
||||
description: SecureRandom.hex
|
||||
}
|
||||
}
|
||||
|
||||
# XXX: No tenemos reload
|
||||
@site = Site.find @site.id
|
||||
|
||||
assert_equal 3, @site.posts.size
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
test 'no se pueden enviar algunos valores' do
|
||||
uuid = SecureRandom.uuid
|
||||
date = Date.today + 2.days
|
||||
slug = SecureRandom.hex
|
||||
title = SecureRandom.hex
|
||||
order = (rand * 100).to_i
|
||||
|
||||
get v1_site_invitades_cookie_url(@site)
|
||||
|
||||
post v1_site_posts_url(@site),
|
||||
headers: {
|
||||
cookies: cookies,
|
||||
origin: "https://#{@site.name}"
|
||||
},
|
||||
params: {
|
||||
post: {
|
||||
title: title,
|
||||
description: SecureRandom.hex,
|
||||
uuid: uuid,
|
||||
date: date,
|
||||
slug: slug,
|
||||
order: order
|
||||
}
|
||||
}
|
||||
|
||||
# XXX: No tenemos reload
|
||||
@site = Site.find @site.id
|
||||
p = @site.posts.find_by title: title
|
||||
|
||||
assert_not_equal uuid, p.uuid.value
|
||||
assert_not_equal slug, p.slug.value
|
||||
assert_not_equal order, p.order.value
|
||||
assert_not_equal date, p.date.value
|
||||
end
|
||||
|
||||
test 'las cookies tienen un vencimiento interno' do
|
||||
assert_equal 2, @site.posts.size
|
||||
|
||||
get v1_site_invitades_cookie_url(@site)
|
||||
|
||||
Timecop.freeze(Time.now + 31.minutes) do
|
||||
post v1_site_posts_url(@site),
|
||||
headers: {
|
||||
cookies: cookies,
|
||||
origin: "https://#{@site.name}"
|
||||
},
|
||||
params: {
|
||||
post: {
|
||||
title: SecureRandom.hex,
|
||||
description: SecureRandom.hex
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
@site = Site.find @site.id
|
||||
assert_response :no_content
|
||||
assert_equal 2, @site.posts.size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue