From db45e50b7b2055b3ba81c83bebc6269a9d0bab2a Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Wed, 23 Jun 2010 20:15:43 -0400 Subject: [PATCH] Moved 37signals into a single OAuth2 class. --- oa-oauth/lib/omniauth/oauth.rb | 2 - oa-oauth/lib/omniauth/strategies/basecamp.rb | 61 ---------------- oa-oauth/lib/omniauth/strategies/campfire.rb | 52 -------------- .../strategies/thirty_seven_signals.rb | 64 ++++++----------- oa-oauth/oa-oauth.gemspec | 3 +- .../spec/omniauth/strategies/basecamp_spec.rb | 72 ------------------- .../spec/omniauth/strategies/campfire_spec.rb | 72 ------------------- .../strategies/thirty_seven_signals_spec.rb | 13 ++++ rspec.watchr | 59 +++++++++++++++ 9 files changed, 96 insertions(+), 302 deletions(-) delete mode 100644 oa-oauth/lib/omniauth/strategies/basecamp.rb delete mode 100644 oa-oauth/lib/omniauth/strategies/campfire.rb delete mode 100644 oa-oauth/spec/omniauth/strategies/basecamp_spec.rb delete mode 100644 oa-oauth/spec/omniauth/strategies/campfire_spec.rb create mode 100644 oa-oauth/spec/omniauth/strategies/thirty_seven_signals_spec.rb create mode 100644 rspec.watchr diff --git a/oa-oauth/lib/omniauth/oauth.rb b/oa-oauth/lib/omniauth/oauth.rb index 27c660a..a8d7e90 100644 --- a/oa-oauth/lib/omniauth/oauth.rb +++ b/oa-oauth/lib/omniauth/oauth.rb @@ -10,7 +10,5 @@ module OmniAuth autoload :Facebook, 'omniauth/strategies/facebook' autoload :GitHub, 'omniauth/strategies/github' autoload :ThirtySevenSignals, 'omniauth/strategies/thirty_seven_signals' - autoload :Basecamp, 'omniauth/strategies/basecamp' - autoload :Campfire, 'omniauth/strategies/campfire' end end diff --git a/oa-oauth/lib/omniauth/strategies/basecamp.rb b/oa-oauth/lib/omniauth/strategies/basecamp.rb deleted file mode 100644 index 7d5c11d..0000000 --- a/oa-oauth/lib/omniauth/strategies/basecamp.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'omniauth/oauth' -require 'nokogiri' - -module OmniAuth - module Strategies - - # - # Authenticate to Basecamp utilizing OAuth 2.0 and retrieve - # basic user information. - # - # Usage: - # - # use OmniAuth::Strategies::Basecamp, 'app_id', 'app_secret' - class Basecamp < ThirtySevenSignals - - def initialize(app, client_id, client_secret, options = {}) - super(app, :basecamp, client_id, client_secret, options) - end - - protected - - def user_data - @data ||= Nokogiri::XML.parse(@access_token.get('/users/me.xml')) - end - - def site_url - "https://#{subdomain}.basecamphq.com" - end - - def auth_hash - doc = user_data - OmniAuth::Utils.deep_merge(super, { - 'uid' => doc.xpath('person/id').text, - 'user_info' => user_info(doc), - 'credentials' => { - 'token' => doc.xpath('person/token').text - }, - 'extra' => { - 'access_token' => @access_token - } - }) - end - - def user_info(doc) - hash = { - 'first_name' => doc.xpath('person/first-name').text, - 'last_name' => doc.xpath('person/last-name').text, - 'email' => doc.xpath('person/email-address').text, - 'image' => doc.xpath('person/avatar-url').text - } - - hash['name'] = [hash['first_name'], hash['last_name']].join(' ').strip - - hash.delete('image') if hash['image'].include?('missing/avatar.png') - - hash - end - end - - end -end diff --git a/oa-oauth/lib/omniauth/strategies/campfire.rb b/oa-oauth/lib/omniauth/strategies/campfire.rb deleted file mode 100644 index 43f279a..0000000 --- a/oa-oauth/lib/omniauth/strategies/campfire.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'omniauth/oauth' -require 'multi_json' - -module OmniAuth - module Strategies - # - # Authenticate to Campfire utilizing OAuth 2.0 and retrieve - # basic user information. - # - # Usage: - # - # use OmniAuth::Strategies::Campfire, 'app_id', 'app_secret' - class Campfire < ThirtySevenSignals - - def initialize(app, app_id, app_secret, options = {}) - super(app, :campfire, app_id, app_secret, options) - end - - protected - - def user_data - @data ||= MultiJson.decode(@access_token.get('/users/me.json')) - end - - def site_url - "https://#{subdomain}.campfirenow.com" - end - - def auth_hash - data = self.user_data - OmniAuth::Utils.deep_merge(super, { - 'uid' => data['user']['id'].to_s, - 'user_info' => user_info(data), - 'credentials' => { - 'token' => data['api_auth_token'] - }, - 'extra' => { - 'access_token' => @access_token - } - }) - end - - def user_info(hash) - { - 'name' => hash['name'], - 'email' => hash['email_address'] - } - end - end - - end -end diff --git a/oa-oauth/lib/omniauth/strategies/thirty_seven_signals.rb b/oa-oauth/lib/omniauth/strategies/thirty_seven_signals.rb index 2dd041e..d8d8595 100644 --- a/oa-oauth/lib/omniauth/strategies/thirty_seven_signals.rb +++ b/oa-oauth/lib/omniauth/strategies/thirty_seven_signals.rb @@ -1,56 +1,38 @@ require 'omniauth/oauth' +require 'multi_json' module OmniAuth module Strategies - - # Abstract Strategy for 37Signals OAuth2 providers. class ThirtySevenSignals < OAuth2 - - SUBDOMAIN_PARAMETER = 'subdomain' - - def initialize(app, name, client_id, client_secret, options = {}) - super(app, name, client_id, client_secret, options) + def initialize(app, app_id, app_secret, options = {}) + options[:site] = 'https://launchpad.37signals.com/' + options[:authorize_path] = '/authorization/new' + options[:access_token_path] = '/authorization/token' + super(app, :thirty_seven_signals, app_id, app_secret, options) end - protected - - def client - ::OAuth2::Client.new(@client.id, @client.secret, :site => site_url) + def user_data + @data ||= MultiJson.decode(@access_token.get('/authorization.json')) end - def request_phase - if subdomain - super - else - ask_for_subdomain - end + def user_info + { + 'email' => user_data['identity']['email_address'], + 'first_name' => user_data['identity']['first_name'], + 'last_name' => user_data['identity']['last_name'], + 'name' => [user_data['identity']['first_name'], user_data['identity']['last_name']].join(' ').strip + } end - def callback_phase - if subdomain - super - else - ask_for_subdomain - end + def auth_hash + OmniAuth::Utils.deep_merge(super, { + 'uid' => user_data['identity']['id'], + 'user_info' => user_info, + 'extra' => { + 'accounts' => user_data['accounts'] + } + }) end - - def ask_for_subdomain - n = self.name.to_s.capitalize - OmniAuth::Form.build("#{n} Subdomain Required") do - text_field "#{n} Subdomain", ::OmniAuth::Strategies::ThirtySevenSignals::SUBDOMAIN_PARAMETER - end.to_response - end - - def subdomain - ((request.session[:oauth] ||= {})[name.to_sym] ||= {})[:subdomain] ||= request.params[SUBDOMAIN_PARAMETER] - end - - def site_url - raise NotImplementedError.new("Subclasses must define #{site_url}") - end - end - end - end diff --git a/oa-oauth/oa-oauth.gemspec b/oa-oauth/oa-oauth.gemspec index 079baf9..834ce5d 100644 --- a/oa-oauth/oa-oauth.gemspec +++ b/oa-oauth/oa-oauth.gemspec @@ -14,11 +14,10 @@ Gem::Specification.new do |gem| gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc) gem.add_dependency 'oa-core', version - gem.add_dependency 'rack', '~> 1.1.0' gem.add_dependency 'multi_json', '~> 0.0.2' gem.add_dependency 'nokogiri', '~> 1.4.2' gem.add_dependency 'oauth', '~> 0.4.0' - gem.add_dependency 'oauth2', '~> 0.0.8' + gem.add_dependency 'oauth2', '~> 0.0.10' eval File.read(File.join(File.dirname(__FILE__), '../development_dependencies.rb')) end diff --git a/oa-oauth/spec/omniauth/strategies/basecamp_spec.rb b/oa-oauth/spec/omniauth/strategies/basecamp_spec.rb deleted file mode 100644 index 87802b9..0000000 --- a/oa-oauth/spec/omniauth/strategies/basecamp_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -require File.dirname(__FILE__) + '/../../spec_helper' - -describe OmniAuth::Strategies::Basecamp, :type => :strategy do - - include OmniAuth::Test::StrategyTestCase - - def strategy - [OmniAuth::Strategies::Basecamp, 'abc', 'def'] - end - - describe '/auth/basecamp without a subdomain' do - before do - get '/auth/basecamp' - end - - it 'should respond with OK' do - last_response.should be_ok - end - - it 'should respond with HTML' do - last_response.content_type.should == 'text/html' - end - - it 'should render a subdomain input' do - last_response.body.should =~ %r{]*subdomain} - end - end - - describe 'POST /auth/basecamp with a subdomain' do - before do - # the middleware doesn't actually care that it's a POST, - # but it makes the "redirect_to" calculation down below easier - # since the params are passed in the body rather than the URL. - post '/auth/basecamp', {OmniAuth::Strategies::ThirtySevenSignals::SUBDOMAIN_PARAMETER => 'flugle'} - end - - it 'should redirect to the proper authorize_url' do - last_response.should be_redirect - redirect_to = CGI.escape(last_request.url + '/callback') - last_response.headers['Location'].should == "https://flugle.basecamphq.com/oauth/authorize?client_id=abc&redirect_uri=#{redirect_to}&type=web_server" - end - - it 'should set the basecamp subdomain in the session' do - session[:oauth][:basecamp][:subdomain].should == 'flugle' - end - - end - - describe 'followed by GET /auth/basecamp/callback' do - before do - stub_request(:post, 'https://flugle.basecamphq.com/oauth/access_token'). - to_return(:body => %q{{"access_token": "your_token"}}) - stub_request(:get, 'https://flugle.basecamphq.com/users/me.xml?access_token=your_token'). - to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'basecamp_200.xml'))) - get '/auth/basecamp/callback?code=plums', {}, {'rack.session' => {:oauth => {:basecamp => {:subdomain => 'flugle'}}}} - end - - sets_an_auth_hash - sets_provider_to 'basecamp' - sets_uid_to '1827370' - - it 'should exchange the request token for an access token' do - token = last_request['auth']['extra']['access_token'] - token.should be_kind_of(OAuth2::AccessToken) - token.token.should == 'your_token' - end - - it 'should call through to the master app' do - last_response.body.should == 'true' - end - end -end diff --git a/oa-oauth/spec/omniauth/strategies/campfire_spec.rb b/oa-oauth/spec/omniauth/strategies/campfire_spec.rb deleted file mode 100644 index f3e21ad..0000000 --- a/oa-oauth/spec/omniauth/strategies/campfire_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -require File.dirname(__FILE__) + '/../../spec_helper' - -describe OmniAuth::Strategies::Campfire, :type => :strategy do - - include OmniAuth::Test::StrategyTestCase - - def strategy - [OmniAuth::Strategies::Campfire, 'abc', 'def'] - end - - describe '/auth/campfire without a subdomain' do - before do - get '/auth/campfire' - end - - it 'should respond with OK' do - last_response.should be_ok - end - - it 'should respond with HTML' do - last_response.content_type.should == 'text/html' - end - - it 'should render a subdomain input' do - last_response.body.should =~ %r{]*subdomain} - end - end - - describe 'POST /auth/campfire with a subdomain' do - before do - # the middleware doesn't actually care that it's a POST, - # but it makes the "redirect_to" calculation down below easier - # since the params are passed in the body rather than the URL. - post '/auth/campfire', {OmniAuth::Strategies::ThirtySevenSignals::SUBDOMAIN_PARAMETER => 'flugle'} - end - - it 'should redirect to the proper authorize_url' do - last_response.should be_redirect - redirect_to = CGI.escape(last_request.url + '/callback') - last_response.headers['Location'].should == "https://flugle.campfirenow.com/oauth/authorize?client_id=abc&redirect_uri=#{redirect_to}&type=web_server" - end - - it 'should set the campfire subdomain in the session' do - session[:oauth][:campfire][:subdomain].should == 'flugle' - end - - end - - describe 'followed by GET /auth/campfire/callback' do - before do - stub_request(:post, 'https://flugle.campfirenow.com/oauth/access_token'). - to_return(:body => %q{{"access_token": "your_token"}}) - stub_request(:get, 'https://flugle.campfirenow.com/users/me.json?access_token=your_token'). - to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'campfire_200.json'))) - get '/auth/campfire/callback?code=plums', {}, {'rack.session' => {:oauth => {:campfire => {:subdomain => 'flugle'}}}} - end - - sets_an_auth_hash - sets_provider_to 'campfire' - sets_uid_to '92718' - - it 'should exchange the request token for an access token' do - token = last_request['auth']['extra']['access_token'] - token.should be_kind_of(OAuth2::AccessToken) - token.token.should == 'your_token' - end - - it 'should call through to the master app' do - last_response.body.should == 'true' - end - end -end diff --git a/oa-oauth/spec/omniauth/strategies/thirty_seven_signals_spec.rb b/oa-oauth/spec/omniauth/strategies/thirty_seven_signals_spec.rb new file mode 100644 index 0000000..f00bfc6 --- /dev/null +++ b/oa-oauth/spec/omniauth/strategies/thirty_seven_signals_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') + +describe OmniAuth::Strategies::ThirtySevenSignals do + + it 'should subclass OAuth2' do + OmniAuth::Strategies::ThirtySevenSignals.should < OmniAuth::Strategies::OAuth2 + end + + it 'should initialize with just consumer key and secret' do + lambda{OmniAuth::Strategies::ThirtySevenSignals.new({},'abc','def')}.should_not raise_error + end + +end \ No newline at end of file diff --git a/rspec.watchr b/rspec.watchr new file mode 100644 index 0000000..8939673 --- /dev/null +++ b/rspec.watchr @@ -0,0 +1,59 @@ +# Run me with: +# +# $ watchr specs.watchr + +# -------------------------------------------------- +# Convenience Methods +# -------------------------------------------------- +def all_spec_files + Dir['*/spec/**/*_spec.rb'] +end + +def run_spec_matching(thing_to_match) + matches = all_spec_files.grep(/#{thing_to_match}/i) + if matches.empty? + puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}" + else + run matches.join(' ') + end +end + +def run(files_to_run) + puts("Running: #{files_to_run}") + system("clear;rspec -cfs #{files_to_run}") + no_int_for_you +end + +def run_all_specs + run(all_spec_files.join(' ')) +end + +# -------------------------------------------------- +# Watchr Rules +# -------------------------------------------------- +watch('^[^/]+/spec/(.*)_spec\.rb') { |m| run_spec_matching(m[2]) } +watch('^[^/]+/lib/(.*)\.rb') { |m| run_spec_matching(m[2]) } +watch('^spec/spec_helper\.rb') { run_all_specs } +watch('^(.*)/spec/support/.*\.rb') { run_all_specs } + +# -------------------------------------------------- +# Signal Handling +# -------------------------------------------------- + +def no_int_for_you + @sent_an_int = nil +end + +Signal.trap 'INT' do + if @sent_an_int then + puts " A second INT? Ok, I get the message. Shutting down now." + exit + else + puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again." + @sent_an_int = true + Kernel.sleep 1.5 + run_all_specs + end +end + +# vim:ft=ruby \ No newline at end of file