Skip to content

Commit 139c738

Browse files
sql: add e2e tests and fix privilege bug for DROP PROVISIONED ROLES (#167003)
sql: add e2e tests and fix privilege bug for DROP PROVISIONED ROLES
2 parents a081a00 + 4416f36 commit 139c738

4 files changed

Lines changed: 1007 additions & 11 deletions

File tree

pkg/sql/drop_provisioned_roles.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,16 @@ func (n *DropProvisionedRolesNode) startExec(params runParams) error {
129129
}
130130
query, queryArgs := buildProvisionedRolesQuery(sourceVal, lastLoginVal, n.limitCount)
131131

132+
// Use NodeUserSessionDataOverride because the authorization check
133+
// (CREATEROLE) already happened at plan time. A non-admin CREATEROLE
134+
// user does not have SELECT on system.users, so the query must run
135+
// with elevated privileges. The query is hardcoded with only
136+
// parameterized filter values — no user-controlled SQL expressions.
132137
rows, err := params.p.InternalSQLTxn().QueryBufferedEx(
133138
params.ctx,
134139
"drop-provisioned-roles-find",
135140
params.p.txn,
136-
sessiondata.InternalExecutorOverride{User: params.p.User()},
141+
sessiondata.NodeUserSessionDataOverride,
137142
query,
138143
queryArgs...,
139144
)
@@ -149,10 +154,11 @@ func (n *DropProvisionedRolesNode) startExec(params runParams) error {
149154
return err
150155
}
151156

152-
var numDropped, numSkipped int
153-
var droppedNames []string
154-
var numRoleSettingsRowsDeleted int
155-
157+
// First pass: filter candidates by checking permissions and
158+
// dependencies. Accumulate eligible users into a separate slice
159+
// so that the deletion loop is cleanly separated from the
160+
// validation logic.
161+
var usersToDrop []username.SQLUsername
156162
for _, row := range rows {
157163
normalizedUsername := username.MakeSQLUsernameFromPreNormalizedString(
158164
string(tree.MustBeDString(row[0])),
@@ -179,7 +185,6 @@ func (n *DropProvisionedRolesNode) startExec(params runParams) error {
179185
params.ctx,
180186
pgnotice.Newf("skipping %q: must be superuser to drop superusers", normalizedUsername),
181187
)
182-
numSkipped++
183188
continue
184189
}
185190
}
@@ -195,22 +200,26 @@ func (n *DropProvisionedRolesNode) startExec(params runParams) error {
195200
params.ctx,
196201
pgnotice.Newf("skipping %q: role has dependent objects", normalizedUsername),
197202
)
198-
numSkipped++
199203
continue
200204
}
201205

202-
// Delete the role from all system tables.
206+
usersToDrop = append(usersToDrop, normalizedUsername)
207+
}
208+
209+
// Second pass: delete all eligible users from system tables.
210+
var droppedNames []string
211+
var numRoleSettingsRowsDeleted int
212+
for _, normalizedUsername := range usersToDrop {
203213
deleted, err := n.deleteRole(params, normalizedUsername, opName)
204214
if err != nil {
205215
return err
206216
}
207217
numRoleSettingsRowsDeleted += deleted
208-
numDropped++
209218
droppedNames = append(droppedNames, normalizedUsername.Normalized())
210219
}
211220

212221
// Bump table versions if anything was dropped.
213-
if numDropped > 0 {
222+
if len(droppedNames) > 0 {
214223
if sessioninit.CacheEnabled.Get(&params.p.ExecCfg().Settings.SV) {
215224
if err := params.p.bumpUsersTableVersion(params.ctx); err != nil {
216225
return err
@@ -252,6 +261,10 @@ func (n *DropProvisionedRolesNode) evalFilterExprs(
252261
if err != nil {
253262
return nil, nil, err
254263
}
264+
if d == tree.DNull {
265+
return nil, nil, pgerror.Newf(pgcode.InvalidParameterValue,
266+
"SOURCE filter cannot be NULL")
267+
}
255268
s := string(tree.MustBeDString(d))
256269
sourceVal = &s
257270
}
@@ -260,6 +273,10 @@ func (n *DropProvisionedRolesNode) evalFilterExprs(
260273
if err != nil {
261274
return nil, nil, err
262275
}
276+
if d == tree.DNull {
277+
return nil, nil, pgerror.Newf(pgcode.InvalidParameterValue,
278+
"LAST LOGIN BEFORE filter cannot be NULL")
279+
}
263280
v := tree.MustBeDTimestampTZ(d)
264281
lastLoginVal = &v
265282
}

0 commit comments

Comments
 (0)