warden-oauth2-strategies 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9e885487f2c3e237d8a7cffa9020ea86671d015e
4
+ data.tar.gz: 9b6d3b51466a0861cf876fa606d407fd9fd2da18
5
+ SHA512:
6
+ metadata.gz: f3fd02b16da09b9454a707f3d8843e969d907239f824caa136892c80734e63ac37b20befd927c4b5b6985fa87ca0dfcaca25228bd5c8672c8d57c3bbe743b69d
7
+ data.tar.gz: 4a1ddb4c062dec57fbb06ea3e3a7d8da582baba7063d267bef97d2e404f0fa35b07d1ff07955d73a612bc21065e9fb55d46503c0c95d92ee8a5a8475eaa538f5
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 1.9.2
4
+ - 1.8.7
5
+ - jruby
6
+ - rbx
7
+ script: "bundle exec rake"
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in warden-oauth2.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'guard'
8
+ gem 'guard-rspec'
9
+ gem 'guard-bundler'
10
+ gem 'rb-fsevent'
11
+ gem 'growl'
12
+ gem 'pry'
13
+ end
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
7
+
8
+ guard 'bundler' do
9
+ watch('Gemfile')
10
+ watch(/^.+\.gemspec/)
11
+ end
data/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # Warden::OAuth2 [![CI Status](https://secure.travis-ci.org/opperator/warden-oauth2.png)](http://travis-ci.org/opperator/warden-oauth2)
2
+
3
+ This is a fork of [original project](https://github.com/opperator/warden-oauth2) which is actually maintained.
4
+ This library provides a robust set of authorization strategies for
5
+ Warden meant to be used to implement an OAuth 2.0 (targeting RFC6749)
6
+ provider.
7
+
8
+ ## Usage
9
+
10
+ ### Grape API Example
11
+
12
+ ```ruby
13
+ require 'grape'
14
+ require 'warden-oauth2'
15
+
16
+ class MyAPI < Grape::API
17
+ use Warden::Manager do |config|
18
+ strategies.add :bearer, Warden::OAuth2::Strategies::Bearer
19
+ strategies.add :client_credentials, Warden::OAuth2::Strategies::ClientCredentials
20
+ strategies.add :resource_owner_password_credentials, Warden::OAuth2::Strategies::ResourceOwnerPasswordCredentials
21
+ strategies.add :public, Warden::OAuth2::Strategies::Public
22
+
23
+ config.default_strategies :bearer, :client_credentials, :resource_owner_password_credentials, :public
24
+ config.failure_app Warden::OAuth2::FailureApp
25
+ end
26
+
27
+ helpers do
28
+ def warden; env['warden'] end
29
+ end
30
+
31
+ resources :hamburgers do
32
+ before do
33
+ warden.authenticate! scope: :hamburgers
34
+ end
35
+ end
36
+ end
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ You can configure Warden::OAuth2 like so:
42
+
43
+ ```ruby
44
+ Warden::OAuth2.configure do |config|
45
+ config.some_option = some_value
46
+ end
47
+ ```
48
+
49
+ ### Configurable Options
50
+
51
+ * **client_credentials_model:** A client application class used for client credentials authentication. See **Models** below.
52
+ Defaults to `ClientCredentialsApplication`.
53
+ * **resource_owner_password_credentials_model:** A client application class used for resource owner password authentication. See **Models** below.
54
+ Defaults to `ResourceOwnerPasswordCredentialsApplication`.
55
+ * **token_model:** An access token class. See **Models** below. Defaults
56
+ to `AccessToken`.
57
+
58
+ ## Models
59
+
60
+ You will need to supply data models to back up the persistent facets of
61
+ your OAuth 2.0 implementation. Below are examples of the interfaces that
62
+ each require.
63
+
64
+ ### Client Credentials Application
65
+
66
+ ```ruby
67
+ class ClientCredentialsApplication
68
+ # REQUIRED
69
+ def self.locate(client_id, client_secret = nil)
70
+ # Should return a client application matching the client_id
71
+ # provided, but should ONLY match client_secret if it is
72
+ # provided.
73
+ end
74
+
75
+ # OPTIONAL
76
+ def scope?(scope)
77
+ # True if the client should be able to access the scope passed
78
+ # (usually a symbol) without having an access token.
79
+ end
80
+ end
81
+ ```
82
+
83
+ ### Resource Owner Password Credentials Application
84
+
85
+ ```ruby
86
+ class ResourceOwnerPasswordCredentialsApplication
87
+ # REQUIRED
88
+ def self.locate(client_id, client_secret = nil)
89
+ # Should return a client application matching the client_id
90
+ # provided, but should ONLY match client_secret if it is
91
+ # provided.
92
+ end
93
+ #REQUIRED
94
+ def valid?(options={})
95
+ # Use options[:username] and options[:password] to check
96
+ # that specified credentials are valid
97
+ end
98
+
99
+ # OPTIONAL
100
+ def scope?(scope)
101
+ # True if the client should be able to access the scope passed
102
+ # (usually a symbol) without having an access token.
103
+ end
104
+ end
105
+ ```
106
+
107
+ ### Access Token
108
+
109
+ ```ruby
110
+ class AccessToken
111
+ # REQUIRED
112
+ def self.locate(token_string)
113
+ # Should return an access token matching the string provided.
114
+ # Note that you MAY include expired access tokens in the result
115
+ # of this method so long as you implement an instance #expired?
116
+ # method.
117
+ end
118
+
119
+ # OPTIONAL
120
+ def expired?
121
+ # True if the access token has reached its expiration.
122
+ end
123
+
124
+ # OPTIONAL
125
+ def scope?(scope)
126
+ # True if the scope passed in (usually a symbol) has been authorized
127
+ # for this access token.
128
+ end
129
+ end
130
+ ```
131
+
132
+ ## Strategies
133
+
134
+ ### Bearer
135
+
136
+ This strategy authenticates by trying to find an access token that is
137
+ supplied according to the OAuth 2.0 Bearer Token specification
138
+ ([draft 8][oauth2-bearer]). It does this by first extracting the access
139
+ token in string form and then calling the `.locate` method on your
140
+ access token model (see **Configuration** above).
141
+
142
+ Token-based strategies will also fail if they are expired or lack
143
+ sufficient scope. See **Models** above.
144
+
145
+ **User:** The Warden user is set to the client application returned by
146
+ `.locate`.
147
+
148
+ ### Client credentials
149
+
150
+ This strategy authenticates an OAuth 2.0 client application directly for
151
+ endpoints that don't require a specific user. You might use this
152
+ strategy when you want to create an API for client statistics or if you
153
+ wish to rate limit based on a client application even for publicly
154
+ accessible endpoints.
155
+
156
+ **User:** The Warden user is set to the access token returned by `.locate`.
157
+
158
+ ### Resource Owner Password Credential
159
+
160
+ This strategy creates an access token for a user with matching credentials.
161
+ Use `.valid?` on the client application to determine if user credentials are correct.
162
+
163
+ **User:** The Warden user is set to the access token returned by `.locate`.
164
+
165
+ ### Public
166
+
167
+ This strategy succeeds by default and only fails if the authentication
168
+ scope is set and is something other than `:public`.
169
+
170
+ **User:** The Warden user is set to `nil`.
171
+
172
+ [oauth2]: http://tools.ietf.org/html/draft-ietf-oauth-v2-22
173
+ [oauth2-bearer]: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08
174
+
175
+ ## License
176
+ The MIT License
177
+
178
+ Copyright (c) 2014 AirService Pty Ltd. http://www.airservice.com
179
+
180
+ Permission is hereby granted, free of charge, to any person obtaining a copy
181
+ of this software and associated documentation files (the "Software"), to deal
182
+ in the Software without restriction, including without limitation the rights
183
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
184
+ copies of the Software, and to permit persons to whom the Software is
185
+ furnished to do so, subject to the following conditions:
186
+
187
+ The above copyright notice and this permission notice shall be included in
188
+ all copies or substantial portions of the Software.
189
+
190
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
191
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
192
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
193
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
194
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
195
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
196
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+ desc "Run specs."
6
+ RSpec::Core::RakeTask.new
7
+
8
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ class ErrorApp
6
+ def self.call(env)
7
+ new.call(env)
8
+ end
9
+
10
+ def call(env)
11
+ warden = env['warden']
12
+ strategy = warden.winning_strategy
13
+ status = strategy.respond_to?(:error_status) ? strategy.error_status : 401
14
+ error_description = strategy.respond_to?(:error_description) ? strategy.error_description : ''
15
+ headers = {'Content-Type' => 'application/json'}
16
+ headers['X-Accepted-OAuth-Scopes'] = (strategy.scope || :public).to_s
17
+ body = %Q{"error":"#{strategy.message}", "error_description":"#{error_description}"}
18
+
19
+ Rack::Response.new(body, status, headers).finish
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module Warden
2
+ module OAuth2
3
+ class FailureApp
4
+ def self.call(env)
5
+ new.call(env)
6
+ end
7
+
8
+ def call(env)
9
+ warden = env['warden']
10
+ strategy = warden.winning_strategy
11
+
12
+ body = '{"error":"' + strategy.message.to_s + '"}'
13
+ status = strategy.error_status rescue 401
14
+ headers = {'Content-Type' => 'application/json'}
15
+
16
+ headers['X-Accepted-OAuth-Scopes'] = (strategy.scope || :public).to_s
17
+
18
+ [status, headers, [body]]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ module Strategies
6
+ class Base < Warden::Strategies::Base
7
+ def store?
8
+ false
9
+ end
10
+
11
+ def error_status
12
+ 400
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ module Strategies
6
+ class Bearer < Token
7
+ def valid?
8
+ !!token_string
9
+ end
10
+
11
+ def token_string
12
+ token_string_from_header || token_string_from_request_params
13
+ end
14
+
15
+ def token_string_from_header
16
+ Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.each do |key|
17
+ if env.key?(key) && token_string = env[key][/^Bearer (.*)/, 1]
18
+ return token_string
19
+ end
20
+ end
21
+ nil
22
+ end
23
+
24
+ def token_string_from_request_params
25
+ params[:access_token]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,57 @@
1
+ require 'warden-oauth2'
2
+ require 'base64'
3
+
4
+ module Warden
5
+ module OAuth2
6
+ module Strategies
7
+ class Client < Base
8
+ attr_reader :client, :client_id, :client_secret, :error_description
9
+
10
+ def authenticate!
11
+ @client = client_from_http_basic || client_from_request_params
12
+
13
+ if self.client
14
+ fail "invalid_scope" and return if scope && client.respond_to?(:scope) && !client.scope?(scope)
15
+ client_authenticated
16
+ else
17
+ fail "invalid_client"
18
+ end
19
+ end
20
+
21
+ def client_from_http_basic
22
+ return nil unless (env.keys & Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS).any?
23
+ @client_id, @client_secret = *Rack::Auth::Basic::Request.new(env).credentials
24
+ model.locate(self.client_id, self.client_secret)
25
+ end
26
+
27
+ def client_from_request_params
28
+ @client_id, @client_secret = params['client_id'], params['client_secret']
29
+ return nil unless self.client_id
30
+ model.locate(@client_id, @client_secret)
31
+ end
32
+
33
+ def public_client?
34
+ client && !client_secret
35
+ end
36
+
37
+ def error_status
38
+ case message
39
+ when "invalid_client" then 401
40
+ when "invalid_scope" then 403
41
+ else 400
42
+ end
43
+ end
44
+
45
+ protected
46
+ attr_writer :error_description
47
+ def model
48
+ raise 'Model should be defined in a child strategy'
49
+ end
50
+
51
+ def client_authenticated
52
+ success! self.client
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ module Strategies
6
+ class ClientCredentials < Client
7
+ def model
8
+ Warden::OAuth2.config.client_credentials_model
9
+ end
10
+ def valid?
11
+ params['grant_type'] == 'client_credentials'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ module Strategies
6
+ class Public < Base
7
+ def authenticate!
8
+ if scope && scope.to_sym != :public
9
+ fail! "invalid_scope" and return
10
+ end
11
+
12
+ success! nil
13
+ end
14
+
15
+ def error_status
16
+ 401
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ module Strategies
6
+ class ResourceOwnerPasswordCredentials< Client
7
+ def valid?
8
+ params['grant_type'] == 'password'
9
+ end
10
+
11
+ protected
12
+ def model
13
+ Warden::OAuth2.config.resource_owner_password_credentials_model
14
+ end
15
+
16
+ def client_authenticated
17
+ if params['username'] && params['password']
18
+ valid_client = client.valid?(username: params['username'], password: params['password'])
19
+ valid_client ? super : fail("invalid_client")
20
+ else
21
+ fail "invalid_request"
22
+ self.error_description = "username or password are not provided"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ require 'warden-oauth2'
2
+
3
+ module Warden
4
+ module OAuth2
5
+ module Strategies
6
+ class Token < Base
7
+ def valid?
8
+ !!token_string
9
+ end
10
+
11
+ def authenticate!
12
+ if token
13
+ fail! "invalid_token" and return if token.respond_to?(:expired?) && token.expired?
14
+ fail! "invalid_scope" and return if scope && token.respond_to?(:scope?) && !token.scope?(scope)
15
+ success! token
16
+ else
17
+ fail! "invalid_request" and return unless token
18
+ end
19
+ end
20
+
21
+ def token
22
+ Warden::OAuth2.config.token_model.locate(token_string)
23
+ end
24
+
25
+ def token_string
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def error_status
30
+ case message
31
+ when "invalid_token" then 401
32
+ when "invalid_scope" then 403
33
+ when "invalid_request" then 400
34
+ else 400
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ module Warden
2
+ module OAuth2
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ require 'warden'
2
+ require 'warden/oauth2/version'
3
+
4
+ module Warden
5
+ module OAuth2
6
+ class Configuration
7
+ attr_accessor :client_credentials_model, :resource_owner_password_credentials_model, :token_model
8
+
9
+ def initialize
10
+ self.client_credentials_model = ClientCredentialsApplication if defined?(ClientCredentialsApplication)
11
+ self.resource_owner_password_credentials_model = ResourceOwnerPasswordCredentialsApplication if defined?(ResourceOwnerPasswordCredentialsApplication)
12
+ self.token_model = AccessToken if defined?(AccessToken)
13
+ end
14
+ end
15
+
16
+ def self.config
17
+ @@config ||= Configuration.new
18
+ end
19
+
20
+ def self.configure
21
+ yield config
22
+ end
23
+
24
+ autoload :FailureApp, 'warden/oauth2/failure_app'
25
+ module Strategies
26
+ autoload :Base, 'warden/oauth2/strategies/base'
27
+ autoload :Public, 'warden/oauth2/strategies/public'
28
+ autoload :Token, 'warden/oauth2/strategies/token'
29
+ autoload :Client, 'warden/oauth2/strategies/client'
30
+ autoload :ClientCredentials, 'warden/oauth2/strategies/client_credentials'
31
+ autoload :ResourceOwnerPasswordCredentials, 'warden/oauth2/strategies/resource_owner_password_credentials'
32
+ autoload :Bearer, 'warden/oauth2/strategies/bearer'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ require 'warden/oauth2'
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup :default, :test
4
+
5
+ require 'rspec'
6
+ require 'rack/test'
7
+
8
+ require 'warden-oauth2'
9
+
10
+ RSpec.configure do |config|
11
+ config.include Rack::Test::Methods
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::FailureApp do
4
+ let(:app){ subject }
5
+ let(:warden){ double(:winning_strategy => @strategy || strategy) }
6
+ let(:strategy){ double(:message => 'invalid_request') }
7
+
8
+ context 'with all info' do
9
+ before do
10
+ @strategy = double(:error_status => 502, :message => 'custom', :scope => 'random')
11
+ get '/unauthenticated', {}, 'warden' => warden
12
+ end
13
+
14
+ it 'should set the status from error_status if there is one' do
15
+ last_response.status.should == 502
16
+ end
17
+
18
+ it 'should set the message from the message' do
19
+ last_response.body.should == '{"error":"custom"}'
20
+ end
21
+
22
+ it 'should set the content type' do
23
+ last_response.headers['Content-Type'].should == 'application/json'
24
+ end
25
+
26
+ it 'should set the X-OAuth-Accepted-Scopes header' do
27
+ last_response.headers['X-Accepted-OAuth-Scopes'].should == 'random'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::Strategies::Bearer do
4
+ let(:strategy){ Warden::OAuth2::Strategies::Bearer }
5
+ let(:token_model){ double(:AccessToken) }
6
+ subject{ strategy.new({'rack.input' => {}}) }
7
+
8
+ before do
9
+ Warden::OAuth2.config.token_model = token_model
10
+ end
11
+
12
+ describe '#token_string_from_header' do
13
+ Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.each do |key|
14
+ it "should recognize a bearer token in the #{key} environment key" do
15
+ subject.stub(:env).and_return({key => "Bearer abc"})
16
+ subject.token_string_from_header.should == 'abc'
17
+ end
18
+ end
19
+
20
+ it 'should ignore a non-bearer authorization header' do
21
+ subject.stub(:env).and_return('HTTP_AUTHORIZATION' => 'Other do do do')
22
+ subject.token_string_from_header.should be_nil
23
+ end
24
+ end
25
+
26
+ describe '#token_string_from_request_params' do
27
+ it 'should pull the :access_token param' do
28
+ subject.stub(:params).and_return(:access_token => 'abc')
29
+ subject.token_string_from_request_params.should == 'abc'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::Strategies::ClientCredentials do
4
+ let(:strategy){ described_class }
5
+ let(:client_credentials_model){ double(:ClientApplication) }
6
+ subject{ strategy.new({'rack.input' => {}}) }
7
+
8
+ before do
9
+ Warden::OAuth2.config.client_credentials_model = client_credentials_model
10
+ end
11
+
12
+ describe '#valid?' do
13
+ it 'returns false if the grant type is not specified' do
14
+ subject.stub(:params).and_return({})
15
+ subject.should_not be_valid
16
+ end
17
+
18
+ it 'returns true if the grant type is client_credentials' do
19
+ subject.stub(:params).and_return({'grant_type' => 'client_credentials'})
20
+ subject.should be_valid
21
+ end
22
+
23
+ it 'returns false if the grant type is not client_credentials' do
24
+ subject.stub(:params).and_return({'grant_type' => 'whatever'})
25
+ subject.should_not be_valid
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::Strategies::Client do
4
+ let(:strategy){ Warden::OAuth2::Strategies::Client }
5
+ let(:client_model){ double(:ClientApplication) }
6
+ subject{ strategy.new({'rack.input' => {}}) }
7
+
8
+ before do
9
+ subject.stub(:model).and_return(client_model)
10
+ end
11
+
12
+ describe '#client_from_http_basic' do
13
+ it 'should call through to the client application class locate method' do
14
+ subject.stub(:env).and_return({
15
+ 'HTTP_AUTHORIZATION' => "Basic #{Base64.encode64('id:secret')}"
16
+ })
17
+
18
+ client_model.should_receive(:locate).with('id','secret').and_return("booya")
19
+ subject.client_from_http_basic.should == "booya"
20
+ end
21
+
22
+ it 'should return nil if no HTTP Basic credentials are provided' do
23
+ subject.client_from_http_basic.should be_nil
24
+ end
25
+ end
26
+
27
+ describe '#client_from_request_params' do
28
+ it 'should be nil if no client_id is provided' do
29
+ subject.stub(:params).and_return({'client_secret' => 'abc'})
30
+ subject.client_from_request_params.should be_nil
31
+ end
32
+
33
+ it 'should call through to locate if a client_id is present' do
34
+ subject.stub(:params).and_return({'client_id' => 'abc'})
35
+ client_model.should_receive(:locate).with('abc',nil)
36
+ subject.client_from_request_params
37
+ end
38
+
39
+ it 'should call through to locate if a client_id and secret are present' do
40
+ subject.stub(:params).and_return({'client_id' => 'abc', 'client_secret' => 'def'})
41
+ client_model.should_receive(:locate).with('abc','def')
42
+ subject.client_from_request_params
43
+ end
44
+ end
45
+
46
+ describe '#authorize!' do
47
+ it 'should succeed if a client is around' do
48
+ client_instance = double
49
+ client_model.stub(:locate).and_return(client_instance)
50
+ subject.stub(:params).and_return('client_id' => 'awesome')
51
+ subject._run!
52
+ subject.user.should == client_instance
53
+ subject.result.should == :success
54
+ end
55
+
56
+ it 'should fail if no credentials are passed' do
57
+ subject._run!
58
+ subject.result.should == :failure
59
+ subject.message.should == "invalid_client"
60
+ subject.error_status.should == 401
61
+ end
62
+
63
+ it 'should fail if insufficient scope is provided' do
64
+ client_model.stub(:locate).and_return(double(:respond_to? => true, :scope? => false))
65
+ subject.stub(:params).and_return('client_id' => 'abc')
66
+ subject.stub(:scope).and_return(:confidential_client)
67
+ subject._run!
68
+ subject.result.should == :failure
69
+ subject.message.should == "invalid_scope"
70
+ subject.error_status.should == 403
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::Strategies::Public do
4
+ let(:env){ {'PATH_INFO' => '/resource'} }
5
+ let(:strategy){ Warden::OAuth2::Strategies::Public }
6
+ subject{ strategy.new(env) }
7
+
8
+ it 'should succeed with no scope' do
9
+ subject._run!
10
+ subject.result.should == :success
11
+ end
12
+
13
+ it 'should succeed with a :public scope' do
14
+ subject.stub(:scope).and_return(:public)
15
+ subject._run!
16
+ subject.result.should == :success
17
+ end
18
+
19
+ it 'should fail and halt with another scope' do
20
+ subject.stub(:scope).and_return(:user)
21
+ subject._run!
22
+ subject.should be_halted
23
+ subject.message.should == "invalid_scope"
24
+ subject.result.should == :failure
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::Strategies::ResourceOwnerPasswordCredentials do
4
+ let(:strategy){ described_class }
5
+ let(:client_model){ double(:ClientApplication) }
6
+ subject{ strategy.new({'rack.input' => {}}) }
7
+
8
+ before do
9
+ Warden::OAuth2.config.resource_owner_password_credentials_model = client_model
10
+ end
11
+
12
+ describe '#valid?' do
13
+ it 'returns false if the grant type is not specified' do
14
+ subject.stub(:params).and_return({})
15
+ subject.should_not be_valid
16
+ end
17
+
18
+ it 'returns true if the grant type is password' do
19
+ subject.stub(:params).and_return({'grant_type' => 'password'})
20
+ subject.should be_valid
21
+ end
22
+
23
+ it 'returns false if the grant type is not password' do
24
+ subject.stub(:params).and_return({'grant_type' => 'whatever'})
25
+ subject.should_not be_valid
26
+ end
27
+ end
28
+
29
+ describe '#authorize!' do
30
+ it 'should fail if a client is around but not valid' do
31
+ client_instance = double(:client_instance, valid?: false)
32
+ client_model.stub(locate: client_instance)
33
+ subject.stub(:params).and_return('client_id' => 'awesome', 'username' => 'someuser', 'password' => 'incorrect')
34
+ subject._run!
35
+ subject.message.should == "invalid_client"
36
+ subject.error_status.should == 401
37
+ end
38
+ it 'should fail if username and password are not provided' do
39
+ client_model.stub(locate: double)
40
+ subject.stub(:params).and_return('client_id' => 'awesome')
41
+ subject._run!
42
+ subject.message.should == "invalid_request"
43
+ subject.error_status.should == 400
44
+ subject.error_description.should_not be_empty
45
+ end
46
+ it 'should pass username and password to validation check' do
47
+ client_instance = double(:client_instance)
48
+ client_model.stub(locate: client_instance)
49
+ subject.stub(:params).and_return('client_id' => 'awesome', 'username' => 'username', 'password' => 'password')
50
+
51
+ client_instance.should_receive(:valid?).with(username: 'username', password: 'password').and_return(false)
52
+
53
+ subject._run!
54
+ end
55
+ it 'should succeed if a client is around and valid' do
56
+ client_instance = double(:client_instance, valid?: true)
57
+ client_model.stub(locate: client_instance)
58
+ subject.stub(:params).and_return('client_id' => 'awesome', 'username' => 'username', 'password' => 'correct')
59
+ subject._run!
60
+ subject.user.should == client_instance
61
+ subject.result.should == :success
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::OAuth2::Strategies::Token do
4
+ let(:token_model){ double }
5
+ let(:strategy){ Warden::OAuth2::Strategies::Token }
6
+ subject{ strategy.new({'rack.input' => {}}) }
7
+
8
+ before do
9
+ Warden::OAuth2.config.token_model = token_model
10
+ end
11
+
12
+ describe '#token' do
13
+ it 'should call through to .locate on the token_class with the token string' do
14
+ token_model.should_receive(:locate).with('abc')
15
+ subject.stub(:token_string).and_return('abc')
16
+ subject.token
17
+ end
18
+ end
19
+
20
+ describe '#authenticate!' do
21
+ it 'should be successful if there is a token' do
22
+ token_instance = double
23
+ subject.stub(:token).and_return(token_instance)
24
+ subject._run!
25
+ subject.result.should == :success
26
+ subject.user.should == token_instance
27
+ end
28
+
29
+ it 'should fail if there is not a token' do
30
+ subject.stub(token: nil)
31
+ subject._run!
32
+ subject.result.should == :failure
33
+ subject.message.should == "invalid_request"
34
+ subject.error_status.should == 400
35
+ end
36
+
37
+ it 'should fail if the access token is expired' do
38
+ token_instance = double(:respond_to? => true, :expired? => true, :scope? => true)
39
+ subject.stub(:token).and_return(token_instance)
40
+ subject._run!
41
+ subject.result.should == :failure
42
+ subject.message.should == "invalid_token"
43
+ subject.error_status.should == 401
44
+ end
45
+
46
+ it 'should fail if there is insufficient scope' do
47
+ token_instance = double(:respond_to? => true, :expired? => false, :scope? => false)
48
+ subject.stub(:token).and_return(token_instance)
49
+ subject.stub(:scope).and_return(:secret)
50
+ subject._run!
51
+ subject.result.should == :failure
52
+ subject.message.should == "invalid_scope"
53
+ subject.error_status.should == 403
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/warden/oauth2/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["AirService"]
6
+ gem.email = ["devs@airservice.co"]
7
+ gem.description = %q{OAuth 2.0 strategies for Warden}
8
+ gem.summary = %q{OAuth 2.0 strategies for Warden}
9
+ gem.homepage = "https://github.com/airservice/warden-oauth2"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "warden-oauth2-strategies"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Warden::OAuth2::VERSION
17
+
18
+ gem.add_dependency 'warden'
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_development_dependency 'rspec'
21
+ gem.add_development_dependency 'rack-test'
22
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: warden-oauth2-strategies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - AirService
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: warden
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: OAuth 2.0 strategies for Warden
70
+ email:
71
+ - devs@airservice.co
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - Gemfile
80
+ - Guardfile
81
+ - README.md
82
+ - Rakefile
83
+ - lib/warden-oauth2.rb
84
+ - lib/warden/oauth2.rb
85
+ - lib/warden/oauth2/error_app.rb
86
+ - lib/warden/oauth2/failure_app.rb
87
+ - lib/warden/oauth2/strategies/base.rb
88
+ - lib/warden/oauth2/strategies/bearer.rb
89
+ - lib/warden/oauth2/strategies/client.rb
90
+ - lib/warden/oauth2/strategies/client_credentials.rb
91
+ - lib/warden/oauth2/strategies/public.rb
92
+ - lib/warden/oauth2/strategies/resource_owner_password_credentials.rb
93
+ - lib/warden/oauth2/strategies/token.rb
94
+ - lib/warden/oauth2/version.rb
95
+ - spec/spec_helper.rb
96
+ - spec/warden/oauth2/failure_app_spec.rb
97
+ - spec/warden/oauth2/strategies/bearer_spec.rb
98
+ - spec/warden/oauth2/strategies/client_credentials_spec.rb
99
+ - spec/warden/oauth2/strategies/client_spec.rb
100
+ - spec/warden/oauth2/strategies/public_spec.rb
101
+ - spec/warden/oauth2/strategies/resource_owner_password_credentials_spec.rb
102
+ - spec/warden/oauth2/strategies/token_spec.rb
103
+ - warden-oauth2.gemspec
104
+ homepage: https://github.com/airservice/warden-oauth2
105
+ licenses: []
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.2.1
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: OAuth 2.0 strategies for Warden
127
+ test_files:
128
+ - spec/spec_helper.rb
129
+ - spec/warden/oauth2/failure_app_spec.rb
130
+ - spec/warden/oauth2/strategies/bearer_spec.rb
131
+ - spec/warden/oauth2/strategies/client_credentials_spec.rb
132
+ - spec/warden/oauth2/strategies/client_spec.rb
133
+ - spec/warden/oauth2/strategies/public_spec.rb
134
+ - spec/warden/oauth2/strategies/resource_owner_password_credentials_spec.rb
135
+ - spec/warden/oauth2/strategies/token_spec.rb