warden-oauth2-strategies 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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