Skip to content

Commit 8c883e3

Browse files
authored
Implement REVOKE ALL PRIVILEGES, GRANT OPTION ... (#2988)
1 parent 8ba9a26 commit 8c883e3

File tree

9 files changed

+183
-127
lines changed

9 files changed

+183
-127
lines changed

enginetest/queries/priv_auth_queries.go

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,27 @@ var UserPrivTests = []UserPrivilegeTest{
12301230
Query: "SELECT User, Host, Select_priv FROM mysql.user WHERE User = 'tester';",
12311231
Expected: []sql.Row{{"tester", "localhost", "N"}},
12321232
},
1233+
1234+
{
1235+
// Re-revoking does nothing
1236+
User: "root",
1237+
Host: "localhost",
1238+
Query: "REVOKE SELECT ON *.* FROM tester@localhost;",
1239+
Expected: []sql.Row{{types.NewOkResult(0)}},
1240+
},
1241+
{
1242+
// IF EXISTS option does nothing
1243+
User: "root",
1244+
Host: "localhost",
1245+
Query: "REVOKE IF EXISTS SELECT ON *.* FROM tester@localhost;",
1246+
Expected: []sql.Row{{types.NewOkResult(0)}},
1247+
},
1248+
{
1249+
User: "root",
1250+
Host: "localhost",
1251+
Query: "SELECT User, Host, Select_priv FROM mysql.user WHERE User = 'tester';",
1252+
Expected: []sql.Row{{"tester", "localhost", "N"}},
1253+
},
12331254
},
12341255
},
12351256
{
@@ -1238,7 +1259,11 @@ var UserPrivTests = []UserPrivilegeTest{
12381259
"CREATE TABLE test (pk BIGINT PRIMARY KEY);",
12391260
"INSERT INTO test VALUES (1), (2), (3);",
12401261
"CREATE USER tester@localhost;",
1262+
"CREATE USER tester1@localhost;",
1263+
"CREATE USER tester2@localhost;",
12411264
"GRANT ALL ON *.* TO tester@localhost;",
1265+
"GRANT ALL ON *.* TO tester1@localhost WITH GRANT OPTION;",
1266+
"GRANT ALL ON *.* TO tester2@localhost WITH GRANT OPTION;",
12421267
},
12431268
Assertions: []UserPrivilegeTestAssertion{
12441269
{
@@ -1280,8 +1305,79 @@ var UserPrivTests = []UserPrivilegeTest{
12801305
{
12811306
User: "root",
12821307
Host: "localhost",
1283-
Query: "SELECT User, Host, Select_priv, Insert_priv FROM mysql.user WHERE User = 'tester';",
1284-
Expected: []sql.Row{{"tester", "localhost", "N", "N"}},
1308+
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester';",
1309+
Expected: []sql.Row{{"tester", "localhost", "N", "N", "N"}},
1310+
},
1311+
1312+
{
1313+
User: "root",
1314+
Host: "localhost",
1315+
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester1';",
1316+
Expected: []sql.Row{{"tester1", "localhost", "Y", "Y", "Y"}},
1317+
},
1318+
{
1319+
User: "root",
1320+
Host: "localhost",
1321+
Query: "REVOKE ALL, GRANT OPTION FROM tester1@localhost;",
1322+
Expected: []sql.Row{{types.NewOkResult(0)}},
1323+
},
1324+
{
1325+
User: "root",
1326+
Host: "localhost",
1327+
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester1';",
1328+
Expected: []sql.Row{{"tester1", "localhost", "N", "N", "N"}},
1329+
},
1330+
1331+
{
1332+
User: "root",
1333+
Host: "localhost",
1334+
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester2';",
1335+
Expected: []sql.Row{{"tester2", "localhost", "Y", "Y", "Y"}},
1336+
},
1337+
{
1338+
User: "root",
1339+
Host: "localhost",
1340+
Query: "REVOKE ALL PRIVILEGES, GRANT OPTION FROM tester2@localhost;",
1341+
Expected: []sql.Row{{types.NewOkResult(0)}},
1342+
},
1343+
{
1344+
User: "root",
1345+
Host: "localhost",
1346+
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester2';",
1347+
Expected: []sql.Row{{"tester2", "localhost", "N", "N", "N"}},
1348+
},
1349+
{
1350+
// Re-revoking does nothing
1351+
User: "root",
1352+
Host: "localhost",
1353+
Query: "REVOKE ALL PRIVILEGES, GRANT OPTION FROM tester2@localhost;",
1354+
Expected: []sql.Row{{types.NewOkResult(0)}},
1355+
},
1356+
{
1357+
// IF EXISTS does nothing
1358+
User: "root",
1359+
Host: "localhost",
1360+
Query: "REVOKE IF EXISTS ALL PRIVILEGES, GRANT OPTION FROM tester2@localhost;",
1361+
Expected: []sql.Row{{types.NewOkResult(0)}},
1362+
},
1363+
{
1364+
User: "root",
1365+
Host: "localhost",
1366+
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester2';",
1367+
Expected: []sql.Row{{"tester2", "localhost", "N", "N", "N"}},
1368+
},
1369+
{
1370+
User: "root",
1371+
Host: "localhost",
1372+
Query: "REVOKE IF EXISTS ALL PRIVILEGES, GRANT OPTION FROM fake1@localhost, fake2@localhost, fake3@localhost;",
1373+
ExpectedErr: sql.ErrRevokeUserDoesNotExist,
1374+
},
1375+
{
1376+
// TODO: check warnings
1377+
User: "root",
1378+
Host: "localhost",
1379+
Query: "REVOKE IF EXISTS ALL PRIVILEGES, GRANT OPTION FROM fake1@localhost, fake2@localhost, fake3@localhost IGNORE UNKNOWN USER;",
1380+
Expected: []sql.Row{{types.NewOkResult(0)}},
12851381
},
12861382
},
12871383
},
@@ -1402,6 +1498,32 @@ var UserPrivTests = []UserPrivilegeTest{
14021498
Query: "SELECT COUNT(*) FROM mysql.user WHERE User = 'tester';",
14031499
Expected: []sql.Row{{1}},
14041500
},
1501+
{
1502+
User: "root",
1503+
Host: "localhost",
1504+
Query: "REVOKE fake_role FROM tester@localhost;",
1505+
ExpectedErr: sql.ErrGrantRevokeRoleDoesNotExist,
1506+
},
1507+
{
1508+
// TODO: check for warning
1509+
User: "root",
1510+
Host: "localhost",
1511+
Query: "REVOKE IF EXISTS fake_role FROM tester@localhost;",
1512+
Expected: []sql.Row{{types.NewOkResult(0)}},
1513+
},
1514+
{
1515+
User: "root",
1516+
Host: "localhost",
1517+
Query: "REVOKE test_role FROM fake_user@localhost;",
1518+
ExpectedErr: sql.ErrGrantRevokeRoleDoesNotExist,
1519+
},
1520+
{
1521+
// TODO: check for warning
1522+
User: "root",
1523+
Host: "localhost",
1524+
Query: "REVOKE test_role FROM fake_user@localhost IGNORE UNKNOWN USER;",
1525+
Expected: []sql.Row{{types.NewOkResult(0)}},
1526+
},
14051527
},
14061528
},
14071529
{

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad
77
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71
88
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81
9-
github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c
9+
github.com/dolthub/vitess v0.0.0-20250523011542-0f6cf9472d1c
1010
github.com/go-kit/kit v0.10.0
1111
github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d
1212
github.com/gocraft/dbr/v2 v2.7.2

go.sum

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,8 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE
5858
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI=
5959
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE=
6060
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
61-
github.com/dolthub/vitess v0.0.0-20250430180243-0eee73763bc5 h1:eyC/UHnNsCham/65hV9p/Si0S+cq774kbgk0/KPFYws=
62-
github.com/dolthub/vitess v0.0.0-20250430180243-0eee73763bc5/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
63-
github.com/dolthub/vitess v0.0.0-20250508181115-8941c8d71af1 h1:kH6+SaEzpm9QFKqWsznzwxcYrXIB4HZq6yOaZSXwsng=
64-
github.com/dolthub/vitess v0.0.0-20250508181115-8941c8d71af1/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
65-
github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c h1:imdag6PPCHAO2rZNsFoQoR4I/vIVTmO/czoOl5rUnbk=
66-
github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
61+
github.com/dolthub/vitess v0.0.0-20250523011542-0f6cf9472d1c h1:23KvsBtJk2GmHpXwQ/RkwIkdNpWL8tWdHRCiidhnaUA=
62+
github.com/dolthub/vitess v0.0.0-20250523011542-0f6cf9472d1c/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
6763
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
6864
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
6965
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=

sql/plan/revoke.go

Lines changed: 26 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import (
2626

2727
// Revoke represents the statement REVOKE [privilege...] ON [item] FROM [user...].
2828
type Revoke struct {
29-
Privileges []Privilege
30-
ObjectType ObjectType
31-
PrivilegeLevel PrivilegeLevel
32-
Users []UserName
33-
MySQLDb sql.Database
29+
Privileges []Privilege
30+
ObjectType ObjectType
31+
PrivilegeLevel PrivilegeLevel
32+
Users []UserName
33+
IgnoreUnknownUser bool
34+
MySQLDb sql.Database
3435
}
3536

3637
var _ sql.Node = (*Revoke)(nil)
@@ -425,81 +426,13 @@ func (n *Revoke) HandleRoutinePrivileges(user *mysql_db.User, dbName string, rou
425426
return nil
426427
}
427428

428-
// RevokeAll represents the statement REVOKE ALL PRIVILEGES.
429-
type RevokeAll struct {
430-
Users []UserName
431-
}
432-
433-
var _ sql.Node = (*RevokeAll)(nil)
434-
var _ sql.CollationCoercible = (*RevokeAll)(nil)
435-
var _ sql.AuthorizationCheckerNode = (*RevokeAll)(nil)
436-
437-
// NewRevokeAll returns a new RevokeAll node.
438-
func NewRevokeAll(users []UserName) *RevokeAll {
439-
return &RevokeAll{
440-
Users: users,
441-
}
442-
}
443-
444-
// Schema implements the interface sql.Node.
445-
func (n *RevokeAll) Schema() sql.Schema {
446-
return types.OkResultSchema
447-
}
448-
449-
func (n *RevokeAll) IsReadOnly() bool {
450-
return false
451-
}
452-
453-
// String implements the interface sql.Node.
454-
func (n *RevokeAll) String() string {
455-
users := make([]string, len(n.Users))
456-
for i, user := range n.Users {
457-
users[i] = user.String("")
458-
}
459-
return fmt.Sprintf("RevokeAll(From: %s)", strings.Join(users, ", "))
460-
}
461-
462-
// Resolved implements the interface sql.Node.
463-
func (n *RevokeAll) Resolved() bool {
464-
return true
465-
}
466-
467-
// Children implements the interface sql.Node.
468-
func (n *RevokeAll) Children() []sql.Node {
469-
return nil
470-
}
471-
472-
// WithChildren implements the interface sql.Node.
473-
func (n *RevokeAll) WithChildren(children ...sql.Node) (sql.Node, error) {
474-
if len(children) != 0 {
475-
return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0)
476-
}
477-
return n, nil
478-
}
479-
480-
// CheckAuth implements the interface sql.AuthorizationCheckerNode.
481-
func (n *RevokeAll) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
482-
createUser := sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_CreateUser)
483-
superUser := sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_Super)
484-
485-
subject := sql.PrivilegeCheckSubject{Database: "mysql"}
486-
mysqlUpdate := sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Update)
487-
488-
return opChecker.UserHasPrivileges(ctx, createUser) ||
489-
opChecker.UserHasPrivileges(ctx, superUser) ||
490-
opChecker.UserHasPrivileges(ctx, mysqlUpdate)
491-
}
492-
493-
// CollationCoercibility implements the interface sql.CollationCoercible.
494-
func (*RevokeAll) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
495-
return sql.Collation_binary, 7
496-
}
497-
498429
// RevokeRole represents the statement REVOKE [role...] FROM [user...].
499430
type RevokeRole struct {
500-
Roles []UserName
501-
TargetUsers []UserName
502-
MySQLDb sql.Database
431+
Roles []UserName
432+
TargetUsers []UserName
433+
IfExists bool
434+
IgnoreUnknownUser bool
435+
MySQLDb sql.Database
503436
}
504437

505438
var _ sql.Node = (*RevokeRole)(nil)
@@ -508,11 +441,13 @@ var _ sql.CollationCoercible = (*RevokeRole)(nil)
508441
var _ sql.AuthorizationCheckerNode = (*RevokeRole)(nil)
509442

510443
// NewRevokeRole returns a new RevokeRole node.
511-
func NewRevokeRole(roles []UserName, users []UserName) *RevokeRole {
444+
func NewRevokeRole(roles []UserName, users []UserName, ifExists, ignoreUnknownUser bool) *RevokeRole {
512445
return &RevokeRole{
513-
Roles: roles,
514-
TargetUsers: users,
515-
MySQLDb: sql.UnresolvedDatabase("mysql"),
446+
Roles: roles,
447+
TargetUsers: users,
448+
IfExists: ifExists,
449+
IgnoreUnknownUser: ignoreUnknownUser,
450+
MySQLDb: sql.UnresolvedDatabase("mysql"),
516451
}
517452
}
518453

@@ -613,19 +548,23 @@ func (*RevokeRole) CollationCoercibility(ctx *sql.Context) (collation sql.Collat
613548

614549
// RevokeProxy represents the statement REVOKE PROXY.
615550
type RevokeProxy struct {
616-
On UserName
617-
From []UserName
551+
On UserName
552+
From []UserName
553+
IfExists bool
554+
ignoreUnknownUser bool
618555
}
619556

620557
var _ sql.Node = (*RevokeProxy)(nil)
621558
var _ sql.CollationCoercible = (*RevokeProxy)(nil)
622559
var _ sql.AuthorizationCheckerNode = (*RevokeProxy)(nil)
623560

624561
// NewRevokeProxy returns a new RevokeProxy node.
625-
func NewRevokeProxy(on UserName, from []UserName) *RevokeProxy {
562+
func NewRevokeProxy(on UserName, from []UserName, ifExists, ignoreUnknownUser bool) *RevokeProxy {
626563
return &RevokeProxy{
627-
On: on,
628-
From: from,
564+
On: on,
565+
From: from,
566+
IfExists: ifExists,
567+
ignoreUnknownUser: ignoreUnknownUser,
629568
}
630569
}
631570

sql/planbuilder/builder.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,6 @@ func (b *Builder) buildSubquery(inScope *scope, stmt ast.Statement, subQuery str
375375
return b.buildGrantProxy(inScope, n)
376376
case *ast.RevokePrivilege:
377377
return b.buildRevokePrivilege(inScope, n)
378-
case *ast.RevokeAllPrivileges:
379-
return b.buildRevokeAllPrivileges(inScope, n)
380378
case *ast.RevokeRole:
381379
return b.buildRevokeRole(inScope, n)
382380
case *ast.RevokeProxy:

sql/planbuilder/priv.go

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -516,11 +516,12 @@ func (b *Builder) buildRevokePrivilege(inScope *scope, n *ast.RevokePrivilege) (
516516
}
517517
outScope = inScope.push()
518518
outScope.node = &plan.Revoke{
519-
Privileges: privs,
520-
ObjectType: objType,
521-
PrivilegeLevel: level,
522-
Users: users,
523-
MySQLDb: b.resolveDb("mysql"),
519+
Privileges: privs,
520+
ObjectType: objType,
521+
PrivilegeLevel: level,
522+
Users: users,
523+
IgnoreUnknownUser: n.IgnoreUnknownUser,
524+
MySQLDb: b.resolveDb("mysql"),
524525
}
525526
n.Auth.Extra = outScope.node
526527
if err := b.cat.AuthorizationHandler().HandleAuth(b.ctx, b.authQueryState, n.Auth); err != nil && b.authEnabled {
@@ -529,22 +530,14 @@ func (b *Builder) buildRevokePrivilege(inScope *scope, n *ast.RevokePrivilege) (
529530
return
530531
}
531532

532-
func (b *Builder) buildRevokeAllPrivileges(inScope *scope, n *ast.RevokeAllPrivileges) (outScope *scope) {
533-
outScope = inScope.push()
534-
outScope.node = plan.NewRevokeAll(convertAccountName(n.From...))
535-
n.Auth.Extra = outScope.node
536-
if err := b.cat.AuthorizationHandler().HandleAuth(b.ctx, b.authQueryState, n.Auth); err != nil && b.authEnabled {
537-
b.handleErr(err)
538-
}
539-
return
540-
}
541-
542533
func (b *Builder) buildRevokeRole(inScope *scope, n *ast.RevokeRole) (outScope *scope) {
543534
outScope = inScope.push()
544535
outScope.node = &plan.RevokeRole{
545-
Roles: convertAccountName(n.Roles...),
546-
TargetUsers: convertAccountName(n.From...),
547-
MySQLDb: b.resolveDb("mysql"),
536+
Roles: convertAccountName(n.Roles...),
537+
TargetUsers: convertAccountName(n.From...),
538+
IfExists: n.IfExists,
539+
IgnoreUnknownUser: n.IgnoreUnknownUser,
540+
MySQLDb: b.resolveDb("mysql"),
548541
}
549542
n.Auth.Extra = outScope.node
550543
if err := b.cat.AuthorizationHandler().HandleAuth(b.ctx, b.authQueryState, n.Auth); err != nil && b.authEnabled {
@@ -558,7 +551,7 @@ func (b *Builder) buildRevokeProxy(inScope *scope, n *ast.RevokeProxy) (outScope
558551
b.handleErr(err)
559552
}
560553
outScope = inScope.push()
561-
outScope.node = plan.NewRevokeProxy(convertAccountName(n.On)[0], convertAccountName(n.From...))
554+
outScope.node = plan.NewRevokeProxy(convertAccountName(n.On)[0], convertAccountName(n.From...), n.IfExists, n.IgnoreUnknownUser)
562555
return
563556
}
564557

0 commit comments

Comments
 (0)