merges master and fix conflicts
This commit is contained in:
9
Gemfile
9
Gemfile
@@ -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'
|
||||
|
||||
|
||||
104
Gemfile.lock
104
Gemfile.lock
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
29
app/controllers/users/omniauth_callbacks_controller.rb
Normal file
29
app/controllers/users/omniauth_callbacks_controller.rb
Normal 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
|
||||
@@ -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
17
app/models/identity.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
7
app/views/devise/_omniauth_form.html.erb
Normal file
7
app/views/devise/_omniauth_form.html.erb
Normal 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/>
|
||||
@@ -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">
|
||||
|
||||
@@ -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 -%>
|
||||
|
||||
15
app/views/organizations/registrations/success.html.erb
Normal file
15
app/views/organizations/registrations/success.html.erb
Normal 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>
|
||||
13
app/views/users/registrations/finish_signup.html.erb
Normal file
13
app/views/users/registrations/finish_signup.html.erb
Normal 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>
|
||||
@@ -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 %>
|
||||
|
||||
|
||||
@@ -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) %>
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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".
|
||||
|
||||
@@ -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
|
||||
11
db/migrate/20150824144524_create_identities.rb
Normal file
11
db/migrate/20150824144524_create_identities.rb
Normal 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
|
||||
17
db/schema.rb
17
db/schema.rb
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
9
spec/models/identity_spec.rb
Normal file
9
spec/models/identity_spec.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -22,3 +22,5 @@ RSpec.configure do |config|
|
||||
end
|
||||
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
|
||||
OmniAuth.config.test_mode = true
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user