/
access_grant_test.rb
264 lines (218 loc) · 7.87 KB
/
access_grant_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
require "test/setup"
# 4. Obtaining an Access Token
class AccessGrantTest < Test::Unit::TestCase
module Helpers
def should_return_error(error)
should "respond with status 400 (Bad Request)" do
assert_equal 400, last_response.status
end
should "respond with JSON document" do
assert_equal "application/json", last_response.content_type
end
should "respond with error code #{error}" do
assert_equal error.to_s, JSON.parse(last_response.body)["error"]
end
end
def should_respond_with_authentication_error(error)
should "respond with status 401 (Unauthorized)" do
assert_equal 401, last_response.status
end
should "respond with authentication method OAuth" do
assert_equal "OAuth", last_response["WWW-Authenticate"].split.first
end
should "respond with realm" do
assert_match " realm=\"example.org\"", last_response["WWW-Authenticate"]
end
should "respond with error code #{error}" do
assert_match " error=\"#{error}\"", last_response["WWW-Authenticate"]
end
end
def should_respond_with_access_token(scope = "read write")
should "respond with status 200" do
assert_equal 200, last_response.status
end
should "respond with JSON document" do
assert_equal "application/json", last_response.content_type
end
should "respond with cache control no-store" do
assert_equal "no-store", last_response["Cache-Control"]
end
should "not respond with error code" do
assert JSON.parse(last_response.body)["error"].nil?
end
should "response with access token" do
assert_match /[a-f0-9]{32}/i, JSON.parse(last_response.body)["access_token"]
end
should "response with scope" do
assert_equal scope || "", JSON.parse(last_response.body)["scope"]
end
end
end
extend Helpers
def setup
super
# Get authorization code.
params = { :redirect_uri=>client.redirect_uri, :client_id=>client.id, :client_secret=>client.secret, :response_type=>"code",
:scope=>"read write", :state=>"bring this back" }
get "/oauth/authorize?" + Rack::Utils.build_query(params)
get last_response["Location"] if last_response.status == 303
authorization = last_response.body[/authorization:\s*(\S+)/, 1]
post "/oauth/grant", :authorization=>authorization
@code = Rack::Utils.parse_query(URI.parse(last_response["Location"]).query)["code"]
end
def request_access_token(changes = nil)
params = { :client_id=>client.id, :client_secret=>client.secret, :scope=>"read write",
:grant_type=>"authorization_code", :code=>@code, :redirect_uri=>client.redirect_uri }.merge(changes || {})
basic_authorize params.delete(:client_id), params.delete(:client_secret)
post "/oauth/access_token", params
end
def request_with_username_password(username, password, scope = nil)
basic_authorize client.id, client.secret
params = { :grant_type=>"password" }
params[:scope] = scope if scope
params[:username] = username if username
params[:password] = password if password
post "/oauth/access_token", params
end
# 4. Obtaining an Access Token
context "GET request" do
setup { get "/oauth/access_token" }
should "respond with status 405 (Method Not Allowed)" do
assert_equal 405, last_response.status
end
end
context "no client ID" do
setup { request_access_token :client_id=>nil }
should_respond_with_authentication_error :invalid_client
end
context "invalid client ID" do
setup { request_access_token :client_id=>"foobar" }
should_respond_with_authentication_error :invalid_client
end
context "client ID but no such client" do
setup { request_access_token :client_id=>"4cc7bc483321e814b8000000" }
should_respond_with_authentication_error :invalid_client
end
context "no client secret" do
setup { request_access_token :client_secret=>nil }
should_respond_with_authentication_error :invalid_client
end
context "wrong client secret" do
setup { request_access_token :client_secret=>"plain wrong" }
should_respond_with_authentication_error :invalid_client
end
context "client revoked" do
setup do
client.revoke!
request_access_token
end
should_respond_with_authentication_error :invalid_client
end
context "unsupported grant type" do
setup { request_access_token :grant_type=>"bogus" }
should_return_error :unsupported_grant_type
end
# 4.1.1. Authorization Code
context "no authorization code" do
setup { request_access_token :code=>nil }
should_return_error :invalid_grant
end
context "unknown authorization code" do
setup { request_access_token :code=>"unknown" }
should_return_error :invalid_grant
end
context "authorization code for different client" do
setup do
grant = Server::AccessGrant.create("foo bar", Server.register(:scope=>%w{read write}), "read write", nil)
request_access_token :code=>grant.code
end
should_return_error :invalid_grant
end
context "authorization code revoked" do
setup do
Server::AccessGrant.from_code(@code).revoke!
request_access_token
end
should_return_error :invalid_grant
end
context "mistmatched redirect URI" do
setup { request_access_token :redirect_uri=>"http://uberclient.dot/oz" }
should_return_error :invalid_grant
end
context "no redirect URI to match" do
setup do
@client = Server.register(:display_name=>"No rediret", :scope=>"read write")
grant = Server::AccessGrant.create("foo bar", client, "read write", nil)
request_access_token :code=>grant.code, :redirect_uri=>"http://uberclient.dot/oz"
end
should_respond_with_access_token
end
context "access grant expired" do
setup do
Timecop.travel 300 do
request_access_token
end
end
should_return_error :invalid_grant
end
context "access grant spent" do
setup do
request_access_token
request_access_token
end
should_return_error :invalid_grant
end
# 4.1.2. Resource Owner Password Credentials
context "no username" do
setup { request_with_username_password nil, "more" }
should_return_error :invalid_grant
end
context "no password" do
setup { request_with_username_password nil, "more" }
should_return_error :invalid_grant
end
context "not authorized" do
setup { request_with_username_password "cowbell", "less" }
should_return_error :invalid_grant
end
context "no scope specified" do
setup { request_with_username_password "cowbell", "more" }
should_respond_with_access_token "oauth-admin read write"
end
context "given scope" do
setup { request_with_username_password "cowbell", "more", "read" }
should_respond_with_access_token "read"
end
context "unsupported scope" do
setup { request_with_username_password "cowbell", "more", "read write math" }
should_return_error :invalid_scope
end
context "authenticator with 4 parameters" do
setup do
@old = config.authenticator
config.authenticator = lambda do |username, password, client_id, scope|
@client_id = client_id
@scope = scope
"Batman"
end
request_with_username_password "cowbell", "more", "read"
end
should_respond_with_access_token "read"
should "receive client identifier" do
assert_equal client.id, @client_id
end
should "receive scope" do
assert_equal %w{read}, @scope
end
teardown { config.authenticator = @old }
end
# 4.2. Access Token Response
context "using authorization code" do
setup { request_access_token }
should_respond_with_access_token "read write"
end
context "using username/password" do
setup { request_with_username_password "cowbell", "more", "read" }
should_respond_with_access_token "read"
end
end