Skip to content

Commit ff23cba

Browse files
craig[bot]dt
andcommitted
Merge #152853
152853: server: lookup job messages as node r=dt a=dt Direct system table access as non-superusers is generally not allowed. The messages are only fetched after a 'row != nil' check on the query to lookup the job itself, which does permission-checks and filters its results; if we got past that check, the user has access to the job, so we should fetch the job's messages for them -- and do so as node, so we can access the table in which they're stored. Release note (ui change): the log of messages and events recorded by a job is now shown on the job page for non-admins. Epic: none. Fixes #152831. Co-authored-by: David Taylor <davidt@davidt.io>
2 parents 01a8e6a + c2ece87 commit ff23cba

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

pkg/server/admin.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2449,12 +2449,15 @@ func jobHelper(
24492449
return &job, nil
24502450
}
24512451

2452+
// fetchJobMessages retrieves the messages associated with a job using the node
2453+
// user; its results should only be shown to a user already known to have access
2454+
// to the passed job.
24522455
func fetchJobMessages(
24532456
ctx context.Context, jobID int64, user username.SQLUsername, sqlServer *SQLServer,
24542457
) (messages []serverpb.JobMessage) {
24552458
const msgQuery = `SELECT kind, written, message FROM system.job_message WHERE job_id = $1 ORDER BY written DESC`
24562459
it, err := sqlServer.internalExecutor.QueryIteratorEx(ctx, "admin-job-messages", nil,
2457-
sessiondata.InternalExecutorOverride{User: user},
2460+
sessiondata.NodeUserSessionDataOverride, // We are post-priv check and sys table requires node.
24582461
msgQuery,
24592462
jobID,
24602463
)

pkg/server/application_api/jobs_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,66 @@ func TestJobStatusResponse(t *testing.T) {
431431
require.Equal(t, job.Payload(), *response.Job.Payload)
432432
require.Equal(t, job.Progress(), *response.Job.Progress)
433433
}
434+
435+
func TestJobMessagesAccess(t *testing.T) {
436+
defer leaktest.AfterTest(t)()
437+
defer log.Scope(t).Close(t)
438+
439+
s, conn, _ := serverutils.StartServer(t, base.TestServerArgs{})
440+
defer s.Stopper().Stop(context.Background())
441+
sqlDB := sqlutils.MakeSQLRunner(conn)
442+
443+
testCases := []struct {
444+
jobID int64
445+
owner username.SQLUsername
446+
isAdmin bool
447+
expectSuccess bool
448+
expectMsgCnt int
449+
}{
450+
{1000, apiconstants.TestingUserNameNoAdmin(), true, true, 2}, // admin can see non-admin job + messages
451+
{1001, apiconstants.TestingUserNameNoAdmin(), false, true, 2}, // non-admin can see own job + messages
452+
{1002, username.RootUserName(), false, false, 0}, // non-admin cannot see non-owned job
453+
}
454+
455+
for _, tc := range testCases {
456+
t.Run(fmt.Sprintf("jobID=%d/owner=%s/isAdmin=%t", tc.jobID, tc.owner.Normalized(), tc.isAdmin), func(t *testing.T) {
457+
// Create job and messages
458+
payload := jobspb.Payload{
459+
UsernameProto: tc.owner.EncodeProto(),
460+
Details: jobspb.WrapPayloadDetails(jobspb.BackupDetails{}),
461+
}
462+
payloadBytes, err := protoutil.Marshal(&payload)
463+
require.NoError(t, err)
464+
465+
progress := jobspb.Progress{
466+
Details: jobspb.WrapProgressDetails(jobspb.BackupProgress{}),
467+
Progress: &jobspb.Progress_FractionCompleted{FractionCompleted: 1.0},
468+
}
469+
progressBytes, err := protoutil.Marshal(&progress)
470+
require.NoError(t, err)
471+
472+
sqlDB.Exec(t, `INSERT INTO system.jobs (id, status, job_type, owner) VALUES ($1, $2, $3, $4)`,
473+
tc.jobID, jobs.StateSucceeded, payload.Type().String(), tc.owner.Normalized())
474+
sqlDB.Exec(t, `INSERT INTO system.job_info (job_id, info_key, value) VALUES ($1, $2, $3)`,
475+
tc.jobID, jobs.GetLegacyPayloadKey(), payloadBytes)
476+
sqlDB.Exec(t, `INSERT INTO system.job_info (job_id, info_key, value) VALUES ($1, $2, $3)`,
477+
tc.jobID, jobs.GetLegacyProgressKey(), progressBytes)
478+
sqlDB.Exec(t, `INSERT INTO system.job_message (job_id, kind, written, message) VALUES ($1, $2, now(), $3)`,
479+
tc.jobID, "info", "Job started successfully")
480+
sqlDB.Exec(t, `INSERT INTO system.job_message (job_id, kind, written, message) VALUES ($1, $2, now(), $3)`,
481+
tc.jobID, "warning", "Minor issue encountered")
482+
483+
// Test access
484+
var res serverpb.JobResponse
485+
err = srvtestutils.GetAdminJSONProtoWithAdminOption(s, fmt.Sprintf("jobs/%d", tc.jobID), &res, tc.isAdmin)
486+
487+
if tc.expectSuccess {
488+
require.NoError(t, err)
489+
require.Equal(t, tc.jobID, res.ID)
490+
require.Len(t, res.Messages, tc.expectMsgCnt)
491+
} else {
492+
require.Error(t, err)
493+
}
494+
})
495+
}
496+
}

0 commit comments

Comments
 (0)