merges master and fix conflicts

This commit is contained in:
kikito
2015-08-25 19:31:25 +02:00
41 changed files with 572 additions and 96 deletions

View File

@@ -1,7 +1,7 @@
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.3'
gem 'rails', '4.2.4'
# Use PostgreSQL
gem 'pg'
# Use SCSS for stylesheets
@@ -21,6 +21,11 @@ gem 'turbolinks'
gem 'devise'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
gem 'kaminari'
gem 'acts_as_commentable_with_threading'
gem 'acts-as-taggable-on'
@@ -32,7 +37,7 @@ gem 'simple_captcha2', require: 'simple_captcha'
gem 'ckeditor'
gem 'cancancan'
gem 'social-share-button'
gem 'initialjs-rails'
gem 'initialjs-rails', '0.2.0'
gem 'unicorn'
gem 'paranoia'

View File

@@ -1,36 +1,36 @@
GEM
remote: https://rubygems.org/
specs:
actionmailer (4.2.3)
actionpack (= 4.2.3)
actionview (= 4.2.3)
activejob (= 4.2.3)
actionmailer (4.2.4)
actionpack (= 4.2.4)
actionview (= 4.2.4)
activejob (= 4.2.4)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.3)
actionview (= 4.2.3)
activesupport (= 4.2.3)
actionpack (4.2.4)
actionview (= 4.2.4)
activesupport (= 4.2.4)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.3)
activesupport (= 4.2.3)
actionview (4.2.4)
activesupport (= 4.2.4)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.3)
activesupport (= 4.2.3)
activejob (4.2.4)
activesupport (= 4.2.4)
globalid (>= 0.3.0)
activemodel (4.2.3)
activesupport (= 4.2.3)
activemodel (4.2.4)
activesupport (= 4.2.4)
builder (~> 3.1)
activerecord (4.2.3)
activemodel (= 4.2.3)
activesupport (= 4.2.3)
activerecord (4.2.4)
activemodel (= 4.2.4)
activesupport (= 4.2.4)
arel (~> 6.0)
activesupport (4.2.3)
activesupport (4.2.4)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
@@ -133,6 +133,8 @@ GEM
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
faraday (0.9.1)
multipart-post (>= 1.2, < 3)
foundation-rails (5.5.2.1)
railties (>= 3.1.0)
sass (>= 3.3.0, < 3.5)
@@ -150,6 +152,7 @@ GEM
activesupport (>= 4.1.0)
groupdate (2.4.0)
activesupport (>= 3)
hashie (3.4.2)
highline (1.7.3)
http-cookie (1.0.2)
domain_name (~> 0.5)
@@ -162,13 +165,14 @@ GEM
i18n
term-ansicolor (>= 1.3.2)
terminal-table (>= 1.5.1)
initialjs-rails (0.1.0)
initialjs-rails (0.2.0)
railties (>= 3.1, < 5.0)
jquery-rails (4.0.4)
rails-dom-testing (~> 1.0)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
jwt (1.5.1)
kaminari (0.16.3)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
@@ -181,7 +185,7 @@ GEM
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
loofah (2.0.2)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.3)
mime-types (>= 1.16, < 3)
@@ -189,12 +193,38 @@ GEM
mini_portile (0.6.2)
minitest (5.8.0)
multi_json (1.11.2)
multi_xml (0.5.5)
multipart-post (2.0.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
net-ssh (2.9.2)
netrc (0.10.3)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
oauth (0.4.7)
oauth2 (1.0.0)
faraday (>= 0.8, < 0.10)
jwt (~> 1.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (~> 1.2)
omniauth (1.2.2)
hashie (>= 1.2, < 4)
rack (~> 1.0)
omniauth-facebook (2.0.1)
omniauth-oauth2 (~> 1.2)
omniauth-google-oauth2 (0.2.6)
omniauth (> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-oauth (1.1.0)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.3.1)
oauth2 (~> 1.0)
omniauth (~> 1.2)
omniauth-twitter (1.2.1)
json (~> 1.3)
omniauth-oauth (~> 1.1)
orm_adapter (0.5.0)
paranoia (2.1.3)
activerecord (~> 4.0)
@@ -209,28 +239,28 @@ GEM
rack (1.6.4)
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.3)
actionmailer (= 4.2.3)
actionpack (= 4.2.3)
actionview (= 4.2.3)
activejob (= 4.2.3)
activemodel (= 4.2.3)
activerecord (= 4.2.3)
activesupport (= 4.2.3)
rails (4.2.4)
actionmailer (= 4.2.4)
actionpack (= 4.2.4)
actionview (= 4.2.4)
activejob (= 4.2.4)
activemodel (= 4.2.4)
activerecord (= 4.2.4)
activesupport (= 4.2.4)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.3)
railties (= 4.2.4)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.6)
rails-dom-testing (1.0.7)
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.2)
loofah (~> 2.0)
railties (4.2.3)
actionpack (= 4.2.3)
activesupport (= 4.2.3)
railties (4.2.4)
actionpack (= 4.2.4)
activesupport (= 4.2.4)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
raindrops (0.15.0)
@@ -283,7 +313,7 @@ GEM
coffee-rails
sass-rails
spring (1.3.6)
sprockets (3.2.0)
sprockets (3.3.3)
rack (~> 1.0)
sprockets-rails (2.3.2)
actionpack (>= 3.0)
@@ -357,16 +387,20 @@ DEPENDENCIES
fuubar
groupdate
i18n-tasks
initialjs-rails
initialjs-rails (= 0.2.0)
jquery-rails
kaminari
launchy
letter_opener_web (~> 1.3.0)
omniauth
omniauth-facebook
omniauth-google-oauth2
omniauth-twitter
paranoia
pg
poltergeist
quiet_assets
rails (= 4.2.3)
rails (= 4.2.4)
responders
rspec-rails (~> 3.0)
sass-rails (~> 5.0)

View File

@@ -44,6 +44,12 @@ Para ejecutar los tests:
bundle exec bin/rspec
```
### OAuth
Para probar los servicios de autenticación mediante proveedores externos OAuth — en este momento Twitter, Facebook y Google —, necesitas crear una "aplicación" en cada una de las plataformas soportadas y configurar la *key* y el *secret* proporcionados en tu *secrets.yml*
En el caso de Google, comprueba que las APIs *Contacts API* y *Google+ API* están habilitadas para la aplicación.
## Licencia
El código de este proyecto está publicado bajo la licencia AFFERO GPL v3 (ver [LICENSE-AGPLv3.txt](LICENSE-AGPLv3.txt))

View File

@@ -152,7 +152,6 @@ h1, h2, h3, h4, h5, h6 {
}
.f-dropdown {
li a {
font-size: rem-calc(12);
@@ -170,6 +169,19 @@ h1, h2, h3, h4, h5, h6 {
}
}
.margin {
margin-top: $line-height;
margin-bottom: $line-height;
}
.margin-top {
margin-top: $line-height;
}
.margin-bottom {
margin-bottom: $line-height;
}
// 04. Header
// - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -514,10 +526,15 @@ footer {
h2 {
clear: both;
font-size: rem-calc(30);
font-size: rem-calc(18);
font-weight: bold;
line-height: $line-height;
@media (min-width: $small-breakpoint) {
font-size: rem-calc(30);
line-height: $line-height*2;
}
}
.back, .icon-angle-left {
@include back;

View File

@@ -85,6 +85,7 @@ $comment-official: rgba(70,219,145,.3);
// 06. Responsive
// - - - - - - - - - - - - - - - - - - - - - - - - -
$small: em-calc(480);
$small-breakpoint: em-calc(640);
$medium-breakpoint: em-calc(1024);
$large-breakpoint: em-calc(1440);

View File

@@ -13,6 +13,8 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :ensure_signup_complete
rescue_from CanCan::AccessDenied do |exception|
redirect_to main_app.root_url, alert: exception.message
end
@@ -40,4 +42,13 @@ class ApplicationController < ActionController::Base
def set_debate_votes(debates)
@voted_values = current_user ? current_user.debate_votes(debates) : {}
end
def ensure_signup_complete
# Ensure we don't go into an infinite loop
return if action_name.in? %w(finish_signup do_finish_signup)
if user_signed_in? && !current_user.email_provided?
redirect_to finish_signup_path
end
end
end

View File

@@ -5,6 +5,9 @@ class Organizations::RegistrationsController < Devise::RegistrationsController
end
end
def success
end
def create
build_resource(sign_up_params)
if resource.valid_with_captcha?
@@ -17,6 +20,11 @@ class Organizations::RegistrationsController < Devise::RegistrationsController
end
end
protected
def after_inactive_sign_up_path_for(resource)
organizations_sign_up_success_path
end
private
def sign_up_params

View File

@@ -0,0 +1,29 @@
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(env["omniauth.auth"], current_user)
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
[:twitter, :facebook, :google_oauth2].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_provided?
super(resource)
else
finish_signup_path
end
end
end

View File

@@ -1,4 +1,5 @@
class Users::RegistrationsController < Devise::RegistrationsController
prepend_before_filter :authenticate_scope!, only: [:edit, :update, :destroy, :finish_signup, :do_finish_signup]
def create
build_resource(sign_up_params)
@@ -9,6 +10,19 @@ class Users::RegistrationsController < Devise::RegistrationsController
end
end
def finish_signup
end
def do_finish_signup
if current_user.update(sign_up_params)
current_user.skip_reconfirmation!
sign_in(current_user, bypass: true)
redirect_to root_url
else
render :finish_signup
end
end
private
def sign_up_params

17
app/models/identity.rb Normal file
View File

@@ -0,0 +1,17 @@
class Identity < ActiveRecord::Base
belongs_to :user
validates :provider, presence: true
validates :uid, presence: true, uniqueness: { scope: :provider }
def self.find_for_oauth(auth)
where(uid: auth.uid, provider: auth.provider).first_or_create
end
def update_user(new_user)
return unless user != new_user
self.user = new_user
save!
end
end

View File

@@ -1,8 +1,12 @@
class User < ActiveRecord::Base
include ActsAsParanoidAliases
OMNIAUTH_EMAIL_PREFIX = 'omniauth@participacion'
OMNIAUTH_EMAIL_REGEX = /\A#{OMNIAUTH_EMAIL_PREFIX}/
apply_simple_captcha
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
acts_as_voter
acts_as_paranoid column: :hidden_at
@@ -11,9 +15,11 @@ class User < ActiveRecord::Base
has_one :moderator
has_one :organization
has_many :inappropiate_flags
has_many :identities, dependent: :destroy
validates :username, presence: true, unless: :organization?
validates :official_level, inclusion: {in: 0..5}
validates_format_of :email, without: OMNIAUTH_EMAIL_REGEX, on: :update
validates_associated :organization, message: false
@@ -25,6 +31,43 @@ class User < ActiveRecord::Base
scope :organizations, -> { joins(:organization) }
scope :officials, -> { where("official_level > 0") }
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
user ||= first_or_create_for_oauth(auth)
# Associate the identity with the user if needed
identity.update_user(user)
user
end
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via RegistrationsController.finish_signup
def self.first_or_create_for_oauth(auth)
email = auth.info.email if auth.info.verified || auth.info.verified_email
user = User.where(email: email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
username: auth.info.nickname || auth.extra.raw_info.name.parameterize('-') || auth.uid,
email: email ? email : "#{OMNIAUTH_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
user
end
def name
organization? ? organization.name : username
end
@@ -67,4 +110,8 @@ class User < ActiveRecord::Base
e.present? ? where(email: e) : none
end
def email_provided?
!!(email && email !~ OMNIAUTH_EMAIL_REGEX) ||
!!(unconfirmed_email && unconfirmed_email !~ OMNIAUTH_EMAIL_REGEX)
end
end

View File

@@ -26,7 +26,7 @@
<div class="small-12 medium-6 column">
<h2><%= t("account.show.avatar")%></h2>
<%= avatar_image(@account, size: 100) %>
<%= avatar_image(@account, seed: @account.id, size: 100) %>
<h2><%= t("account.show.notifications")%></h2>

View File

@@ -14,7 +14,7 @@
<% if comment.user.organization? %>
<%= image_tag("collective_avatar.png", size: 32, class: "avatar left") %>
<% else %>
<%= avatar_image(comment.user, size: 32, class: "left") %>
<%= avatar_image(comment.user, seed: comment.user_id, size: 32, class: "left") %>
<% end %>
<% if comment.user.hidden? %>
<i class="icon-deleted user-deleted"></i>

View File

@@ -13,7 +13,7 @@
<h1><%= @debate.title %></h1>
<div class="debate-info">
<%= avatar_image(@debate.author, size: 32, class: 'author-photo') %>
<%= avatar_image(@debate.author, seed: @debate.author_id, size: 32, class: 'author-photo') %>
<% if @debate.author.hidden? %>
<i class="icon-deleted author-deleted"></i>
<span class="author">

View File

@@ -0,0 +1,7 @@
<br/>
<%= link_to t("omniauth.twitter.sign_in"), user_omniauth_authorize_path(:twitter), class: 'button radius expand' %>
<%= link_to t("omniauth.facebook.sign_in"), user_omniauth_authorize_path(:facebook), class: 'button radius expand' %>
<%= link_to t("omniauth.google_oauth2.sign_in"), user_omniauth_authorize_path(:google_oauth2), class: 'button radius expand' %>
<hr/>

View File

@@ -3,6 +3,8 @@
<div class="panel">
<h2><%= t("devise_views.sessions.new.title") %></h2>
<%= render 'devise/omniauth_form' %>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="row">
<div class="small-12 columns">

View File

@@ -25,7 +25,7 @@
<%= link_to t("devise_views.shared.links.new_unlock"), new_unlock_path(resource_name) %><br>
<% end -%>
<%- if devise_mapping.omniauthable? %>
<%- if devise_mapping.omniauthable? && devise_mapping.name == 'user' %>
<%- resource_class.omniauth_providers.each do |provider| %>
<%= link_to t("devise_views.shared.links.signin_with_provider", provider: provider.to_s.titleize), omniauth_authorize_path(resource_name, provider) %><br>
<% end -%>

View File

@@ -0,0 +1,15 @@
<div class="auth row">
<div class="small-12 medium-8 column small-centered">
<div class="panel">
<h2><%= t("devise_views.organizations.registrations.success.title") %></h2>
<p><%= t("devise_views.organizations.registrations.success.thank_you") %></p>
<p><%= t("devise_views.organizations.registrations.success.instructions_1_html") %></p>
<p><%= t("devise_views.organizations.registrations.success.instructions_2_html") %></p>
<p><%= t("devise_views.organizations.registrations.success.instructions_3_html") %></p>
<p>
<%= link_to t("devise_views.organizations.registrations.success.back_to_index"),
root_path, class: "button radius small margin-top" %>
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,13 @@
<div class="auth row">
<div class="small-12 medium-8 large-5 column small-centered">
<div class="panel">
<h1><%= t('omniauth.finish_signup.title') %></h1>
<%= form_for current_user, as: :user, url: do_finish_signup_path, html: { role: 'form'} do |f| %>
<%= render 'shared/errors', resource: current_user %>
<%= f.email_field :email, placeholder: t("devise_views.users.registrations.new.email_label"), value: nil %>
<%= f.submit t("devise_views.users.registrations.new.submit"), class: 'button radius' %>
<% end %>
</div>
</div>
</div>

View File

@@ -2,6 +2,9 @@
<div class="small-12 medium-8 large-5 column small-centered">
<div class="panel">
<h2><%= t("devise_views.users.registrations.new.title") %></h2>
<%= render 'devise/omniauth_form' %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render 'shared/errors', resource: resource %>

View File

@@ -2,4 +2,10 @@
recaptcha_public_key: <%= ENV["MADRID_RECAPTCHA_PUBLIC_KEY"] %>
recaptcha_private_key: <%= ENV["MADRID_RECAPTCHA_PRIVATE_KEY"] %>
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
twitter_key: <%= ENV["TWITTER_KEY"] %>
twitter_secret: <%= ENV["TWITTER_SECRET"] %>
facebook_key: <%= ENV["FACEBOOK_KEY"] %>
facebook_secret: <%= ENV["FACEBOOK_SECRET"] %>
google_oauth2_key: <%= ENV["GOOGLE_KEY"] %>
google_oauth2_secret: <%= ENV["GOOGLE_SECRET"] %>
server_name: <%= fetch(:server_name) %>

View File

@@ -42,7 +42,7 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.

View File

@@ -42,7 +42,7 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.

View File

@@ -239,6 +239,9 @@ Devise.setup do |config|
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :twitter, Rails.application.secrets.twitter_key, Rails.application.secrets.twitter_secret
config.omniauth :facebook, Rails.application.secrets.facebook_key, Rails.application.secrets.facebook_secret
config.omniauth :google_oauth2, Rails.application.secrets.google_oauth2_key, Rails.application.secrets.google_oauth2_secret
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or

View File

@@ -66,6 +66,13 @@ en:
phone_number_label: "Phone number"
password_confirmation_label: "Confirm password"
submit: "Sign up"
success:
title: "Registration of organization / collective"
thank_you: "Thank you for registering your organization or collective in the website. Now is <b>pending verification</b>."
instructions_1_html: "We will <b>contact you soon</b> in order to verify that you represent your collective."
instructions_2_html: "Meanwhile, <b>review your email</b>. We have sent you a <b>confirmation link to activate your account</b>."
instructions_3_html: "When you confirm your account will then be able to participate as a non-verified organization."
back_to_index: "Ok, go back to index"
sessions:
new:
title: "Log in"

View File

@@ -66,6 +66,13 @@ es:
phone_number_label: "Teléfono"
password_confirmation_label: "Confirmar contraseña"
submit: "Registrarse"
success:
title: "Registro de organización / colectivo"
thank_you: "Gracias por registrar tu colectivo en la web. Ahora está <b>pendiente de verificación</b>."
instructions_1_html: "En breve <b>nos pondremos en contacto contigo</b> para verificar que realmente representas a este colectivo."
instructions_2_html: "Mientras <b>revisa tu correo electrónico</b>, te hemos enviado un <b>enlace para confirmar tu cuenta</b>."
instructions_3_html: "Una vez confirmado, podrás empezar a participar como colectivo no verificado."
back_to_index: "Entendido, volver a la página principal"
sessions:
new:
title: "Entrar"

View File

@@ -148,3 +148,12 @@ en:
all: "You are not authorized to %{action} %{subject}."
welcome:
last_debates: Last debates
omniauth:
finish_signup:
title: Add Email
twitter:
sign_in: Sign in with Twitter
facebook:
sign_in: Sign in with Facebook
google_oauth2:
sign_in: Sign in with Google

View File

@@ -148,3 +148,12 @@ es:
all: "No tienes permiso para realizar la acción '%{action}' sobre %{subject}."
welcome:
last_debates: Últimos debates
omniauth:
finish_signup:
title: Añade tu email
twitter:
sign_in: Entra con Twitter
facebook:
sign_in: Entra con Facebook
google_oauth2:
sign_in: Entra con Google

View File

@@ -1,10 +1,23 @@
Rails.application.routes.draw do
devise_for :users, controllers: { registrations: 'users/registrations' }
devise_for :users, controllers: {
registrations: 'users/registrations',
omniauth_callbacks: 'users/omniauth_callbacks'
}
devise_for :organizations, class_name: 'User',
controllers: {
registrations: 'organizations/registrations',
sessions: 'devise/sessions'
}
sessions: 'devise/sessions',
},
skip: [:omniauth_callbacks]
devise_scope :organization do
get "organizations/sign_up/success", to: "organizations/registrations#success"
end
devise_scope :user do
get :finish_signup, to: 'users/registrations#finish_signup'
patch :do_finish_signup, to: 'users/registrations#do_finish_signup'
end
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".

View File

@@ -14,12 +14,30 @@ default: &default
development:
secret_key_base: 56792feef405a59b18ea7db57b4777e855103882b926413d4afdfb8c0ea8aa86ea6649da4e729c5f5ae324c0ab9338f789174cf48c544173bc18fdc3b14262e4
twitter_key: AAAA
twitter_secret: BBBB
facebook_key: AAAA
facebook_secret: BBBB
google_oauth2_key: AAAA
google_oauth2_secret: BBBB
<<: *default
test:
secret_key_base: 4d5adf961ddd27aef19622d6c0b3234d555f9ee003f022b1f829c92bbe33aaee907be7feb67bd54c14a1a32512fa968565ad405971fbc41bd0797af73c26a796
twitter_key: AAAA
twitter_secret: BBBB
facebook_key: AAAA
facebook_secret: BBBB
google_oauth2_key: AAAA
google_oauth2_secret: BBBB
<<: *default
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
twitter_key: <%= ENV["TWITTER_KEY"] %>
twitter_secret: <%= ENV["TWITTER_SECRET"] %>
facebook_key: <%= ENV["FACEBOOK_KEY"] %>
facebook_secret: <%= ENV["FACEBOOK_SECRET"] %>
google_oauth2_key: <%= ENV["GOOGLE_KEY"] %>
google_oauth2_secret: <%= ENV["GOOGLE_SECRET"] %>
<<: *default

View File

@@ -0,0 +1,11 @@
class CreateIdentities < ActiveRecord::Migration
def change
create_table :identities do |t|
t.references :user, index: true, foreign_key: true
t.string :provider
t.string :uid
t.timestamps null: false
end
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150824113326) do
ActiveRecord::Schema.define(version: 20150824144524) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -66,8 +66,8 @@ ActiveRecord::Schema.define(version: 20150824113326) do
t.integer "author_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "hidden_at"
t.string "visit_id"
t.datetime "hidden_at"
t.datetime "flagged_as_inappropiate_at"
t.integer "inappropiate_flags_count", default: 0
t.datetime "reviewed_at"
@@ -75,6 +75,16 @@ ActiveRecord::Schema.define(version: 20150824113326) do
add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree
create_table "identities", force: :cascade do |t|
t.integer "user_id"
t.string "provider"
t.string "uid"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
create_table "inappropiate_flags", force: :cascade do |t|
t.integer "user_id"
t.string "flaggable_type"
@@ -156,10 +166,10 @@ ActiveRecord::Schema.define(version: 20150824113326) do
t.string "unconfirmed_email"
t.boolean "email_on_debate_comment", default: false
t.boolean "email_on_comment_reply", default: false
t.string "phone_number", limit: 30
t.string "official_position"
t.integer "official_level", default: 0
t.datetime "hidden_at"
t.string "phone_number", limit: 30
t.string "username"
end
@@ -214,6 +224,7 @@ ActiveRecord::Schema.define(version: 20150824113326) do
add_index "votes", ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope", using: :btree
add_foreign_key "administrators", "users"
add_foreign_key "identities", "users"
add_foreign_key "inappropiate_flags", "users"
add_foreign_key "moderators", "users"
add_foreign_key "organizations", "users"

View File

@@ -1,5 +1,4 @@
FactoryGirl.define do
factory :user do
username 'Manuela'
sequence(:email) { |n| "manuela#{n}@madrid.es" }
@@ -7,6 +6,12 @@ FactoryGirl.define do
confirmed_at { Time.now }
end
factory :identity do
user nil
provider "Twitter"
uid "MyString"
end
factory :debate do
sequence(:title) { |n| "Debate #{n} title" }
description 'Debate description'

View File

@@ -2,6 +2,7 @@ require 'rails_helper'
feature 'Users' do
context 'Regular authentication' do
scenario 'Sign up' do
visit '/'
click_link 'Sign up'
@@ -41,6 +42,120 @@ feature 'Users' do
expect(page).to have_content 'Signed in successfully.'
end
end
context 'OAuth authentication' do
context 'Twitter' do
background do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
scenario 'Sign up, when email was provided by OAuth provider' do
omniauth_twitter_hash = { 'provider' => 'twitter',
'uid' => '12345',
'info' => {
'name' => 'manuela',
'email' => 'manuelacarmena@example.com',
'nickname' => 'ManuelaRocks',
'verified' => '1'
},
'extra' => { 'raw_info' =>
{ 'location' => 'Madrid',
'name' => 'Manuela de las Carmenas'
}
}
}
OmniAuth.config.add_mock(:twitter, omniauth_twitter_hash)
visit '/'
click_link 'Sign up'
expect do
expect do
expect do
click_link 'Sign in with Twitter'
end.not_to change { ActionMailer::Base.deliveries.size }
end.to change { Identity.count }.by(1)
end.to change { User.count }.by(1)
expect(current_path).to eq(root_path)
expect_to_be_signed_in
user = User.last
expect(user.username).to eq('ManuelaRocks')
expect(user.email).to eq('manuelacarmena@example.com')
expect(user.confirmed?).to eq(true)
end
scenario 'Sign up, when neither email nor nickname were provided by OAuth provider' do
omniauth_twitter_hash = { 'provider' => 'twitter',
'uid' => '12345',
'info' => {
'name' => 'manuela'
},
'extra' => { 'raw_info' =>
{ 'location' => 'Madrid',
'name' => 'Manuela de las Carmenas'
}
}
}
OmniAuth.config.add_mock(:twitter, omniauth_twitter_hash)
visit '/'
click_link 'Sign up'
expect do
expect do
expect do
click_link 'Sign in with Twitter'
end.not_to change { ActionMailer::Base.deliveries.size }
end.to change { Identity.count }.by(1)
end.to change { User.count }.by(1)
expect(current_path).to eq(finish_signup_path)
user = User.last
expect(user.username).to eq('manuela-de-las-carmenas')
expect(user.email).to eq("omniauth@participacion-12345-twitter.com")
fill_in 'user_email', with: 'manueladelascarmenas@example.com'
click_button 'Sign up'
sent_token = /.*confirmation_token=(.*)".*/.match(ActionMailer::Base.deliveries.last.body.to_s)[1]
visit user_confirmation_path(confirmation_token: sent_token)
expect(page).to have_content "Your email address has been successfully confirmed"
expect(user.reload.email).to eq('manueladelascarmenas@example.com')
end
scenario 'Sign in, user was already signed up with OAuth' do
user = create(:user, email: 'manuela@madrid.es', password: 'judgementday')
identity = create(:identity, uid: '12345', provider: 'twitter', user: user)
omniauth_twitter_hash = { 'provider' => 'twitter',
'uid' => '12345',
'info' => {
'name' => 'manuela'
}
}
OmniAuth.config.add_mock(:twitter, omniauth_twitter_hash)
visit '/'
click_link 'Log in'
expect do
expect do
click_link 'Sign in with Twitter'
end.not_to change { Identity.count }
end.not_to change { User.count }
expect_to_be_signed_in
end
end
end
scenario 'Sign out' do
user = create(:user)
@@ -73,5 +188,4 @@ feature 'Users' do
expect(page).to have_content "Your password has been changed successfully. You are now signed in."
end
end

View File

@@ -0,0 +1,9 @@
require 'rails_helper'
RSpec.describe Identity, type: :model do
let(:identity) { build(:identity) }
it "should be valid" do
expect(identity).to be_valid
end
end

View File

@@ -50,6 +50,26 @@ describe User do
end
end
describe 'OmniAuth' do
describe '#email_provided?' do
it "is false if the email matchs was temporarely assigned by the OmniAuth process" do
subject.email = 'omniauth@participacion-ABCD-twitter.com'
expect(subject.email_provided?).to eq(false)
end
it "is true if the email is not omniauth-like" do
subject.email = 'manuelacarmena@example.com'
expect(subject.email_provided?).to eq(true)
end
it "is true if the user's real email is pending to be confirmed" do
subject.email = 'omniauth@participacion-ABCD-twitter.com'
subject.unconfirmed_email = 'manuelacarmena@example.com'
expect(subject.email_provided?).to eq(true)
end
end
end
describe "administrator?" do
it "is false when the user is not an admin" do
expect(subject.administrator?).to be false

View File

@@ -22,3 +22,5 @@ RSpec.configure do |config|
end
Capybara.javascript_driver = :poltergeist
OmniAuth.config.test_mode = true

View File

@@ -77,4 +77,7 @@ module CommonActions
/\d errors? prohibited this (.*) from being saved:/
end
def expect_to_be_signed_in
expect(find('.top-bar')).to have_content 'My account'
end
end