oauth2-rack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ .DS_Store
2
+ .bundle
3
+ \#*
4
+ .\#*
5
+ *~
6
+ *swp
7
+ TAGS
8
+ .rvmrc
9
+ .yardoc
10
+ /doc/*
11
+ Gemfile.lock
12
+ *.gem
13
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'libnotify'
6
+ gem 'rb-inotify'
7
+
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # -*- ruby -*-
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec/" }
8
+ watch('spec/spec_helper.rb') { "spec/" }
9
+ end
10
+
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+
4
+ RSpec::Core::RakeTask.new
data/config.ru ADDED
@@ -0,0 +1,50 @@
1
+ $: << File.expand_path('../lib', __FILE__)
2
+ require 'oauth2/rack'
3
+
4
+ class AccessTokenIssuer
5
+ def self.call(opts = {})
6
+ opts.merge('access_token' => 'test')
7
+ end
8
+ end
9
+ class Authenticator
10
+ def self.call(opts = {})
11
+ if opts[:username]
12
+ authenticate_resource_owner(opts)
13
+ elsif opts[:client_id]
14
+ authenticate_client(opts)
15
+ end
16
+ end
17
+
18
+ def self.authenticate_resource_owner(opts)
19
+ OpenStruct.new(:username => opts[:username])
20
+ end
21
+
22
+ def self.authenticate_client(opts)
23
+ OpenStruct.new(:client_id => opts[:client_id])
24
+ end
25
+ end
26
+
27
+ map '/password/access_token' do
28
+ use OAuth2::Rack::Authentication::Client::HTTPBasic, :required => false, :authenticator => Authenticator
29
+ use OAuth2::Rack::Authentication::Client::RequestParams, :required => false, :authenticator => Authenticator
30
+
31
+ use OAuth2::Rack::Authentication::ResourceOwner::RequestParams, :authenticator => Authenticator
32
+
33
+ use OAuth2::Rack::Authorization::Password::AccessTokenIssuer, :issuer => AccessTokenIssuer
34
+
35
+ run proc { |env| [200, {'Content-Type' => 'text/html'}, ['Hello']] }
36
+ end
37
+
38
+ map '/client_credentials/access_token' do
39
+ use OAuth2::Rack::Authentication::Client::HTTPBasic, :required => false, :authenticator => Authenticator
40
+ use OAuth2::Rack::Authentication::Client::RequestParams, :required => false, :authenticator => Authenticator
41
+
42
+ use OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer, :issuer => AccessTokenIssuer
43
+
44
+ run proc { |env| [200, {'Content-Type' => 'text/html'}, ['Hello']] }
45
+ end
46
+
47
+
48
+ map '/inspect' do
49
+ run proc { |env| [200, {'Content-Type' => 'text/html'}, [env.inspect]] }
50
+ end
@@ -0,0 +1,6 @@
1
+ require 'oauth2'
2
+
3
+ client = OAuth2::Client.new('client_id', 'client_secret', :site => 'http://localhost:9393/', :token_url => '/password/access_token')
4
+ token = client.password.get_token('username', 'password')
5
+
6
+ p token
@@ -0,0 +1,61 @@
1
+ require 'oauth2/rack'
2
+
3
+ # 2.4.1. Client Password
4
+ class OAuth2::Rack::Authentication::Client::HTTPBasic
5
+ HEADER_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
6
+
7
+ def initialize(app, opts = {}, &authenticator)
8
+ @app = app
9
+ @realm = opts.delete(:realm)
10
+ @required = opts.fetch(:required, true)
11
+ opts.delete(:required)
12
+ @authenticator = authenticator || opts.delete(:authenticator)
13
+ end
14
+
15
+ def call(env)
16
+ return @app.call(env) if env.has_key?('oauth2.client')
17
+
18
+ key = HEADER_KEYS.find { |k| env.has_key?(k) }
19
+ auth_string = env[key]
20
+
21
+ if auth_string.nil?
22
+ return @required ? unauthorized : @app.call(env)
23
+ end
24
+
25
+ schema, credentials = auth_string.split(' ', 2)
26
+ if schema.downcase != 'basic'
27
+ return bad_request
28
+ end
29
+
30
+ client_id, client_secret = credentials.unpack('m*').first.split(':', 2)
31
+ client = @authenticator.call(:client_id => client_id, :client_secret => client_secret)
32
+ if client
33
+ env['oauth2.client'] = client
34
+ @app.call(env)
35
+ else
36
+ unauthorized
37
+ end
38
+ end
39
+
40
+ private
41
+ def authenticate(opts)
42
+ @authenticator && @authenticator.call(opts)
43
+ end
44
+
45
+ def unauthorized
46
+ [ 401,
47
+ { 'Content-Type' => 'text/plain',
48
+ 'Content-Length' => '0',
49
+ 'WWW-Authenticate' => 'Basic realm="%s"' % @realm },
50
+ []
51
+ ]
52
+ end
53
+
54
+ def bad_request
55
+ [ 400,
56
+ { 'Content-Type' => 'text/plain',
57
+ 'Content-Length' => '0' },
58
+ []
59
+ ]
60
+ end
61
+ end
@@ -0,0 +1,55 @@
1
+ require 'oauth2/rack'
2
+
3
+ # 2.4.1. Client Password
4
+ # Send client_id and client_secret in request params
5
+ class OAuth2::Rack::Authentication::Client::RequestParams
6
+ def initialize(app, opts = {}, &authenticator)
7
+ @app = app
8
+ @required = opts.fetch(:required, true)
9
+ opts.delete(:required)
10
+ @authenticator = authenticator || opts.delete(:authenticator)
11
+ end
12
+
13
+ def call(env)
14
+ return @app.call(env) if env.has_key?('oauth2.client')
15
+
16
+ @request = Rack::Request.new(env)
17
+
18
+ client_id = @request['client_id']
19
+ client_secret = @request['client_secret']
20
+ if client_id.nil? && client_secret.nil?
21
+ return @required ? unauthorized : @app.call(env)
22
+ elsif client_id.nil? || client_secret.nil?
23
+ return bad_request
24
+ end
25
+
26
+ client = @authenticator.call(:client_id => client_id, :client_secret => client_secret)
27
+ if client
28
+ env['oauth2.client'] = client
29
+ @app.call(env)
30
+ else
31
+ unauthorized
32
+ end
33
+ end
34
+
35
+ private
36
+ def authenticate(opts)
37
+ @authenticator && @authenticator.call(opts)
38
+ end
39
+
40
+ def unauthorized
41
+ [ 401,
42
+ { 'Content-Type' => 'text/plain',
43
+ 'Content-Length' => '0' },
44
+ []
45
+ ]
46
+ end
47
+
48
+ def bad_request
49
+ [ 400,
50
+ { 'Content-Type' => 'text/plain',
51
+ 'Content-Length' => '0' },
52
+ []
53
+ ]
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ # Client authentication
2
+ module OAuth2::Rack::Authentication::Client
3
+ autoload :HTTPBasic, 'oauth2/rack/authentication/client/http_basic'
4
+ autoload :RequestParams, 'oauth2/rack/authentication/client/request_params'
5
+ end
@@ -0,0 +1,54 @@
1
+ require 'oauth2/rack'
2
+
3
+ # Authenticate resource owner with request params
4
+ class OAuth2::Rack::Authentication::ResourceOwner::RequestParams
5
+ def initialize(app, opts = {}, &authenticator)
6
+ @app = app
7
+ @required = opts.fetch(:required, true)
8
+ opts.delete(:required)
9
+ @authenticator = authenticator || opts.delete(:authenticator)
10
+ end
11
+
12
+ def call(env)
13
+ return @app.call(env) if env.has_key?('oauth2.resource_owner')
14
+
15
+ @request = Rack::Request.new(env)
16
+
17
+ username = @request['username']
18
+ password = @request['password']
19
+ if username.nil? && password.nil?
20
+ return @required ? unauthorized : @app.call(env)
21
+ elsif username.nil? || password.nil?
22
+ return bad_request
23
+ end
24
+
25
+ resource_owner = @authenticator.call(:username => username, :password => password)
26
+ if resource_owner
27
+ env['oauth2.resource_owner'] = resource_owner
28
+ @app.call(env)
29
+ else
30
+ unauthorized
31
+ end
32
+ end
33
+
34
+ private
35
+ def authenticate(opts)
36
+ @authenticator && @authenticator.call(opts)
37
+ end
38
+
39
+ def unauthorized
40
+ [ 401,
41
+ { 'Content-Type' => 'text/plain',
42
+ 'Content-Length' => '0' },
43
+ []
44
+ ]
45
+ end
46
+
47
+ def bad_request
48
+ [ 400,
49
+ { 'Content-Type' => 'text/plain',
50
+ 'Content-Length' => '0' },
51
+ []
52
+ ]
53
+ end
54
+ end
@@ -0,0 +1,4 @@
1
+ # Client authentication
2
+ module OAuth2::Rack::Authentication::ResourceOwner
3
+ autoload :RequestParams, 'oauth2/rack/authentication/resource_owner/request_params'
4
+ end
@@ -0,0 +1,6 @@
1
+ # Middlewares for authorization server to authenticate client or user
2
+ module OAuth2::Rack::Authentication
3
+ autoload :Client, 'oauth2/rack/authentication/client'
4
+ autoload :ResourceOwner, 'oauth2/rack/authentication/resource_owner'
5
+ end
6
+
@@ -0,0 +1,58 @@
1
+ # @see 4.4.1 Client Credentials
2
+ require 'oauth2/rack'
3
+ require 'multi_json'
4
+
5
+ class OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer
6
+ def initialize(app, opts = {}, &issuer)
7
+ @app = app
8
+
9
+ @issuer = issuer || opts.delete(:issuer)
10
+ end
11
+
12
+ def call(env)
13
+ client = env['oauth2.client']
14
+ unless client
15
+ return error_response(:error => 'invalid_client')
16
+ end
17
+
18
+ request = Rack::Request.new(env)
19
+ unless request['grant_type'] == 'client_credentials'
20
+ return error_response(:error => 'invalid_request')
21
+ end
22
+
23
+ access_token = find_acccess_token(:grant_type => 'client_credentials',
24
+ :client => client,
25
+ :scope => request['scope'])
26
+
27
+ if access_token['error']
28
+ error_response(access_token)
29
+ else
30
+ successful_response(access_token)
31
+ end
32
+ end
33
+
34
+ private
35
+ def find_acccess_token(opts)
36
+ if @issuer
37
+ @issuer.call(opts)
38
+ end || { 'error' => 'unauthorized_client' }
39
+ end
40
+
41
+ def successful_response(response_object)
42
+ headers = {
43
+ 'Content-Type' => 'application/json;charset=UTF-8',
44
+ 'Cache-Control' => 'no-store',
45
+ 'Pragma' => 'no-cache'
46
+ }
47
+
48
+ [200, headers, [MultiJson.encode(response_object)]]
49
+ end
50
+
51
+ def error_response(response_object)
52
+ headers = {
53
+ 'Content-Type' => 'application/json;charset=UTF-8'
54
+ }
55
+
56
+ [400, headers, [MultiJson.encode(response_object)]]
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ # Middlewares for authorization server
2
+ module OAuth2::Rack::Authorization::ClientCredentials
3
+ autoload :AccessTokenIssuer, 'oauth2/rack/authorization/client_credentials/access_token_issuer'
4
+ end
5
+
@@ -0,0 +1,60 @@
1
+ # @see 4.3. Resource Owner Password Credentials
2
+ require 'oauth2/rack'
3
+ require 'multi_json'
4
+
5
+ class OAuth2::Rack::Authorization::Password::AccessTokenIssuer
6
+ def initialize(app, opts = {}, &issuer)
7
+ @app = app
8
+
9
+ @issuer = issuer || opts.delete(:issuer)
10
+ end
11
+
12
+ def call(env)
13
+ resource_owner = env['oauth2.resource_owner']
14
+ unless resource_owner
15
+ return error_response(:error => 'invalid_grant')
16
+ end
17
+
18
+ request = Rack::Request.new(env)
19
+ unless request['grant_type'] == 'password'
20
+ return error_response(:error => 'invalid_request')
21
+ end
22
+
23
+ # oauth2.client is set in client authentication
24
+ access_token = find_acccess_token(:grant_type => 'password',
25
+ :resource_owner => resource_owner,
26
+ :client => env['oath2.client'],
27
+ :scope => request['scope'])
28
+
29
+ if access_token['error']
30
+ error_response(access_token)
31
+ else
32
+ successful_response(access_token)
33
+ end
34
+ end
35
+
36
+ private
37
+ def find_acccess_token(opts)
38
+ if @issuer
39
+ @issuer.call(opts)
40
+ end || { 'error' => 'unauthorized_client' }
41
+ end
42
+
43
+ def successful_response(response_object)
44
+ headers = {
45
+ 'Content-Type' => 'application/json;charset=UTF-8',
46
+ 'Cache-Control' => 'no-store',
47
+ 'Pragma' => 'no-cache'
48
+ }
49
+
50
+ [200, headers, [MultiJson.encode(response_object)]]
51
+ end
52
+
53
+ def error_response(response_object)
54
+ headers = {
55
+ 'Content-Type' => 'application/json;charset=UTF-8'
56
+ }
57
+
58
+ [400, headers, [MultiJson.encode(response_object)]]
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # Middlewares for authorization server
2
+ module OAuth2::Rack::Authorization::Password
3
+ autoload :AccessTokenIssuer, 'oauth2/rack/authorization/password/access_token_issuer'
4
+ end
5
+
@@ -0,0 +1,6 @@
1
+ # Middlewares for authorization server
2
+ module OAuth2::Rack::Authorization
3
+ autoload :ClientCredentials, 'oauth2/rack/authorization/client_credentials'
4
+ autoload :Password, 'oauth2/rack/authorization/password'
5
+ end
6
+
@@ -0,0 +1,9 @@
1
+ require 'oauth2/rack/version'
2
+
3
+ module OAuth2
4
+ # Oauth2::Rack module, the top namespace for all oauth2-rack modules and classes
5
+ module Rack
6
+ autoload :Authorization, 'oauth2/rack/authorization'
7
+ autoload :Authentication, 'oauth2/rack/authentication'
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require 'oauth2/rack'
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe OAuth2::Rack::Authentication::Client::HTTPBasic do
4
+ let(:client) { double('client').as_null_object }
5
+ let(:authenticator) { double('authenticator').as_null_object }
6
+ let(:opts) { Hash.new }
7
+ let(:chained_app_response) { [200, { 'Content-Type' => 'text/plain' }, []] }
8
+
9
+ def do_request
10
+ post '/', opts
11
+ end
12
+
13
+ context 'when oauth2.client is already set' do
14
+ it 'continues the app' do
15
+ opts['oauth2.client'] = client
16
+ chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
17
+ do_request
18
+ response.status.should eq(200)
19
+ end
20
+ end
21
+
22
+ context 'when auth header is not specified' do
23
+ context 'and http basic auth is required' do
24
+ it 'responds with 401 unauthorized' do
25
+ do_request
26
+ response.status.should eq(401)
27
+ end
28
+ end
29
+ context 'and http basic auth is optional' do
30
+ app { OAuth2::Rack::Authentication::Client::HTTPBasic.new(chained_app, :required => false) }
31
+ it 'continues the app' do
32
+ chained_app.should_receive(:call).with(hash_not_including('oauth2.client')).and_return(chained_app_response)
33
+ do_request
34
+ response.status.should eq(200)
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'when schema is not basic' do
40
+ before { opts['HTTP_AUTHORIZATION'] = 'Digest xxx' }
41
+
42
+ it 'responds with 400 bad request' do
43
+ do_request
44
+ response.status.should eq(400)
45
+ end
46
+ end
47
+
48
+ context 'when schema is basic' do
49
+ app {
50
+ OAuth2::Rack::Authentication::Client::HTTPBasic.new(chained_app) do |opts|
51
+ authenticator.call(opts)
52
+ end
53
+ }
54
+ before { opts['HTTP_AUTHORIZATION'] = "Basic #{["user:pass"].pack('m*')}" }
55
+
56
+ context 'and credentials are valid' do
57
+ it 'sets oauth2.client in env' do
58
+ authenticator.should_receive(:call).with(:client_id => 'user', :client_secret => 'pass').and_return(client)
59
+ chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
60
+
61
+ do_request
62
+ end
63
+ end
64
+
65
+ context 'but credentials are invalid' do
66
+ it 'responds with 401 unauthorized' do
67
+ authenticator.should_receive(:call).with(:client_id => 'user', :client_secret => 'pass').and_return(nil)
68
+ chained_app.should_not_receive(:call)
69
+
70
+ do_request
71
+
72
+ response.status.should eq(401)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe OAuth2::Rack::Authentication::Client::RequestParams do
4
+ let(:client) { double('client').as_null_object }
5
+ let(:authenticator) { double('authenticator').as_null_object }
6
+ let(:params) { Hash.new }
7
+ let(:opts) { Hash[:params => params] }
8
+ let(:chained_app_response) { [200, { 'Content-Type' => 'text/plain' }, []] }
9
+
10
+ def do_request
11
+ post '/', opts
12
+ end
13
+
14
+ context 'when oauth2.client is already set' do
15
+ it 'continues the app' do
16
+ opts['oauth2.client'] = client
17
+ chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
18
+ do_request
19
+ response.status.should eq(200)
20
+ end
21
+ end
22
+
23
+ context 'when client_id and client_secret are both missed' do
24
+ context 'and request params auth is required' do
25
+ it 'responds with 401 unauthorized' do
26
+ do_request
27
+ response.status.should eq(401)
28
+ end
29
+ end
30
+ context 'and request params auth is optional' do
31
+ app { OAuth2::Rack::Authentication::Client::RequestParams.new(chained_app, :required => false) }
32
+ it 'continues the app' do
33
+ chained_app.should_receive(:call).with(hash_not_including('oauth2.client')).and_return(chained_app_response)
34
+ do_request
35
+ response.status.should eq(200)
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'when client_id is missed' do
41
+ before { params['client_secret'] = 'secret' }
42
+
43
+ it 'responds with 400 bad request' do
44
+ do_request
45
+ response.status.should eq(400)
46
+ end
47
+ end
48
+ context 'when client_secret is missed' do
49
+ before { params['client_id'] = 'client_x' }
50
+
51
+ it 'responds with 400 bad request' do
52
+ do_request
53
+ response.status.should eq(400)
54
+ end
55
+ end
56
+
57
+ context 'when client_id and client_secret are specified' do
58
+ app {
59
+ OAuth2::Rack::Authentication::Client::RequestParams.new(chained_app) do |opts|
60
+ authenticator.call(opts)
61
+ end
62
+ }
63
+ let(:credentials) { Hash[:client_id => 'client_x', :client_secret => 'secret'] }
64
+ before { params.merge! credentials }
65
+
66
+ context 'and credentials are valid' do
67
+ it 'sets oauth2.client in env' do
68
+ authenticator.should_receive(:call).with(credentials).and_return(client)
69
+ chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
70
+
71
+ do_request
72
+ end
73
+ end
74
+
75
+ context 'but credentials are invalid' do
76
+ it 'responds with 401 unauthorized' do
77
+ authenticator.should_receive(:call).with(credentials).and_return(nil)
78
+ chained_app.should_not_receive(:call)
79
+
80
+ do_request
81
+
82
+ response.status.should eq(401)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe OAuth2::Rack::Authentication::ResourceOwner::RequestParams do
4
+ let(:resource_owner) { double('resource_owner').as_null_object }
5
+ let(:authenticator) { double('authenticator').as_null_object }
6
+ let(:params) { Hash.new }
7
+ let(:opts) { Hash[:params => params] }
8
+ let(:chained_app_response) { [200, { 'Content-Type' => 'text/plain' }, []] }
9
+
10
+ def do_request
11
+ post '/', opts
12
+ end
13
+
14
+ context 'when oauth2.resource_owner is already set' do
15
+ it 'continues the app' do
16
+ opts['oauth2.resource_owner'] = resource_owner
17
+ chained_app.should_receive(:call).with(hash_including('oauth2.resource_owner' => resource_owner)).and_return(chained_app_response)
18
+ do_request
19
+ response.status.should eq(200)
20
+ end
21
+ end
22
+
23
+ context 'when username and password are both missed' do
24
+ context 'and request params auth is required' do
25
+ it 'responds with 401 unauthorized' do
26
+ do_request
27
+ response.status.should eq(401)
28
+ end
29
+ end
30
+ context 'and request params auth is optional' do
31
+ app { OAuth2::Rack::Authentication::ResourceOwner::RequestParams.new(chained_app, :required => false) }
32
+ it 'continues the app' do
33
+ chained_app.should_receive(:call).with(hash_not_including('oauth2.resource_owner')).and_return(chained_app_response)
34
+ do_request
35
+ response.status.should eq(200)
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'when username is missed' do
41
+ before { params['password'] = 'secret' }
42
+
43
+ it 'responds with 400 bad request' do
44
+ do_request
45
+ response.status.should eq(400)
46
+ end
47
+ end
48
+ context 'when password is missed' do
49
+ before { params['username'] = 'user_x' }
50
+
51
+ it 'responds with 400 bad request' do
52
+ do_request
53
+ response.status.should eq(400)
54
+ end
55
+ end
56
+
57
+ context 'when username and password are specified' do
58
+ app {
59
+ OAuth2::Rack::Authentication::ResourceOwner::RequestParams.new(chained_app) do |opts|
60
+ authenticator.call(opts)
61
+ end
62
+ }
63
+ let(:credentials) { Hash[:username => 'user_x', :password => 'secret'] }
64
+ before { params.merge! credentials }
65
+
66
+ context 'and credentials are valid' do
67
+ it 'sets oauth2.resource_owner in env' do
68
+ authenticator.should_receive(:call).with(credentials).and_return(resource_owner)
69
+ chained_app.should_receive(:call).with(hash_including('oauth2.resource_owner' => resource_owner)).and_return(chained_app_response)
70
+
71
+ do_request
72
+ end
73
+ end
74
+
75
+ context 'but credentials are invalid' do
76
+ it 'responds with 401 unauthorized' do
77
+ authenticator.should_receive(:call).with(credentials).and_return(nil)
78
+ chained_app.should_not_receive(:call)
79
+
80
+ do_request
81
+
82
+ response.status.should eq(401)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer do
4
+ let(:client) { double('client') }
5
+ let(:opts) { Hash.new }
6
+
7
+ def do_request
8
+ post '/', opts
9
+ end
10
+
11
+ context 'when client is authenticated' do
12
+ before { opts['oauth2.client'] = client }
13
+
14
+ context 'and grant_type is invalid' do
15
+ let(:params) { Hash[:grant_type => 'xclient_credentials'] }
16
+ before { opts[:params] = params }
17
+
18
+ it 'responds with invalid_request' do
19
+ do_request
20
+
21
+ response.status.should eq(400)
22
+ response_object['error'].should eq('invalid_request')
23
+ end
24
+ end
25
+
26
+ context 'and grant_type is valid' do
27
+ let(:params) { Hash[:grant_type => 'client_credentials'] }
28
+ before { opts[:params] = params }
29
+
30
+ context 'and issuer is not specified' do
31
+ it 'responds with unauthorized_client' do
32
+ do_request
33
+
34
+ response.status.should eq(400)
35
+ response_object['error'].should eq('unauthorized_client')
36
+ end
37
+ end
38
+
39
+ context 'and issuer is specified' do
40
+ let(:issuer) { double('issuer') }
41
+ let(:expected_find_opts) {
42
+ Hash[:grant_type => 'client_credentials',
43
+ :client => client,
44
+ :scope => nil]
45
+ }
46
+
47
+ app { OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer.new(chained_app) { |opts| issuer.call(opts) } }
48
+
49
+ context 'but token is not found for the client' do
50
+ context 'and error is returned' do
51
+ before {
52
+ issuer.should_receive(:call).with(expected_find_opts).and_return({'error' => 'customized_error'})
53
+ }
54
+ it 'responds with the that error' do
55
+ do_request
56
+
57
+ response.status.should eq(400)
58
+ response_object['error'].should eq('customized_error')
59
+ end
60
+ end
61
+ context 'and nothing is returned' do
62
+ before {
63
+ issuer.should_receive(:call).with(expected_find_opts).and_return(nil)
64
+ }
65
+ it 'responds with unauthorized_client' do
66
+ do_request
67
+
68
+ response.status.should eq(400)
69
+ response_object['error'].should eq('unauthorized_client')
70
+ end
71
+ end
72
+
73
+ end
74
+ context 'and token is found for the client' do
75
+ before {
76
+ issuer.should_receive(:call).with(expected_find_opts).and_return({:access_token => 'X'})
77
+ }
78
+
79
+ it 'responds with the found token' do
80
+ do_request
81
+
82
+ response.status.should eq(200)
83
+ response_object['access_token'].should eq('X')
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ context 'when client not authenticated' do
92
+ before { post '/' }
93
+
94
+ it 'responds with invalid_client' do
95
+ response.status.should eq(400)
96
+ response_object['error'].should eq('invalid_client')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe OAuth2::Rack::Authorization::Password::AccessTokenIssuer do
4
+ let(:resource_owner) { double('resource_owner') }
5
+ let(:client) { double('client') }
6
+ let(:opts) { Hash.new }
7
+
8
+ def do_request
9
+ post '/', opts
10
+ end
11
+
12
+ context 'when resource_owner is authenticated' do
13
+ before { opts['oauth2.resource_owner'] = resource_owner }
14
+
15
+ context 'and grant_type is invalid' do
16
+ let(:params) { Hash[:grant_type => 'xpassword'] }
17
+ before { opts[:params] = params }
18
+
19
+ it 'responds with invalid_request' do
20
+ do_request
21
+
22
+ response.status.should eq(400)
23
+ response_object['error'].should eq('invalid_request')
24
+ end
25
+ end
26
+
27
+ context 'and grant_type is valid' do
28
+ let(:params) { Hash[:grant_type => 'password'] }
29
+ before { opts[:params] = params }
30
+
31
+ context 'and issuer is not specified' do
32
+ it 'responds with unauthorized_client' do
33
+ do_request
34
+
35
+ response.status.should eq(400)
36
+ response_object['error'].should eq('unauthorized_client')
37
+ end
38
+ end
39
+
40
+ context 'and issuer is specified' do
41
+ let(:issuer) { double('issuer') }
42
+ let(:expected_find_opts) {
43
+ Hash[:grant_type => 'password',
44
+ :resource_owner => resource_owner,
45
+ :client => nil,
46
+ :scope => nil]
47
+ }
48
+
49
+ app { OAuth2::Rack::Authorization::Password::AccessTokenIssuer.new(chained_app) { |opts| issuer.call(opts) } }
50
+
51
+ context 'but token is not found for the resource owner' do
52
+ context 'and error is returned' do
53
+ before {
54
+ issuer.should_receive(:call).with(expected_find_opts).and_return({'error' => 'customized_error'})
55
+ }
56
+ it 'responds with the that error' do
57
+ do_request
58
+
59
+ response.status.should eq(400)
60
+ response_object['error'].should eq('customized_error')
61
+ end
62
+ end
63
+ context 'and nothing is returned' do
64
+ before {
65
+ issuer.should_receive(:call).with(expected_find_opts).and_return(nil)
66
+ }
67
+ it 'responds with unauthorized_client' do
68
+ do_request
69
+
70
+ response.status.should eq(400)
71
+ response_object['error'].should eq('unauthorized_client')
72
+ end
73
+ end
74
+
75
+ end
76
+ context 'and token is found for the client' do
77
+ before {
78
+ issuer.should_receive(:call).with(expected_find_opts).and_return({:access_token => 'X'})
79
+ }
80
+
81
+ it 'responds with the found token' do
82
+ do_request
83
+
84
+ response.status.should eq(200)
85
+ response_object['access_token'].should eq('X')
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ context 'when resource owner not authenticated' do
94
+ before { post '/' }
95
+
96
+ it 'responds with invalid_grant' do
97
+ response.status.should eq(400)
98
+ response_object['error'].should eq('invalid_grant')
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,11 @@
1
+ $: << File.expand_path('../../lib', __FILE__)
2
+ require 'oauth2/rack'
3
+ require 'multi_json'
4
+
5
+ # Requires supporting ruby files with custom matchers and macros, etc,
6
+ # in spec/support/ and its subdirectories.
7
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
8
+
9
+ RSpec.configure do |config|
10
+ config.mock_with :rspec
11
+ end
@@ -0,0 +1,97 @@
1
+ require 'rack/mock'
2
+ require 'rack/utils'
3
+
4
+ module RackTestHelper
5
+ def self.included(klass)
6
+ klass.extend ClassMethods
7
+ end
8
+
9
+ attr_accessor :response
10
+
11
+ def chained_app
12
+ if defined?(@original_chained_app)
13
+ @original_chained_app
14
+ else
15
+ @original_chained_app = instance_eval(&self.class.chained_app)
16
+ end
17
+ end
18
+
19
+ def app
20
+ if defined?(@original_app)
21
+ @original_app
22
+ else
23
+ @original_app = instance_eval(&self.class.app)
24
+ end
25
+ end
26
+
27
+ def request
28
+ @request ||= Rack::MockRequest.new(app)
29
+ end
30
+
31
+ def response_object
32
+ return unless response
33
+
34
+ case response['Content-Type']
35
+ when %r{^application/json}
36
+ MultiJson::decode(response.body)
37
+ when %r{^application/x-www-form-urlencoded}
38
+ Rack::Utils.new.parse(response.body)
39
+ else
40
+ response.body
41
+ end
42
+ end
43
+
44
+ def get(uri, opts = {})
45
+ self.response = request.get(uri, opts)
46
+ end
47
+ def post(uri, opts = {})
48
+ self.response = request.post(uri, opts)
49
+ end
50
+ def put(uri, opts = {})
51
+ self.response = request.put(uri, opts)
52
+ end
53
+ def delete(uri, opts = {})
54
+ self.response = request.delete(uri, opts)
55
+ end
56
+
57
+ module ClassMethods
58
+ attr_reader :explicit_chained_app_block
59
+ def chained_app(&block)
60
+ block ? @explicit_chained_app_block = block : explicit_chained_app || implicit_chained_app
61
+ end
62
+
63
+ attr_reader :explicit_app_block
64
+ def app(&block)
65
+ block ? @explicit_app_block = block : explicit_app || implicit_app
66
+ end
67
+
68
+ def explicit_app
69
+ group = self
70
+ while group.respond_to?(:explicit_app_block)
71
+ return group.explicit_app_block if group.explicit_app_block
72
+ group = group.superclass
73
+ end
74
+ end
75
+
76
+ def implicit_app
77
+ described = describes || description
78
+ Class === described ? proc { described.new(chained_app) } : proc { described }
79
+ end
80
+
81
+ def explicit_chained_app
82
+ group = self
83
+ while group.respond_to?(:explicit_chained_app_block)
84
+ return group.explicit_chained_app_block if group.explicit_chained_app_block
85
+ group = group.superclass
86
+ end
87
+ end
88
+
89
+ def implicit_chained_app
90
+ proc { |env| [200, {"Content-Type" => 'text/html'}, ["<p>OK</p>"]] }
91
+ end
92
+ end
93
+ end
94
+
95
+ RSpec.configure do |config|
96
+ config.include RackTestHelper
97
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oauth2-rack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ian Yang
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-26 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multi_json
16
+ requirement: &8610000 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *8610000
25
+ - !ruby/object:Gem::Dependency
26
+ name: rack
27
+ requirement: &8609580 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *8609580
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &8609160 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *8609160
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &8608740 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *8608740
58
+ - !ruby/object:Gem::Dependency
59
+ name: yard
60
+ requirement: &8608320 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *8608320
69
+ - !ruby/object:Gem::Dependency
70
+ name: shotgun
71
+ requirement: &8607900 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *8607900
80
+ - !ruby/object:Gem::Dependency
81
+ name: guard-rspec
82
+ requirement: &8607480 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *8607480
91
+ - !ruby/object:Gem::Dependency
92
+ name: oauth2
93
+ requirement: &8607060 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *8607060
102
+ description: Rack middlewares for OAuth2 authorization server and resource server
103
+ email:
104
+ - me@iany.me
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - .gitignore
110
+ - .rspec
111
+ - Gemfile
112
+ - Guardfile
113
+ - Rakefile
114
+ - config.ru
115
+ - examples/ruby-client/password_authorization.rb
116
+ - lib/oauth2-rack.rb
117
+ - lib/oauth2/rack.rb
118
+ - lib/oauth2/rack/authentication.rb
119
+ - lib/oauth2/rack/authentication/client.rb
120
+ - lib/oauth2/rack/authentication/client/http_basic.rb
121
+ - lib/oauth2/rack/authentication/client/request_params.rb
122
+ - lib/oauth2/rack/authentication/resource_owner.rb
123
+ - lib/oauth2/rack/authentication/resource_owner/request_params.rb
124
+ - lib/oauth2/rack/authorization.rb
125
+ - lib/oauth2/rack/authorization/client_credentials.rb
126
+ - lib/oauth2/rack/authorization/client_credentials/access_token_issuer.rb
127
+ - lib/oauth2/rack/authorization/password.rb
128
+ - lib/oauth2/rack/authorization/password/access_token_issuer.rb
129
+ - spec/oauth2/rack/authentication/client/http_basic_spec.rb
130
+ - spec/oauth2/rack/authentication/client/request_params_spec.rb
131
+ - spec/oauth2/rack/authentication/resource_owner/request_params_spec.rb
132
+ - spec/oauth2/rack/authorization/client_credentials/access_token_issuer_spec.rb
133
+ - spec/oauth2/rack/authorization/password/access_token_issuer_spec.rb
134
+ - spec/spec_helper.rb
135
+ - spec/support/rake_test_helper.rb
136
+ homepage: https://github.com/doitian/oauth2-rack
137
+ licenses: []
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project: oauth2-rack
156
+ rubygems_version: 1.8.6
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: Rack middlewares for OAuth2 authorization server and resource server
160
+ test_files:
161
+ - spec/oauth2/rack/authentication/client/http_basic_spec.rb
162
+ - spec/oauth2/rack/authentication/client/request_params_spec.rb
163
+ - spec/oauth2/rack/authentication/resource_owner/request_params_spec.rb
164
+ - spec/oauth2/rack/authorization/client_credentials/access_token_issuer_spec.rb
165
+ - spec/oauth2/rack/authorization/password/access_token_issuer_spec.rb
166
+ - spec/spec_helper.rb
167
+ - spec/support/rake_test_helper.rb