rack-oauth2 0.2.3 → 0.3.0.alpha

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.
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