-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sql,backupccl: allow owners to also control schedules #87287
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ import ( | |
"github.com/cockroachdb/cockroach/pkg/scheduledjobs" | ||
"github.com/cockroachdb/cockroach/pkg/security/username" | ||
"github.com/cockroachdb/cockroach/pkg/server/telemetry" | ||
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" | ||
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sessiondata" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" | ||
|
@@ -57,19 +59,22 @@ func JobSchedulerEnv(execCfg *ExecutorConfig) scheduledjobs.JobSchedulerEnv { | |
return scheduledjobs.ProdJobSchedulerEnv | ||
} | ||
|
||
// loadSchedule loads schedule information. | ||
// loadSchedule loads schedule information as the node user. | ||
func loadSchedule(params runParams, scheduleID tree.Datum) (*jobs.ScheduledJob, error) { | ||
env := JobSchedulerEnv(params.ExecCfg()) | ||
schedule := jobs.NewScheduledJob(env) | ||
|
||
// Load schedule expression. This is needed for resume command, but we | ||
// also use this query to check for the schedule existence. | ||
// | ||
// Run the query as the node user since we perform our own privilege checks | ||
// before using the returned schedule. | ||
datums, cols, err := params.ExecCfg().InternalExecutor.QueryRowExWithCols( | ||
params.ctx, | ||
"load-schedule", | ||
params.p.Txn(), sessiondata.InternalExecutorOverride{User: username.RootUserName()}, | ||
params.p.Txn(), sessiondata.InternalExecutorOverride{User: username.NodeUserName()}, | ||
fmt.Sprintf( | ||
"SELECT schedule_id, next_run, schedule_expr, executor_type, execution_args FROM %s WHERE schedule_id = $1", | ||
"SELECT schedule_id, next_run, schedule_expr, executor_type, execution_args, owner FROM %s WHERE schedule_id = $1", | ||
env.ScheduledJobsTableName(), | ||
), | ||
scheduleID) | ||
|
@@ -136,6 +141,16 @@ func (n *controlSchedulesNode) startExec(params runParams) error { | |
continue // not an error if schedule does not exist | ||
} | ||
|
||
isAdmin, err := params.p.UserHasAdminRole(params.ctx, params.p.User()) | ||
if err != nil { | ||
return err | ||
} | ||
isOwner := schedule.Owner() == params.p.User() | ||
if !isAdmin && !isOwner { | ||
return pgerror.Newf(pgcode.InsufficientPrivilege, "must be admin or owner of the "+ | ||
"schedule %d to %s it", schedule.ScheduleID(), n.command.String()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤷 it makes for a more informative error message that you're not allowed to |
||
} | ||
|
||
switch n.command { | ||
case tree.PauseSchedule: | ||
schedule.Pause() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
happy to support this, but why? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that
NodeUser
is what we should be using for intra-cluster traffic -cockroach/pkg/security/username/username.go
Line 67 in bf75f1d
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be running on its own behalf? Or should loadSchedule take a context and run as the user executing the query?
More concretely, I guess, can we imagine a world where we want a user to see/load some schedules but not others? (yes, I think?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be tricky because only
admin
is allowed to select against a system table. So a non-admin owner would get an error runningloadSchedule
. The scan of the system table is an internal implementation detail that expects other pieces of the logic to perform the appropriate privilege checks.Yes, I think so, but "load" is an internal implementation detail so I think the filtering needs to be in the logic that uses the loaded schedule, which is what we're doing here. We are already allowing users to only alter a limited set of schedules if they are non-admin, i.e. the schedules they own. Similarly if we want to limit what schedules a user sees we should be adding this privilege check to
SHOW SCHEDULES
and other user facing queries that allow for introspection.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, exactly this - I'd worry about a developer forgetting that. It's not obvious to me that somebody calling something called
loadSchedule()
will be executing node-level privileges under the hood. That said, if it's obvious to you (i.e. common practice :) ) then I'm fine with it.