From 9189d937f79b6e54c44d4b2f5f21adf726a7b657 Mon Sep 17 00:00:00 2001 From: xdite Date: Tue, 12 Feb 2013 21:47:22 +0800 Subject: [PATCH] move all logic to omniauth implement omniauth-facebook / omniauth-twitter --- Gemfile | 4 +- Gemfile.lock | 29 +++- SOFTWARE.md | 5 +- .../views/modal/login_view.js.coffee | 4 +- app/controllers/facebook_controller.rb | 93 ---------- app/controllers/twitter_controller.rb | 85 --------- app/controllers/user_open_ids_controller.rb | 72 -------- .../users/omniauth_callbacks_controller.rb | 163 ++++++++++++++++++ app/views/twitter/complete.html.erb | 9 - app/views/user_open_ids/complete.html.erb | 9 - .../omniauth_callbacks}/complete.html.erb | 0 config/initializers/omniauth.rb | 3 +- config/routes.rb | 2 +- 13 files changed, 196 insertions(+), 282 deletions(-) delete mode 100644 app/controllers/facebook_controller.rb delete mode 100644 app/controllers/twitter_controller.rb delete mode 100644 app/controllers/user_open_ids_controller.rb create mode 100644 app/controllers/users/omniauth_callbacks_controller.rb delete mode 100644 app/views/twitter/complete.html.erb delete mode 100644 app/views/user_open_ids/complete.html.erb rename app/views/{facebook => users/omniauth_callbacks}/complete.html.erb (100%) diff --git a/Gemfile b/Gemfile index bdc99261d..be92a1d59 100644 --- a/Gemfile +++ b/Gemfile @@ -23,13 +23,13 @@ gem 'hiredis' gem 'hpricot' gem 'i18n-js' gem 'jquery-rails' -gem 'koala', require: false gem 'multi_json' gem 'mustache' gem 'nokogiri' gem "omniauth" gem "omniauth-openid" -gem 'oauth', require: false +gem "omniauth-facebook" +gem "omniauth-twitter" gem 'oj' gem 'pbkdf2' gem 'pg' diff --git a/Gemfile.lock b/Gemfile.lock index 08ad61e1a..513b7daea 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -95,7 +95,6 @@ GEM multi_json (~> 1.0) acts_as_paranoid (0.4.1) activerecord (~> 3.2) - addressable (2.3.2) airbrake (3.1.2) activesupport builder @@ -178,6 +177,7 @@ GEM hike (1.2.1) hiredis (0.4.5) hpricot (0.8.6) + httpauth (0.2.0) i18n (0.6.1) i18n-js (2.1.2) i18n @@ -196,10 +196,8 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.7.7) - koala (1.6.0) - addressable (~> 2.2) - faraday (~> 0.8) - multi_json (~> 1.3) + jwt (0.1.5) + multi_json (>= 1.0) libv8 (3.11.8.13) listen (0.7.2) lumberjack (1.0.2) @@ -220,13 +218,30 @@ GEM net-ssh (2.6.3) nokogiri (1.5.6) oauth (0.4.7) + oauth2 (0.8.0) + faraday (~> 0.8) + httpauth (~> 0.1) + jwt (~> 0.1.4) + multi_json (~> 1.0) + rack (~> 1.2) oj (2.0.3) omniauth (1.1.1) hashie (~> 1.2) rack + omniauth-facebook (1.4.1) + omniauth-oauth2 (~> 1.1.0) + omniauth-oauth (1.0.1) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.1.1) + oauth2 (~> 0.8.0) + omniauth (~> 1.0) omniauth-openid (1.0.1) omniauth (~> 1.0) rack-openid (~> 1.3.1) + omniauth-twitter (0.0.14) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) pbkdf2 (0.1.0) pg (0.14.1) polyglot (0.3.3) @@ -408,16 +423,16 @@ DEPENDENCIES image_optim jasminerice jquery-rails - koala message_bus! mocha multi_json mustache nokogiri - oauth oj omniauth + omniauth-facebook omniauth-openid + omniauth-twitter pbkdf2 pg pry-rails diff --git a/SOFTWARE.md b/SOFTWARE.md index 5919b682e..d59991fb1 100644 --- a/SOFTWARE.md +++ b/SOFTWARE.md @@ -23,7 +23,10 @@ The following Ruby Gems are used in Discourse: * [i18n-js](https://rubygems.org/gems/i18n-js) * [pbkdf2](https://rubygems.org/gems/pbkdf2) * [fast_xs](https://rubygems.org/gems/fast_xs) -* [koala](https://rubygems.org/gems/koala) +* [omniauth](https://github.com/intridea/omniauth) +* [omniauth-openid](https://github.com/intridea/omniauth-openid) +* [omniauth-facebook](https://github.com/mkdynamic/omniauth-facebook) +* [omniauth-twitter](https://github.com/arunagw/omniauth-twitter) * [has_ip_address](https://rubygems.org/gems/has_ip_address) * [vestal_versions](https://rubygems.org/gems/vestal_versions) * [coffee-rails](https://rubygems.org/gems/coffee-rails) diff --git a/app/assets/javascripts/discourse/views/modal/login_view.js.coffee b/app/assets/javascripts/discourse/views/modal/login_view.js.coffee index 756c4ae5f..014b03d3c 100644 --- a/app/assets/javascripts/discourse/views/modal/login_view.js.coffee +++ b/app/assets/javascripts/discourse/views/modal/login_view.js.coffee @@ -47,13 +47,13 @@ window.Discourse.LoginView = window.Discourse.ModalBodyView.extend Discourse.Pre @set('authenticate', 'twitter') left = @get('lastX') - 400 top = @get('lastY') - 200 - window.open("/twitter/frame", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top) + window.open("/auth/twitter", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top) facebookLogin: ()-> @set('authenticate', 'facebook') left = @get('lastX') - 400 top = @get('lastY') - 200 - window.open("/facebook/frame", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top) + window.open("/auth/facebook", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top) openidLogin: (provider)-> left = @get('lastX') - 400 diff --git a/app/controllers/facebook_controller.rb b/app/controllers/facebook_controller.rb deleted file mode 100644 index 0d498e8e6..000000000 --- a/app/controllers/facebook_controller.rb +++ /dev/null @@ -1,93 +0,0 @@ -class FacebookController < ApplicationController - skip_before_filter :check_xhr, only: [:frame, :complete] - layout false - - def frame - redirect_to oauth_consumer.url_for_oauth_code(:permissions => "email") - end - - def complete - consumer = oauth_consumer - token = consumer.get_access_token(params[:code]) - - graph = Koala::Facebook::API.new(token) - me = graph.get_object("me") - - email = me["email"] - verified = me["verified"] - - name = me["name"] - username = User.suggest_username(me["username"]) - - verified = me["verified"] - - # non verified accounts are just trouble - unless verified - render text: "Your account must be verified with facebook, before authenticating with facebook" - return - end - - session[:authentication] = { - facebook: { - facebook_user_id: me["id"], - link: me["link"], - username: me["username"], - first_name: me["first_name"], - last_name: me["last_name"], - email: me["email"], - gender: me["gender"], - name: me["name"] - }, - email: me["email"], - email_valid: true - } - - user_info = FacebookUserInfo.where(:facebook_user_id => me["id"]).first - - @data = { - username: username, - name: name, - email: email, - auth_provider: "Facebook", - email_valid: true - } - - if user_info - user = user_info.user - if user - unless user.active - user.active = true - user.save - end - log_on_user(user) - @data[:authenticated] = true - end - else - user = User.where(email: me["email"]).first - if user - FacebookUserInfo.create!(session[:authentication][:facebook].merge(user_id: user.id)) - unless user.active - user.active = true - user.save - end - log_on_user(user) - @data[:authenticated] = true - end - end - - end - - - protected - - def oauth_consumer - require 'koala' - - host = request.host - host = "#{host}:#{request.port}" if request.port != 80 - callback_url = "http://#{host}/facebook/complete" - - oauth = Koala::Facebook::OAuth.new(SiteSetting.facebook_app_id, SiteSetting.facebook_app_secret, callback_url) - end - -end diff --git a/app/controllers/twitter_controller.rb b/app/controllers/twitter_controller.rb deleted file mode 100644 index 2f034d8df..000000000 --- a/app/controllers/twitter_controller.rb +++ /dev/null @@ -1,85 +0,0 @@ -class TwitterController < ApplicationController - skip_before_filter :check_xhr, only: [:frame, :complete] - layout false - - def frame - - # defer the require as late as possible - require 'oauth' - - consumer = oauth_consumer - host = request.host - host = "#{host}:#{request.port}" if request.port != 80 - request_token = consumer.get_request_token(:oauth_callback => "http://#{host}/twitter/complete") - - session[:request_token] = request_token.token - session[:request_token_secret] = request_token.secret - - redirect_to request_token.authorize_url - end - - def complete - - require 'oauth' - - consumer = oauth_consumer - - unless session[:request_token] && session[:request_token_secret] - render :text => ('No authentication information was found in the session. Please try again.') and return - end - - unless params[:oauth_token].blank? || session[:request_token] == params[:oauth_token] - render :text => ('Authentication information does not match session information. Please try again.') and return - end - - request_token = OAuth::RequestToken.new(consumer, session[:request_token], session[:request_token_secret]) - access_token = request_token.get_access_token(:oauth_verifier => params[:oauth_verifier]) - - session[:request_token] = request_token.token - session[:request_token_secret] = request_token.secret - - screen_name = access_token.params["screen_name"] - twitter_user_id = access_token.params["user_id"] - - session[:authentication] = { - twitter_user_id: twitter_user_id, - twitter_screen_name: screen_name - } - - user_info = TwitterUserInfo.where(:twitter_user_id => twitter_user_id).first - - @data = { - username: screen_name, - auth_provider: "Twitter" - } - - if user_info - if user_info.user.active - log_on_user(user_info.user) - @data[:authenticated] = true - else - @data[:awaiting_activation] = true - # send another email ? - end - else - #TODO typheous or some other webscale http request lib that does not block thins - require 'open-uri' - parsed = ::JSON.parse(open("http://api.twitter.com/1/users/show.json?screen_name=#{screen_name}").read) - @data[:name] = parsed["name"] - end - - end - - - protected - - def oauth_consumer - OAuth::Consumer.new( - SiteSetting.twitter_consumer_key, - SiteSetting.twitter_consumer_secret, - :site => "https://api.twitter.com", - :authorize_path => '/oauth/authenticate' - ) - end - -end diff --git a/app/controllers/user_open_ids_controller.rb b/app/controllers/user_open_ids_controller.rb deleted file mode 100644 index 58a8a0acb..000000000 --- a/app/controllers/user_open_ids_controller.rb +++ /dev/null @@ -1,72 +0,0 @@ -require_dependency 'email' - -class UserOpenIdsController < ApplicationController - layout false - - # need to be able to call this - skip_before_filter :check_xhr - - # must be done, cause we may trigger a POST - skip_before_filter :verify_authenticity_token, :only => :complete - - def destroy - @open_id = UserOpenId.find(params[:id]) - if @open_id.user.id == current_user.id - @open_id.destroy - end - redirect_to current_user - end - - def new - @open_id = UserOpenId.new - end - - def complete - auth_token = env["omniauth.auth"] - create_or_sign_on_user(auth_token) - end - - def create_or_sign_on_user(auth_token) - - data = auth_token[:info] - identity_url = auth_token[:extra][:identity_url] - - email = data[:email] - - user_open_id = UserOpenId.find_by_url(identity_url) - - if user_open_id.blank? && user = User.find_by_email(email) - # we trust so do an email lookup - user_open_id = UserOpenId.create(url: identity_url , user_id: user.id, email: email, active: true) - end - - authenticated = user_open_id # if authed before - - if authenticated - user = user_open_id.user - - # If we have to approve users - if SiteSetting.must_approve_users? and !user.approved? - @data = {awaiting_approval: true} - else - log_on_user(user) - @data = {authenticated: true} - end - - else - @data = { - email: email, - name: User.suggest_name(email), - username: User.suggest_username(email), - email_valid: true , - auth_provider: data[:provider] - } - session[:authentication] = { - email: @data[:email], - email_valid: @data[:email_valid], - openid_url: identity_url - } - end - end - -end diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb new file mode 100644 index 000000000..4e45b155b --- /dev/null +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -0,0 +1,163 @@ +# -*- encoding : utf-8 -*- +require_dependency 'email' +class Users::OmniauthCallbacksController < ApplicationController + + layout false + + # need to be able to call this + skip_before_filter :check_xhr + + # must be done, cause we may trigger a POST + skip_before_filter :verify_authenticity_token, :only => :complete + + def complete + auth_token = env["omniauth.auth"] + case params[:provider] + when "facebook" + create_or_sign_on_user_using_facebook(auth_token) + when "twitter" + create_or_sign_on_user_using_twitter(auth_token) + when "google", "yahoo" + create_or_sign_on_user_using_openid(auth_token) + end + end + + def create_or_sign_on_user_using_twitter(auth_token) + + data = auth_token[:info] + screen_name = data["nickname"] + twitter_user_id = auth_token["uid"] + + session[:authentication] = { + twitter_user_id: twitter_user_id, + twitter_screen_name: screen_name + } + + user_info = TwitterUserInfo.where(:twitter_user_id => twitter_user_id).first + + @data = { + username: screen_name, + auth_provider: "Twitter" + } + + if user_info + if user_info.user.active + log_on_user(user_info.user) + @data[:authenticated] = true + else + @data[:awaiting_activation] = true + # send another email ? + end + else + @data[:name] = screen_name + end + + end + + def create_or_sign_on_user_using_facebook(auth_token) + + data = auth_token[:info] + raw_info = auth_token["extra"]["raw_info"] + + email = data[:email] + name = data["name"] + fb_uid = auth_token["uid"] + + + username = User.suggest_username(name) + + + session[:authentication] = { + facebook: { + facebook_user_id: fb_uid , + link: raw_info["link"], + username: raw_info["username"], + first_name: raw_info["first_name"], + last_name: raw_info["last_name"], + email: raw_info["email"], + gender: raw_info["gender"], + name: raw_info["name"] + }, + email: email, + email_valid: true + } + + user_info = FacebookUserInfo.where(:facebook_user_id => fb_uid ).first + + @data = { + username: username, + name: name, + email: email, + auth_provider: "Facebook", + email_valid: true + } + + if user_info + user = user_info.user + if user + unless user.active + user.active = true + user.save + end + log_on_user(user) + @data[:authenticated] = true + end + else + user = User.where(email: email).first + if user + FacebookUserInfo.create!(session[:authentication][:facebook].merge(user_id: user.id)) + unless user.active + user.active = true + user.save + end + log_on_user(user) + @data[:authenticated] = true + end + end + + end + + def create_or_sign_on_user_using_openid(auth_token) + + data = auth_token[:info] + identity_url = auth_token[:extra][:identity_url] + + email = data[:email] + + user_open_id = UserOpenId.find_by_url(identity_url) + + if user_open_id.blank? && user = User.find_by_email(email) + # we trust so do an email lookup + user_open_id = UserOpenId.create(url: identity_url , user_id: user.id, email: email, active: true) + end + + authenticated = user_open_id # if authed before + + if authenticated + user = user_open_id.user + + # If we have to approve users + if SiteSetting.must_approve_users? and !user.approved? + @data = {awaiting_approval: true} + else + log_on_user(user) + @data = {authenticated: true} + end + + else + @data = { + email: email, + name: User.suggest_name(email), + username: User.suggest_username(email), + email_valid: true , + auth_provider: data[:provider] + } + session[:authentication] = { + email: @data[:email], + email_valid: @data[:email_valid], + openid_url: identity_url + } + end + end + +end diff --git a/app/views/twitter/complete.html.erb b/app/views/twitter/complete.html.erb deleted file mode 100644 index b51f8055c..000000000 --- a/app/views/twitter/complete.html.erb +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/app/views/user_open_ids/complete.html.erb b/app/views/user_open_ids/complete.html.erb deleted file mode 100644 index 98a3d9dec..000000000 --- a/app/views/user_open_ids/complete.html.erb +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/app/views/facebook/complete.html.erb b/app/views/users/omniauth_callbacks/complete.html.erb similarity index 100% rename from app/views/facebook/complete.html.erb rename to app/views/users/omniauth_callbacks/complete.html.erb diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index a525180c7..f0ad76812 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -11,5 +11,6 @@ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE Rails.application.config.middleware.use OmniAuth::Builder do provider :open_id, :store => OpenID::Store::Filesystem.new('/tmp'), :name => 'google', :identifier => 'https://www.google.com/accounts/o8/id', :require => 'omniauth-openid' provider :open_id, :store => OpenID::Store::Filesystem.new('/tmp'), :name => 'yahoo', :identifier => 'https://me.yahoo.com', :require => 'omniauth-openid' - + provider :facebook, SiteSetting.facebook_app_id, SiteSetting.facebook_app_secret, :scope => "email" + provider :twitter, SiteSetting.twitter_consumer_key , SiteSetting.twitter_consumer_secret end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 87d5d3b0b..a5980b3e6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -112,7 +112,7 @@ Discourse::Application.routes.draw do resources :notifications resources :categories - match '/auth/:provider/callback', to: 'user_open_ids#complete' + match "/auth/:provider/callback", to: "users/omniauth_callbacks#complete" get 'twitter/frame' => 'twitter#frame' get 'twitter/complete' => 'twitter#complete'