rack-oauth2-server 1.0.beta
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.
- data/CHANGELOG +3 -0
- data/Gemfile +17 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +423 -0
- data/Rakefile +60 -0
- data/lib/rack-oauth2-server.rb +1 -0
- data/lib/rack/oauth2/models.rb +37 -0
- data/lib/rack/oauth2/models/access_grant.rb +75 -0
- data/lib/rack/oauth2/models/access_token.rb +65 -0
- data/lib/rack/oauth2/models/auth_request.rb +88 -0
- data/lib/rack/oauth2/models/client.rb +73 -0
- data/lib/rack/oauth2/rails.rb +105 -0
- data/lib/rack/oauth2/server.rb +312 -0
- data/lib/rack/oauth2/server/errors.rb +97 -0
- data/lib/rack/oauth2/server/helper.rb +142 -0
- data/lib/rack/oauth2/server/utils.rb +24 -0
- data/lib/rack/oauth2/server/version.rb +9 -0
- data/lib/rack/oauth2/sinatra.rb +71 -0
- data/rack-oauth2-server.gemspec +25 -0
- data/test/access_grant_test.rb +216 -0
- data/test/access_token_test.rb +237 -0
- data/test/authorization_test.rb +267 -0
- data/test/rails/app/controllers/api_controller.rb +40 -0
- data/test/rails/app/controllers/application_controller.rb +4 -0
- data/test/rails/app/controllers/oauth_controller.rb +14 -0
- data/test/rails/config/environment.rb +12 -0
- data/test/rails/config/environments/test.rb +0 -0
- data/test/rails/config/routes.rb +13 -0
- data/test/rails/log/test.log +14710 -0
- data/test/setup.rb +73 -0
- data/test/sinatra/my_app.rb +67 -0
- metadata +148 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class Server
|
4
|
+
|
5
|
+
module Utils
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Parses the redirect URL, normalizes it and returns a URI object.
|
9
|
+
#
|
10
|
+
# Raises InvalidRequestError if not an absolute HTTP/S URL.
|
11
|
+
def parse_redirect_uri(redirect_uri)
|
12
|
+
uri = URI.parse(redirect_uri).normalize
|
13
|
+
raise InvalidRequestError, "Redirect URL must be absolute URL" unless uri.absolute? && uri.host
|
14
|
+
raise InvalidRequestError, "Redirect URL must point to HTTP/S location" unless uri.scheme == "http" || uri.scheme == "https"
|
15
|
+
uri
|
16
|
+
rescue
|
17
|
+
raise InvalidRequestError, "Redirect URL looks fishy to me"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "rack/oauth2/server"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module OAuth2
|
5
|
+
|
6
|
+
# Sinatra support.
|
7
|
+
#
|
8
|
+
# Adds oauth instance method that returns Rack::OAuth2::Helper, see there for
|
9
|
+
# more details.
|
10
|
+
#
|
11
|
+
# Adds oauth_required class method. Use this filter with paths that require
|
12
|
+
# authentication, and with paths that require client to have a specific
|
13
|
+
# access scope.
|
14
|
+
#
|
15
|
+
# Adds oauth setting you can use to configure the module (e.g. setting
|
16
|
+
# available scopes, see example).
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# require "rack/oauth2/sinatra"
|
20
|
+
# class MyApp < Sinatra::Base
|
21
|
+
# register Rack::OAuth2::Sinatra
|
22
|
+
# oauth[:scopes] = %w{read write}
|
23
|
+
#
|
24
|
+
# oauth_required "/api"
|
25
|
+
# oauth_required "/api/edit", :scope=>"write"
|
26
|
+
#
|
27
|
+
# before { @user = User.find(oauth.resource) if oauth.authenticated? }
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# @see Helpers
|
31
|
+
module Sinatra
|
32
|
+
|
33
|
+
# Adds before filter to require authentication on all the listed paths.
|
34
|
+
# Use the :scope option if client must also have access to that scope.
|
35
|
+
#
|
36
|
+
# @param [String, ...] path One or more paths that require authentication
|
37
|
+
# @param [optional, Hash] options Currently only :scope is supported.
|
38
|
+
def oauth_required(*args)
|
39
|
+
options = args.pop if Hash === args.last
|
40
|
+
scope = options[:scope] if options
|
41
|
+
args.each do |path|
|
42
|
+
before path do
|
43
|
+
if oauth.authenticated?
|
44
|
+
if scope && !oauth.scope.include?(scope)
|
45
|
+
oauth.no_scope! scope
|
46
|
+
end
|
47
|
+
else
|
48
|
+
oauth.no_access!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module Helpers
|
55
|
+
# Returns the OAuth helper.
|
56
|
+
#
|
57
|
+
# @return [Server::Helper]
|
58
|
+
def oauth
|
59
|
+
@oauth ||= Rack::OAuth2::Server::Helper.new(request, response)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.registered(base)
|
64
|
+
base.helpers Helpers
|
65
|
+
base.set :oauth, {}
|
66
|
+
base.use Rack::OAuth2::Server, base.settings.oauth
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + "/lib"
|
2
|
+
require "rack/oauth2/server/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "rack-oauth2-server"
|
6
|
+
spec.version = Rack::OAuth2::Server::VERSION
|
7
|
+
spec.author = "Assaf Arkin"
|
8
|
+
spec.email = "assaf@labnotes.org"
|
9
|
+
spec.homepage = "http://github.com/assaf/#{spec.name}"
|
10
|
+
spec.summary = "OAuth 2.0 Authorization Server as a Rack module"
|
11
|
+
spec.description = "Because you don't allow strangers into your app, and OAuth 2.0 is the new awesome."
|
12
|
+
spec.post_install_message = ""
|
13
|
+
|
14
|
+
spec.files = Dir["{bin,lib,test}/**/*", "CHANGELOG", "MIT-LICENSE", "README.rdoc", "Rakefile", "Gemfile", "*.gemspec"]
|
15
|
+
|
16
|
+
spec.has_rdoc = true
|
17
|
+
spec.extra_rdoc_files = "README.rdoc", "CHANGELOG"
|
18
|
+
spec.rdoc_options = "--title", "rack-oauth2-server #{spec.version}", "--main", "README.rdoc",
|
19
|
+
"--webcvs", "http://github.com/assaf/#{spec.name}"
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 1.8.7'
|
22
|
+
spec.add_dependency "rack", "~>1"
|
23
|
+
spec.add_dependency "mongo", "~>1"
|
24
|
+
spec.add_dependency "bson_ext"
|
25
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/setup"
|
2
|
+
|
3
|
+
|
4
|
+
# 4. Obtaining an Access Token
|
5
|
+
class AccessGrantTest < Test::Unit::TestCase
|
6
|
+
module Helpers
|
7
|
+
|
8
|
+
def should_return_error(error)
|
9
|
+
should "respond with status 400 (Bad Request)" do
|
10
|
+
assert_equal 400, last_response.status
|
11
|
+
end
|
12
|
+
should "respond with JSON document" do
|
13
|
+
assert_equal "application/json", last_response.content_type
|
14
|
+
end
|
15
|
+
should "respond with error code #{error}" do
|
16
|
+
assert_equal error.to_s, JSON.parse(last_response.body)["error"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def should_respond_with_authentication_error(error)
|
21
|
+
should "respond with status 401 (Unauthorized)" do
|
22
|
+
assert_equal 401, last_response.status
|
23
|
+
end
|
24
|
+
should "respond with authentication method OAuth" do
|
25
|
+
assert_equal "OAuth", last_response["WWW-Authenticate"].split.first
|
26
|
+
end
|
27
|
+
should "respond with realm" do
|
28
|
+
assert_match " realm=\"example.org\"", last_response["WWW-Authenticate"]
|
29
|
+
end
|
30
|
+
should "respond with error code #{error}" do
|
31
|
+
assert_match " error=\"#{error}\"", last_response["WWW-Authenticate"]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def should_respond_with_access_token(scope = "read write")
|
36
|
+
should "respond with status 200" do
|
37
|
+
assert_equal 200, last_response.status
|
38
|
+
end
|
39
|
+
should "respond with JSON document" do
|
40
|
+
assert_equal "application/json", last_response.content_type
|
41
|
+
end
|
42
|
+
should "respond with cache control no-store" do
|
43
|
+
assert_equal "no-store", last_response["Cache-Control"]
|
44
|
+
end
|
45
|
+
should "not respond with error code" do
|
46
|
+
assert JSON.parse(last_response.body)["error"].nil?
|
47
|
+
end
|
48
|
+
should "response with access token" do
|
49
|
+
assert_match /[a-f0-9]{32}/i, JSON.parse(last_response.body)["access_token"]
|
50
|
+
end
|
51
|
+
should "response with scope" do
|
52
|
+
assert_equal scope, JSON.parse(last_response.body)["scope"]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
end
|
58
|
+
extend Helpers
|
59
|
+
|
60
|
+
def setup
|
61
|
+
super
|
62
|
+
# Get authorization code.
|
63
|
+
params = { :redirect_uri=>client.redirect_uri, :client_id=>client.id, :client_secret=>client.secret, :response_type=>"code",
|
64
|
+
:scope=>"read write", :state=>"bring this back" }
|
65
|
+
get "/oauth/authorize?" + Rack::Utils.build_query(params)
|
66
|
+
post "/oauth/grant"
|
67
|
+
@code = Rack::Utils.parse_query(URI.parse(last_response["Location"]).query)["code"]
|
68
|
+
end
|
69
|
+
|
70
|
+
def request_access_token(changes = nil)
|
71
|
+
params = { :client_id=>client.id, :client_secret=>client.secret, :scope=>"read write",
|
72
|
+
:grant_type=>"authorization_code", :code=>@code, :redirect_uri=>client.redirect_uri }.merge(changes || {})
|
73
|
+
basic_authorize params.delete(:client_id), params.delete(:client_secret)
|
74
|
+
post "/oauth/access_token", params
|
75
|
+
end
|
76
|
+
|
77
|
+
def request_with_username_password(username, password, scope = "read write")
|
78
|
+
basic_authorize client.id, client.secret
|
79
|
+
params = { :grant_type=>"password", :scope=>scope }
|
80
|
+
params[:username] = username if username
|
81
|
+
params[:password] = password if password
|
82
|
+
post "/oauth/access_token", params
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# 4. Obtaining an Access Token
|
87
|
+
|
88
|
+
context "GET request" do
|
89
|
+
setup { get "/oauth/access_token" }
|
90
|
+
|
91
|
+
should "respond with status 405 (Method Not Allowed)" do
|
92
|
+
assert_equal 405, last_response.status
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "no client ID" do
|
97
|
+
setup { request_access_token :client_id=>nil }
|
98
|
+
should_respond_with_authentication_error :invalid_client
|
99
|
+
end
|
100
|
+
|
101
|
+
context "invalid client ID" do
|
102
|
+
setup { request_access_token :client_id=>"foobar" }
|
103
|
+
should_respond_with_authentication_error :invalid_client
|
104
|
+
end
|
105
|
+
|
106
|
+
context "client ID but no such client" do
|
107
|
+
setup { request_access_token :client_id=>"4cc7bc483321e814b8000000" }
|
108
|
+
should_respond_with_authentication_error :invalid_client
|
109
|
+
end
|
110
|
+
|
111
|
+
context "no client secret" do
|
112
|
+
setup { request_access_token :client_secret=>nil }
|
113
|
+
should_respond_with_authentication_error :invalid_client
|
114
|
+
end
|
115
|
+
|
116
|
+
context "wrong client secret" do
|
117
|
+
setup { request_access_token :client_secret=>"plain wrong" }
|
118
|
+
should_respond_with_authentication_error :invalid_client
|
119
|
+
end
|
120
|
+
|
121
|
+
context "client revoked" do
|
122
|
+
setup do
|
123
|
+
client.revoke!
|
124
|
+
request_access_token
|
125
|
+
end
|
126
|
+
should_respond_with_authentication_error :invalid_client
|
127
|
+
end
|
128
|
+
|
129
|
+
context "unsupported grant type" do
|
130
|
+
setup { request_access_token :grant_type=>"bogus" }
|
131
|
+
should_return_error :unsupported_grant_type
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# 4.1.1. Authorization Code
|
136
|
+
|
137
|
+
context "no authorization code" do
|
138
|
+
setup { request_access_token :code=>nil }
|
139
|
+
should_return_error :invalid_grant
|
140
|
+
end
|
141
|
+
|
142
|
+
context "unknown authorization code" do
|
143
|
+
setup { request_access_token :code=>"unknown" }
|
144
|
+
should_return_error :invalid_grant
|
145
|
+
end
|
146
|
+
|
147
|
+
context "authorization code for different client" do
|
148
|
+
setup do
|
149
|
+
grant = Rack::OAuth2::Server::AccessGrant.create("foo bar", "read write", "4cc7bc483321e814b8000000", nil)
|
150
|
+
request_access_token :code=>grant.code
|
151
|
+
end
|
152
|
+
should_return_error :invalid_grant
|
153
|
+
end
|
154
|
+
|
155
|
+
context "authorization code revoked" do
|
156
|
+
setup do
|
157
|
+
Rack::OAuth2::Server::AccessGrant.from_code(@code).revoke!
|
158
|
+
request_access_token
|
159
|
+
end
|
160
|
+
should_return_error :invalid_grant
|
161
|
+
end
|
162
|
+
|
163
|
+
context "mistmatched redirect URI" do
|
164
|
+
setup { request_access_token :redirect_uri=>"http://uberclient.dot/oz" }
|
165
|
+
should_return_error :invalid_grant
|
166
|
+
end
|
167
|
+
|
168
|
+
context "no redirect URI to match" do
|
169
|
+
setup do
|
170
|
+
grant = Rack::OAuth2::Server::AccessGrant.create("foo bar", "read write", client.id, nil)
|
171
|
+
request_access_token :code=>grant.code, :redirect_uri=>"http://uberclient.dot/oz"
|
172
|
+
end
|
173
|
+
should_respond_with_access_token
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# 4.1.2. Resource Owner Password Credentials
|
178
|
+
|
179
|
+
context "no username" do
|
180
|
+
setup { request_with_username_password nil, "more" }
|
181
|
+
should_return_error :invalid_grant
|
182
|
+
end
|
183
|
+
|
184
|
+
context "no password" do
|
185
|
+
setup { request_with_username_password nil, "more" }
|
186
|
+
should_return_error :invalid_grant
|
187
|
+
end
|
188
|
+
|
189
|
+
context "not authorized" do
|
190
|
+
setup { request_with_username_password "cowbell", "less" }
|
191
|
+
should_return_error :invalid_grant
|
192
|
+
end
|
193
|
+
|
194
|
+
context "no scope specified" do
|
195
|
+
setup { request_with_username_password "cowbell", "more", nil }
|
196
|
+
should_respond_with_access_token nil
|
197
|
+
end
|
198
|
+
|
199
|
+
context "unsupported scope" do
|
200
|
+
setup { request_with_username_password "cowbell", "more", "read write math" }
|
201
|
+
should_return_error :invalid_scope
|
202
|
+
end
|
203
|
+
|
204
|
+
# 4.2. Access Token Response
|
205
|
+
|
206
|
+
context "using authorization code" do
|
207
|
+
setup { request_access_token }
|
208
|
+
should_respond_with_access_token "read write"
|
209
|
+
end
|
210
|
+
|
211
|
+
context "using username/password" do
|
212
|
+
setup { request_with_username_password "cowbell", "more", "read" }
|
213
|
+
should_respond_with_access_token "read"
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/setup"
|
2
|
+
|
3
|
+
|
4
|
+
# 5. Accessing a Protected Resource
|
5
|
+
class AccessTokenTest < Test::Unit::TestCase
|
6
|
+
module Helpers
|
7
|
+
|
8
|
+
def should_return_resource(content)
|
9
|
+
should "respond with status 200" do
|
10
|
+
assert_equal 200, last_response.status
|
11
|
+
end
|
12
|
+
should "respond with resource name" do
|
13
|
+
assert_equal content, last_response.body
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def should_fail_authentication(error = nil)
|
18
|
+
should "respond with status 401 (Unauthorized)" do
|
19
|
+
assert_equal 401, last_response.status
|
20
|
+
end
|
21
|
+
should "respond with authentication method OAuth" do
|
22
|
+
assert_equal "OAuth", last_response["WWW-Authenticate"].split.first
|
23
|
+
end
|
24
|
+
should "respond with realm" do
|
25
|
+
assert_match " realm=\"example.org\"", last_response["WWW-Authenticate"]
|
26
|
+
end
|
27
|
+
if error
|
28
|
+
should "respond with error code #{error}" do
|
29
|
+
assert_match " error=\"#{error}\"", last_response["WWW-Authenticate"]
|
30
|
+
end
|
31
|
+
else
|
32
|
+
should "not respond with error code" do
|
33
|
+
assert !last_response["WWW-Authenticate"]["error="]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
extend Helpers
|
40
|
+
|
41
|
+
|
42
|
+
def setup
|
43
|
+
super
|
44
|
+
# Get authorization code.
|
45
|
+
params = { :redirect_uri=>client.redirect_uri, :client_id=>client.id, :client_secret=>client.secret, :response_type=>"code",
|
46
|
+
:scope=>"read write", :state=>"bring this back" }
|
47
|
+
get "/oauth/authorize?" + Rack::Utils.build_query(params)
|
48
|
+
post "/oauth/grant"
|
49
|
+
code = Rack::Utils.parse_query(URI.parse(last_response["Location"]).query)["code"]
|
50
|
+
# Get access token
|
51
|
+
basic_authorize client.id, client.secret
|
52
|
+
post "/oauth/access_token", :scope=>"read write", :grant_type=>"authorization_code", :code=>code, :redirect_uri=>client.redirect_uri
|
53
|
+
@token = JSON.parse(last_response.body)["access_token"]
|
54
|
+
header "Authorization", nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_token(token = @token)
|
58
|
+
header "Authorization", "OAuth #{token}"
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# 5. Accessing a Protected Resource
|
63
|
+
|
64
|
+
context "public resource" do
|
65
|
+
context "no authorization" do
|
66
|
+
setup { get "/public" }
|
67
|
+
should_return_resource "HAI"
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with authorization" do
|
71
|
+
setup do
|
72
|
+
with_token
|
73
|
+
get "/public"
|
74
|
+
end
|
75
|
+
should_return_resource "HAI from Superman"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "private resource" do
|
80
|
+
context "no authorization" do
|
81
|
+
setup { get "/private" }
|
82
|
+
should_fail_authentication
|
83
|
+
end
|
84
|
+
|
85
|
+
context "HTTP authentication" do
|
86
|
+
context "valid token" do
|
87
|
+
setup do
|
88
|
+
with_token
|
89
|
+
get "/private"
|
90
|
+
end
|
91
|
+
should_return_resource "Shhhh"
|
92
|
+
end
|
93
|
+
|
94
|
+
context "unknown token" do
|
95
|
+
setup do
|
96
|
+
with_token "dingdong"
|
97
|
+
get "/private"
|
98
|
+
end
|
99
|
+
should_fail_authentication :invalid_token
|
100
|
+
end
|
101
|
+
|
102
|
+
context "revoked HTTP token" do
|
103
|
+
setup do
|
104
|
+
Rack::OAuth2::Server::AccessToken.from_token(@token).revoke!
|
105
|
+
with_token
|
106
|
+
get "/private"
|
107
|
+
end
|
108
|
+
should_fail_authentication :invalid_token
|
109
|
+
end
|
110
|
+
|
111
|
+
context "revoked client" do
|
112
|
+
setup do
|
113
|
+
client.revoke!
|
114
|
+
with_token
|
115
|
+
get "/private"
|
116
|
+
end
|
117
|
+
should_fail_authentication :invalid_token
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# 5.1.2. URI Query Parameter
|
122
|
+
|
123
|
+
context "query parameter" do
|
124
|
+
context "valid token" do
|
125
|
+
setup { get "/private?oauth_token=#{@token}" }
|
126
|
+
should_return_resource "Shhhh"
|
127
|
+
end
|
128
|
+
|
129
|
+
context "invalid token" do
|
130
|
+
setup { get "/private?oauth_token=dingdong" }
|
131
|
+
should_fail_authentication :invalid_token
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "POST" do
|
137
|
+
context "no authorization" do
|
138
|
+
setup { post "/change" }
|
139
|
+
should_fail_authentication
|
140
|
+
end
|
141
|
+
|
142
|
+
context "HTTP authentication" do
|
143
|
+
context "valid token" do
|
144
|
+
setup do
|
145
|
+
with_token
|
146
|
+
post "/change"
|
147
|
+
end
|
148
|
+
should_return_resource "Woot!"
|
149
|
+
end
|
150
|
+
|
151
|
+
context "unknown token" do
|
152
|
+
setup do
|
153
|
+
with_token "dingdong"
|
154
|
+
post "/change"
|
155
|
+
end
|
156
|
+
should_fail_authentication :invalid_token
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
# 5.1.3. Form-Encoded Body Parameter
|
162
|
+
|
163
|
+
context "body parameter" do
|
164
|
+
context "valid token" do
|
165
|
+
setup { post "/change", :oauth_token=>@token }
|
166
|
+
should_return_resource "Woot!"
|
167
|
+
end
|
168
|
+
|
169
|
+
context "invalid token" do
|
170
|
+
setup { post "/change", :oauth_token=>"dingdong" }
|
171
|
+
should_fail_authentication :invalid_token
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
context "insufficient scope" do
|
178
|
+
context "valid token" do
|
179
|
+
setup { get "/calc?oauth_token=#@token" }
|
180
|
+
|
181
|
+
should "respond with status 403 (Forbidden)" do
|
182
|
+
assert_equal 403, last_response.status
|
183
|
+
end
|
184
|
+
should "respond with authentication method OAuth" do
|
185
|
+
assert_equal "OAuth", last_response["WWW-Authenticate"].split.first
|
186
|
+
end
|
187
|
+
should "respond with realm" do
|
188
|
+
assert_match " realm=\"example.org\"", last_response["WWW-Authenticate"]
|
189
|
+
end
|
190
|
+
should "respond with error code insufficient_scope" do
|
191
|
+
assert_match " error=\"insufficient_scope\"", last_response["WWW-Authenticate"]
|
192
|
+
end
|
193
|
+
should "respond with scope name" do
|
194
|
+
assert_match " scope=\"math\"", last_response["WWW-Authenticate"]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
context "setting resource" do
|
201
|
+
context "authenticated" do
|
202
|
+
setup do
|
203
|
+
with_token
|
204
|
+
get "/user"
|
205
|
+
end
|
206
|
+
|
207
|
+
should "render user name" do
|
208
|
+
assert_equal "Superman", last_response.body
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "not authenticated" do
|
213
|
+
setup do
|
214
|
+
get "/user"
|
215
|
+
end
|
216
|
+
|
217
|
+
should "not render user name" do
|
218
|
+
assert last_response.body.empty?
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context "list tokens" do
|
224
|
+
setup do
|
225
|
+
@other = Rack::OAuth2::Server::AccessToken.get_token_for("foobar", "read", client.id).token
|
226
|
+
get "/list_tokens"
|
227
|
+
end
|
228
|
+
|
229
|
+
should "return access token" do
|
230
|
+
assert_contains last_response.body.split, @token
|
231
|
+
end
|
232
|
+
|
233
|
+
should "not return other resource's token" do
|
234
|
+
assert !last_response.body.split.include?(@other)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|