|
8 | 8 | "time" |
9 | 9 |
|
10 | 10 | "github.com/influxdata/influxdb/query" |
| 11 | + "github.com/influxdata/influxdb/services/meta" |
11 | 12 | "github.com/influxdata/influxql" |
| 13 | + "github.com/stretchr/testify/require" |
12 | 14 | ) |
13 | 15 |
|
14 | 16 | var errUnexpected = errors.New("unexpected error") |
@@ -354,6 +356,126 @@ func TestQueryExecutor_ShowQueries(t *testing.T) { |
354 | 356 | } |
355 | 357 | } |
356 | 358 |
|
| 359 | +// TestQueryExecutor_ShowQueries_NonAdminFiltering verifies that SHOW QUERIES |
| 360 | +// filters results based on the requesting user's database-level read |
| 361 | +// permissions. A non-admin user should only see queries running against |
| 362 | +// databases they have read access to, while an admin should see all queries. |
| 363 | +func TestQueryExecutor_ShowQueries_NonAdminFiltering(t *testing.T) { |
| 364 | + const ( |
| 365 | + dbColumn = 2 |
| 366 | + allowedDB = "mydb" |
| 367 | + forbiddenDB = "secretdb" |
| 368 | + nonAdminUser = "bar" |
| 369 | + adminUser = "alice" |
| 370 | + ) |
| 371 | + |
| 372 | + e := NewQueryExecutor() |
| 373 | + |
| 374 | + // blockCh keeps the "long-running" queries alive so they appear in SHOW QUERIES. |
| 375 | + blockCh := make(chan struct{}) |
| 376 | + defer close(blockCh) |
| 377 | + |
| 378 | + e.StatementExecutor = &StatementExecutor{ |
| 379 | + ExecuteStatementFn: func(stmt influxql.Statement, ctx *query.ExecutionContext) error { |
| 380 | + switch stmt.(type) { |
| 381 | + case *influxql.ShowQueriesStatement: |
| 382 | + return e.TaskManager.ExecuteStatement(ctx, stmt) |
| 383 | + case *influxql.SelectStatement: |
| 384 | + // Block until the test completes so this query stays visible. |
| 385 | + <-blockCh |
| 386 | + return nil |
| 387 | + } |
| 388 | + t.Errorf("unexpected statement: %s", stmt) |
| 389 | + return errUnexpected |
| 390 | + }, |
| 391 | + } |
| 392 | + |
| 393 | + // Start a "long-running" query on the forbidden database (by the admin user). |
| 394 | + q1, err := influxql.ParseQuery("SELECT * FROM cpu") |
| 395 | + require.NoError(t, err) |
| 396 | + go func() { |
| 397 | + discardOutput(e.ExecuteQuery(q1, query.ExecutionOptions{ |
| 398 | + Database: forbiddenDB, |
| 399 | + UserID: adminUser, |
| 400 | + }, nil)) |
| 401 | + }() |
| 402 | + |
| 403 | + // Start a "long-running" query on the allowed database (by the non-admin user). |
| 404 | + q2, err := influxql.ParseQuery("SELECT * FROM mem") |
| 405 | + require.NoError(t, err) |
| 406 | + go func() { |
| 407 | + discardOutput(e.ExecuteQuery(q2, query.ExecutionOptions{ |
| 408 | + Database: allowedDB, |
| 409 | + UserID: nonAdminUser, |
| 410 | + }, nil)) |
| 411 | + }() |
| 412 | + |
| 413 | + // Give the background queries a moment to register with the TaskManager. |
| 414 | + time.Sleep(50 * time.Millisecond) |
| 415 | + |
| 416 | + // Verify both queries are tracked (sanity check). |
| 417 | + allQueries := e.TaskManager.Queries() |
| 418 | + require.GreaterOrEqual(t, len(allQueries), 2, "expected at least 2 running queries") |
| 419 | + |
| 420 | + // Helper to collect database names from SHOW QUERIES result rows. |
| 421 | + databases := func(rows [][]interface{}) []string { |
| 422 | + var dbs []string |
| 423 | + for _, row := range rows { |
| 424 | + if db, ok := row[dbColumn].(string); ok && db != "" { |
| 425 | + dbs = append(dbs, db) |
| 426 | + } |
| 427 | + } |
| 428 | + return dbs |
| 429 | + } |
| 430 | + |
| 431 | + t.Run("non-admin user only sees allowed databases", func(t *testing.T) { |
| 432 | + showQ, err := influxql.ParseQuery("SHOW QUERIES") |
| 433 | + require.NoError(t, err) |
| 434 | + |
| 435 | + nonAdminUserInfo := &meta.UserInfo{ |
| 436 | + Name: nonAdminUser, |
| 437 | + Admin: false, |
| 438 | + Privileges: map[string]influxql.Privilege{ |
| 439 | + allowedDB: influxql.AllPrivileges, |
| 440 | + }, |
| 441 | + } |
| 442 | + |
| 443 | + results := e.ExecuteQuery(showQ, query.ExecutionOptions{ |
| 444 | + UserID: nonAdminUser, |
| 445 | + CoarseAuthorizer: nonAdminUserInfo, |
| 446 | + }, nil) |
| 447 | + result := <-results |
| 448 | + require.NoError(t, result.Err) |
| 449 | + require.Len(t, result.Series, 1) |
| 450 | + |
| 451 | + dbs := databases(result.Series[0].Values) |
| 452 | + require.Contains(t, dbs, allowedDB, "non-admin user should see queries on databases they have read access to") |
| 453 | + require.NotContains(t, dbs, forbiddenDB, "non-admin user should not see queries on databases they lack read access to") |
| 454 | + }) |
| 455 | + |
| 456 | + t.Run("admin user sees all databases", func(t *testing.T) { |
| 457 | + showQ, err := influxql.ParseQuery("SHOW QUERIES") |
| 458 | + require.NoError(t, err) |
| 459 | + |
| 460 | + adminUserInfo := &meta.UserInfo{ |
| 461 | + Name: adminUser, |
| 462 | + Admin: true, |
| 463 | + } |
| 464 | + |
| 465 | + results := e.ExecuteQuery(showQ, query.ExecutionOptions{ |
| 466 | + UserID: adminUser, |
| 467 | + CoarseAuthorizer: adminUserInfo, |
| 468 | + }, nil) |
| 469 | + result := <-results |
| 470 | + require.NoError(t, result.Err) |
| 471 | + require.Len(t, result.Series, 1) |
| 472 | + |
| 473 | + dbs := databases(result.Series[0].Values) |
| 474 | + require.Contains(t, dbs, allowedDB, "admin user should see all queries") |
| 475 | + require.Contains(t, dbs, forbiddenDB, "admin user should see all queries") |
| 476 | + }) |
| 477 | +} |
| 478 | + |
357 | 479 | func TestQueryExecutor_Limit_Timeout(t *testing.T) { |
358 | 480 | q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`) |
359 | 481 | if err != nil { |
|
0 commit comments