mirror of
https://github.com/omniauth/omniauth.git
synced 2022-11-09 12:31:49 -05:00
Moved 37signals into a single OAuth2 class.
This commit is contained in:
parent
00de993ae4
commit
db45e50b7b
9 changed files with 96 additions and 302 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{<input[^>]*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
|
|
@ -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{<input[^>]*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
|
|
@ -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
|
59
rspec.watchr
Normal file
59
rspec.watchr
Normal file
|
@ -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
|
Loading…
Reference in a new issue