20978: Merge branch 'main' into 20978-instance-types
[arvados.git] / services / api / test / integration / api_client_authorizations_scopes_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 # The v1 API uses token scopes to control access to the REST API at the path
6 # level.  This is enforced in the base ApplicationController, making it a
7 # functional test that we can run against many different controllers.
8
9 require 'test_helper'
10
11 class ApiTokensScopeTest < ActionDispatch::IntegrationTest
12   fixtures :all
13
14   def v1_url(*parts)
15     (['', 'arvados', 'v1'] + parts).join('/')
16   end
17
18   test "user list token can only list users" do
19     get_args = {params: {}, headers: auth(:active_userlist)}
20     get(v1_url('users'), **get_args)
21     assert_response :success
22     get(v1_url('users', ''), **get_args)  # Add trailing slash.
23     assert_response :success
24     get(v1_url('users', 'current'), **get_args)
25     assert_response 403
26     get(v1_url('virtual_machines'), **get_args)
27     assert_response 403
28   end
29
30   test "narrow + wide scoped tokens for different users" do
31     get_args = {
32       params: {
33         reader_tokens: [api_client_authorizations(:anonymous).api_token]
34       },
35       headers: auth(:active_userlist),
36     }
37     get(v1_url('users'), **get_args)
38     assert_response :success
39     get(v1_url('users', ''), **get_args)  # Add trailing slash.
40     assert_response :success
41     get(v1_url('users', 'current'), **get_args)
42     assert_response 403
43     get(v1_url('virtual_machines'), **get_args)
44     assert_response 403
45    end
46
47   test "specimens token can see exactly owned specimens" do
48     get_args = {params: {}, headers: auth(:active_specimens)}
49     get(v1_url('specimens'), **get_args)
50     assert_response 403
51     get(v1_url('specimens', specimens(:owned_by_active_user).uuid), **get_args)
52     assert_response :success
53     head(v1_url('specimens', specimens(:owned_by_active_user).uuid), **get_args)
54     assert_response :success
55     get(v1_url('specimens', specimens(:owned_by_spectator).uuid), **get_args)
56     assert_includes(403..404, @response.status)
57   end
58
59   test "token with multiple scopes can use them all" do
60     def get_token_count
61       get(v1_url('api_client_authorizations'),
62         params: {},
63         headers: auth(:active_apitokens))
64       assert_response :success
65       token_count = JSON.parse(@response.body)['items_available']
66       assert_not_nil(token_count, "could not find token count")
67       token_count
68     end
69     # Test the GET scope.
70     token_count = get_token_count
71     # Test the POST scope.
72     post(v1_url('api_client_authorizations'),
73       params: {api_client_authorization: {user_id: users(:active).id}},
74       headers: auth(:active_apitokens))
75     assert_response :success
76     assert_equal(token_count + 1, get_token_count,
77                  "token count suggests POST was not accepted")
78     # Test other requests are denied.
79     get(v1_url('api_client_authorizations',
80                api_client_authorizations(:active_apitokens).uuid),
81         params: {}, headers: auth(:active_apitokens))
82     assert_response 403
83   end
84
85   test "token without scope has no access" do
86     # Logs are good for this test, because logs have relatively
87     # few access controls enforced at the model level.
88     req_args = {params: {}, headers: auth(:admin_noscope)}
89     get(v1_url('logs'), **req_args)
90     assert_response 403
91     get(v1_url('logs', logs(:noop).uuid), **req_args)
92     assert_response 403
93     post(v1_url('logs'), **req_args)
94     assert_response 403
95   end
96
97   test "VM login scopes work" do
98     # A system administration script makes an API token with limited scope
99     # for virtual machines to let it see logins.
100     def vm_logins_url(name)
101       v1_url('virtual_machines', virtual_machines(name).uuid, 'logins')
102     end
103     get_args = {params: {}, headers: auth(:admin_vm)}
104     get(vm_logins_url(:testvm), **get_args)
105     assert_response :success
106     get(vm_logins_url(:testvm2), **get_args)
107     assert_includes(400..419, @response.status,
108                     "getting testvm2 logins should have failed")
109   end
110 end