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
|
# frozen_string_literal: true
require_relative '../application_system_test_case'
require 'oauth2'
require 'webrick'
class OauthProviderSystemTest < ApplicationSystemTestCase
fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles,
:trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues,
:enumerations, :custom_fields, :custom_values, :custom_fields_trackers,
:watchers, :journals, :journal_details, :versions,
:workflows
test 'application creation and authorization' do
#
# admin creates the application, granting permissions and generating a uuid
# and secret.
#
log_user 'admin', 'admin'
with_settings rest_api_enabled: 1 do
visit '/admin'
within 'div#admin-menu ul' do
click_link 'Applications'
end
click_link 'New Application'
fill_in 'Name', with: 'Oauth Test'
# as per https://tools.ietf.org/html/rfc8252#section-7.3, the port can be
# anything when the redirect URI's host is 127.0.0.1.
fill_in 'Redirect URI', with: 'http://127.0.0.1'
check 'View Issues'
click_button 'Create'
end
assert_text "Application created"
assert app = Doorkeeper::Application.find_by_name('Oauth Test')
find 'h2', visible: true, text: /Oauth Test/
find 'p code', visible: true, text: app.uid
find 'p strong', visible: true, text: /will not be shown again/
find 'p code', visible: true, text: /View Issues/
# scrape the clear text secret from the page
app_secret = all(:css, 'p code')[1].text
click_link 'Sign out'
#
# regular user authorizes the application
#
client = OAuth2::Client.new(app.uid, app_secret, site: "http://127.0.0.1:#{test_port}/")
# set up a dummy http listener to handle the redirect
port = rand 10000..20000
redirect_uri = "http://127.0.0.1:#{port}"
# the request handler below will set this to the auth token
token = nil
# launches webrick, listening for the redirect with the auth code.
launch_client_app(port: port) do |req, res|
# get access code from code url param
if code = req.query['code'].presence
# exchange it for token
token = client.auth_code.get_token(code, redirect_uri: redirect_uri)
res.body = "<html><body><p>Authorization succeeded, you may close this window now.</p></body></html>"
end
end
log_user 'jsmith', 'jsmith'
with_settings rest_api_enabled: 1 do
visit '/my/account'
click_link 'Authorized applications'
find 'p.nodata', visible: true
# an oauth client would send the user to this url to request permission
url = client.auth_code.authorize_url redirect_uri: redirect_uri, scope: 'view_issues view_project'
uri = URI.parse url
visit uri.path + '?' + uri.query
find 'h2', visible: true, text: 'Authorization required'
find 'p', visible: true, text: /Authorize Oauth Test/
find '.oauth-permissions', visible: true, text: /View Issues/
find '.oauth-permissions', visible: true, text: /View project/
click_button 'Authorize'
assert grant = app.access_grants.last
assert_equal 'view_issues view_project', grant.scopes.to_s
# check for output defined above in the request handler
find 'p', visible: true, text: /Authorization succeeded/
assert token.present?
visit '/my/account'
click_link 'Authorized applications'
find 'td', visible: true, text: /Oauth Test/
click_link 'Sign out'
# Now, use the token for some API requests
assert_raise(RestClient::Unauthorized) do
RestClient.get "http://localhost:#{test_port}/projects/onlinestore/issues.json"
end
headers = { 'Authorization' => "Bearer #{token.token}" }
r = RestClient.get "http://localhost:#{test_port}/projects/onlinestore/issues.json", headers
issues = JSON.parse(r.body)['issues']
assert issues.any?
# time entries access is not part of the granted scopes
assert_raise(RestClient::Forbidden) do
RestClient.get "http://localhost:#{test_port}/projects/onlinestore/time_entries.json", headers
end
end
end
private
def launch_client_app(port: 12345, path: '/', &block)
server = WEBrick::HTTPServer.new Port: port
trap('INT') { server.shutdown }
server.mount_proc(path, block)
Thread.new { server.start }
port
end
def test_port
Capybara.current_session.server.port
end
end
|