Skip to content

Commit 7057256

Browse files
souravcrlclaude
andcommitted
sql: add VIEWEVENTLOG system privilege for event log visibility
Previously, viewing the event log required `admin` or `VIEWCLUSTERMETADATA`, both of which are overprivileged for users who only need to read cluster events (e.g. SREs and auditors correlating events during incidents). This change introduces a new `VIEWEVENTLOG` system privilege that grants read-only access to `system.eventlog`. The gRPC Events API now accepts either `VIEWEVENTLOG` or the existing `VIEWCLUSTERMETADATA` privilege, and the error message mentions both options. The privilege also grants implicit `SELECT` on `system.eventlog` so the DB Console SQL-based events page works correctly. Non-admin users can now be granted event log visibility via: ```sql GRANT SYSTEM VIEWEVENTLOG TO <user>; ``` Fixes: #169421 Epic: none Release note (sql change): Added a new `VIEWEVENTLOG` system privilege that grants read-only access to the event log. Non-admin users can be granted event log visibility via `GRANT SYSTEM VIEWEVENTLOG TO <user>` without needing `VIEWCLUSTERMETADATA` or the `admin` role. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 201d80a commit 7057256

7 files changed

Lines changed: 136 additions & 18 deletions

File tree

pkg/server/admin.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,11 +1478,29 @@ func (s *adminServer) Events(
14781478
) (_ *serverpb.EventsResponse, retErr error) {
14791479
ctx = s.AnnotateCtx(ctx)
14801480

1481-
err := s.privilegeChecker.RequireViewClusterMetadataPermission(ctx)
1481+
// Accept either VIEWEVENTLOG or VIEWCLUSTERMETADATA.
1482+
userName, isAdmin, err := s.privilegeChecker.GetUserAndRole(ctx)
14821483
if err != nil {
1483-
// NB: not using srverrors.ServerError() here since the priv checker
1484-
// already returns a proper gRPC error status.
1485-
return nil, err
1484+
return nil, srverrors.ServerError(ctx, err)
1485+
}
1486+
if !isAdmin {
1487+
hasEventLog, err := s.privilegeChecker.HasGlobalPrivilege(ctx, userName, privilege.VIEWEVENTLOG)
1488+
if err != nil {
1489+
return nil, srverrors.ServerError(ctx, err)
1490+
}
1491+
if !hasEventLog {
1492+
hasClusterMeta, err := s.privilegeChecker.HasGlobalPrivilege(ctx, userName, privilege.VIEWCLUSTERMETADATA)
1493+
if err != nil {
1494+
return nil, srverrors.ServerError(ctx, err)
1495+
}
1496+
if !hasClusterMeta {
1497+
return nil, grpcstatus.Errorf(
1498+
codes.PermissionDenied,
1499+
"this operation requires the %s or %s system privilege",
1500+
privilege.VIEWEVENTLOG.DisplayName(),
1501+
privilege.VIEWCLUSTERMETADATA.DisplayName())
1502+
}
1503+
}
14861504
}
14871505
redactEvents := !req.UnredactedEvents
14881506

@@ -1491,10 +1509,6 @@ func (s *adminServer) Events(
14911509
limit = apiconstants.DefaultAPIEventLimit
14921510
}
14931511

1494-
userName, err := authserver.UserFromIncomingRPCContext(ctx)
1495-
if err != nil {
1496-
return nil, srverrors.ServerError(ctx, err)
1497-
}
14981512
r, err := s.eventsHelper(ctx, req, userName, int(limit), 0, redactEvents)
14991513
if err != nil {
15001514
return nil, srverrors.ServerError(ctx, err)

pkg/sql/authorization.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,18 @@ func (p *planner) CheckPrivilegeForUser(
286286
return nil
287287
}
288288
}
289+
// VIEWEVENTLOG grants implicit SELECT on system.eventlog.
290+
if tableID == keys.EventLogTableID {
291+
hasViewEventLog, err := p.HasPrivilege(
292+
ctx, syntheticprivilege.GlobalPrivilegeObject, privilege.VIEWEVENTLOG, user,
293+
)
294+
if err != nil {
295+
return err
296+
}
297+
if hasViewEventLog {
298+
return nil
299+
}
300+
}
289301
}
290302
}
291303
return insufficientPrivilegeError(user, privilegeKind, privilegeObject)

pkg/sql/delegate/show_schedules.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,22 @@ func (d *delegator) delegateShowSchedules(n *tree.ShowSchedules) (tree.Statement
2626
// delegated query would fail with "does not have SELECT privilege on
2727
// relation scheduled_jobs", which doesn't tell the user what privilege
2828
// they actually need. The authorization.go layer grants implicit SELECT
29-
// on the underlying system tables when VIEWSCHEDULE is held.
30-
if err := d.catalog.CheckPrivilege(
31-
d.ctx, syntheticprivilege.GlobalPrivilegeObject,
32-
d.catalog.GetCurrentUser(), privilege.VIEWSCHEDULE,
33-
); err != nil {
34-
if pgerror.GetPGCode(err) == pgcode.InsufficientPrivilege {
29+
// on the underlying system tables when VIEWSCHEDULE or VIEWSYSTEMTABLE
30+
// is held, so we let those users through.
31+
cat := d.catalog
32+
user := cat.GetCurrentUser()
33+
hasViewSchedule := cat.CheckPrivilege(
34+
d.ctx, syntheticprivilege.GlobalPrivilegeObject, user, privilege.VIEWSCHEDULE,
35+
) == nil
36+
if !hasViewSchedule {
37+
hasViewSystemTable := cat.CheckPrivilege(
38+
d.ctx, syntheticprivilege.GlobalPrivilegeObject, user, privilege.VIEWSYSTEMTABLE,
39+
) == nil
40+
if !hasViewSystemTable {
3541
return nil, pgerror.Newf(pgcode.InsufficientPrivilege,
3642
"user %s does not have %s system privilege",
37-
d.catalog.GetCurrentUser(), privilege.VIEWSCHEDULE)
43+
user, privilege.VIEWSCHEDULE)
3844
}
39-
return nil, err
4045
}
4146

4247
sqltelemetry.IncrementShowCounter(sqltelemetry.Schedules)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# LogicTest: local
2+
3+
# Verify that the VIEWEVENTLOG privilege grants access to system.eventlog.
4+
5+
# testuser should not be able to read system.eventlog.
6+
user testuser
7+
8+
statement error user testuser does not have SELECT privilege on relation eventlog
9+
SELECT * FROM system.eventlog LIMIT 0
10+
11+
user root
12+
13+
# Grant VIEWEVENTLOG to testuser.
14+
statement ok
15+
GRANT SYSTEM VIEWEVENTLOG TO testuser
16+
17+
user testuser
18+
19+
# testuser should now be able to read system.eventlog.
20+
statement ok
21+
SELECT * FROM system.eventlog LIMIT 0
22+
23+
user root
24+
25+
# Revoke VIEWEVENTLOG from testuser.
26+
statement ok
27+
REVOKE SYSTEM VIEWEVENTLOG FROM testuser
28+
29+
user testuser
30+
31+
# testuser should no longer be able to read system.eventlog.
32+
statement error user testuser does not have SELECT privilege on relation eventlog
33+
SELECT * FROM system.eventlog LIMIT 0
34+
35+
user root
36+
37+
# Test that inheriting VIEWEVENTLOG through a role works.
38+
statement ok
39+
CREATE ROLE eventlogviewer
40+
41+
statement ok
42+
GRANT SYSTEM VIEWEVENTLOG TO eventlogviewer
43+
44+
statement ok
45+
GRANT eventlogviewer TO testuser
46+
47+
user testuser
48+
49+
# testuser should be able to read system.eventlog through role membership.
50+
statement ok
51+
SELECT * FROM system.eventlog LIMIT 0
52+
53+
user root
54+
55+
statement ok
56+
REVOKE eventlogviewer FROM testuser
57+
58+
user testuser
59+
60+
# testuser should no longer be able to read system.eventlog.
61+
statement error user testuser does not have SELECT privilege on relation eventlog
62+
SELECT * FROM system.eventlog LIMIT 0

pkg/sql/logictest/testdata/logic_test/show_schedules_privilege

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,25 @@ user testuser
6060
# testuser should no longer be able to SHOW SCHEDULES.
6161
statement error user testuser does not have VIEWSCHEDULE system privilege
6262
SHOW SCHEDULES
63+
64+
user root
65+
66+
# Test that VIEWSYSTEMTABLE also grants access to SHOW SCHEDULES (no
67+
# regression for users who previously relied on this broader privilege).
68+
statement ok
69+
GRANT SYSTEM VIEWSYSTEMTABLE TO testuser
70+
71+
user testuser
72+
73+
statement ok
74+
SHOW SCHEDULES
75+
76+
user root
77+
78+
statement ok
79+
REVOKE SYSTEM VIEWSYSTEMTABLE FROM testuser
80+
81+
user testuser
82+
83+
statement error user testuser does not have VIEWSCHEDULE system privilege
84+
SHOW SCHEDULES

pkg/sql/privilege/kind.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ const (
7474
MAINTAIN Kind = 43
7575
TEMPORARY Kind = 44
7676
VIEWSCHEDULE Kind = 45
77-
largestKind = VIEWSCHEDULE
77+
VIEWEVENTLOG Kind = 46
78+
largestKind = VIEWEVENTLOG
7879

7980
// RULE, SET, and ALTERSYSTEM are PostgreSQL ACL-only pseudo-privileges.
8081
// They exist solely for ACL character mapping used by acldefault, aclexplode,
@@ -190,6 +191,8 @@ func (k Kind) InternalKey() KindInternalKey {
190191
return "TEMPORARY"
191192
case VIEWSCHEDULE:
192193
return "VIEWSCHEDULE"
194+
case VIEWEVENTLOG:
195+
return "VIEWEVENTLOG"
193196
case SET:
194197
return "SET"
195198
case ALTERSYSTEM:

pkg/sql/privilege/privilege.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ var (
101101
ALL, BACKUP, RESTORE, MODIFYCLUSTERSETTING, EXTERNALCONNECTION, VIEWACTIVITY, VIEWACTIVITYREDACTED,
102102
VIEWCLUSTERSETTING, CANCELQUERY, NOSQLLOGIN, VIEWCLUSTERMETADATA, VIEWDEBUG, EXTERNALIOIMPLICITACCESS, VIEWJOB,
103103
MODIFYSQLCLUSTERSETTING, REPLICATION, MANAGEVIRTUALCLUSTER, VIEWSYSTEMTABLE, CREATEROLE, CREATELOGIN, CREATEDB, CONTROLJOB,
104-
REPAIRCLUSTER, BYPASSRLS, REPLICATIONDEST, REPLICATIONSOURCE, INSPECT, VIEWSCHEDULE,
104+
REPAIRCLUSTER, BYPASSRLS, REPLICATIONDEST, REPLICATIONSOURCE, INSPECT, VIEWSCHEDULE, VIEWEVENTLOG,
105105
}
106106
VirtualTablePrivileges = List{ALL, SELECT}
107107
ExternalConnectionPrivileges = List{ALL, USAGE, DROP, UPDATE}

0 commit comments

Comments
 (0)