diff --git a/ast/misc.go b/ast/misc.go index 66d079d39..4056017d9 100755 --- a/ast/misc.go +++ b/ast/misc.go @@ -1640,6 +1640,60 @@ func (n *GrantStmt) Accept(v Visitor) (Node, bool) { return v.Leave(n) } +// GrantRoleStmt is the struct for GRANT TO statement. +type GrantRoleStmt struct { + stmtNode + + Roles []*auth.RoleIdentity + Users []*auth.UserIdentity +} + +// Accept implements Node Accept interface. +func (n *GrantRoleStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*GrantRoleStmt) + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *GrantRoleStmt) Restore(ctx *RestoreCtx) error { + ctx.WriteKeyWord("GRANT ") + if len(n.Roles) > 0 { + for i, role := range n.Roles { + if i != 0 { + ctx.WritePlain(", ") + } + if err := role.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantRoleStmt.Roles[%d]", i) + } + } + } + ctx.WriteKeyWord(" TO ") + for i, v := range n.Users { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) + } + } + return nil +} + +// SecureText implements SensitiveStatement interface. +func (n *GrantRoleStmt) SecureText() string { + text := n.text + // Filter "identified by xxx" because it would expose password information. + idx := strings.Index(strings.ToLower(text), "identified") + if idx > 0 { + text = text[:idx] + } + return text +} + // Ident is the table identifier composed of schema name and table name. type Ident struct { Schema model.CIStr diff --git a/auth/auth.go b/auth/auth.go index d8d805e9f..fa65ef4a1 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -139,3 +139,18 @@ func DecodePassword(pwd string) ([]byte, error) { } return x, nil } + +func (role *RoleIdentity) Restore(ctx *RestoreCtx) error { + ctx.WriteName(role.Username) + if role.Hostname != "" { + ctx.WritePlain("@") + ctx.WriteName(role.Hostname) + } + return nil +} + +// String converts UserIdentity to the format user@host. +func (role *RoleIdentity) String() string { + // TODO: Escape username and hostname. + return fmt.Sprintf("`%s`@`%s`", role.Username, role.Hostname) +} diff --git a/parser.go b/parser.go index 0590d3078..340e7abd3 100644 --- a/parser.go +++ b/parser.go @@ -12926,6 +12926,13 @@ yynewstate: WithGrant: yyS[yypt-0].item.(bool), } } + case 1428: + { + parser.yyVAL.statement = &ast.GrantRoleStmt{ + Roles: yyS[yypt-2].item.([]*auth.RoleIdentity), + Users: yyS[yypt-0].item.([]*auth.UserIdentity), + } + } case 1429: { parser.yyVAL.item = false diff --git a/parser.y b/parser.y index 335f9b0ac..75cc6fcce 100644 --- a/parser.y +++ b/parser.y @@ -7688,6 +7688,10 @@ GrantStmt: GrantRoleStmt: "GRANT" RolenameList "TO" UsernameList { + $$ = &ast.GrantRoleStmt { + Roles: $2.([]*auth.RoleIdentity), + Users: $4.([]*auth.UserIdentity), + } } WithGrantOptionOpt: diff --git a/parser_test.go b/parser_test.go index 95f92c372..433c6e230 100755 --- a/parser_test.go +++ b/parser_test.go @@ -2135,9 +2135,9 @@ func (s *testParserSuite) TestPrivilege(c *C) { {"GRANT SELECT ON test.* to 'test'", true, "GRANT SELECT ON `test`.* TO `test`@`%`"}, // For issue 2654. {"grant PROCESS,usage, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'xxxxxxxxxx'@'%' identified by password 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'", true, "GRANT PROCESS /* UNSUPPORTED TYPE */ /* UNSUPPORTED TYPE */ /* UNSUPPORTED TYPE */ ON *.* TO `xxxxxxxxxx`@`%` IDENTIFIED BY PASSWORD 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'"}, // For issue 4865 {"/* rds internal mark */ GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, RELOAD, PROCESS, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER on *.* to 'root2'@'%' identified by password '*sdsadsdsadssadsadsadsadsada' with grant option", true, "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES /* UNSUPPORTED TYPE */, PROCESS, INDEX, ALTER /* UNSUPPORTED TYPE */ /* UNSUPPORTED TYPE */, EXECUTE /* UNSUPPORTED TYPE */ /* UNSUPPORTED TYPE */, CREATE VIEW, SHOW VIEW /* UNSUPPORTED TYPE */ /* UNSUPPORTED TYPE */, CREATE USER /* UNSUPPORTED TYPE */, TRIGGER ON *.* TO `root2`@`%` IDENTIFIED BY PASSWORD '*sdsadsdsadssadsadsadsadsada' WITH GRANT OPTION"}, - {"GRANT 'role1', 'role2' TO 'user1'@'localhost', 'user2'@'localhost';", true, ""}, - {"GRANT 'u1' TO 'u1';", true, ""}, - {"GRANT 'app_developer' TO 'dev1'@'localhost';", true, ""}, + {"GRANT 'role1', 'role2' TO 'user1'@'localhost', 'user2'@'localhost';", true, "GRANT `role1`@`%`, `role2`@`%` TO `user1`@`localhost`, `user2`@`localhost`"}, + {"GRANT 'u1' TO 'u1';", true, "GRANT `u1`@`%` TO `u1`@`%`"}, + {"GRANT 'app_developer' TO 'dev1'@'localhost';", true, "GRANT `app_developer`@`%` TO `dev1`@`localhost`"}, // for revoke statement {"REVOKE ALL ON db1.* FROM 'jeffrey'@'localhost';", true, "REVOKE ALL ON `db1`.* FROM `jeffrey`@`localhost`"},