Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions pkg/server/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1478,23 +1478,23 @@ func (s *adminServer) Events(
) (_ *serverpb.EventsResponse, retErr error) {
ctx = s.AnnotateCtx(ctx)

err := s.privilegeChecker.RequireViewClusterMetadataPermission(ctx)
if err != nil {
if err := s.privilegeChecker.RequireViewEventLogPermission(ctx); err != nil {
// NB: not using srverrors.ServerError() here since the priv checker
// already returns a proper gRPC error status.
return nil, err
}
redactEvents := !req.UnredactedEvents

userName, err := authserver.UserFromIncomingRPCContext(ctx)
if err != nil {
return nil, srverrors.ServerError(ctx, err)
}

limit := req.Limit
if limit == 0 {
limit = apiconstants.DefaultAPIEventLimit
}

userName, err := authserver.UserFromIncomingRPCContext(ctx)
if err != nil {
return nil, srverrors.ServerError(ctx, err)
}
r, err := s.eventsHelper(ctx, req, userName, int(limit), 0, redactEvents)
if err != nil {
return nil, srverrors.ServerError(ctx, err)
Expand Down
1 change: 1 addition & 0 deletions pkg/server/privchecker/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ go_test(
"//pkg/server",
"//pkg/sql",
"//pkg/sql/isql",
"//pkg/sql/privilege",
"//pkg/testutils/serverutils",
"//pkg/testutils/sqlutils",
"//pkg/testutils/testcluster",
Expand Down
1 change: 1 addition & 0 deletions pkg/server/privchecker/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type CheckerForRPCHandlers interface {
RequireViewClusterMetadataPermission(ctx context.Context) error
RequireRepairClusterPermission(ctx context.Context) error
RequireViewDebugPermission(ctx context.Context) error
RequireViewEventLogPermission(ctx context.Context) error
}

// SQLPrivilegeChecker is the part of the privilege checker that can
Expand Down
27 changes: 27 additions & 0 deletions pkg/server/privchecker/privchecker.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,33 @@ func (c *adminPrivilegeChecker) RequireViewDebugPermission(ctx context.Context)
privilege.VIEWDEBUG.DisplayName())
}

// RequireViewEventLogPermission requires the user have admin or the
// VIEWEVENTLOG or VIEWCLUSTERMETADATA system privilege and returns an
// error if the user does not have any.
func (c *adminPrivilegeChecker) RequireViewEventLogPermission(ctx context.Context) (err error) {
userName, isAdmin, err := c.GetUserAndRole(ctx)
if err != nil {
return srverrors.ServerError(ctx, err)
}
if isAdmin {
return nil
}
if hasViewEventLog, err := c.HasGlobalPrivilege(ctx, userName, privilege.VIEWEVENTLOG); err != nil {
return srverrors.ServerError(ctx, err)
} else if hasViewEventLog {
return nil
}
if hasViewClusterMetadata, err := c.HasGlobalPrivilege(ctx, userName, privilege.VIEWCLUSTERMETADATA); err != nil {
return srverrors.ServerError(ctx, err)
} else if hasViewClusterMetadata {
return nil
}
return grpcstatus.Errorf(
codes.PermissionDenied, "this operation requires the %s or %s system privilege",
privilege.VIEWEVENTLOG.DisplayName(),
privilege.VIEWCLUSTERMETADATA.DisplayName())
}

// GetUserAndRole is part of the CheckerForRPCHandlers interface.
func (c *adminPrivilegeChecker) GetUserAndRole(
ctx context.Context,
Expand Down
34 changes: 34 additions & 0 deletions pkg/server/privchecker/privchecker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/server/privchecker"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/sql/isql"
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
Expand Down Expand Up @@ -56,6 +57,10 @@ func TestAdminPrivilegeChecker(t *testing.T) {
sqlDB.Exec(t, "GRANT SYSTEM VIEWCLUSTERMETADATA TO withviewclustermetadata")
sqlDB.Exec(t, "CREATE USER withviewdebug")
sqlDB.Exec(t, "GRANT SYSTEM VIEWDEBUG TO withviewdebug")
sqlDB.Exec(t, "CREATE USER withviewjob")
sqlDB.Exec(t, "GRANT SYSTEM VIEWJOB TO withviewjob")
sqlDB.Exec(t, "CREATE USER withvieweventlog")
sqlDB.Exec(t, "GRANT SYSTEM VIEWEVENTLOG TO withvieweventlog")

execCfg := ts.ExecutorConfig().(sql.ExecutorConfig)
kvDB := ts.DB()
Expand Down Expand Up @@ -97,6 +102,28 @@ func TestAdminPrivilegeChecker(t *testing.T) {
withVaAndRedactedGlobalPrivilege := username.MakeSQLUsernameFromPreNormalizedString("withvaandredactedglobalprivilege")
withviewclustermetadata := username.MakeSQLUsernameFromPreNormalizedString("withviewclustermetadata")
withViewDebug := username.MakeSQLUsernameFromPreNormalizedString("withviewdebug")
withViewJob := username.MakeSQLUsernameFromPreNormalizedString("withviewjob")
withViewEventLog := username.MakeSQLUsernameFromPreNormalizedString("withvieweventlog")

// Test HasGlobalPrivilege for VIEWJOB and VIEWEVENTLOG.
globalPrivTests := []struct {
name string
privilege privilege.Kind
username username.SQLUsername
wantHas bool
}{
{"viewjob-granted", privilege.VIEWJOB, withViewJob, true},
{"viewjob-not-granted", privilege.VIEWJOB, withoutPrivs, false},
{"vieweventlog-granted", privilege.VIEWEVENTLOG, withViewEventLog, true},
{"vieweventlog-not-granted", privilege.VIEWEVENTLOG, withoutPrivs, false},
}
for _, tt := range globalPrivTests {
t.Run(tt.name, func(t *testing.T) {
has, err := underTest.HasGlobalPrivilege(ctx, tt.username, tt.privilege)
require.NoError(t, err)
require.Equal(t, tt.wantHas, has)
})
}

tests := []struct {
name string
Expand Down Expand Up @@ -141,6 +168,13 @@ func TestAdminPrivilegeChecker(t *testing.T) {
withAdmin: false, withoutPrivs: true, withViewDebug: false,
},
},
{
"requireViewEventLogPermission",
underTest.RequireViewEventLogPermission,
map[username.SQLUsername]bool{
withAdmin: false, withoutPrivs: true, withViewEventLog: false, withviewclustermetadata: false,
},
},
}

for _, tt := range tests {
Expand Down
26 changes: 26 additions & 0 deletions pkg/sql/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,32 @@ func (p *planner) CheckPrivilegeForUser(
if hasViewSystemTablePriv {
return nil
}
// VIEWJOB grants implicit SELECT on system.scheduled_jobs and
// system.jobs, the two tables underlying SHOW SCHEDULES.
tableID := d.GetID()
if tableID == keys.ScheduledJobsTableID || tableID == keys.JobsTableID {
hasViewJob, err := p.HasPrivilege(
ctx, syntheticprivilege.GlobalPrivilegeObject, privilege.VIEWJOB, user,
)
if err != nil {
return err
}
if hasViewJob {
return nil
}
}
// VIEWEVENTLOG grants implicit SELECT on system.eventlog.
if tableID == keys.EventLogTableID {
hasViewEventLog, err := p.HasPrivilege(
ctx, syntheticprivilege.GlobalPrivilegeObject, privilege.VIEWEVENTLOG, user,
)
if err != nil {
return err
}
if hasViewEventLog {
return nil
}
}
}
}
return insufficientPrivilegeError(user, privilegeKind, privilegeObject)
Expand Down
39 changes: 39 additions & 0 deletions pkg/sql/delegate/show_schedules.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,53 @@ import (
"strings"

"github.com/cockroachdb/cockroach/pkg/jobs"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
"github.com/cockroachdb/cockroach/pkg/sql/syntheticprivilege"
)

// commandColumn converts executor execution arguments into jsonb representation.
const commandColumn = `crdb_internal.pb_to_json('cockroach.jobs.jobspb.ExecutionArguments', execution_args, false, true)->'args'`

func (d *delegator) delegateShowSchedules(n *tree.ShowSchedules) (tree.Statement, error) {
// Provide a clear privilege error upfront. Without this check, the
// delegated query would fail with "does not have SELECT privilege on
// relation scheduled_jobs", which doesn't tell the user what privilege
// they actually need. The authorization.go layer grants implicit SELECT
// on the underlying system tables when VIEWJOB or VIEWSYSTEMTABLE
// is held, so we let those users through.
cat := d.catalog
user := cat.GetCurrentUser()
globalPrivObj := syntheticprivilege.GlobalPrivilegeObject

// processPrivError returns nil if the error is an InsufficientPrivilege
// error (meaning the user simply lacks the privilege), or the original
// error for any unexpected failure.
processPrivError := func(err error) error {
if pgerror.GetPGCode(err) == pgcode.InsufficientPrivilege {
return nil
}
return err
}

if err := cat.CheckPrivilege(d.ctx, globalPrivObj, user, privilege.VIEWJOB); err != nil {
if unexpectedErr := processPrivError(err); unexpectedErr != nil {
return nil, unexpectedErr
}
// User lacks VIEWJOB; check VIEWSYSTEMTABLE as a fallback.
if err := cat.CheckPrivilege(d.ctx, globalPrivObj, user, privilege.VIEWSYSTEMTABLE); err != nil {
if unexpectedErr := processPrivError(err); unexpectedErr != nil {
return nil, unexpectedErr
}
return nil, pgerror.Newf(pgcode.InsufficientPrivilege,
"user %s does not have %s system privilege",
user, privilege.VIEWJOB)
}
}

sqltelemetry.IncrementShowCounter(sqltelemetry.Schedules)

columnExprs := []string{
Expand Down
62 changes: 62 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/show_eventlog_privilege
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# LogicTest: local

# Verify that the VIEWEVENTLOG privilege grants access to system.eventlog.

# testuser should not be able to read system.eventlog.
user testuser

statement error user testuser does not have SELECT privilege on relation eventlog
SELECT * FROM system.eventlog LIMIT 0

user root

# Grant VIEWEVENTLOG to testuser.
statement ok
GRANT SYSTEM VIEWEVENTLOG TO testuser

user testuser

# testuser should now be able to read system.eventlog.
statement ok
SELECT * FROM system.eventlog LIMIT 0

user root

# Revoke VIEWEVENTLOG from testuser.
statement ok
REVOKE SYSTEM VIEWEVENTLOG FROM testuser

user testuser

# testuser should no longer be able to read system.eventlog.
statement error user testuser does not have SELECT privilege on relation eventlog
SELECT * FROM system.eventlog LIMIT 0

user root

# Test that inheriting VIEWEVENTLOG through a role works.
statement ok
CREATE ROLE eventlogviewer

statement ok
GRANT SYSTEM VIEWEVENTLOG TO eventlogviewer

statement ok
GRANT eventlogviewer TO testuser

user testuser

# testuser should be able to read system.eventlog through role membership.
statement ok
SELECT * FROM system.eventlog LIMIT 0

user root

statement ok
REVOKE eventlogviewer FROM testuser

user testuser

# testuser should no longer be able to read system.eventlog.
statement error user testuser does not have SELECT privilege on relation eventlog
SELECT * FROM system.eventlog LIMIT 0
84 changes: 84 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/show_schedules_privilege
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# LogicTest: local

# Verify that SHOW SCHEDULES requires the VIEWJOB privilege.

# testuser should not be able to SHOW SCHEDULES.
user testuser

statement error user testuser does not have VIEWJOB system privilege
SHOW SCHEDULES

user root

# Grant VIEWJOB to testuser.
statement ok
GRANT SYSTEM VIEWJOB TO testuser

user testuser

# testuser should now be able to SHOW SCHEDULES.
statement ok
SHOW SCHEDULES

user root

# Revoke VIEWJOB from testuser.
statement ok
REVOKE SYSTEM VIEWJOB FROM testuser

user testuser

# testuser should no longer be able to SHOW SCHEDULES.
statement error user testuser does not have VIEWJOB system privilege
SHOW SCHEDULES

user root

# Test that inheriting VIEWJOB through a role works.
statement ok
CREATE ROLE scheduleviewer

statement ok
GRANT SYSTEM VIEWJOB TO scheduleviewer

statement ok
GRANT scheduleviewer TO testuser

user testuser

# testuser should be able to SHOW SCHEDULES through role membership.
statement ok
SHOW SCHEDULES

user root

statement ok
REVOKE scheduleviewer FROM testuser

user testuser

# testuser should no longer be able to SHOW SCHEDULES.
statement error user testuser does not have VIEWJOB system privilege
SHOW SCHEDULES

user root

# Test that VIEWSYSTEMTABLE also grants access to SHOW SCHEDULES (no
# regression for users who previously relied on this broader privilege).
statement ok
GRANT SYSTEM VIEWSYSTEMTABLE TO testuser

user testuser

statement ok
SHOW SCHEDULES

user root

statement ok
REVOKE SYSTEM VIEWSYSTEMTABLE FROM testuser

user testuser

statement error user testuser does not have VIEWJOB system privilege
SHOW SCHEDULES
Loading
Loading