rack-oauth2 0.2.3 → 0.3.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/Gemfile +1 -0
  2. data/README.rdoc +1 -0
  3. data/VERSION +1 -1
  4. data/lib/rack/oauth2.rb +1 -7
  5. data/lib/rack/oauth2/server.rb +0 -1
  6. data/lib/rack/oauth2/server/abstract.rb +2 -1
  7. data/lib/rack/oauth2/server/abstract/error.rb +55 -0
  8. data/lib/rack/oauth2/server/abstract/handler.rb +2 -3
  9. data/lib/rack/oauth2/server/abstract/request.rb +2 -3
  10. data/lib/rack/oauth2/server/abstract/response.rb +0 -5
  11. data/lib/rack/oauth2/server/authorize.rb +19 -14
  12. data/lib/rack/oauth2/server/authorize/code.rb +8 -19
  13. data/lib/rack/oauth2/server/authorize/error.rb +60 -0
  14. data/lib/rack/oauth2/server/authorize/token.rb +15 -24
  15. data/lib/rack/oauth2/server/resource.rb +1 -79
  16. data/lib/rack/oauth2/server/resource/bearer.rb +74 -0
  17. data/lib/rack/oauth2/server/resource/bearer/error.rb +80 -0
  18. data/lib/rack/oauth2/server/token.rb +12 -19
  19. data/lib/rack/oauth2/server/token/authorization_code.rb +4 -5
  20. data/lib/rack/oauth2/server/token/error.rb +54 -0
  21. data/lib/rack/oauth2/server/token/password.rb +0 -2
  22. data/lib/rack/oauth2/server/token/refresh_token.rb +1 -1
  23. data/lib/rack/oauth2/server/util.rb +29 -0
  24. data/rack-oauth2.gemspec +1 -1
  25. data/spec/rack/oauth2/server/abstract/error_spec.rb +51 -0
  26. data/spec/rack/oauth2/server/authorize/code_spec.rb +42 -28
  27. data/spec/rack/oauth2/server/authorize/error_spec.rb +103 -0
  28. data/spec/rack/oauth2/server/authorize/token_spec.rb +55 -26
  29. data/spec/rack/oauth2/server/authorize_spec.rb +24 -68
  30. data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +118 -0
  31. data/spec/rack/oauth2/server/resource/bearer_spec.rb +117 -0
  32. data/spec/rack/oauth2/server/token/authorization_code_spec.rb +26 -109
  33. data/spec/rack/oauth2/server/token/error_spec.rb +77 -0
  34. data/spec/rack/oauth2/server/token/password_spec.rb +27 -47
  35. data/spec/rack/oauth2/server/token/refresh_token_spec.rb +22 -43
  36. data/spec/rack/oauth2/server/token_spec.rb +77 -116
  37. data/spec/rack/oauth2/server/util_spec.rb +75 -16
  38. data/spec/spec_helper.rb +0 -12
  39. metadata +25 -29
  40. data/lib/rack/oauth2/server/authorize/code_and_token.rb +0 -62
  41. data/lib/rack/oauth2/server/error.rb +0 -73
  42. data/lib/rack/oauth2/server/error/authorize.rb +0 -54
  43. data/lib/rack/oauth2/server/error/resource.rb +0 -50
  44. data/lib/rack/oauth2/server/error/token.rb +0 -59
  45. data/lib/rack/oauth2/server/token/assertion.rb +0 -29
  46. data/spec/rack/oauth2/server/authorize/code_and_token_spec.rb +0 -53
  47. data/spec/rack/oauth2/server/error/authorize_spec.rb +0 -102
  48. data/spec/rack/oauth2/server/error/resource_spec.rb +0 -69
  49. data/spec/rack/oauth2/server/error/token_spec.rb +0 -115
  50. data/spec/rack/oauth2/server/error_spec.rb +0 -107
  51. data/spec/rack/oauth2/server/resource_spec.rb +0 -141
  52. data/spec/rack/oauth2/server/token/assertion_spec.rb +0 -56
@@ -0,0 +1,74 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ module Resource
5
+ class Bearer < Abstract::Handler
6
+ ACCESS_TOKEN = 'rack.oauth2.bearer.oauth_token'
7
+
8
+ def initialize(app, &authenticator)
9
+ @app = app
10
+ super(&authenticator)
11
+ end
12
+
13
+ def call(env)
14
+ request = Request.new(env)
15
+ if request.bearer?
16
+ authenticate!(request)
17
+ env[ACCESS_TOKEN] = request.access_token
18
+ end
19
+ @app.call(env)
20
+ rescue Rack::OAuth2::Server::Abstract::Error => e
21
+ e.finish
22
+ end
23
+
24
+ private
25
+
26
+ def authenticate!(request)
27
+ @authenticator.call(request)
28
+ end
29
+
30
+ class Request < Rack::Request
31
+ def initialize(env)
32
+ @env = env
33
+ @auth_header = Rack::Auth::AbstractRequest.new(env)
34
+ end
35
+
36
+ def bearer?
37
+ access_token.present?
38
+ end
39
+
40
+ def access_token
41
+ tokens = [access_token_in_haeder, access_token_in_payload].compact
42
+ case Array(tokens).size
43
+ when 0
44
+ nil
45
+ when 1
46
+ tokens.first
47
+ else
48
+ invalid_request!('Both Authorization header and payload includes access token.')
49
+ end
50
+ end
51
+
52
+ def access_token_in_haeder
53
+ if @auth_header.provided? && @auth_header.scheme == :bearer
54
+ @auth_header.params
55
+ else
56
+ nil
57
+ end
58
+ end
59
+
60
+ def access_token_in_payload
61
+ if params['oauth_token'] && !params['oauth_signature_method']
62
+ params['oauth_token']
63
+ else
64
+ nil # This is OAuth1 request
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ require 'rack/oauth2/server/resource/bearer/error'
@@ -0,0 +1,80 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ module Resource
5
+ class Bearer
6
+ class BadRequest < Abstract::BadRequest
7
+ end
8
+
9
+ class Unauthorized < Abstract::Unauthorized
10
+ def finish
11
+ super do |response|
12
+ response.header['WWW-Authenticate'] = if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error)
13
+ header = "Bearer error=\"#{error}\""
14
+ header += " error_description=\"#{description}\"" if description.present?
15
+ header += " error_uri=\"#{uri}\"" if uri.present?
16
+ header
17
+ else
18
+ 'Bearer'
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class Forbidden < Abstract::Forbidden
25
+ attr_accessor :scope
26
+
27
+ def initialize(error = :forbidden, description = nil, options = {})
28
+ super
29
+ @scope = options[:scope]
30
+ end
31
+
32
+ def protocol_params
33
+ super.merge(:scope => Array(scope).join(' '))
34
+ end
35
+ end
36
+
37
+ module ErrorMethods
38
+ DEFAULT_DESCRIPTION = {
39
+ :invalid_request => "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.",
40
+ :invalid_token => "The access token provided is expired, revoked, malformed or invalid for other reasons.",
41
+ :insufficient_scope => "The request requires higher privileges than provided by the access token."
42
+ }
43
+
44
+ def self.included(klass)
45
+ DEFAULT_DESCRIPTION.each do |error, default_description|
46
+ error_method = case error
47
+ when :invalid_request
48
+ :bad_request!
49
+ when :insufficient_scope
50
+ :forbidden!
51
+ else
52
+ :unauthorized!
53
+ end
54
+ klass.class_eval <<-ERROR
55
+ def #{error}!(description = "#{default_description}", options = {})
56
+ #{error_method} :#{error}, description, options
57
+ end
58
+ ERROR
59
+ end
60
+ end
61
+
62
+ def bad_request!(error, description = nil, options = {})
63
+ raise BadRequest.new(error, description, options)
64
+ end
65
+
66
+ def unauthorized!(error = nil, description = nil, options = {})
67
+ raise Unauthorized.new(error, description, options)
68
+ end
69
+
70
+ def forbidden!(error, description = nil, options = {})
71
+ raise Forbidden.new(error, description, options)
72
+ end
73
+ end
74
+
75
+ Request.send :include, ErrorMethods
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -4,26 +4,21 @@ module Rack
4
4
  module OAuth2
5
5
  module Server
6
6
  class Token < Abstract::Handler
7
-
8
7
  def call(env)
9
8
  request = Request.new(env)
10
- request.profile.new(@realm, &@authenticator).call(env).finish
11
- rescue Error => e
9
+ request.profile.new(&@authenticator).call(env).finish
10
+ rescue Rack::OAuth2::Server::Abstract::Error => e
12
11
  e.finish
13
12
  end
14
13
 
15
14
  class Request < Abstract::Request
16
- include Error::Token
17
-
18
15
  attr_required :grant_type
19
16
  attr_optional :client_secret
20
- attr_accessor :via_authorization_header
21
17
 
22
18
  def initialize(env)
23
19
  auth = Rack::Auth::Basic::Request.new(env)
24
20
  if auth.provided? && auth.basic?
25
21
  @client_id, @client_secret = auth.credentials
26
- @via_authorization_header = true
27
22
  super
28
23
  else
29
24
  super
@@ -32,14 +27,12 @@ module Rack
32
27
  @grant_type = params['grant_type']
33
28
  end
34
29
 
35
- def profile(allow_no_profile = false)
30
+ def profile
36
31
  case params['grant_type'].to_s
37
32
  when 'authorization_code'
38
33
  AuthorizationCode
39
34
  when 'password'
40
35
  Password
41
- when 'assertion'
42
- Assertion
43
36
  when 'refresh_token'
44
37
  RefreshToken
45
38
  when ''
@@ -48,33 +41,33 @@ module Rack
48
41
  unsupported_grant_type!("'#{params['grant_type']}' isn't supported.")
49
42
  end
50
43
  end
51
-
52
44
  end
53
45
 
54
46
  class Response < Abstract::Response
55
47
  attr_required :access_token
56
48
  attr_optional :expires_in, :refresh_token, :scope
57
49
 
58
- def finish
59
- params = {
50
+ def protocol_params
51
+ {
60
52
  :access_token => access_token,
61
53
  :expires_in => expires_in,
62
54
  :scope => Array(scope).join(' ')
63
- }.delete_if do |key, value|
64
- value.blank?
65
- end
66
- write params.to_json
55
+ }
56
+ end
57
+
58
+ def finish
59
+ write Util.compact_hash(protocol_params).to_json
67
60
  header['Content-Type'] = "application/json"
61
+ attr_missing!
68
62
  super
69
63
  end
70
64
  end
71
-
72
65
  end
73
66
  end
74
67
  end
75
68
  end
76
69
 
70
+ require 'rack/oauth2/server/token/error'
77
71
  require 'rack/oauth2/server/token/authorization_code'
78
72
  require 'rack/oauth2/server/token/password'
79
- require 'rack/oauth2/server/token/assertion'
80
73
  require 'rack/oauth2/server/token/refresh_token'
@@ -3,7 +3,6 @@ module Rack
3
3
  module Server
4
4
  class Token
5
5
  class AuthorizationCode < Abstract::Handler
6
-
7
6
  def call(env)
8
7
  @request = Request.new(env)
9
8
  @response = Response.new(request)
@@ -11,16 +10,16 @@ module Rack
11
10
  end
12
11
 
13
12
  class Request < Token::Request
14
- attr_required :code
13
+ attr_required :code, :redirect_uri
15
14
 
16
15
  def initialize(env)
17
16
  super
18
- @grant_type = :authorization_code
19
- @code = params['code']
17
+ @grant_type = :authorization_code
18
+ @code = params['code']
19
+ @redirect_uri = params['redirect_uri']
20
20
  attr_missing!
21
21
  end
22
22
  end
23
-
24
23
  end
25
24
  end
26
25
  end
@@ -0,0 +1,54 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ class Token
5
+ class BadRequest < Abstract::BadRequest
6
+ end
7
+
8
+ class Unauthorized < Abstract::Unauthorized
9
+ def finish
10
+ super do |response|
11
+ response.header['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
12
+ end
13
+ end
14
+ end
15
+
16
+ module ErrorMethods
17
+ DEFAULT_DESCRIPTION = {
18
+ :invalid_request => "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed.",
19
+ :invalid_client => "The client identifier provided is invalid, the client failed to authenticate, the client did not include its credentials, provided multiple client credentials, or used unsupported credentials type.",
20
+ :invalid_grant => "The provided access grant is invalid, expired, or revoked (e.g. invalid assertion, expired authorization token, bad end-user password credentials, or mismatching authorization code and redirection URI).",
21
+ :unauthorized_client => "The authenticated client is not authorized to use the access grant type provided.",
22
+ :unsupported_grant_type => "The access grant included - its type or another attribute - is not supported by the authorization server.",
23
+ :invalid_scope => "The requested scope is invalid, unknown, malformed, or exceeds the previously granted scope."
24
+ }
25
+
26
+ def self.included(klass)
27
+ DEFAULT_DESCRIPTION.each do |error, default_description|
28
+ error_method = if error == :invalid_client
29
+ :unauthorized!
30
+ else
31
+ :bad_request!
32
+ end
33
+ klass.class_eval <<-ERROR
34
+ def #{error}!(description = "#{default_description}", options = {})
35
+ #{error_method} :#{error}, description, options
36
+ end
37
+ ERROR
38
+ end
39
+ end
40
+
41
+ def bad_request!(error, description = nil, options = {})
42
+ raise BadRequest.new(error, description, options)
43
+ end
44
+
45
+ def unauthorized!(error, description = nil, options = {})
46
+ raise Unauthorized.new(error, description, options)
47
+ end
48
+ end
49
+
50
+ Request.send :include, ErrorMethods
51
+ end
52
+ end
53
+ end
54
+ end
@@ -3,7 +3,6 @@ module Rack
3
3
  module Server
4
4
  class Token
5
5
  class Password < Abstract::Handler
6
-
7
6
  def call(env)
8
7
  @request = Request.new(env)
9
8
  @response = Response.new(request)
@@ -21,7 +20,6 @@ module Rack
21
20
  attr_missing!
22
21
  end
23
22
  end
24
-
25
23
  end
26
24
  end
27
25
  end
@@ -15,7 +15,7 @@ module Rack
15
15
 
16
16
  def initialize(env)
17
17
  super
18
- @grant_type = 'refresh_token'
18
+ @grant_type = :refresh_token
19
19
  @refresh_token = params['refresh_token']
20
20
  attr_missing!
21
21
  end
@@ -3,6 +3,12 @@ module Rack
3
3
  module Server
4
4
  module Util
5
5
  class << self
6
+ def compact_hash(hash)
7
+ hash.reject do |key, value|
8
+ value.blank?
9
+ end
10
+ end
11
+
6
12
  def parse_uri(uri)
7
13
  case uri
8
14
  when URI::Generic
@@ -13,6 +19,29 @@ module Rack
13
19
  raise "Invalid format of URI is given."
14
20
  end
15
21
  end
22
+
23
+ def redirect_uri(base_uri, location, params)
24
+ redirect_uri = parse_uri base_uri
25
+ case location
26
+ when :query
27
+ redirect_uri.query = [redirect_uri.query, Util.compact_hash(params).to_query].compact.join('&')
28
+ when :fragment
29
+ redirect_uri.fragment = Util.compact_hash(params).to_query
30
+ end
31
+ redirect_uri.to_s
32
+ end
33
+
34
+ def verify_redirect_uri(registered, given)
35
+ registered = parse_uri(registered)
36
+ given = parse_uri(given)
37
+ registered.path = '/' if registered.path.blank?
38
+ given.path = '/' if given.path.blank?
39
+ [:scheme, :host, :port].all? do |key|
40
+ registered.send(key) == given.send(key)
41
+ end && /^#{registered.path}/ =~ given.path
42
+ rescue
43
+ false
44
+ end
16
45
  end
17
46
  end
18
47
  end
data/rack-oauth2.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.add_runtime_dependency "json", ">= 1.4.3"
18
18
  s.add_runtime_dependency "activesupport", ">= 2.3"
19
19
  s.add_runtime_dependency "i18n"
20
- s.add_runtime_dependency "attr_required", ">= 0.0.2"
20
+ s.add_runtime_dependency "attr_required", ">= 0.0.3"
21
21
  s.add_development_dependency "rake", ">= 0.8"
22
22
  s.add_development_dependency "rcov", ">= 0.9"
23
23
  s.add_development_dependency "rspec", ">= 2"
@@ -0,0 +1,51 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Rack::OAuth2::Server::Abstract::Error do
4
+
5
+ context 'when full attributes are given' do
6
+ subject do
7
+ Rack::OAuth2::Server::Abstract::Error.new 400, :invalid_request, 'Missing some required params', :uri => 'http://server.example.com/error'
8
+ end
9
+ its(:status) { should == 400 }
10
+ its(:error) { should == :invalid_request }
11
+ its(:description) { should == 'Missing some required params' }
12
+ its(:uri) { should == 'http://server.example.com/error' }
13
+ its(:protocol_params) do
14
+ should == {
15
+ :error => :invalid_request,
16
+ :error_description => 'Missing some required params',
17
+ :error_uri => 'http://server.example.com/error'
18
+ }
19
+ end
20
+ end
21
+
22
+ context 'when optional attributes are not given' do
23
+ subject do
24
+ Rack::OAuth2::Server::Abstract::Error.new 400, :invalid_request
25
+ end
26
+ its(:status) { should == 400 }
27
+ its(:error) { should == :invalid_request }
28
+ its(:description) { should be_nil }
29
+ its(:uri) { should be_nil }
30
+ its(:protocol_params) do
31
+ should == {
32
+ :error => :invalid_request,
33
+ :error_description => nil,
34
+ :error_uri => nil
35
+ }
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ describe Rack::OAuth2::Server::Abstract::BadRequest do
42
+ its(:status) { should == 400 }
43
+ end
44
+
45
+ describe Rack::OAuth2::Server::Abstract::Unauthorized do
46
+ its(:status) { should == 401 }
47
+ end
48
+
49
+ describe Rack::OAuth2::Server::Abstract::Forbidden do
50
+ its(:status) { should == 403 }
51
+ end