rockoauth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +5 -0
  3. data/README.rdoc +422 -0
  4. data/example/README.rdoc +11 -0
  5. data/example/application.rb +158 -0
  6. data/example/config.ru +3 -0
  7. data/example/environment.rb +11 -0
  8. data/example/models/connection.rb +9 -0
  9. data/example/models/note.rb +4 -0
  10. data/example/models/user.rb +5 -0
  11. data/example/public/style.css +78 -0
  12. data/example/schema.rb +22 -0
  13. data/example/views/authorize.erb +28 -0
  14. data/example/views/create_user.erb +3 -0
  15. data/example/views/error.erb +6 -0
  16. data/example/views/home.erb +24 -0
  17. data/example/views/layout.erb +24 -0
  18. data/example/views/login.erb +20 -0
  19. data/example/views/new_client.erb +25 -0
  20. data/example/views/new_user.erb +22 -0
  21. data/example/views/show_client.erb +15 -0
  22. data/lib/rockoauth/model/authorization.rb +132 -0
  23. data/lib/rockoauth/model/client.rb +54 -0
  24. data/lib/rockoauth/model/client_owner.rb +13 -0
  25. data/lib/rockoauth/model/hashing.rb +26 -0
  26. data/lib/rockoauth/model/helpers.rb +14 -0
  27. data/lib/rockoauth/model/resource_owner.rb +22 -0
  28. data/lib/rockoauth/model.rb +38 -0
  29. data/lib/rockoauth/provider/access_token.rb +70 -0
  30. data/lib/rockoauth/provider/authorization.rb +185 -0
  31. data/lib/rockoauth/provider/error.rb +19 -0
  32. data/lib/rockoauth/provider/exchange.rb +225 -0
  33. data/lib/rockoauth/provider.rb +133 -0
  34. data/lib/rockoauth/router.rb +75 -0
  35. data/lib/rockoauth/schema/20120828112156_rockoauth_schema_original_schema.rb +35 -0
  36. data/lib/rockoauth/schema/20121024180930_rockoauth_schema_add_authorization_index.rb +13 -0
  37. data/lib/rockoauth/schema/20121025180447_rockoauth_schema_add_unique_indexes.rb +31 -0
  38. data/lib/rockoauth/schema.rb +25 -0
  39. data/lib/rockoauth.rb +1 -0
  40. data/spec/factories.rb +20 -0
  41. data/spec/request_helpers.rb +62 -0
  42. data/spec/rockoauth/model/authorization_spec.rb +237 -0
  43. data/spec/rockoauth/model/client_spec.rb +44 -0
  44. data/spec/rockoauth/model/helpers_spec.rb +25 -0
  45. data/spec/rockoauth/model/resource_owner_spec.rb +87 -0
  46. data/spec/rockoauth/provider/access_token_spec.rb +138 -0
  47. data/spec/rockoauth/provider/authorization_spec.rb +356 -0
  48. data/spec/rockoauth/provider/exchange_spec.rb +361 -0
  49. data/spec/rockoauth/provider_spec.rb +560 -0
  50. data/spec/spec_helper.rb +80 -0
  51. data/spec/test_app/helper.rb +36 -0
  52. data/spec/test_app/provider/application.rb +67 -0
  53. data/spec/test_app/provider/views/authorize.erb +19 -0
  54. metadata +238 -0
@@ -0,0 +1,185 @@
1
+ module RockOAuth
2
+ class Provider
3
+
4
+ class Authorization
5
+ attr_reader :owner, :client,
6
+ :code, :access_token,
7
+ :expires_in, :refresh_token,
8
+ :error, :error_description
9
+
10
+ REQUIRED_PARAMS = [RESPONSE_TYPE, CLIENT_ID, REDIRECT_URI]
11
+ VALID_PARAMS = REQUIRED_PARAMS + [SCOPE, STATE]
12
+ VALID_RESPONSES = [CODE, TOKEN, CODE_AND_TOKEN]
13
+
14
+ def initialize(resource_owner, params, transport_error = nil)
15
+ @owner = resource_owner
16
+ @params = params
17
+ @scope = params[SCOPE]
18
+ @state = params[STATE]
19
+
20
+ @transport_error = transport_error
21
+
22
+ validate!
23
+
24
+ return unless @owner and not @error
25
+
26
+ @model = @owner.oauth2_authorization_for(@client)
27
+ return unless @model and @model.in_scope?(scopes) and not @model.expired?
28
+
29
+ @authorized = true
30
+
31
+ if @params[RESPONSE_TYPE] =~ /code/
32
+ @code = @model.generate_code
33
+ end
34
+
35
+ if @params[RESPONSE_TYPE] =~ /token/
36
+ @access_token = @model.generate_access_token
37
+ end
38
+ end
39
+
40
+ def scopes
41
+ scopes = @scope ? @scope.split(/\s+/).delete_if { |s| s.empty? } : []
42
+ Set.new(scopes)
43
+ end
44
+
45
+ def unauthorized_scopes
46
+ @model ? scopes.select { |s| not @model.in_scope?(s) } : scopes
47
+ end
48
+
49
+ def grant_access!(options = {})
50
+ @model = Model::Authorization.for(@owner, @client,
51
+ :response_type => @params[RESPONSE_TYPE],
52
+ :scope => @scope,
53
+ :duration => options[:duration])
54
+
55
+ @code = @model.code
56
+ @access_token = @model.access_token
57
+ @refresh_token = @model.refresh_token
58
+ @expires_in = @model.expires_in
59
+
60
+ unless @params[RESPONSE_TYPE] == CODE
61
+ @expires_in = @model.expires_in
62
+ end
63
+
64
+ @authorized = true
65
+ end
66
+
67
+ def deny_access!
68
+ @code = @access_token = @refresh_token = nil
69
+ @error = ACCESS_DENIED
70
+ @error_description = "The user denied you access"
71
+ end
72
+
73
+ def params
74
+ params = {}
75
+ VALID_PARAMS.each { |key| params[key] = @params[key] if @params.has_key?(key) }
76
+ params
77
+ end
78
+
79
+ def redirect?
80
+ @client and (@authorized or not valid?)
81
+ end
82
+
83
+ def redirect_uri
84
+ return nil unless @client
85
+ base_redirect_uri = @client.redirect_uri
86
+ q = (base_redirect_uri =~ /\?/) ? '&' : '?'
87
+
88
+ if not valid?
89
+ query = to_query_string(ERROR, ERROR_DESCRIPTION, STATE)
90
+ "#{ base_redirect_uri }#{ q }#{ query }"
91
+
92
+ elsif @params[RESPONSE_TYPE] == CODE_AND_TOKEN
93
+ query = to_query_string(CODE, STATE)
94
+ fragment = to_query_string(ACCESS_TOKEN, EXPIRES_IN, SCOPE)
95
+ "#{ base_redirect_uri }#{ query.empty? ? '' : q + query }##{ fragment }"
96
+
97
+ elsif @params[RESPONSE_TYPE] == TOKEN
98
+ fragment = to_query_string(ACCESS_TOKEN, EXPIRES_IN, SCOPE, STATE)
99
+ "#{ base_redirect_uri }##{ fragment }"
100
+
101
+ else
102
+ query = to_query_string(CODE, SCOPE, STATE)
103
+ "#{ base_redirect_uri }#{ q }#{ query }"
104
+ end
105
+ end
106
+
107
+ def response_body
108
+ warn "RockOAuth::Provider::Authorization no longer returns a response body "+
109
+ "when the request is invalid. You should call valid? to determine "+
110
+ "whether to render your login page or an error page."
111
+ nil
112
+ end
113
+
114
+ def response_headers
115
+ redirect? ? {} : {'Cache-Control' => 'no-store'}
116
+ end
117
+
118
+ def response_status
119
+ return 302 if redirect?
120
+ return 200 if valid?
121
+ @client ? 302 : 400
122
+ end
123
+
124
+ def valid?
125
+ @error.nil?
126
+ end
127
+
128
+ private
129
+
130
+ def validate!
131
+ if @transport_error
132
+ @error = @transport_error.error
133
+ @error_description = @transport_error.error_description
134
+ return
135
+ end
136
+
137
+ @client = @params[CLIENT_ID] && Model::Client.find_by_client_id(@params[CLIENT_ID])
138
+ unless @client
139
+ @error = INVALID_CLIENT
140
+ @error_description = "Unknown client ID #{@params[CLIENT_ID]}"
141
+ end
142
+
143
+ REQUIRED_PARAMS.each do |param|
144
+ next if @params.has_key?(param)
145
+ @error = INVALID_REQUEST
146
+ @error_description = "Missing required parameter #{param}"
147
+ end
148
+ return if @error
149
+
150
+ [SCOPE, STATE].each do |param|
151
+ next unless @params.has_key?(param)
152
+ if @params[param] =~ /\r\n/
153
+ @error = INVALID_REQUEST
154
+ @error_description = "Illegal value for #{param} parameter"
155
+ end
156
+ end
157
+
158
+ unless VALID_RESPONSES.include?(@params[RESPONSE_TYPE])
159
+ @error = UNSUPPORTED_RESPONSE
160
+ @error_description = "Response type #{@params[RESPONSE_TYPE]} is not supported"
161
+ end
162
+
163
+ @client = Model::Client.find_by_client_id(@params[CLIENT_ID])
164
+ unless @client
165
+ @error = INVALID_CLIENT
166
+ @error_description = "Unknown client ID #{@params[CLIENT_ID]}"
167
+ end
168
+
169
+ if @client and @client.redirect_uri and @client.redirect_uri != @params[REDIRECT_URI]
170
+ @error = REDIRECT_MISMATCH
171
+ @error_description = "Parameter #{REDIRECT_URI} does not match registered URI"
172
+ end
173
+ end
174
+
175
+ def to_query_string(*ivars)
176
+ ivars.map { |key|
177
+ value = instance_variable_get("@#{key}")
178
+ value = value.join(' ') if Array === value
179
+ value ? "#{ key }=#{ CGI.escape(value.to_s) }" : nil
180
+ }.compact.join('&')
181
+ end
182
+ end
183
+
184
+ end
185
+ end
@@ -0,0 +1,19 @@
1
+ module RockOAuth
2
+ class Provider
3
+
4
+ class Error
5
+ def initialize(message = nil)
6
+ @message = message
7
+ end
8
+
9
+ def error
10
+ INVALID_REQUEST
11
+ end
12
+
13
+ def error_description
14
+ 'Bad request' + (@message ? ": #{@message}" : '')
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,225 @@
1
+ module RockOAuth
2
+ class Provider
3
+
4
+ class Exchange
5
+ attr_reader :client, :error, :error_description
6
+
7
+ REQUIRED_PARAMS = [CLIENT_ID, CLIENT_SECRET, GRANT_TYPE]
8
+ VALID_GRANT_TYPES = [AUTHORIZATION_CODE, PASSWORD, ASSERTION, REFRESH_TOKEN]
9
+
10
+ REQUIRED_PASSWORD_PARAMS = [USERNAME, PASSWORD]
11
+ REQUIRED_ASSERTION_PARAMS = [ASSERTION_TYPE, ASSERTION]
12
+
13
+ RESPONSE_HEADERS = {
14
+ 'Cache-Control' => 'no-store',
15
+ 'Content-Type' => 'application/json'
16
+ }
17
+
18
+ def initialize(resource_owner, params, transport_error = nil)
19
+ @params = params
20
+ @scope = params[SCOPE]
21
+ @grant_type = @params[GRANT_TYPE]
22
+ @resource_owner = resource_owner
23
+
24
+ @transport_error = transport_error
25
+
26
+ validate!
27
+ end
28
+
29
+ def owner
30
+ @authorization && @authorization.owner
31
+ end
32
+
33
+ def redirect?
34
+ false
35
+ end
36
+
37
+ def response_body
38
+ return jsonize(ERROR, ERROR_DESCRIPTION) unless valid?
39
+ update_authorization
40
+
41
+ response = {}
42
+ [ACCESS_TOKEN, REFRESH_TOKEN, SCOPE].each do |key|
43
+ value = @authorization.__send__(key)
44
+ response[key] = value if value
45
+ end
46
+ if expiry = @authorization.expires_in
47
+ response[EXPIRES_IN] = expiry
48
+ end
49
+
50
+ JSON.unparse(response)
51
+ end
52
+
53
+ def response_headers
54
+ RESPONSE_HEADERS
55
+ end
56
+
57
+ def response_status
58
+ valid? ? 200 : 400
59
+ end
60
+
61
+ def scopes
62
+ scopes = @scope ? @scope.split(/\s+/).delete_if { |s| s.empty? } : []
63
+ Set.new(scopes)
64
+ end
65
+
66
+ def update_authorization
67
+ return if not valid? or @already_updated
68
+ @authorization.exchange!
69
+ @already_updated = true
70
+ end
71
+
72
+ def valid?
73
+ @error.nil?
74
+ end
75
+
76
+ private
77
+
78
+ def jsonize(*ivars)
79
+ hash = {}
80
+ ivars.each { |key| hash[key] = instance_variable_get("@#{key}") }
81
+ JSON.unparse(hash)
82
+ end
83
+
84
+ def validate!
85
+ if @transport_error
86
+ @error = @transport_error.error
87
+ @error_description = @transport_error.error_description
88
+ return
89
+ end
90
+
91
+ validate_required_params
92
+
93
+ return if @error
94
+ validate_client
95
+
96
+ unless VALID_GRANT_TYPES.include?(@grant_type)
97
+ @error = UNSUPPORTED_GRANT_TYPE
98
+ @error_description = "The grant type #{@grant_type} is not recognized"
99
+ end
100
+ return if @error
101
+
102
+ __send__("validate_#{@grant_type}")
103
+ validate_scope
104
+ end
105
+
106
+ def validate_required_params
107
+ REQUIRED_PARAMS.each do |param|
108
+ next if @params.has_key?(param)
109
+ @error = INVALID_REQUEST
110
+ @error_description = "Missing required parameter #{param}"
111
+ end
112
+ end
113
+
114
+ def validate_client
115
+ @client = Model::Client.find_by_client_id(@params[CLIENT_ID])
116
+ unless @client
117
+ @error = INVALID_CLIENT
118
+ @error_description = "Unknown client ID #{@params[CLIENT_ID]}"
119
+ end
120
+
121
+ if @client and not @client.valid_client_secret?(@params[CLIENT_SECRET])
122
+ @error = INVALID_CLIENT
123
+ @error_description = 'Parameter client_secret does not match'
124
+ end
125
+ end
126
+
127
+ def validate_scope
128
+ if @authorization and not @authorization.in_scope?(scopes)
129
+ @error = INVALID_SCOPE
130
+ @error_description = 'The request scope was never granted by the user'
131
+ end
132
+ end
133
+
134
+ def validate_authorization_code
135
+ unless @params[CODE]
136
+ @error = INVALID_REQUEST
137
+ @error_description = "Missing required parameter code"
138
+ end
139
+
140
+ if @client.redirect_uri and @client.redirect_uri != @params[REDIRECT_URI]
141
+ @error = REDIRECT_MISMATCH
142
+ @error_description = "Parameter redirect_uri does not match registered URI"
143
+ end
144
+
145
+ unless @params.has_key?(REDIRECT_URI)
146
+ @error = INVALID_REQUEST
147
+ @error_description = "Missing required parameter redirect_uri"
148
+ end
149
+
150
+ return if @error
151
+
152
+ @authorization = @client.authorizations.find_by_code(@params[CODE])
153
+ validate_authorization
154
+ end
155
+
156
+ def validate_password
157
+ REQUIRED_PASSWORD_PARAMS.each do |param|
158
+ next if @params.has_key?(param)
159
+ @error = INVALID_REQUEST
160
+ @error_description = "Missing required parameter #{param}"
161
+ end
162
+
163
+ return if @error
164
+
165
+ @authorization = Provider.handle_password(@client, @params[USERNAME], @params[PASSWORD], scopes)
166
+ return validate_authorization if @authorization
167
+
168
+ @error = INVALID_GRANT
169
+ @error_description = 'The access grant you supplied is invalid'
170
+ end
171
+
172
+ def validate_assertion
173
+ REQUIRED_ASSERTION_PARAMS.each do |param|
174
+ next if @params.has_key?(param)
175
+ @error = INVALID_REQUEST
176
+ @error_description = "Missing required parameter #{param}"
177
+ end
178
+
179
+ if @params[ASSERTION_TYPE]
180
+ uri = URI.parse(@params[ASSERTION_TYPE]) rescue nil
181
+ unless uri and uri.absolute?
182
+ @error = INVALID_REQUEST
183
+ @error_description = 'Parameter assertion_type must be an absolute URI'
184
+ end
185
+ end
186
+
187
+ return if @error
188
+
189
+ assertion = Assertion.new(@params)
190
+ @authorization = Provider.handle_assertion(@client, assertion, scopes, @resource_owner)
191
+ return validate_authorization if @authorization
192
+
193
+ @error = UNAUTHORIZED_CLIENT
194
+ @error_description = 'Client cannot use the given assertion type'
195
+ end
196
+
197
+ def validate_refresh_token
198
+ refresh_token_hash = RockOAuth.hashify(@params[REFRESH_TOKEN])
199
+ @authorization = @client.authorizations.find_by_refresh_token_hash(refresh_token_hash)
200
+ validate_authorization
201
+ end
202
+
203
+ def validate_authorization
204
+ unless @authorization
205
+ @error = INVALID_GRANT
206
+ @error_description = 'The access grant you supplied is invalid'
207
+ end
208
+
209
+ if @authorization and @authorization.expired?
210
+ @error = INVALID_GRANT
211
+ @error_description = 'The access grant you supplied is invalid'
212
+ end
213
+ end
214
+ end
215
+
216
+ class Assertion
217
+ attr_reader :type, :value
218
+ def initialize(params)
219
+ @type = params[ASSERTION_TYPE]
220
+ @value = params[ASSERTION]
221
+ end
222
+ end
223
+
224
+ end
225
+ end
@@ -0,0 +1,133 @@
1
+ require 'base64'
2
+ require 'bcrypt'
3
+ require 'cgi'
4
+ require 'digest/sha1'
5
+ require 'json'
6
+ require 'logger'
7
+ require 'rack'
8
+
9
+ begin
10
+ require 'securerandom'
11
+ rescue LoadError
12
+ end
13
+
14
+ module RockOAuth
15
+ ROOT = File.dirname(__FILE__)
16
+ TOKEN_SIZE = 160
17
+
18
+ autoload :Model, ROOT + '/model'
19
+ autoload :Router, ROOT + '/router'
20
+ autoload :Schema, ROOT + '/schema'
21
+
22
+ def self.random_string
23
+ if defined? SecureRandom
24
+ SecureRandom.hex(TOKEN_SIZE / 8).to_i(16).to_s(36)
25
+ else
26
+ rand(2 ** TOKEN_SIZE).to_s(36)
27
+ end
28
+ end
29
+
30
+ def self.generate_id(&predicate)
31
+ id = random_string
32
+ id = random_string until predicate.call(id)
33
+ id
34
+ end
35
+
36
+ def self.hashify(token)
37
+ return nil unless String === token
38
+ Digest::SHA1.hexdigest(token)
39
+ end
40
+
41
+ ACCESS_TOKEN = 'access_token'
42
+ ASSERTION = 'assertion'
43
+ ASSERTION_TYPE = 'assertion_type'
44
+ AUTHORIZATION_CODE = 'authorization_code'
45
+ CLIENT_ID = 'client_id'
46
+ CLIENT_SECRET = 'client_secret'
47
+ CODE = 'code'
48
+ CODE_AND_TOKEN = 'code_and_token'
49
+ DURATION = 'duration'
50
+ ERROR = 'error'
51
+ ERROR_DESCRIPTION = 'error_description'
52
+ EXPIRES_IN = 'expires_in'
53
+ GRANT_TYPE = 'grant_type'
54
+ OAUTH_TOKEN = 'oauth_token'
55
+ PASSWORD = 'password'
56
+ REDIRECT_URI = 'redirect_uri'
57
+ REFRESH_TOKEN = 'refresh_token'
58
+ RESPONSE_TYPE = 'response_type'
59
+ SCOPE = 'scope'
60
+ STATE = 'state'
61
+ TOKEN = 'token'
62
+ USERNAME = 'username'
63
+
64
+ INVALID_REQUEST = 'invalid_request'
65
+ UNSUPPORTED_RESPONSE = 'unsupported_response_type'
66
+ REDIRECT_MISMATCH = 'redirect_uri_mismatch'
67
+ UNSUPPORTED_GRANT_TYPE = 'unsupported_grant_type'
68
+ INVALID_GRANT = 'invalid_grant'
69
+ INVALID_CLIENT = 'invalid_client'
70
+ UNAUTHORIZED_CLIENT = 'unauthorized_client'
71
+ INVALID_SCOPE = 'invalid_scope'
72
+ INVALID_TOKEN = 'invalid_token'
73
+ EXPIRED_TOKEN = 'expired_token'
74
+ INSUFFICIENT_SCOPE = 'insufficient_scope'
75
+ ACCESS_DENIED = 'access_denied'
76
+
77
+ class Provider
78
+ EXPIRY_TIME = 3600
79
+
80
+ autoload :Authorization, ROOT + '/provider/authorization'
81
+ autoload :Exchange, ROOT + '/provider/exchange'
82
+ autoload :AccessToken, ROOT + '/provider/access_token'
83
+ autoload :Error, ROOT + '/provider/error'
84
+
85
+ class << self
86
+ attr_accessor :realm, :enforce_ssl
87
+ end
88
+
89
+ def self.clear_assertion_handlers!
90
+ @password_handler = nil
91
+ @assertion_handlers = {}
92
+ @assertion_filters = []
93
+ end
94
+
95
+ clear_assertion_handlers!
96
+
97
+ def self.handle_passwords(&block)
98
+ @password_handler = block
99
+ end
100
+
101
+ def self.handle_password(client, username, password, scopes)
102
+ return nil unless @password_handler
103
+ @password_handler.call(client, username, password, scopes)
104
+ end
105
+
106
+ def self.filter_assertions(&filter)
107
+ @assertion_filters.push(filter)
108
+ end
109
+
110
+ def self.handle_assertions(assertion_type, &handler)
111
+ @assertion_handlers[assertion_type] = handler
112
+ end
113
+
114
+ def self.handle_assertion(client, assertion, scopes, resource_owner)
115
+ return nil unless @assertion_filters.all? { |f| f.call(client) }
116
+ handler = @assertion_handlers[assertion.type]
117
+ handler ? handler.call(client, assertion.value, scopes, resource_owner) : nil
118
+ end
119
+
120
+ def self.parse(*args)
121
+ Router.parse(*args)
122
+ end
123
+
124
+ def self.access_token(*args)
125
+ Router.access_token(*args)
126
+ end
127
+
128
+ def self.access_token_from_request(*args)
129
+ Router.access_token_from_request(*args)
130
+ end
131
+ end
132
+
133
+ end
@@ -0,0 +1,75 @@
1
+ module RockOAuth
2
+ class Router
3
+
4
+ # Public methods in the namespace take either Rack env objects, or Request
5
+ # objects from Rails/Sinatra and an optional params hash which it then
6
+ # coerces to Rack requests. This is for backward compatibility; originally
7
+ # it only took request objects.
8
+
9
+ class << self
10
+ def parse(resource_owner, env)
11
+ error = detect_transport_error(env)
12
+ request = request_from(env)
13
+ params = request.params
14
+ auth = auth_params(env)
15
+
16
+ if auth[CLIENT_ID] and auth[CLIENT_ID] != params[CLIENT_ID]
17
+ error ||= Provider::Error.new("#{CLIENT_ID} from Basic Auth and request body do not match")
18
+ end
19
+
20
+ params = params.merge(auth)
21
+
22
+ if params[GRANT_TYPE]
23
+ error ||= Provider::Error.new('must be a POST request') unless request.post?
24
+ Provider::Exchange.new(resource_owner, params, error)
25
+ else
26
+ Provider::Authorization.new(resource_owner, params, error)
27
+ end
28
+ end
29
+
30
+ def access_token(resource_owner, scopes, env)
31
+ access_token = access_token_from_request(env)
32
+ Provider::AccessToken.new(resource_owner,
33
+ scopes,
34
+ access_token,
35
+ detect_transport_error(env))
36
+ end
37
+
38
+ def access_token_from_request(env)
39
+ request = request_from(env)
40
+ params = request.params
41
+ header = request.env['HTTP_AUTHORIZATION']
42
+
43
+ header && header =~ /^OAuth\s+/ ?
44
+ header.gsub(/^OAuth\s+/, '') :
45
+ params[OAUTH_TOKEN]
46
+ end
47
+
48
+ private
49
+
50
+ def request_from(env_or_request)
51
+ env = env_or_request.respond_to?(:env) ? env_or_request.env : env_or_request
52
+ env = Rack::MockRequest.env_for(env['REQUEST_URI'] || '', :input => env['RAW_POST_DATA']).merge(env)
53
+ Rack::Request.new(env)
54
+ end
55
+
56
+ def auth_params(env)
57
+ return {} unless basic = env['HTTP_AUTHORIZATION']
58
+ parts = basic.split(/\s+/)
59
+ username, password = Base64.decode64(parts.last).split(':')
60
+ {CLIENT_ID => username, CLIENT_SECRET => password}
61
+ end
62
+
63
+ def detect_transport_error(env)
64
+ request = request_from(env)
65
+
66
+ if Provider.enforce_ssl and not request.ssl?
67
+ Provider::Error.new('must make requests using HTTPS')
68
+ elsif request.GET['client_secret']
69
+ Provider::Error.new('must not send client credentials in the URI')
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,35 @@
1
+ class RockoauthSchemaOriginalSchema < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :oauth2_clients do |t|
4
+ t.timestamps
5
+ t.string :oauth2_client_owner_type
6
+ t.integer :oauth2_client_owner_id
7
+ t.string :name
8
+ t.string :client_id
9
+ t.string :client_secret_hash
10
+ t.string :redirect_uri
11
+ end
12
+ add_index :oauth2_clients, [:client_id]
13
+
14
+ create_table :oauth2_authorizations do |t|
15
+ t.timestamps
16
+ t.string :oauth2_resource_owner_type
17
+ t.integer :oauth2_resource_owner_id
18
+ t.belongs_to :client
19
+ t.string :scope
20
+ t.string :code, :limit => 40
21
+ t.string :access_token_hash, :limit => 40
22
+ t.string :refresh_token_hash, :limit => 40
23
+ t.datetime :expires_at
24
+ end
25
+ add_index :oauth2_authorizations, [:access_token_hash]
26
+ add_index :oauth2_authorizations, [:client_id, :code], name: :index_oauth2_client_id_and_code
27
+ add_index :oauth2_authorizations, [:client_id, :access_token_hash], name: :index_oauth2_client_id_and_access_token_hash
28
+ add_index :oauth2_authorizations, [:client_id, :refresh_token_hash], name: :index_oauth2_client_id_and_refresh_token_hash
29
+ end
30
+
31
+ def self.down
32
+ drop_table :oauth2_clients
33
+ drop_table :oauth2_authorizations
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ class RockoauthSchemaAddAuthorizationIndex < ActiveRecord::Migration
2
+ INDEX_NAME = 'index_owner_client_pairs'
3
+
4
+ def self.up
5
+ remove_index :oauth2_authorizations, name: "index_oauth2_client_id_and_access_token_hash"
6
+ add_index :oauth2_authorizations, [:client_id, :oauth2_resource_owner_type, :oauth2_resource_owner_id], :name => INDEX_NAME, :unique => true
7
+ end
8
+
9
+ def self.down
10
+ remove_index :oauth2_authorizations, :name => INDEX_NAME
11
+ add_index :oauth2_authorizations, [:client_id, :access_token_hash]
12
+ end
13
+ end