rack-oauth2-revibe 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +22 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +3 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.rdoc +78 -0
  9. data/Rakefile +25 -0
  10. data/VERSION +1 -0
  11. data/lib/rack/oauth2.rb +67 -0
  12. data/lib/rack/oauth2/access_token.rb +36 -0
  13. data/lib/rack/oauth2/access_token/authenticator.rb +24 -0
  14. data/lib/rack/oauth2/access_token/bearer.rb +11 -0
  15. data/lib/rack/oauth2/access_token/legacy.rb +23 -0
  16. data/lib/rack/oauth2/access_token/mac.rb +103 -0
  17. data/lib/rack/oauth2/access_token/mac/sha256_hex_verifier.rb +17 -0
  18. data/lib/rack/oauth2/access_token/mac/signature.rb +34 -0
  19. data/lib/rack/oauth2/access_token/mac/verifier.rb +44 -0
  20. data/lib/rack/oauth2/client.rb +139 -0
  21. data/lib/rack/oauth2/client/error.rb +14 -0
  22. data/lib/rack/oauth2/client/grant.rb +30 -0
  23. data/lib/rack/oauth2/client/grant/authorization_code.rb +12 -0
  24. data/lib/rack/oauth2/client/grant/client_credentials.rb +10 -0
  25. data/lib/rack/oauth2/client/grant/facebook_token.rb +12 -0
  26. data/lib/rack/oauth2/client/grant/password.rb +11 -0
  27. data/lib/rack/oauth2/client/grant/refresh_token.rb +11 -0
  28. data/lib/rack/oauth2/debugger.rb +3 -0
  29. data/lib/rack/oauth2/debugger/request_filter.rb +30 -0
  30. data/lib/rack/oauth2/server.rb +4 -0
  31. data/lib/rack/oauth2/server/abstract.rb +4 -0
  32. data/lib/rack/oauth2/server/abstract/error.rb +69 -0
  33. data/lib/rack/oauth2/server/abstract/handler.rb +20 -0
  34. data/lib/rack/oauth2/server/abstract/request.rb +29 -0
  35. data/lib/rack/oauth2/server/abstract/response.rb +15 -0
  36. data/lib/rack/oauth2/server/authorize.rb +117 -0
  37. data/lib/rack/oauth2/server/authorize/code.rb +39 -0
  38. data/lib/rack/oauth2/server/authorize/error.rb +71 -0
  39. data/lib/rack/oauth2/server/authorize/extension.rb +12 -0
  40. data/lib/rack/oauth2/server/authorize/extension/code_and_token.rb +39 -0
  41. data/lib/rack/oauth2/server/authorize/token.rb +43 -0
  42. data/lib/rack/oauth2/server/resource.rb +55 -0
  43. data/lib/rack/oauth2/server/resource/bearer.rb +47 -0
  44. data/lib/rack/oauth2/server/resource/bearer/error.rb +24 -0
  45. data/lib/rack/oauth2/server/resource/error.rb +81 -0
  46. data/lib/rack/oauth2/server/resource/mac.rb +36 -0
  47. data/lib/rack/oauth2/server/resource/mac/error.rb +24 -0
  48. data/lib/rack/oauth2/server/token.rb +87 -0
  49. data/lib/rack/oauth2/server/token/authorization_code.rb +28 -0
  50. data/lib/rack/oauth2/server/token/client_credentials.rb +23 -0
  51. data/lib/rack/oauth2/server/token/error.rb +54 -0
  52. data/lib/rack/oauth2/server/token/extension.rb +12 -0
  53. data/lib/rack/oauth2/server/token/extension/jwt.rb +37 -0
  54. data/lib/rack/oauth2/server/token/facebook_token.rb +27 -0
  55. data/lib/rack/oauth2/server/token/password.rb +27 -0
  56. data/lib/rack/oauth2/server/token/refresh_token.rb +26 -0
  57. data/lib/rack/oauth2/util.rb +58 -0
  58. data/rack-oauth2.gemspec +30 -0
  59. data/spec/helpers/time.rb +19 -0
  60. data/spec/helpers/webmock_helper.rb +41 -0
  61. data/spec/mock_response/blank +0 -0
  62. data/spec/mock_response/errors/invalid_request.json +4 -0
  63. data/spec/mock_response/resources/fake.txt +1 -0
  64. data/spec/mock_response/tokens/_Bearer.json +6 -0
  65. data/spec/mock_response/tokens/bearer.json +6 -0
  66. data/spec/mock_response/tokens/legacy.json +5 -0
  67. data/spec/mock_response/tokens/legacy.txt +1 -0
  68. data/spec/mock_response/tokens/legacy_without_expires_in.txt +1 -0
  69. data/spec/mock_response/tokens/mac.json +8 -0
  70. data/spec/mock_response/tokens/unknown.json +6 -0
  71. data/spec/rack/oauth2/access_token/authenticator_spec.rb +43 -0
  72. data/spec/rack/oauth2/access_token/bearer_spec.rb +18 -0
  73. data/spec/rack/oauth2/access_token/legacy_spec.rb +23 -0
  74. data/spec/rack/oauth2/access_token/mac/sha256_hex_verifier_spec.rb +28 -0
  75. data/spec/rack/oauth2/access_token/mac/signature_spec.rb +59 -0
  76. data/spec/rack/oauth2/access_token/mac/verifier_spec.rb +25 -0
  77. data/spec/rack/oauth2/access_token/mac_spec.rb +141 -0
  78. data/spec/rack/oauth2/access_token_spec.rb +69 -0
  79. data/spec/rack/oauth2/client/error_spec.rb +18 -0
  80. data/spec/rack/oauth2/client/grant/authorization_code_spec.rb +37 -0
  81. data/spec/rack/oauth2/client/grant/client_credentials_spec.rb +7 -0
  82. data/spec/rack/oauth2/client/grant/password_spec.rb +33 -0
  83. data/spec/rack/oauth2/client/grant/refresh_token_spec.rb +21 -0
  84. data/spec/rack/oauth2/client_spec.rb +287 -0
  85. data/spec/rack/oauth2/debugger/request_filter_spec.rb +33 -0
  86. data/spec/rack/oauth2/oauth2_spec.rb +74 -0
  87. data/spec/rack/oauth2/server/abstract/error_spec.rb +59 -0
  88. data/spec/rack/oauth2/server/authorize/code_spec.rb +57 -0
  89. data/spec/rack/oauth2/server/authorize/error_spec.rb +103 -0
  90. data/spec/rack/oauth2/server/authorize/extensions/code_and_token_spec.rb +60 -0
  91. data/spec/rack/oauth2/server/authorize/token_spec.rb +73 -0
  92. data/spec/rack/oauth2/server/authorize_spec.rb +214 -0
  93. data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +52 -0
  94. data/spec/rack/oauth2/server/resource/bearer_spec.rb +123 -0
  95. data/spec/rack/oauth2/server/resource/error_spec.rb +147 -0
  96. data/spec/rack/oauth2/server/resource/mac/error_spec.rb +52 -0
  97. data/spec/rack/oauth2/server/resource/mac_spec.rb +119 -0
  98. data/spec/rack/oauth2/server/resource_spec.rb +23 -0
  99. data/spec/rack/oauth2/server/token/authorization_code_spec.rb +43 -0
  100. data/spec/rack/oauth2/server/token/client_credentials_spec.rb +23 -0
  101. data/spec/rack/oauth2/server/token/error_spec.rb +77 -0
  102. data/spec/rack/oauth2/server/token/password_spec.rb +37 -0
  103. data/spec/rack/oauth2/server/token/refresh_token_spec.rb +34 -0
  104. data/spec/rack/oauth2/server/token_spec.rb +134 -0
  105. data/spec/rack/oauth2/util_spec.rb +97 -0
  106. data/spec/spec_helper.rb +14 -0
  107. metadata +326 -0
@@ -0,0 +1,17 @@
1
+ module Rack
2
+ module OAuth2
3
+ class AccessToken
4
+ class MAC
5
+ class Sha256HexVerifier < Verifier
6
+ attr_optional :raw_body
7
+
8
+ def calculate
9
+ return nil unless raw_body.present?
10
+
11
+ OpenSSL::Digest::SHA256.new.digest(raw_body).unpack('H*').first
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ module Rack
2
+ module OAuth2
3
+ class AccessToken
4
+ class MAC
5
+ class Signature < Verifier
6
+ attr_required :secret, :ts, :nonce, :method, :request_uri, :host, :port
7
+ attr_optional :ext, :query
8
+
9
+ def calculate
10
+ Rack::OAuth2::Util.base64_encode OpenSSL::HMAC.digest(
11
+ hash_generator,
12
+ secret,
13
+ normalized_request_string
14
+ )
15
+ end
16
+
17
+ def normalized_request_string
18
+ [
19
+ ts.to_i,
20
+ nonce,
21
+ method.to_s.upcase,
22
+ request_uri,
23
+ host,
24
+ port,
25
+ ext || '',
26
+ nil
27
+ ].join("\n")
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ module Rack
2
+ module OAuth2
3
+ class AccessToken
4
+ class MAC
5
+ class Verifier
6
+ include AttrRequired, AttrOptional
7
+ attr_required :algorithm
8
+
9
+ class VerificationFailed < StandardError; end
10
+
11
+ def initialize(attributes = {})
12
+ (required_attributes + optional_attributes).each do |key|
13
+ self.send :"#{key}=", attributes[key]
14
+ end
15
+ attr_missing!
16
+ rescue AttrRequired::AttrMissing => e
17
+ raise VerificationFailed.new("#{self.class.name.demodulize} Invalid: #{e.message}")
18
+ end
19
+
20
+ def verify!(expected)
21
+ if expected == self.calculate
22
+ :verified
23
+ else
24
+ raise VerificationFailed.new("#{self.class.name.demodulize} Invalid")
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def hash_generator
31
+ case algorithm.to_s
32
+ when 'hmac-sha-1'
33
+ OpenSSL::Digest::SHA1.new
34
+ when 'hmac-sha-256'
35
+ OpenSSL::Digest::SHA256.new
36
+ else
37
+ raise 'Unsupported Algorithm'
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,139 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ include AttrRequired, AttrOptional
5
+ attr_required :identifier
6
+ attr_optional :secret, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint
7
+
8
+ def initialize(attributes = {})
9
+ (required_attributes + optional_attributes).each do |key|
10
+ self.send :"#{key}=", attributes[key]
11
+ end
12
+ @grant = Grant::ClientCredentials.new
13
+ @authorization_endpoint ||= '/oauth2/authorize'
14
+ @token_endpoint ||= '/oauth2/token'
15
+ attr_missing!
16
+ end
17
+
18
+ def authorization_uri(params = {})
19
+ params[:response_type] ||= :code
20
+ params[:response_type] = Array(params[:response_type]).join(' ')
21
+ params[:scope] = Array(params[:scope]).join(' ')
22
+ Util.redirect_uri absolute_uri_for(authorization_endpoint), :query, params.merge(
23
+ :client_id => self.identifier,
24
+ :redirect_uri => self.redirect_uri
25
+ )
26
+ end
27
+
28
+ def authorization_code=(code)
29
+ @grant = Grant::AuthorizationCode.new(
30
+ :code => code,
31
+ :redirect_uri => self.redirect_uri
32
+ )
33
+ end
34
+
35
+ def resource_owner_credentials=(credentials)
36
+ @grant = Grant::Password.new(
37
+ :username => credentials.first,
38
+ :password => credentials.last
39
+ )
40
+ end
41
+
42
+ def facebook_token=(token)
43
+ @grant = Grant::FacebookToken.new(
44
+ :facebook_token => token
45
+ )
46
+ end
47
+
48
+ def refresh_token=(token)
49
+ @grant = Grant::RefreshToken.new(
50
+ :refresh_token => token
51
+ )
52
+ end
53
+
54
+ def access_token!(*args)
55
+ headers, params = {}, @grant.as_json
56
+
57
+ # NOTE:
58
+ # Using Array#estract_options! for backward compatibility.
59
+ # Until v1.0.5, the first argument was 'client_auth_method' in scalar.
60
+ options = args.extract_options!
61
+ client_auth_method = args.first || options[:client_auth_method] || :basic
62
+
63
+ params[:scope] = Array(options[:scope]).join(' ') if options[:scope].present?
64
+
65
+ if secret && client_auth_method == :basic
66
+ cred = ["#{identifier}:#{secret}"].pack('m').tr("\n", '')
67
+ headers.merge!(
68
+ 'Authorization' => "Basic #{cred}"
69
+ )
70
+ else
71
+ params.merge!(
72
+ :client_id => identifier,
73
+ :client_secret => secret
74
+ )
75
+ end
76
+ handle_response do
77
+ Rack::OAuth2.http_client.post(
78
+ absolute_uri_for(token_endpoint),
79
+ Util.compact_hash(params),
80
+ headers
81
+ )
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def absolute_uri_for(endpoint)
88
+ _endpoint_ = Util.parse_uri endpoint
89
+ _endpoint_.scheme ||= self.scheme || 'https'
90
+ _endpoint_.host ||= self.host
91
+ _endpoint_.port ||= self.port
92
+ raise 'No Host Info' unless _endpoint_.host
93
+ _endpoint_.to_s
94
+ end
95
+
96
+ def handle_response
97
+ response = yield
98
+ case response.status
99
+ when 200..201
100
+ handle_success_response response
101
+ else
102
+ handle_error_response response
103
+ end
104
+ end
105
+
106
+ def handle_success_response(response)
107
+ token_hash = parse_json response.body
108
+ case token_hash[:token_type].try(:downcase)
109
+ when 'bearer'
110
+ AccessToken::Bearer.new(token_hash)
111
+ when 'mac'
112
+ AccessToken::MAC.new(token_hash)
113
+ when nil
114
+ AccessToken::Legacy.new(token_hash)
115
+ else
116
+ raise 'Unknown Token Type'
117
+ end
118
+ rescue MultiJson::DecodeError
119
+ # NOTE: Facebook support (They don't use JSON as token response)
120
+ AccessToken::Legacy.new Rack::Utils.parse_nested_query(response.body).with_indifferent_access
121
+ end
122
+
123
+ def handle_error_response(response)
124
+ error = parse_json response.body
125
+ raise Error.new(response.status, error)
126
+ rescue MultiJson::DecodeError
127
+ raise Error.new(response.status, :error => 'Unknown', :error_description => response.body)
128
+ end
129
+
130
+ def parse_json(raw_json)
131
+ # MultiJson.parse('') returns nil when using MultiJson::Adapters::JsonGem
132
+ MultiJson.load(raw_json).try(:with_indifferent_access) || {}
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ require 'rack/oauth2/client/error'
139
+ require 'rack/oauth2/client/grant'
@@ -0,0 +1,14 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Error < StandardError
5
+ attr_accessor :status, :response
6
+ def initialize(status, response)
7
+ @status = status
8
+ @response = response
9
+ super response[:error_description]
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Grant
5
+ include AttrRequired, AttrOptional
6
+
7
+ def initialize(attributes = {})
8
+ (required_attributes + optional_attributes).each do |key|
9
+ self.send "#{key}=", attributes[key]
10
+ end
11
+ attr_missing!
12
+ end
13
+
14
+ def as_json(options = {})
15
+ (required_attributes + optional_attributes).inject({
16
+ :grant_type => self.class.name.demodulize.underscore.to_sym
17
+ }) do |hash, key|
18
+ hash.merge! key => self.send(key)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ require 'rack/oauth2/client/grant/authorization_code'
27
+ require 'rack/oauth2/client/grant/password'
28
+ require 'rack/oauth2/client/grant/facebook_token'
29
+ require 'rack/oauth2/client/grant/client_credentials'
30
+ require 'rack/oauth2/client/grant/refresh_token'
@@ -0,0 +1,12 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Grant
5
+ class AuthorizationCode < Grant
6
+ attr_required :code
7
+ attr_optional :redirect_uri
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Grant
5
+ class ClientCredentials < Grant
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Grant
5
+ class FacebookToken < Grant
6
+ attr_required :facebook_token
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,11 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Grant
5
+ class Password < Grant
6
+ attr_required :username, :password
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Rack
2
+ module OAuth2
3
+ class Client
4
+ class Grant
5
+ class RefreshToken < Grant
6
+ attr_required :refresh_token
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__) + '/debugger/*.rb'].each do |file|
2
+ require file
3
+ end
@@ -0,0 +1,30 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Debugger
4
+ class RequestFilter
5
+ # Callback called in HTTPClient (before sending a request)
6
+ # request:: HTTP::Message
7
+ def filter_request(request)
8
+ started = "======= [Rack::OAuth2] HTTP REQUEST STARTED ======="
9
+ log started, request.dump
10
+ end
11
+
12
+ # Callback called in HTTPClient (after received a response)
13
+ # request:: HTTP::Message
14
+ # response:: HTTP::Message
15
+ def filter_response(request, response)
16
+ finished = "======= [Rack::OAuth2] HTTP REQUEST FINISHED ======="
17
+ log '-' * 50, response.dump, finished
18
+ end
19
+
20
+ private
21
+
22
+ def log(*outputs)
23
+ outputs.each do |output|
24
+ OAuth2.logger.info output
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ require 'rack/oauth2/server/abstract'
2
+ require 'rack/oauth2/server/authorize'
3
+ require 'rack/oauth2/server/token'
4
+ require 'rack/oauth2/server/resource'
@@ -0,0 +1,4 @@
1
+ require 'rack/oauth2/server/abstract/handler'
2
+ require 'rack/oauth2/server/abstract/request'
3
+ require 'rack/oauth2/server/abstract/response'
4
+ require 'rack/oauth2/server/abstract/error'
@@ -0,0 +1,69 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ module Abstract
5
+ class Error < StandardError
6
+ attr_accessor :status, :error, :description, :uri, :realm
7
+
8
+ def initialize(status, error, description = nil, options = {})
9
+ @status = status
10
+ @error = error
11
+ @description = description
12
+ @uri = options[:uri]
13
+ @realm = options[:realm]
14
+ super [error, description].compact.join(' :: ')
15
+ end
16
+
17
+ def protocol_params
18
+ {
19
+ :error => error,
20
+ :error_description => description,
21
+ :error_uri => uri
22
+ }
23
+ end
24
+
25
+ def finish
26
+ response = Rack::Response.new
27
+ response.status = status
28
+ yield response if block_given?
29
+ unless response.redirect?
30
+ response.header['Content-Type'] = 'application/json'
31
+ response.write MultiJson.dump(Util.compact_hash(protocol_params))
32
+ end
33
+ response.finish
34
+ end
35
+ end
36
+
37
+ class BadRequest < Error
38
+ def initialize(error = :bad_request, description = nil, options = {})
39
+ super 400, error, description, options
40
+ end
41
+ end
42
+
43
+ class Unauthorized < Error
44
+ def initialize(error = :unauthorized, description = nil, options = {})
45
+ super 401, error, description, options
46
+ end
47
+ end
48
+
49
+ class Forbidden < Error
50
+ def initialize(error = :forbidden, description = nil, options = {})
51
+ super 403, error, description, options
52
+ end
53
+ end
54
+
55
+ class ServerError < Error
56
+ def initialize(error = :forbidden, description = nil, options = {})
57
+ super 500, error, description, options
58
+ end
59
+ end
60
+
61
+ class TemporarilyUnavailable < Error
62
+ def initialize(error = :forbidden, description = nil, options = {})
63
+ super 503, error, description, options
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end