]> git.arvados.org - arvados.git/blob - services/api/test/integration/credentials_test.rb
22680: Include credential_id in credential_secret API
[arvados.git] / services / api / test / integration / credentials_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'test_helper'
6
7 class CredentialsApiTest < ActionDispatch::IntegrationTest
8   fixtures :all
9
10   def credential_create_helper
11     post "/arvados/v1/credentials",
12          params: {:format => :json,
13                   credential: {
14                     name: "test credential",
15                     description: "the credential for test",
16                     credential_class: "basic_auth",
17                     credential_id: "my_username",
18                     credential_secret: "my_password",
19                     expires_at: Time.now+2.weeks
20                   }
21                  },
22          headers: auth(:active),
23          as: :json
24     assert_response :success
25     json_response
26   end
27
28   test "credential create and query" do
29     jr = credential_create_helper
30
31     # fields other than secret is are returned by the API
32     assert_equal "test credential", jr["name"]
33     assert_equal "the credential for test", jr["description"]
34     assert_equal "basic_auth", jr["credential_class"]
35     assert_equal "my_username", jr["credential_id"]
36     assert_nil jr["credential_secret"]
37
38     # secret is not returned by the API
39     get "/arvados/v1/credentials/#{jr['uuid']}", headers: auth(:active)
40     assert_response :success
41     jr = json_response
42     assert_equal "test credential", jr["name"]
43     assert_equal "the credential for test", jr["description"]
44     assert_equal "basic_auth", jr["credential_class"]
45     assert_equal "my_username", jr["credential_id"]
46     assert_nil jr["credential_secret"]
47
48     # can get credential from the database and it has the password
49     assert_equal "my_password", Credential.find_by_uuid(jr["uuid"]).credential_secret
50
51     # credential_secret cannot appear in queries
52     get "/arvados/v1/credentials",
53         params: {:format => :json,
54                  :filters => [["credential_secret", "=", "my_password"]].to_json,
55                 },
56         headers: auth(:active)
57     assert_response 403
58     assert_match(/Cannot filter on credential_secret/, json_response["errors"][0])
59
60     get "/arvados/v1/credentials",
61         params: {:format => :json,
62                  :where => {credential_secret: "my_password"}.to_json
63                 },
64         headers: auth(:active)
65     assert_response 403
66     assert_match(/Cannot use credential_secret in where clause/, json_response["errors"][0])
67
68     get "/arvados/v1/credentials",
69         params: {:format => :json,
70                  :order => ["credential_secret"].to_json
71                 },
72         headers: auth(:active)
73     assert_response 403
74     assert_match(/Cannot order by credential_secret/, json_response["errors"][0])
75   end
76
77   test "credential fetch by container" do
78     jr = credential_create_helper
79
80     # cannot fetch secret using a regular token
81     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret", headers: auth(:active)
82     assert_response 403
83
84     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret",
85         headers: {'HTTP_AUTHORIZATION' => "Bearer #{api_client_authorizations(:running_container_auth).token}/#{containers(:running).uuid}"}
86     assert_response :success
87     assert_equal "my_password", json_response["credential_secret"]
88
89     lg = Log.where(object_uuid: jr['uuid'], event_type: "credential_secret_access").first
90     assert_equal jr["name"], lg["properties"]["name"]
91     assert_equal jr["credential_class"], lg["properties"]["credential_class"]
92     assert_equal jr["credential_id"], lg["properties"]["credential_id"]
93   end
94
95   test "credential owned by admin" do
96     post "/arvados/v1/credentials",
97          params: {:format => :json,
98                   credential: {
99                     name: "test credential",
100                     description: "the credential for test",
101                     credential_class: "basic_auth",
102                     credential_id: "my_username",
103                     credential_secret: "my_password",
104                     expires_at: Time.now+2.weeks
105                   }
106                  },
107          headers: auth(:admin),
108          as: :json
109     assert_response :success
110     jr = json_response
111
112     # cannot fetch secret using a regular token, even by admin
113     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret", headers: auth(:admin)
114     assert_response 403
115
116     # user 'active' can't see it
117     get "/arvados/v1/credentials/#{jr['uuid']}", headers: auth(:active)
118     assert_response 404
119
120     # not readable by container run by 'active' user returns a 404
121     # here like the previous check because the credential itself isn't
122     # considered visible to the user
123     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret",
124         headers: {'HTTP_AUTHORIZATION' => "Bearer #{api_client_authorizations(:running_container_auth).token}/#{containers(:running).uuid}"}
125     assert_response 404
126   end
127
128   test "credential sharing" do
129     post "/arvados/v1/credentials",
130          params: {:format => :json,
131                   credential: {
132                     name: "test credential",
133                     description: "the credential for test",
134                     credential_class: "basic_auth",
135                     credential_id: "my_username",
136                     credential_secret: "my_password",
137                     expires_at: Time.now+2.weeks
138                   }
139                  },
140          headers: auth(:admin),
141          as: :json
142     assert_response :success
143     jr = json_response
144
145     # user 'active' can't see it
146     get "/arvados/v1/credentials/#{jr['uuid']}", headers: auth(:active)
147     assert_response 404
148
149     # not readable by container run by 'active' user returns a 404
150     # here like the previous check because the credential itself isn't
151     # considered visible to the user
152     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret",
153         headers: {'HTTP_AUTHORIZATION' => "Bearer #{api_client_authorizations(:running_container_auth).token}/#{containers(:running).uuid}"}
154     assert_response 404
155
156     # active user can't share
157     post "/arvados/v1/links",
158       params: {
159         :format => :json,
160         :link => {
161           tail_uuid: users(:active).uuid,
162           link_class: 'permission',
163           name: 'can_read',
164           head_uuid: jr["uuid"],
165           properties: {}
166         }
167       },
168       headers: auth(:active)
169     assert_response 422
170
171     # admin can share
172     post "/arvados/v1/links",
173       params: {
174         :format => :json,
175         :link => {
176           tail_uuid: users(:active).uuid,
177           link_class: 'permission',
178           name: 'can_read',
179           head_uuid: jr["uuid"],
180           properties: {}
181         }
182       },
183       headers: auth(:admin)
184     assert_response :success
185
186     # now the 'active' user can read it
187     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret",
188         headers: {'HTTP_AUTHORIZATION' => "Bearer #{api_client_authorizations(:running_container_auth).token}/#{containers(:running).uuid}"}
189     assert_response :success
190   end
191
192   test "credential expiration" do
193     post "/arvados/v1/credentials",
194          params: {:format => :json,
195                   credential: {
196                     name: "test credential",
197                     description: "the credential for test",
198                     credential_class: "basic_auth",
199                     credential_id: "my_username",
200                     credential_secret: "my_password",
201                     expires_at: Time.now+1.seconds
202                   }
203                  },
204          headers: auth(:active),
205          as: :json
206     assert_response :success
207     jr = json_response
208
209     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret",
210         headers: {'HTTP_AUTHORIZATION' => "Bearer #{api_client_authorizations(:running_container_auth).token}/#{containers(:running).uuid}"}
211     assert_response :success
212     assert_equal "my_username", json_response["credential_id"]
213     assert_equal "my_password", json_response["credential_secret"]
214
215     assert_equal "my_password", Credential.find_by_uuid(jr["uuid"]).credential_secret
216
217     sleep(1)
218
219     get "/arvados/v1/credentials/#{jr['uuid']}/credential_secret",
220         headers: {'HTTP_AUTHORIZATION' => "Bearer #{api_client_authorizations(:running_container_auth).token}/#{containers(:running).uuid}"}
221     assert_response 403
222     assert_match(/Credential has expired/, json_response["errors"][0])
223
224     post "/sys/trash_sweep",
225       headers: auth(:admin)
226     assert_response :success
227
228     assert_equal "", Credential.find_by_uuid(jr["uuid"]).credential_secret
229   end
230
231   test "credential names are unique" do
232     post "/arvados/v1/credentials",
233          params: {:format => :json,
234                   credential: {
235                     name: "test credential",
236                     description: "the credential for test",
237                     credential_class: "basic_auth",
238                     credential_id: "my_username",
239                     credential_secret: "my_password",
240                     expires_at: Time.now+2.weeks
241                   }
242                  },
243          headers: auth(:active),
244          as: :json
245     assert_response :success
246
247     post "/arvados/v1/credentials",
248          params: {:format => :json,
249                   credential: {
250                     name: "test credential",
251                     description: "the credential for test",
252                     credential_class: "basic_auth",
253                     credential_id: "my_username",
254                     credential_secret: "my_password",
255                     expires_at: Time.now+2.weeks
256                   }
257                  },
258          headers: auth(:active),
259          as: :json
260     assert_response 422
261     assert_match(/RecordNotUnique/, json_response["errors"][0])
262   end
263
264   test "credential expires_at must be set" do
265     post "/arvados/v1/credentials",
266          params: {:format => :json,
267                   credential: {
268                     name: "test credential",
269                     description: "the credential for test",
270                     credential_class: "basic_auth",
271                     credential_id: "my_username",
272                     credential_secret: "my_password"
273                   }
274                  },
275          headers: auth(:active),
276          as: :json
277     assert_response 422
278     assert_match(/NotNullViolation/, json_response["errors"][0])
279   end
280 end