Skip to content
Merged
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
126 changes: 124 additions & 2 deletions enginetest/queries/priv_auth_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,27 @@ var UserPrivTests = []UserPrivilegeTest{
Query: "SELECT User, Host, Select_priv FROM mysql.user WHERE User = 'tester';",
Expected: []sql.Row{{"tester", "localhost", "N"}},
},

{
// Re-revoking does nothing
User: "root",
Host: "localhost",
Query: "REVOKE SELECT ON *.* FROM tester@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
// IF EXISTS option does nothing
User: "root",
Host: "localhost",
Query: "REVOKE IF EXISTS SELECT ON *.* FROM tester@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv FROM mysql.user WHERE User = 'tester';",
Expected: []sql.Row{{"tester", "localhost", "N"}},
},
},
},
{
Expand All @@ -1238,7 +1259,11 @@ var UserPrivTests = []UserPrivilegeTest{
"CREATE TABLE test (pk BIGINT PRIMARY KEY);",
"INSERT INTO test VALUES (1), (2), (3);",
"CREATE USER tester@localhost;",
"CREATE USER tester1@localhost;",
"CREATE USER tester2@localhost;",
"GRANT ALL ON *.* TO tester@localhost;",
"GRANT ALL ON *.* TO tester1@localhost WITH GRANT OPTION;",
"GRANT ALL ON *.* TO tester2@localhost WITH GRANT OPTION;",
},
Assertions: []UserPrivilegeTestAssertion{
{
Expand Down Expand Up @@ -1280,8 +1305,79 @@ var UserPrivTests = []UserPrivilegeTest{
{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv, Insert_priv FROM mysql.user WHERE User = 'tester';",
Expected: []sql.Row{{"tester", "localhost", "N", "N"}},
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester';",
Expected: []sql.Row{{"tester", "localhost", "N", "N", "N"}},
},

{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester1';",
Expected: []sql.Row{{"tester1", "localhost", "Y", "Y", "Y"}},
},
{
User: "root",
Host: "localhost",
Query: "REVOKE ALL, GRANT OPTION FROM tester1@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester1';",
Expected: []sql.Row{{"tester1", "localhost", "N", "N", "N"}},
},

{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester2';",
Expected: []sql.Row{{"tester2", "localhost", "Y", "Y", "Y"}},
},
{
User: "root",
Host: "localhost",
Query: "REVOKE ALL PRIVILEGES, GRANT OPTION FROM tester2@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester2';",
Expected: []sql.Row{{"tester2", "localhost", "N", "N", "N"}},
},
{
// Re-revoking does nothing
User: "root",
Host: "localhost",
Query: "REVOKE ALL PRIVILEGES, GRANT OPTION FROM tester2@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
// IF EXISTS does nothing
User: "root",
Host: "localhost",
Query: "REVOKE IF EXISTS ALL PRIVILEGES, GRANT OPTION FROM tester2@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
User: "root",
Host: "localhost",
Query: "SELECT User, Host, Select_priv, Insert_priv, Grant_priv FROM mysql.user WHERE User = 'tester2';",
Expected: []sql.Row{{"tester2", "localhost", "N", "N", "N"}},
},
{
User: "root",
Host: "localhost",
Query: "REVOKE IF EXISTS ALL PRIVILEGES, GRANT OPTION FROM fake1@localhost, fake2@localhost, fake3@localhost;",
ExpectedErr: sql.ErrRevokeUserDoesNotExist,
},
{
// TODO: check warnings
User: "root",
Host: "localhost",
Query: "REVOKE IF EXISTS ALL PRIVILEGES, GRANT OPTION FROM fake1@localhost, fake2@localhost, fake3@localhost IGNORE UNKNOWN USER;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
},
},
Expand Down Expand Up @@ -1402,6 +1498,32 @@ var UserPrivTests = []UserPrivilegeTest{
Query: "SELECT COUNT(*) FROM mysql.user WHERE User = 'tester';",
Expected: []sql.Row{{1}},
},
{
User: "root",
Host: "localhost",
Query: "REVOKE fake_role FROM tester@localhost;",
ExpectedErr: sql.ErrGrantRevokeRoleDoesNotExist,
},
{
// TODO: check for warning
User: "root",
Host: "localhost",
Query: "REVOKE IF EXISTS fake_role FROM tester@localhost;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
User: "root",
Host: "localhost",
Query: "REVOKE test_role FROM fake_user@localhost;",
ExpectedErr: sql.ErrGrantRevokeRoleDoesNotExist,
},
{
// TODO: check for warning
User: "root",
Host: "localhost",
Query: "REVOKE test_role FROM fake_user@localhost IGNORE UNKNOWN USER;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81
github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c
github.com/dolthub/vitess v0.0.0-20250523011542-0f6cf9472d1c
github.com/go-kit/kit v0.10.0
github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d
github.com/gocraft/dbr/v2 v2.7.2
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,8 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI=
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE=
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
github.com/dolthub/vitess v0.0.0-20250430180243-0eee73763bc5 h1:eyC/UHnNsCham/65hV9p/Si0S+cq774kbgk0/KPFYws=
github.com/dolthub/vitess v0.0.0-20250430180243-0eee73763bc5/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
github.com/dolthub/vitess v0.0.0-20250508181115-8941c8d71af1 h1:kH6+SaEzpm9QFKqWsznzwxcYrXIB4HZq6yOaZSXwsng=
github.com/dolthub/vitess v0.0.0-20250508181115-8941c8d71af1/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c h1:imdag6PPCHAO2rZNsFoQoR4I/vIVTmO/czoOl5rUnbk=
github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
github.com/dolthub/vitess v0.0.0-20250523011542-0f6cf9472d1c h1:23KvsBtJk2GmHpXwQ/RkwIkdNpWL8tWdHRCiidhnaUA=
github.com/dolthub/vitess v0.0.0-20250523011542-0f6cf9472d1c/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
Expand Down
113 changes: 26 additions & 87 deletions sql/plan/revoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ import (

// Revoke represents the statement REVOKE [privilege...] ON [item] FROM [user...].
type Revoke struct {
Privileges []Privilege
ObjectType ObjectType
PrivilegeLevel PrivilegeLevel
Users []UserName
MySQLDb sql.Database
Privileges []Privilege
ObjectType ObjectType
PrivilegeLevel PrivilegeLevel
Users []UserName
IgnoreUnknownUser bool
MySQLDb sql.Database
}

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

// RevokeAll represents the statement REVOKE ALL PRIVILEGES.
type RevokeAll struct {
Users []UserName
}

var _ sql.Node = (*RevokeAll)(nil)
var _ sql.CollationCoercible = (*RevokeAll)(nil)
var _ sql.AuthorizationCheckerNode = (*RevokeAll)(nil)

// NewRevokeAll returns a new RevokeAll node.
func NewRevokeAll(users []UserName) *RevokeAll {
return &RevokeAll{
Users: users,
}
}

// Schema implements the interface sql.Node.
func (n *RevokeAll) Schema() sql.Schema {
return types.OkResultSchema
}

func (n *RevokeAll) IsReadOnly() bool {
return false
}

// String implements the interface sql.Node.
func (n *RevokeAll) String() string {
users := make([]string, len(n.Users))
for i, user := range n.Users {
users[i] = user.String("")
}
return fmt.Sprintf("RevokeAll(From: %s)", strings.Join(users, ", "))
}

// Resolved implements the interface sql.Node.
func (n *RevokeAll) Resolved() bool {
return true
}

// Children implements the interface sql.Node.
func (n *RevokeAll) Children() []sql.Node {
return nil
}

// WithChildren implements the interface sql.Node.
func (n *RevokeAll) WithChildren(children ...sql.Node) (sql.Node, error) {
if len(children) != 0 {
return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0)
}
return n, nil
}

// CheckAuth implements the interface sql.AuthorizationCheckerNode.
func (n *RevokeAll) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
createUser := sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_CreateUser)
superUser := sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_Super)

subject := sql.PrivilegeCheckSubject{Database: "mysql"}
mysqlUpdate := sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Update)

return opChecker.UserHasPrivileges(ctx, createUser) ||
opChecker.UserHasPrivileges(ctx, superUser) ||
opChecker.UserHasPrivileges(ctx, mysqlUpdate)
}

// CollationCoercibility implements the interface sql.CollationCoercible.
func (*RevokeAll) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
return sql.Collation_binary, 7
}

// RevokeRole represents the statement REVOKE [role...] FROM [user...].
type RevokeRole struct {
Roles []UserName
TargetUsers []UserName
MySQLDb sql.Database
Roles []UserName
TargetUsers []UserName
IfExists bool
IgnoreUnknownUser bool
MySQLDb sql.Database
}

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

// NewRevokeRole returns a new RevokeRole node.
func NewRevokeRole(roles []UserName, users []UserName) *RevokeRole {
func NewRevokeRole(roles []UserName, users []UserName, ifExists, ignoreUnknownUser bool) *RevokeRole {
return &RevokeRole{
Roles: roles,
TargetUsers: users,
MySQLDb: sql.UnresolvedDatabase("mysql"),
Roles: roles,
TargetUsers: users,
IfExists: ifExists,
IgnoreUnknownUser: ignoreUnknownUser,
MySQLDb: sql.UnresolvedDatabase("mysql"),
}
}

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

// RevokeProxy represents the statement REVOKE PROXY.
type RevokeProxy struct {
On UserName
From []UserName
On UserName
From []UserName
IfExists bool
ignoreUnknownUser bool
}

var _ sql.Node = (*RevokeProxy)(nil)
var _ sql.CollationCoercible = (*RevokeProxy)(nil)
var _ sql.AuthorizationCheckerNode = (*RevokeProxy)(nil)

// NewRevokeProxy returns a new RevokeProxy node.
func NewRevokeProxy(on UserName, from []UserName) *RevokeProxy {
func NewRevokeProxy(on UserName, from []UserName, ifExists, ignoreUnknownUser bool) *RevokeProxy {
return &RevokeProxy{
On: on,
From: from,
On: on,
From: from,
IfExists: ifExists,
ignoreUnknownUser: ignoreUnknownUser,
}
}

Expand Down
2 changes: 0 additions & 2 deletions sql/planbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,6 @@ func (b *Builder) buildSubquery(inScope *scope, stmt ast.Statement, subQuery str
return b.buildGrantProxy(inScope, n)
case *ast.RevokePrivilege:
return b.buildRevokePrivilege(inScope, n)
case *ast.RevokeAllPrivileges:
return b.buildRevokeAllPrivileges(inScope, n)
case *ast.RevokeRole:
return b.buildRevokeRole(inScope, n)
case *ast.RevokeProxy:
Expand Down
31 changes: 12 additions & 19 deletions sql/planbuilder/priv.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,11 +516,12 @@ func (b *Builder) buildRevokePrivilege(inScope *scope, n *ast.RevokePrivilege) (
}
outScope = inScope.push()
outScope.node = &plan.Revoke{
Privileges: privs,
ObjectType: objType,
PrivilegeLevel: level,
Users: users,
MySQLDb: b.resolveDb("mysql"),
Privileges: privs,
ObjectType: objType,
PrivilegeLevel: level,
Users: users,
IgnoreUnknownUser: n.IgnoreUnknownUser,
MySQLDb: b.resolveDb("mysql"),
}
n.Auth.Extra = outScope.node
if err := b.cat.AuthorizationHandler().HandleAuth(b.ctx, b.authQueryState, n.Auth); err != nil && b.authEnabled {
Expand All @@ -529,22 +530,14 @@ func (b *Builder) buildRevokePrivilege(inScope *scope, n *ast.RevokePrivilege) (
return
}

func (b *Builder) buildRevokeAllPrivileges(inScope *scope, n *ast.RevokeAllPrivileges) (outScope *scope) {
outScope = inScope.push()
outScope.node = plan.NewRevokeAll(convertAccountName(n.From...))
n.Auth.Extra = outScope.node
if err := b.cat.AuthorizationHandler().HandleAuth(b.ctx, b.authQueryState, n.Auth); err != nil && b.authEnabled {
b.handleErr(err)
}
return
}

func (b *Builder) buildRevokeRole(inScope *scope, n *ast.RevokeRole) (outScope *scope) {
outScope = inScope.push()
outScope.node = &plan.RevokeRole{
Roles: convertAccountName(n.Roles...),
TargetUsers: convertAccountName(n.From...),
MySQLDb: b.resolveDb("mysql"),
Roles: convertAccountName(n.Roles...),
TargetUsers: convertAccountName(n.From...),
IfExists: n.IfExists,
IgnoreUnknownUser: n.IgnoreUnknownUser,
MySQLDb: b.resolveDb("mysql"),
}
n.Auth.Extra = outScope.node
if err := b.cat.AuthorizationHandler().HandleAuth(b.ctx, b.authQueryState, n.Auth); err != nil && b.authEnabled {
Expand All @@ -558,7 +551,7 @@ func (b *Builder) buildRevokeProxy(inScope *scope, n *ast.RevokeProxy) (outScope
b.handleErr(err)
}
outScope = inScope.push()
outScope.node = plan.NewRevokeProxy(convertAccountName(n.On)[0], convertAccountName(n.From...))
outScope.node = plan.NewRevokeProxy(convertAccountName(n.On)[0], convertAccountName(n.From...), n.IfExists, n.IgnoreUnknownUser)
return
}

Expand Down
Loading