panjiva-oauth2 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.document +5 -0
- data/.gemtest +0 -0
- data/.gitignore +35 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/README.md +102 -0
- data/Rakefile +16 -0
- data/lib/oauth2.rb +13 -0
- data/lib/oauth2/access_token.rb +53 -0
- data/lib/oauth2/client.rb +105 -0
- data/lib/oauth2/response_object.rb +58 -0
- data/lib/oauth2/strategy/base.rb +38 -0
- data/lib/oauth2/strategy/password.rb +38 -0
- data/lib/oauth2/strategy/web_server.rb +58 -0
- data/lib/oauth2/version.rb +3 -0
- data/oauth2.gemspec +24 -0
- data/spec/oauth2/access_token_spec.rb +90 -0
- data/spec/oauth2/client_spec.rb +168 -0
- data/spec/oauth2/strategy/base_spec.rb +7 -0
- data/spec/oauth2/strategy/password_spec.rb +57 -0
- data/spec/oauth2/strategy/web_server_spec.rb +138 -0
- data/spec/spec_helper.rb +11 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZTRiNmRhZTJhZjVlNTY5ZDU1MDQzZjEyNDg1NzRhM2QyYWJlOTIxMw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTI0MjQ4MzBkYTlmM2ExZTZjZDg5ODgzMDZiYjZhMjUyZWM4M2RmMw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MTJkYTgzNjM2MTQ3YzVkYTk0YTNhNDFmZDQwZjNlNmE2NjBmM2I4Y2RhMWI1
|
10
|
+
Njk0NjA1N2ViZjI0NzA3Njc2NDkwMTY0OThmM2ZkMjFjNTQzZjY5NmZlNTEw
|
11
|
+
Y2NkNTdmYTEyOWFjMzJkMGRjNGRjMGI2NWM2YWRjZTAyYTBmMjI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTFiNzYwMDY4ZWQxZWEwMzBmYmM4YThiOTM0ZTA5OTk3ZGY2OTBmYmYyOGEy
|
14
|
+
MTYyYTkyZWE3YjA5ZGY2OWJmOGMyYTY1ZTJmMmQ4OTlmMmU0ZGZjYzMzZTc2
|
15
|
+
NDkyYTVhMWVkMjAxMmI4M2M5NTZlMGJjZDU4MWJhYjRhZjVhYWM=
|
data/.document
ADDED
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
.rvmrc
|
2
|
+
/live
|
3
|
+
|
4
|
+
## MAC OS
|
5
|
+
.DS_Store
|
6
|
+
|
7
|
+
## TEXTMATE
|
8
|
+
*.tmproj
|
9
|
+
tmtags
|
10
|
+
|
11
|
+
## EMACS
|
12
|
+
*~
|
13
|
+
\#*
|
14
|
+
.\#*
|
15
|
+
|
16
|
+
## VIM
|
17
|
+
*.swp
|
18
|
+
|
19
|
+
## PROJECT::GENERAL
|
20
|
+
coverage
|
21
|
+
doc
|
22
|
+
rdoc
|
23
|
+
log
|
24
|
+
|
25
|
+
## BUNDLER
|
26
|
+
*.gem
|
27
|
+
.bundle
|
28
|
+
pkg
|
29
|
+
Gemfile.lock
|
30
|
+
|
31
|
+
## RCOV
|
32
|
+
coverage.data
|
33
|
+
|
34
|
+
## PROJECT::SPECIFIC
|
35
|
+
.svn
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
* [0.4.1 - April 20, 2011](https://github.com/intridea/oauth2/compare/v0.4.0...v0.4.1)
|
4
|
+
* [0.4.0 - April 20, 2011](https://github.com/intridea/oauth2/compare/v0.3.0...v0.4.0)
|
5
|
+
* [0.3.0 - April 8, 2011](https://github.com/intridea/oauth2/compare/v0.2.0...v0.3.0)
|
6
|
+
* [0.2.0 - April 1, 2011](https://github.com/intridea/oauth2/compare/v0.1.1...v0.2.0)
|
7
|
+
* [0.1.1 - January 12, 2011](https://github.com/intridea/oauth2/compare/v0.1.0...v0.1.1)
|
8
|
+
* [0.1.0 - October 13, 2010](https://github.com/intridea/oauth2/compare/v0.0.13...v0.1.0)
|
9
|
+
* [0.0.13 - August 17, 2010](https://github.com/intridea/oauth2/compare/v0.0.12...v0.0.13)
|
10
|
+
* [0.0.12 - August 17, 2010](https://github.com/intridea/oauth2/compare/v0.0.11...v0.0.12)
|
11
|
+
* [0.0.11 - August 17, 2010](https://github.com/intridea/oauth2/compare/v0.0.10...v0.0.11)
|
12
|
+
* [0.0.10 - June 19, 2010](https://github.com/intridea/oauth2/compare/v0.0.9...v0.0.10)
|
13
|
+
* [0.0.9 - June 18, 2010](https://github.com/intridea/oauth2/compare/v0.0.8...v0.0.9)
|
14
|
+
* [0.0.8 - April 27, 2010](https://github.com/intridea/oauth2/compare/v0.0.7...v0.0.8)
|
15
|
+
* [0.0.7 - April 27, 2010](https://github.com/intridea/oauth2/compare/v0.0.6...v0.0.7)
|
16
|
+
* [0.0.6 - April 25, 2010](https://github.com/intridea/oauth2/compare/v0.0.5...v0.0.6)
|
17
|
+
* [0.0.5 - April 23, 2010](https://github.com/intridea/oauth2/compare/v0.0.4...v0.0.5)
|
18
|
+
* [0.0.4 - April 22, 2010](https://github.com/intridea/oauth2/compare/v0.0.3...v0.0.4)
|
19
|
+
* [0.0.3 - April 22, 2010](https://github.com/intridea/oauth2/compare/v0.0.2...v0.0.3)
|
20
|
+
* [0.0.2 - April 22, 2010](https://github.com/intridea/oauth2/compare/v0.0.1...v0.0.2)
|
21
|
+
* [0.0.1 - April 22, 2010](https://github.com/intridea/oauth2/compare/311d9f42e52b832119170d90e818f0f0b0078851...v0.0.1)
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Intridea, Inc. and Michael Bleigh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
OAuth2 Fork (version 0.4.1 - OUT OF DATE)
|
2
|
+
=========================================
|
3
|
+
|
4
|
+
changelog: relaxed 0.8 > version dependency for faraday
|
5
|
+
-------------------------------------------------------
|
6
|
+
|
7
|
+
|
8
|
+
OAuth2
|
9
|
+
======
|
10
|
+
A Ruby wrapper for the OAuth 2.0 specification. This is a work in progress, being built first to solve the pragmatic process of connecting to existing OAuth 2.0 endpoints (a.k.a. Facebook) with the goal of building it up to meet the entire specification over time.
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
gem install oauth2
|
15
|
+
|
16
|
+
Continuous Integration
|
17
|
+
----------------------
|
18
|
+
[![Build Status](http://travis-ci.org/intridea/oauth2.png)](http://travis-ci.org/intridea/oauth2)
|
19
|
+
|
20
|
+
Resources
|
21
|
+
---------
|
22
|
+
* View Source on GitHub (https://github.com/intridea/oauth2)
|
23
|
+
* Report Issues on GitHub (https://github.com/intridea/oauth2/issues)
|
24
|
+
* Read More at the Wiki (https://wiki.github.com/intridea/oauth2)
|
25
|
+
|
26
|
+
Web Server Example (Sinatra)
|
27
|
+
----------------------------
|
28
|
+
Below is a fully functional example of a Sinatra application that would authenticate to Facebook utilizing the OAuth 2.0 web server flow.
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
require 'sinatra'
|
32
|
+
require 'oauth2'
|
33
|
+
require 'json'
|
34
|
+
|
35
|
+
def client
|
36
|
+
OAuth2::Client.new('app_id', 'app_secret', :site => 'https://graph.facebook.com')
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/auth/facebook' do
|
40
|
+
redirect client.web_server.authorize_url(
|
41
|
+
:redirect_uri => redirect_uri,
|
42
|
+
:scope => 'email,offline_access'
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
get '/auth/facebook/callback' do
|
47
|
+
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => redirect_uri)
|
48
|
+
user = JSON.parse(access_token.get('/me'))
|
49
|
+
user.inspect
|
50
|
+
end
|
51
|
+
|
52
|
+
def redirect_uri
|
53
|
+
uri = URI.parse(request.url)
|
54
|
+
uri.path = '/auth/facebook/callback'
|
55
|
+
uri.query = nil
|
56
|
+
uri.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
That's all there is to it! You can use the access token like you would with the
|
60
|
+
OAuth gem, calling HTTP verbs on it etc. You can view more examples on the [OAuth2
|
61
|
+
Wiki](http://wiki.github.com/intridea/oauth2/examples).
|
62
|
+
|
63
|
+
JSON Parsing
|
64
|
+
------------
|
65
|
+
Because JSON has become the standard format of the OAuth 2.0 specification,
|
66
|
+
the <tt>oauth2</tt> gem contains a mode that will perform automatic parsing
|
67
|
+
of JSON response bodies, returning a hash instead of a string. To enable this
|
68
|
+
mode, simply add the <tt>:parse_json</tt> option to your client initialization:
|
69
|
+
|
70
|
+
client = OAuth2::Client.new(
|
71
|
+
'app_id',
|
72
|
+
'app_secret',
|
73
|
+
:site => 'https://example.com',
|
74
|
+
:parse_json => true,
|
75
|
+
)
|
76
|
+
|
77
|
+
# Obtain an access token using the client
|
78
|
+
token.get('/some/url.json') #=> {"some" => "hash"}
|
79
|
+
|
80
|
+
Testing
|
81
|
+
-------
|
82
|
+
To use the OAuth2 client for testing error conditions do:
|
83
|
+
|
84
|
+
my_client.raise_errors = false
|
85
|
+
|
86
|
+
It will then return the error status and response instead of raising an exception.
|
87
|
+
|
88
|
+
Note on Patches/Pull Requests
|
89
|
+
-----------------------------
|
90
|
+
1. Fork the project.
|
91
|
+
2. Create a topic branch.
|
92
|
+
3. Implement your feature or bug fix.
|
93
|
+
4. Add documentation for your feature or bug fix.
|
94
|
+
5. Add specs for your feature or bug fix.
|
95
|
+
6. Run <tt>bundle exec rake spec</tt>. If your changes are not 100% covered, go back to step 5.
|
96
|
+
7. Commit and push your changes.
|
97
|
+
8. Submit a pull request. Please do not include changes to the [gemspec](https://github.com/intridea/oauth2/blob/master/oauth2.gemspec), [version](https://github.com/intridea/oauth2/blob/master/lib/oauth2/version.rb), or [changelog](https://github.com/intridea/oauth2/blob/master/CHANGELOG.md) file. (If you want to create your own version for some reason, please do so in a separate commit.)
|
98
|
+
|
99
|
+
Copyright
|
100
|
+
---------
|
101
|
+
Copyright (c) 2011 Intridea, Inc. and Michael Bleigh.
|
102
|
+
See [LICENSE](https://github.com/intridea/oauth2/blob/master/LICENSE.md) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
task :test => :spec
|
9
|
+
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
Rake::RDocTask.new do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
13
|
+
rdoc.title = "oauth2 #{OAuth2::VERSION}"
|
14
|
+
rdoc.rdoc_files.include('README*')
|
15
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
16
|
+
end
|
data/lib/oauth2.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module OAuth2
|
2
|
+
class ErrorWithResponse < StandardError; attr_accessor :response end
|
3
|
+
class AccessDenied < ErrorWithResponse; end
|
4
|
+
class Conflict < ErrorWithResponse; end
|
5
|
+
class HTTPError < ErrorWithResponse; end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'oauth2/client'
|
9
|
+
require 'oauth2/strategy/base'
|
10
|
+
require 'oauth2/strategy/web_server'
|
11
|
+
require 'oauth2/strategy/password'
|
12
|
+
require 'oauth2/access_token'
|
13
|
+
require 'oauth2/response_object'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module OAuth2
|
2
|
+
class AccessToken
|
3
|
+
attr_reader :client, :token, :refresh_token, :expires_in, :expires_at, :params
|
4
|
+
attr_accessor :token_param
|
5
|
+
|
6
|
+
def initialize(client, token, refresh_token=nil, expires_in=nil, params={})
|
7
|
+
@client = client
|
8
|
+
@token = token.to_s
|
9
|
+
@refresh_token = refresh_token.to_s
|
10
|
+
@expires_in = (expires_in.nil? || expires_in == '') ? nil : expires_in.to_i
|
11
|
+
@expires_at = Time.now + @expires_in if @expires_in
|
12
|
+
@params = params
|
13
|
+
@token_param = 'oauth_token'
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
@params[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
# True if the token in question has an expiration time.
|
21
|
+
def expires?
|
22
|
+
!!@expires_in
|
23
|
+
end
|
24
|
+
|
25
|
+
def expired?
|
26
|
+
expires? && expires_at < Time.now
|
27
|
+
end
|
28
|
+
|
29
|
+
def request(verb, path, params={}, headers={})
|
30
|
+
if params.instance_of?(Hash)
|
31
|
+
params = params.merge token_param => @token
|
32
|
+
end
|
33
|
+
headers = headers.merge 'Authorization' => "OAuth #{@token}"
|
34
|
+
@client.request(verb, path, params, headers)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(path, params={}, headers={})
|
38
|
+
request(:get, path, params, headers)
|
39
|
+
end
|
40
|
+
|
41
|
+
def post(path, params={}, headers={})
|
42
|
+
request(:post, path, params, headers)
|
43
|
+
end
|
44
|
+
|
45
|
+
def put(path, params={}, headers={})
|
46
|
+
request(:put, path, params, headers)
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete(path, params={}, headers={})
|
50
|
+
request(:delete, path, params, headers)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
class Client
|
5
|
+
attr_accessor :id, :secret, :site, :connection, :options, :raise_errors, :token_method
|
6
|
+
attr_writer :json
|
7
|
+
|
8
|
+
# Instantiate a new OAuth 2.0 client using the
|
9
|
+
# client ID and client secret registered to your
|
10
|
+
# application.
|
11
|
+
#
|
12
|
+
# Options:
|
13
|
+
#
|
14
|
+
# <tt>:site</tt> :: Specify a base URL for your OAuth 2.0 client.
|
15
|
+
# <tt>:authorize_path</tt> :: Specify the path to the authorization endpoint.
|
16
|
+
# <tt>:authorize_url</tt> :: Specify a full URL of the authorization endpoint.
|
17
|
+
# <tt>:access_token_path</tt> :: Specify the path to the access token endpoint.
|
18
|
+
# <tt>:access_token_method</tt> :: Specify the method to use for token endpoints, can be :get or :post
|
19
|
+
# (note: for Facebook this should be :get and for Google this should be :post)
|
20
|
+
# <tt>:access_token_url</tt> :: Specify the full URL of the access token endpoint.
|
21
|
+
# <tt>:parse_json</tt> :: If true, <tt>application/json</tt> responses will be automatically parsed.
|
22
|
+
# <tt>:ssl</tt> :: Specify SSL options for the connection.
|
23
|
+
# <tt>:adapter</tt> :: The name of the Faraday::Adapter::* class to use, e.g. :net_http. To pass arguments
|
24
|
+
# to the adapter pass an array here, e.g. [:action_dispatch, my_test_session]
|
25
|
+
# <tt>:raise_errors</tt> :: Default true. When false it will then return the error status and response instead of raising an exception.
|
26
|
+
def initialize(client_id, client_secret, opts={})
|
27
|
+
self.options = opts.dup
|
28
|
+
self.token_method = self.options.delete(:access_token_method) || :post
|
29
|
+
adapter = self.options.delete(:adapter)
|
30
|
+
ssl_opts = self.options.delete(:ssl) || {}
|
31
|
+
connection_opts = ssl_opts ? {:ssl => ssl_opts} : {}
|
32
|
+
self.id = client_id
|
33
|
+
self.secret = client_secret
|
34
|
+
self.site = self.options.delete(:site) if self.options[:site]
|
35
|
+
self.connection = Faraday::Connection.new(site, connection_opts)
|
36
|
+
self.json = self.options.delete(:parse_json)
|
37
|
+
self.raise_errors = !(self.options.delete(:raise_errors) == false)
|
38
|
+
|
39
|
+
if adapter && adapter != :test
|
40
|
+
connection.build do |b|
|
41
|
+
b.adapter(*[adapter].flatten)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def authorize_url(params=nil)
|
47
|
+
path = options[:authorize_url] || options[:authorize_path] || "/oauth/authorize"
|
48
|
+
connection.build_url(path, params).to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def access_token_url(params=nil)
|
52
|
+
path = options[:access_token_url] || options[:access_token_path] || "/oauth/access_token"
|
53
|
+
connection.build_url(path, params).to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
# Makes a request relative to the specified site root.
|
57
|
+
def request(verb, url, params={}, headers={})
|
58
|
+
if (verb == :get) || (verb == :delete)
|
59
|
+
resp = connection.run_request(verb, url, nil, headers) do |req|
|
60
|
+
req.params.update(params)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
resp = connection.run_request(verb, url, params, headers)
|
64
|
+
end
|
65
|
+
|
66
|
+
if raise_errors
|
67
|
+
case resp.status
|
68
|
+
when 200...299
|
69
|
+
return response_for(resp)
|
70
|
+
when 302
|
71
|
+
return request(verb, resp.headers['location'], params, headers)
|
72
|
+
when 401
|
73
|
+
e = OAuth2::AccessDenied.new("Received HTTP 401 during request.")
|
74
|
+
e.response = resp
|
75
|
+
raise e
|
76
|
+
when 409
|
77
|
+
e = OAuth2::Conflict.new("Received HTTP 409 during request.")
|
78
|
+
e.response = resp
|
79
|
+
raise e
|
80
|
+
else
|
81
|
+
e = OAuth2::HTTPError.new("Received HTTP #{resp.status} during request.")
|
82
|
+
e.response = resp
|
83
|
+
raise e
|
84
|
+
end
|
85
|
+
else
|
86
|
+
response_for resp
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def json?; !!@json end
|
91
|
+
|
92
|
+
def web_server; OAuth2::Strategy::WebServer.new(self) end
|
93
|
+
def password; OAuth2::Strategy::Password.new(self) end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def response_for(resp)
|
98
|
+
if json?
|
99
|
+
return ResponseObject.from(resp)
|
100
|
+
else
|
101
|
+
return ResponseString.new(resp)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module ResponseObject
|
3
|
+
def self.from(response)
|
4
|
+
object = MultiJson.decode(response.body)
|
5
|
+
|
6
|
+
case object
|
7
|
+
when Array
|
8
|
+
ResponseArray.new(response, object)
|
9
|
+
when Hash
|
10
|
+
ResponseHash.new(response, object)
|
11
|
+
else
|
12
|
+
ResponseString.new(response)
|
13
|
+
end
|
14
|
+
rescue
|
15
|
+
ResponseString.new(response)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.class_eval do
|
20
|
+
attr_accessor :response
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def headers; response.headers end
|
25
|
+
def status; response.status end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ResponseHash < Hash
|
29
|
+
include ResponseObject
|
30
|
+
|
31
|
+
def initialize(response, hash)
|
32
|
+
self.response = response
|
33
|
+
hash.keys.each{|k| self[k] = hash[k]}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ResponseArray < Array
|
38
|
+
include ResponseObject
|
39
|
+
|
40
|
+
def initialize(response, array)
|
41
|
+
self.response = response
|
42
|
+
super(array)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# This special String class is returned from HTTP requests
|
47
|
+
# and contains the original full response along with convenience
|
48
|
+
# methods for accessing the HTTP status code and headers. It
|
49
|
+
# is returned from all access token requests.
|
50
|
+
class ResponseString < String
|
51
|
+
include ResponseObject
|
52
|
+
|
53
|
+
def initialize(response)
|
54
|
+
super(response.body.to_s)
|
55
|
+
self.response = response
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Strategy
|
3
|
+
class Base #:nodoc:
|
4
|
+
def initialize(client)#:nodoc:
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
def authorize_url(options={}) #:nodoc:
|
9
|
+
@client.authorize_url(authorize_params(options))
|
10
|
+
end
|
11
|
+
|
12
|
+
def authorize_params(options={}) #:nodoc:
|
13
|
+
options = options.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
14
|
+
{'client_id' => @client.id}.merge(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def access_token_url(options={})
|
18
|
+
@client.access_token_url(access_token_params(options))
|
19
|
+
end
|
20
|
+
|
21
|
+
def access_token_params(options={})
|
22
|
+
return default_params(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def refresh_token_params(options={})
|
26
|
+
return default_params(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def default_params(options={})
|
31
|
+
{
|
32
|
+
'client_id' => @client.id,
|
33
|
+
'client_secret' => @client.secret
|
34
|
+
}.merge(options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
module Strategy
|
5
|
+
class Password < Base
|
6
|
+
def authorize_url
|
7
|
+
raise NotImplementedError, "The authorization endpoint is not used in this strategy"
|
8
|
+
end
|
9
|
+
# Retrieve an access token given the specified validation code.
|
10
|
+
# Note that you must also provide a <tt>:redirect_uri</tt> option
|
11
|
+
# in order to successfully verify your request for most OAuth 2.0
|
12
|
+
# endpoints.
|
13
|
+
def get_access_token(username, password, options={})
|
14
|
+
response = @client.request(:post, @client.access_token_url, access_token_params(username, password, options))
|
15
|
+
|
16
|
+
params = MultiJson.decode(response) rescue nil
|
17
|
+
# the ActiveSupport JSON parser won't cause an exception when
|
18
|
+
# given a formencoded string, so make sure that it was
|
19
|
+
# actually parsed in an Hash. This covers even the case where
|
20
|
+
# it caused an exception since it'll still be nil.
|
21
|
+
params = Rack::Utils.parse_query(response) unless params.is_a? Hash
|
22
|
+
|
23
|
+
access = params.delete('access_token')
|
24
|
+
refresh = params.delete('refresh_token')
|
25
|
+
expires_in = params.delete('expires_in')
|
26
|
+
OAuth2::AccessToken.new(@client, access, refresh, expires_in, params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def access_token_params(username, password, options={}) #:nodoc:
|
30
|
+
super(options).merge({
|
31
|
+
'grant_type' => 'password',
|
32
|
+
'username' => username,
|
33
|
+
'password' => password
|
34
|
+
})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
module Strategy
|
5
|
+
class WebServer < Base
|
6
|
+
def authorize_params(options={}) #:nodoc:
|
7
|
+
super(options).merge('response_type' => 'code')
|
8
|
+
end
|
9
|
+
|
10
|
+
# Retrieve an access token given the specified validation code.
|
11
|
+
# Note that you must also provide a <tt>:redirect_uri</tt> option
|
12
|
+
# in order to successfully verify your request for most OAuth 2.0
|
13
|
+
# endpoints.
|
14
|
+
def get_access_token(code, options={})
|
15
|
+
response = @client.request(@client.token_method, @client.access_token_url, access_token_params(code, options))
|
16
|
+
parse_response(response)
|
17
|
+
end
|
18
|
+
|
19
|
+
def refresh_access_token(refresh_token, options={})
|
20
|
+
response = @client.request(@client.token_method, @client.access_token_url, refresh_token_params(refresh_token, options))
|
21
|
+
parse_response(response, refresh_token)
|
22
|
+
end
|
23
|
+
|
24
|
+
def access_token_params(code, options={}) #:nodoc:
|
25
|
+
super(options).merge({
|
26
|
+
'grant_type' => 'authorization_code',
|
27
|
+
'code' => code,
|
28
|
+
})
|
29
|
+
end
|
30
|
+
|
31
|
+
def refresh_token_params(refresh_token, options={}) #:nodoc:
|
32
|
+
super(options).merge({
|
33
|
+
'grant_type' => 'refresh_token',
|
34
|
+
'refresh_token' => refresh_token,
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_response(response, refresh_token = nil)
|
39
|
+
if response.is_a? Hash
|
40
|
+
params = response
|
41
|
+
else
|
42
|
+
params = MultiJson.decode(response) rescue nil
|
43
|
+
# the ActiveSupport JSON parser won't cause an exception when
|
44
|
+
# given a formencoded string, so make sure that it was
|
45
|
+
# actually parsed in an Hash. This covers even the case where
|
46
|
+
# it caused an exception since it'll still be nil.
|
47
|
+
params = Rack::Utils.parse_query(response) unless params.is_a? Hash
|
48
|
+
end
|
49
|
+
|
50
|
+
access = params.delete('access_token')
|
51
|
+
refresh = params.delete('refresh_token') || refresh_token
|
52
|
+
# params['expires'] is only for Facebook
|
53
|
+
expires_in = params.delete('expires_in') || params.delete('expires')
|
54
|
+
OAuth2::AccessToken.new(@client, access, refresh, expires_in, params)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/oauth2.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/oauth2/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "panjiva-oauth2"
|
6
|
+
s.version = OAuth2::VERSION
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Michael Bleigh"]
|
9
|
+
s.description = %q{A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth gem.}
|
10
|
+
s.summary = %q{A Ruby wrapper for the OAuth 2.0 protocol.}
|
11
|
+
s.email = "michael@intridea.com"
|
12
|
+
s.homepage = "http://github.com/intridea/oauth2"
|
13
|
+
s.require_paths = ["lib"]
|
14
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.add_runtime_dependency("faraday", ">= 0.6.1")
|
18
|
+
s.add_runtime_dependency("multi_json", ">= 0.0.5")
|
19
|
+
s.add_development_dependency("json_pure", "~> 1.5")
|
20
|
+
s.add_development_dependency("rake", "~> 0.8")
|
21
|
+
s.add_development_dependency("simplecov", "~> 0.4")
|
22
|
+
s.add_development_dependency("rspec", "~> 2.5")
|
23
|
+
s.add_development_dependency("ZenTest", "~> 4.5")
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::AccessToken do
|
4
|
+
let(:client) do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.get('/client?oauth_token=monkey') {|env| [200, {}, 'get']}
|
9
|
+
stub.post('/client') {|env| [200, {}, 'oauth_token=' << env[:body]['oauth_token']]}
|
10
|
+
stub.get('/empty_get?oauth_token=monkey') {|env| [204, {}, nil]}
|
11
|
+
stub.put('/client') {|env| [200, {}, 'oauth_token=' << env[:body]['oauth_token']]}
|
12
|
+
stub.delete('/client?oauth_token=monkey') {|env| [200, {}, 'delete']}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
cli
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:token) {'monkey'}
|
19
|
+
|
20
|
+
subject {OAuth2::AccessToken.new(client, token)}
|
21
|
+
|
22
|
+
describe '#initialize' do
|
23
|
+
it 'should assign client and token' do
|
24
|
+
subject.client.should == client
|
25
|
+
subject.token.should == token
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should assign extra params' do
|
29
|
+
target = OAuth2::AccessToken.new(client, token, nil, nil, {'foo' => 'bar'})
|
30
|
+
target.params.should include('foo')
|
31
|
+
target.params['foo'].should == 'bar'
|
32
|
+
end
|
33
|
+
|
34
|
+
%w(get delete).each do |http_method|
|
35
|
+
it "makes #{http_method.upcase} requests with access token" do
|
36
|
+
subject.send(http_method.to_sym, 'client').should == http_method
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
%w(post put).each do |http_method|
|
41
|
+
it "makes #{http_method.upcase} requests with access token" do
|
42
|
+
subject.send(http_method.to_sym, 'client').should == 'oauth_token=monkey'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "works with a null response body" do
|
47
|
+
subject.get('empty_get').should == ''
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#expires?' do
|
52
|
+
it 'should be false if there is no expires_at' do
|
53
|
+
OAuth2::AccessToken.new(client, token).should_not be_expires
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should be true if there is an expires_at' do
|
57
|
+
OAuth2::AccessToken.new(client, token, 'abaca', 600).should be_expires
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#expires_at' do
|
62
|
+
before do
|
63
|
+
@now = Time.now
|
64
|
+
Time.stub!(:now).and_return(@now)
|
65
|
+
end
|
66
|
+
|
67
|
+
subject{OAuth2::AccessToken.new(client, token, 'abaca', 600)}
|
68
|
+
|
69
|
+
it 'should be a time representation of #expires_in' do
|
70
|
+
subject.expires_at.should == (@now + 600)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#expired?' do
|
75
|
+
it 'should be false if there is no expires_at' do
|
76
|
+
OAuth2::AccessToken.new(client, token).should_not be_expired
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should be false if expires_at is in the future' do
|
80
|
+
OAuth2::AccessToken.new(client, token, 'abaca', 10800).should_not be_expired
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should be true if expires_at is in the past' do
|
84
|
+
access = OAuth2::AccessToken.new(client, token, 'abaca', 600)
|
85
|
+
@now = Time.now + 10800
|
86
|
+
Time.stub!(:now).and_return(@now)
|
87
|
+
access.should be_expired
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Client do
|
4
|
+
subject do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.get('/success') {|env| [200, {'Content-Type' => 'text/awesome'}, 'yay']}
|
9
|
+
stub.get('/unauthorized') {|env| [401, {'Content-Type' => 'text/plain'}, 'not authorized']}
|
10
|
+
stub.get('/conflict') {|env| [409, {'Content-Type' => 'text/plain'}, 'not authorized']}
|
11
|
+
stub.get('/redirect') {|env| [302, {'Content-Type' => 'text/plain', 'location' => '/success' }, '']}
|
12
|
+
stub.get('/error') {|env| [500, {}, '']}
|
13
|
+
stub.get('/json') {|env| [200, {'Content-Type' => 'application/json; charset=utf8'}, '{"abc":"def"}']}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
cli
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#initialize' do
|
20
|
+
it 'should assign id and secret' do
|
21
|
+
subject.id.should == 'abc'
|
22
|
+
subject.secret.should == 'def'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should assign site from the options hash' do
|
26
|
+
subject.site.should == 'https://api.example.com'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should assign Faraday::Connection#host' do
|
30
|
+
subject.connection.host.should == 'api.example.com'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should leave Faraday::Connection#ssl unset' do
|
34
|
+
subject.connection.ssl.should == {}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be able to pass parameters to the adapter, e.g. Faraday::Adapter::ActionDispatch" do
|
38
|
+
connection = stub('connection')
|
39
|
+
Faraday::Connection.stub(:new => connection)
|
40
|
+
session = stub('session', :to_ary => nil)
|
41
|
+
builder = stub('builder')
|
42
|
+
connection.stub(:build).and_yield(builder)
|
43
|
+
|
44
|
+
builder.should_receive(:adapter).with(:action_dispatch, session)
|
45
|
+
|
46
|
+
OAuth2::Client.new('abc', 'def', :adapter => [:action_dispatch, session])
|
47
|
+
end
|
48
|
+
|
49
|
+
it "defaults raise_errors to true" do
|
50
|
+
subject.raise_errors.should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "allows true/false for raise_errors option" do
|
54
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => false)
|
55
|
+
client.raise_errors.should be_false
|
56
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true)
|
57
|
+
client.raise_errors.should be_true
|
58
|
+
end
|
59
|
+
|
60
|
+
it "allows get/post for access_token_method option" do
|
61
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :get)
|
62
|
+
client.token_method.should == :get
|
63
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :post)
|
64
|
+
client.token_method.should == :post
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
%w(authorize access_token).each do |path_type|
|
69
|
+
describe "##{path_type}_url" do
|
70
|
+
it "should default to a path of /oauth/#{path_type}" do
|
71
|
+
subject.send("#{path_type}_url").should == "https://api.example.com/oauth/#{path_type}"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should be settable via the :#{path_type}_path option" do
|
75
|
+
subject.options[:"#{path_type}_path"] = '/oauth/custom'
|
76
|
+
subject.send("#{path_type}_url").should == 'https://api.example.com/oauth/custom'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should be settable via the :#{path_type}_url option" do
|
80
|
+
subject.options[:"#{path_type}_url"] = 'https://abc.com/authorize'
|
81
|
+
subject.send("#{path_type}_url").should == 'https://abc.com/authorize'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#request" do
|
87
|
+
it "returns ResponseString on successful response" do
|
88
|
+
response = subject.request(:get, '/success', {}, {})
|
89
|
+
response.should == 'yay'
|
90
|
+
response.status.should == 200
|
91
|
+
response.headers.should == {'Content-Type' => 'text/awesome'}
|
92
|
+
end
|
93
|
+
|
94
|
+
it "follows redirects properly" do
|
95
|
+
response = subject.request(:get, '/redirect', {}, {})
|
96
|
+
response.should == 'yay'
|
97
|
+
response.status.should == 200
|
98
|
+
response.headers.should == {'Content-Type' => 'text/awesome'}
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns ResponseString on error if raise_errors is false" do
|
102
|
+
subject.raise_errors = false
|
103
|
+
response = subject.request(:get, '/unauthorized', {}, {})
|
104
|
+
|
105
|
+
response.should == 'not authorized'
|
106
|
+
response.status.should == 401
|
107
|
+
response.headers.should == {'Content-Type' => 'text/plain'}
|
108
|
+
end
|
109
|
+
|
110
|
+
it "raises OAuth2::AccessDenied on 401 response" do
|
111
|
+
lambda {subject.request(:get, '/unauthorized', {}, {})}.should raise_error(OAuth2::AccessDenied)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "raises OAuth2::Conflict on 409 response" do
|
115
|
+
lambda {subject.request(:get, '/conflict', {}, {})}.should raise_error(OAuth2::Conflict)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "raises OAuth2::HTTPError on error response" do
|
119
|
+
lambda {subject.request(:get, '/error', {}, {})}.should raise_error(OAuth2::HTTPError)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it '#web_server should instantiate a WebServer strategy with this client' do
|
124
|
+
subject.web_server.should be_kind_of(OAuth2::Strategy::WebServer)
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'with JSON parsing' do
|
128
|
+
before do
|
129
|
+
subject.json = true
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#request' do
|
133
|
+
it 'should return a response hash' do
|
134
|
+
response = subject.request(:get, '/json')
|
135
|
+
puts response.inspect
|
136
|
+
response.should be_kind_of(OAuth2::ResponseHash)
|
137
|
+
response['abc'].should == 'def'
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should only try to decode application/json' do
|
141
|
+
subject.request(:get, '/success').should == 'yay'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should set json? based on the :parse_json option' do
|
146
|
+
OAuth2::Client.new('abc', 'def', :site => 'http://example.com', :parse_json => true).should be_json
|
147
|
+
OAuth2::Client.new('abc', 'def', :site => 'http://example.com', :parse_json => false).should_not be_json
|
148
|
+
end
|
149
|
+
|
150
|
+
after do
|
151
|
+
subject.json = false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'with SSL options' do
|
156
|
+
subject do
|
157
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :ssl => {:ca_file => 'foo.pem'})
|
158
|
+
cli.connection.build do |b|
|
159
|
+
b.adapter :test
|
160
|
+
end
|
161
|
+
cli
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should pass the SSL options along to Faraday::Connection#ssl' do
|
165
|
+
subject.connection.ssl.should == {:ca_file => 'foo.pem'}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Strategy::Password do
|
4
|
+
let(:client) do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.post('/oauth/access_token') do |env|
|
9
|
+
case @mode
|
10
|
+
when "formencoded"
|
11
|
+
[200, {}, 'expires_in=600&access_token=salmon&refresh_token=trout']
|
12
|
+
when "json"
|
13
|
+
[200, {}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
cli
|
19
|
+
end
|
20
|
+
subject {client.password}
|
21
|
+
|
22
|
+
describe "#authorize_url" do
|
23
|
+
it "should raise NotImplementedError" do
|
24
|
+
lambda {subject.authorize_url}.should raise_error(NotImplementedError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
%w(json formencoded).each do |mode|
|
29
|
+
describe "#get_access_token (#{mode})" do
|
30
|
+
before do
|
31
|
+
@mode = mode
|
32
|
+
@access = subject.get_access_token('username', 'password')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns AccessToken with same Client' do
|
36
|
+
@access.client.should == client
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns AccessToken with #token' do
|
40
|
+
@access.token.should == 'salmon'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns AccessToken with #refresh_token' do
|
44
|
+
@access.refresh_token.should == 'trout'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns AccessToken with #expires_in' do
|
48
|
+
@access.expires_in.should == 600
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns AccessToken with #expires_at' do
|
52
|
+
@access.expires_at.should be_kind_of(Time)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Strategy::WebServer do
|
4
|
+
let(:client) do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.get('/oauth/access_token?client_id=abc&client_secret=def&code=sushi&grant_type=authorization_code') do |env|
|
9
|
+
case @mode
|
10
|
+
when "formencoded"
|
11
|
+
[200, {}, 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
12
|
+
when "json"
|
13
|
+
[200, {}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout","extra_param":"steve"}']
|
14
|
+
when "from_facebook"
|
15
|
+
[200, {}, 'expires=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
stub.post('/oauth/access_token', { 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code' }) do |env|
|
19
|
+
case @mode
|
20
|
+
when "formencoded"
|
21
|
+
[200, {}, 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
22
|
+
when "json"
|
23
|
+
[200, {}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout","extra_param":"steve"}']
|
24
|
+
when "from_facebook"
|
25
|
+
[200, {}, 'expires=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
stub.get('/oauth/access_token?client_id=abc&client_secret=def&grant_type=refresh_token&refresh_token=trout') do |env|
|
29
|
+
case @mode
|
30
|
+
when "formencoded"
|
31
|
+
[200, {}, 'expires_in=600&access_token=tuna']
|
32
|
+
when "json"
|
33
|
+
[200, {}, '{"expires_in":600,"access_token":"tuna"}']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
stub.post('/oauth/access_token', { 'client_id' => 'abc', 'client_secret' => 'def', 'refresh_token' => 'trout', 'grant_type' => 'refresh_token' }) do |env|
|
37
|
+
case @mode
|
38
|
+
when "formencoded"
|
39
|
+
[200, {}, 'expires_in=600&access_token=tuna']
|
40
|
+
when "json"
|
41
|
+
[200, {}, '{"expires_in":600,"access_token":"tuna"}']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
cli
|
47
|
+
end
|
48
|
+
subject{client.web_server}
|
49
|
+
|
50
|
+
describe '#authorize_url' do
|
51
|
+
it 'should include the client_id' do
|
52
|
+
subject.authorize_url.should be_include('client_id=abc')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should include the type' do
|
56
|
+
subject.authorize_url.should be_include('response_type=code')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should include passed in options' do
|
60
|
+
cb = 'http://myserver.local/oauth/callback'
|
61
|
+
subject.authorize_url(:redirect_uri => cb).should be_include("redirect_uri=#{Rack::Utils.escape(cb)}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
%w(json formencoded from_facebook).each do |mode|
|
66
|
+
[false, true].each do |parse_json|
|
67
|
+
[:get, :post].each do |verb|
|
68
|
+
describe "#get_access_token (#{mode}, token_method=#{verb} parse_json=#{parse_json})" do
|
69
|
+
before do
|
70
|
+
@mode = mode
|
71
|
+
client.json=parse_json
|
72
|
+
client.token_method=verb
|
73
|
+
@access = subject.get_access_token('sushi')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns AccessToken with same Client' do
|
77
|
+
@access.client.should == client
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns AccessToken with #token' do
|
81
|
+
@access.token.should == 'salmon'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns AccessToken with #refresh_token' do
|
85
|
+
@access.refresh_token.should == 'trout'
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'returns AccessToken with #expires_in' do
|
89
|
+
@access.expires_in.should == 600
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'returns AccessToken with #expires_at' do
|
93
|
+
@access.expires_at.should be_kind_of(Time)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'returns AccessToken with params accessible via []' do
|
97
|
+
@access['extra_param'].should == 'steve'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
%w(json formencoded).each do |mode|
|
105
|
+
[false, true].each do |parse_json|
|
106
|
+
[:get].each do |verb|
|
107
|
+
describe "#refresh_access_token (#{mode}, token_method=#{verb} parse_json=#{parse_json})" do
|
108
|
+
before do
|
109
|
+
@mode = mode
|
110
|
+
client.json=parse_json
|
111
|
+
client.token_method=verb
|
112
|
+
@access = subject.refresh_access_token('trout')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns AccessToken with same Client' do
|
116
|
+
@access.client.should == client
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns AccessToken with #token' do
|
120
|
+
@access.token.should == 'tuna'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns AccessToken with #refresh_token' do
|
124
|
+
@access.refresh_token.should == 'trout'
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns AccessToken with #expires_in' do
|
128
|
+
@access.expires_in.should == 600
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns AccessToken with #expires_at' do
|
132
|
+
@access.expires_at.should be_kind_of(Time)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: panjiva-oauth2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Bleigh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.6.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.6.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: multi_json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json_pure
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.5'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.5'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ZenTest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.5'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.5'
|
111
|
+
description: A Ruby wrapper for the OAuth 2.0 protocol built with a similar style
|
112
|
+
to the original OAuth gem.
|
113
|
+
email: michael@intridea.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .document
|
119
|
+
- .gemtest
|
120
|
+
- .gitignore
|
121
|
+
- .rspec
|
122
|
+
- .travis.yml
|
123
|
+
- CHANGELOG.md
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE.md
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- lib/oauth2.rb
|
129
|
+
- lib/oauth2/access_token.rb
|
130
|
+
- lib/oauth2/client.rb
|
131
|
+
- lib/oauth2/response_object.rb
|
132
|
+
- lib/oauth2/strategy/base.rb
|
133
|
+
- lib/oauth2/strategy/password.rb
|
134
|
+
- lib/oauth2/strategy/web_server.rb
|
135
|
+
- lib/oauth2/version.rb
|
136
|
+
- oauth2.gemspec
|
137
|
+
- spec/oauth2/access_token_spec.rb
|
138
|
+
- spec/oauth2/client_spec.rb
|
139
|
+
- spec/oauth2/strategy/base_spec.rb
|
140
|
+
- spec/oauth2/strategy/password_spec.rb
|
141
|
+
- spec/oauth2/strategy/web_server_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
homepage: http://github.com/intridea/oauth2
|
144
|
+
licenses: []
|
145
|
+
metadata: {}
|
146
|
+
post_install_message:
|
147
|
+
rdoc_options: []
|
148
|
+
require_paths:
|
149
|
+
- lib
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ! '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ! '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 1.3.6
|
160
|
+
requirements: []
|
161
|
+
rubyforge_project:
|
162
|
+
rubygems_version: 2.1.11
|
163
|
+
signing_key:
|
164
|
+
specification_version: 4
|
165
|
+
summary: A Ruby wrapper for the OAuth 2.0 protocol.
|
166
|
+
test_files: []
|