Browse Source

Extract service option prviders for non-standard Omniauth payloads (#1655)

* Extract option prviders for non-standard Omniauth payloads

- should enable huginn_agent gem developers to add their own providers
- devs would reopen OmniauthCallbacksController and define their specific action, supplying their own option provider

* Creates a registry for option providers on the Service class

- enables huginn_agent gem developers to register their own omniauth option interpreters

* Cleans up changes to the global Service class after the spec
Will Read 8 years ago
parent
commit
6b459ef412

+ 21 - 10
app/models/service.rb

@@ -57,17 +57,8 @@ class Service < ActiveRecord::Base
     (config = Devise.omniauth_configs[provider.to_sym]) && config.args[1]
   end
 
-  def self.provider_specific_options(omniauth)
-    case omniauth['provider'].to_sym
-      when :'37signals'
-        { user_id: omniauth['extra']['accounts'][0]['id'], name: omniauth['info']['name'] }
-      else
-        { name: omniauth['info']['nickname'] || omniauth['info']['name'] }
-    end
-  end
-
   def self.initialize_or_update_via_omniauth(omniauth)
-    options = provider_specific_options(omniauth)
+    options = get_options(omniauth)
 
     find_or_initialize_by(provider: omniauth['provider'], uid: omniauth['uid'].to_s).tap do |service|
       service.assign_attributes token: omniauth['credentials']['token'],
@@ -78,4 +69,24 @@ class Service < ActiveRecord::Base
                                 options: options
     end
   end
+
+  def self.register_options_provider(provider_name, &block)
+    option_providers[provider_name] = block
+  end
+
+  def self.get_options(omniauth)
+    option_providers.fetch(omniauth['provider'], option_providers['default']).call(omniauth)
+  end
+
+  private
+  @@option_providers = HashWithIndifferentAccess.new
+  cattr_reader :option_providers
+
+  register_options_provider('default') do |omniauth|
+    {name: omniauth['info']['nickname'] || omniauth['info']['name']}
+  end
+
+  register_options_provider('37signals') do |omniauth|
+    {user_id: omniauth['extra']['accounts'][0]['id'], name: omniauth['info']['name']}
+  end
 end

+ 9 - 8
spec/controllers/omniauth_callbacks_controller_spec.rb

@@ -5,22 +5,23 @@ describe OmniauthCallbacksController do
     sign_in users(:bob)
     OmniAuth.config.test_mode = true
     request.env["devise.mapping"] = Devise.mappings[:user]
-    request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/twitter.json')))
   end
 
   describe "accepting a callback url" do
     it "should update the user's credentials" do
+      request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/twitter.json')))
       expect {
         get :twitter
       }.to change { users(:bob).services.count }.by(1)
     end
+  end
 
-    # it "should work with an unknown provider (for now)" do
-    #   request.env["omniauth.auth"]['provider'] = 'unknown'
-    #   expect {
-    #     get :unknown
-    #   }.to change { users(:bob).services.count }.by(1)
-    #   expect(users(:bob).services.first.provider).to eq('unknown')
-    # end
+  describe "handling a provider with non-standard omniauth options" do
+    it "should update the user's credentials" do
+      request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/37signals.json')))
+      expect {
+        get "37signals"
+      }.to change { users(:bob).services.count }.by(1)
+    end
   end
 end

+ 25 - 0
spec/models/service_spec.rb

@@ -98,6 +98,7 @@ describe Service do
       expect(service.token).to eq('a1b2c3d4...')
       expect(service.secret).to eq('abcdef1234')
     end
+
     it "should work with 37signals services" do
       signals = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/37signals.json')))
       expect {
@@ -113,6 +114,7 @@ describe Service do
       expect(service.options[:user_id]).to eq(12345)
       service.expires_at = Time.at(1401554352)
     end
+
     it "should work with github services" do
       signals = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/github.json')))
       expect {
@@ -126,4 +128,27 @@ describe Service do
       expect(service.token).to eq('agithubtoken')
     end
   end
+
+  describe 'omniauth options provider registry for non-conforming omniauth responses' do
+    describe '.register_options_provider' do
+      before do
+        Service.register_options_provider('test-omniauth-provider') do |omniauth|
+          { name: omniauth['special_field'] }
+        end
+      end
+
+      after do
+        Service.option_providers.delete('test-omniauth-provider')
+      end
+
+      it 'allows gem developers to add their own options provider to the registry' do
+        actual_options = Service.get_options({
+          'provider' => 'test-omniauth-provider',
+          'special_field' => 'A Great Name'
+        })
+
+        expect(actual_options[:name]).to eq('A Great Name')
+      end
+    end
+  end
 end