rockoauth 0.1.0

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