diff --git a/Gemfile b/Gemfile index fc3738e4bdd..976d6f87e79 100644 --- a/Gemfile +++ b/Gemfile @@ -97,6 +97,7 @@ gem 'openid-redis-store' gem 'omniauth-facebook' gem 'omniauth-twitter' gem 'omniauth-github' +gem 'omniauth-oauth2', require: false gem 'omniauth-browserid', git: 'https://github.com/callahad/omniauth-browserid.git', branch: 'observer_api' gem 'omniauth-cas' gem 'oj' diff --git a/Gemfile.lock b/Gemfile.lock index 86dc6284cc9..e188624082e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -497,6 +497,7 @@ DEPENDENCIES omniauth-cas omniauth-facebook omniauth-github + omniauth-oauth2 omniauth-openid omniauth-twitter openid-redis-store diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 0edc93bbf3d..9d9ebb4779b 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -29,6 +29,10 @@ class Users::OmniauthCallbacksController < ApplicationController create_or_sign_on_user_using_openid request.env["omniauth.auth"] found = true break + elsif p.name == provider && p.type == :oauth2 + create_or_sign_on_user_using_oauth2 request.env["omniauth.auth"] + found = true + break end end @@ -194,6 +198,58 @@ class Users::OmniauthCallbacksController < ApplicationController end + def create_or_sign_on_user_using_oauth2(auth_token) + oauth2_provider = auth_token[:provider] + oauth2_uid = auth_token[:uid] + data = auth_token[:info] + email = data[:email] + name = data[:name] + + oauth2_user_info = Oauth2UserInfo.where(uid: oauth2_uid, provider: oauth2_provider).first + + if oauth2_user_info.blank? && user = User.find_by_email(email) + # TODO is only safe if we trust our oauth2 provider to return an email + # legitimately owned by our user + oauth2_user_info = Oauth2UserInfo.create(uid: oauth2_uid, + provider: oauth2_provider, + name: name, + email: name, + user: user) + end + + authenticated = oauth2_user_info.present? + + if authenticated + user = oauth2_user_info.user + + # If we have to approve users + if Guardian.new(user).can_access_forum? + log_on_user(user) + @data = {authenticated: true} + else + @data = {awaiting_approval: true} + end + else + @data = { + email: email, + name: User.suggest_name(name), + username: UserNameSuggester.suggest(email), + email_valid: true , + auth_provider: oauth2_provider + } + + session[:authentication] = { + oauth2: { + provider: oauth2_provider, + uid: oauth2_uid, + }, + name: name, + email: @data[:email], + email_valid: @data[:email_valid] + } + end + end + def create_or_sign_on_user_using_openid(auth_token) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 681b4e9fef3..7addd1014fc 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -432,6 +432,16 @@ class UsersController < ApplicationController github_user_id: auth[:github_user_id] ) end + + if oauth2_auth?(auth) + Oauth2UserInfo.create( + uid: auth[:oauth2][:uid], + provider: auth[:oauth2][:provider], + name: auth[:name], + email: auth[:email], + user_id: user.id + ) + end end def twitter_auth?(auth) @@ -448,4 +458,9 @@ class UsersController < ApplicationController auth[:github_user_id] && auth[:github_screen_name] && GithubUserInfo.find_by_github_user_id(auth[:github_user_id]).nil? end + + def oauth2_auth?(auth) + auth[:oauth2].is_a?(Hash) && auth[:oauth2][:provider] && auth[:oauth2][:uid] && + Oauth2UserInfo.where(provider: auth[:oauth2][:provider], uid: auth[:oauth2][:uid]).empty? + end end diff --git a/app/models/oauth2_user_info.rb b/app/models/oauth2_user_info.rb new file mode 100644 index 00000000000..048f05b781c --- /dev/null +++ b/app/models/oauth2_user_info.rb @@ -0,0 +1,4 @@ +class Oauth2UserInfo < ActiveRecord::Base + belongs_to :user + +end diff --git a/app/models/user.rb b/app/models/user.rb index b014563c42f..b9cb05f5961 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -34,6 +34,7 @@ class User < ActiveRecord::Base has_one :twitter_user_info, dependent: :destroy has_one :github_user_info, dependent: :destroy has_one :cas_user_info, dependent: :destroy + has_one :oauth2_user_info, dependent: :destroy belongs_to :approved_by, class_name: 'User' has_many :group_users diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 91c9a5cb005..d6d2b2cad52 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -25,6 +25,14 @@ Rails.application.config.middleware.use OmniAuth::Builder do :store => OpenID::Store::Redis.new($redis), :require => "omniauth-openid" }.merge(p.options) + elsif p.type == :oauth2 + provider :oauth2, + p.options[:client_id], + p.options[:client_secret], + { + :name => p.name, + :require => "omniauth-oauth2" + }.merge(p.options) end end diff --git a/db/migrate/20130816024250_create_oauth2_user_infos.rb b/db/migrate/20130816024250_create_oauth2_user_infos.rb new file mode 100644 index 00000000000..d5a392fb61d --- /dev/null +++ b/db/migrate/20130816024250_create_oauth2_user_infos.rb @@ -0,0 +1,14 @@ +class CreateOauth2UserInfos < ActiveRecord::Migration + def change + create_table :oauth2_user_infos do |t| + t.integer :user_id, null: false + t.string :uid, null: false + t.string :provider, null: false + t.string :email + t.string :name + t.timestamps + end + + add_index :oauth2_user_infos, [:uid, :provider], unique: true + end +end diff --git a/plugins/.gitkeep b/plugins/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index a7c41f18f9c..966fbef70b0 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -164,4 +164,25 @@ describe Users::OmniauthCallbacksController do end + describe 'oauth2' do + before do + Discourse.stubs(:auth_providers).returns([stub(name: 'my_oauth2_provider', type: :oauth2)]) + request.env["omniauth.auth"] = { uid: 'my-uid', provider: 'my-oauth-provider-domain.net', info: {email: 'eviltrout@made.up.email', name: 'Chatanooga'}} + end + + describe "#create_or_sign_on_user_using_oauth2" do + context "User already exists" do + before do + User.stubs(:find_by_email).returns(Fabricate(:user)) + end + + it "should create an OauthUserInfo" do + expect { + post :complete, provider: 'my_oauth2_provider' + }.to change { Oauth2UserInfo.count }.by(1) + end + end + end + end + end