test: add regression tests for API.Authorizations (#11060)
Marcin Rataj committed
Nov 17, 2025 at 18:52 UTC
1404861086ae9fece37c651f451ecad812413140
1 file changed
+123
test/cli/rpc_auth_test.go
+123
@@ -159,4 +159,127 @@ func TestRPCAuth(t *testing.T) {
159
160
node.StopDaemon()
161
})
162
+
163
+ t.Run("Requests without Authorization header are rejected when auth is enabled", func(t *testing.T) {
164
+ t.Parallel()
165
+
166
+ node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
167
+ "userA": {
168
+ AuthSecret: "bearer:mytoken",
169
+ AllowedPaths: []string{"/api/v0"},
170
+ },
171
+ })
172
+
173
+ // Create client with NO auth
174
+ apiClient := node.APIClient() // Uses http.DefaultClient with no auth headers
175
+
176
+ // Should be denied without auth header
177
+ resp := apiClient.Post("/api/v0/id", nil)
178
+ assert.Equal(t, 403, resp.StatusCode)
179
+
180
+ // Should contain denial message
181
+ assert.Contains(t, resp.Body, rpcDeniedMsg)
182
+
183
+ node.StopDaemon()
184
+ })
185
+
186
+ t.Run("Version endpoint is always accessible even with limited AllowedPaths", func(t *testing.T) {
187
+ t.Parallel()
188
+
189
+ node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
190
+ "userA": {
191
+ AuthSecret: "bearer:mytoken",
192
+ AllowedPaths: []string{"/api/v0/id"}, // Only /id allowed
193
+ },
194
+ })
195
+
196
+ apiClient := node.APIClient()
197
+ apiClient.Client = &http.Client{
198
+ Transport: auth.NewAuthorizedRoundTripper("Bearer mytoken", http.DefaultTransport),
199
+ }
200
+
201
+ // Can access /version even though not in AllowedPaths
202
+ resp := apiClient.Post("/api/v0/version", nil)
203
+ assert.Equal(t, 200, resp.StatusCode)
204
+
205
+ node.StopDaemon()
206
+ })
207
+
208
+ t.Run("User cannot access API with another user's secret", func(t *testing.T) {
209
+ t.Parallel()
210
+
211
+ node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
212
+ "alice": {
213
+ AuthSecret: "bearer:alice-secret",
214
+ AllowedPaths: []string{"/api/v0/id"},
215
+ },
216
+ "bob": {
217
+ AuthSecret: "bearer:bob-secret",
218
+ AllowedPaths: []string{"/api/v0/config"},
219
+ },
220
+ })
221
+
222
+ // Alice tries to use Bob's secret
223
+ apiClient := node.APIClient()
224
+ apiClient.Client = &http.Client{
225
+ Transport: auth.NewAuthorizedRoundTripper("Bearer bob-secret", http.DefaultTransport),
226
+ }
227
+
228
+ // Bob's secret should work for Bob's paths
229
+ resp := apiClient.Post("/api/v0/config/show", nil)
230
+ assert.Equal(t, 200, resp.StatusCode)
231
+
232
+ // But not for Alice's paths (Bob doesn't have access to /id)
233
+ resp = apiClient.Post("/api/v0/id", nil)
234
+ assert.Equal(t, 403, resp.StatusCode)
235
+
236
+ node.StopDaemon()
237
+ })
238
+
239
+ t.Run("Empty AllowedPaths denies all access except version", func(t *testing.T) {
240
+ t.Parallel()
241
+
242
+ node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
243
+ "userA": {
244
+ AuthSecret: "bearer:mytoken",
245
+ AllowedPaths: []string{}, // Empty!
246
+ },
247
+ })
248
+
249
+ apiClient := node.APIClient()
250
+ apiClient.Client = &http.Client{
251
+ Transport: auth.NewAuthorizedRoundTripper("Bearer mytoken", http.DefaultTransport),
252
+ }
253
+
254
+ // Should deny everything
255
+ resp := apiClient.Post("/api/v0/id", nil)
256
+ assert.Equal(t, 403, resp.StatusCode)
257
+
258
+ resp = apiClient.Post("/api/v0/config/show", nil)
259
+ assert.Equal(t, 403, resp.StatusCode)
260
+
261
+ // Except version
262
+ resp = apiClient.Post("/api/v0/version", nil)
263
+ assert.Equal(t, 200, resp.StatusCode)
264
+
265
+ node.StopDaemon()
266
+ })
267
+
268
+ t.Run("CLI commands fail without --api-auth when auth is enabled", func(t *testing.T) {
269
+ t.Parallel()
270
+
271
+ node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
272
+ "userA": {
273
+ AuthSecret: "bearer:mytoken",
274
+ AllowedPaths: []string{"/api/v0"},
275
+ },
276
+ })
277
+
278
+ // Try to run command without --api-auth flag
279
+ resp := node.RunIPFS("id") // No --api-auth flag
280
+ require.Error(t, resp.Err)
281
+ require.Contains(t, resp.Stderr.String(), rpcDeniedMsg)
282
+
283
+ node.StopDaemon()
284
+ })
285
}