summaryrefslogtreecommitdiffstats
path: root/test/system/oauth_provider_test.rb
blob: 897af72c056382103d8ca963899bdf88035eb8a5 (plain)
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