From 96aaa94d51f92462e288d317421ff3d110b83e04 Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Wed, 3 Apr 2019 13:35:58 +0800 Subject: [PATCH 1/9] add role support for show grant --- executor/builder.go | 3 +- executor/show.go | 12 +++- go.mod | 2 + go.sum | 2 + planner/core/common_plans.go | 8 +-- planner/core/planbuilder.go | 1 + privilege/privilege.go | 2 +- privilege/privileges/cache.go | 96 ++++++++++++++++++++----- privilege/privileges/privileges.go | 9 ++- privilege/privileges/privileges_test.go | 51 ++++++++++--- 10 files changed, 148 insertions(+), 38 deletions(-) diff --git a/executor/builder.go b/executor/builder.go index 6ccddeb8ca22c..3f500b808eaa5 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -522,6 +522,7 @@ func (b *executorBuilder) buildShow(v *plannercore.Show) Executor { Table: v.Table, Column: v.Column, User: v.User, + Roles: v.Roles, IfNotExists: v.IfNotExists, Flag: v.Flag, Full: v.Full, @@ -1253,7 +1254,7 @@ func (b *executorBuilder) buildUpdate(v *plannercore.Update) Executor { // cols2Handle represents an mapper from column index to handle index. type cols2Handle struct { - // start/end represent the ordinal range [start, end) of the consecutive columns. + // start and end represent the ordinal range [start, end) of the consecutive columns. start, end int32 // handleOrdinal represents the ordinal of the handle column. handleOrdinal int32 diff --git a/executor/show.go b/executor/show.go index c4322a876eb1e..5e526da5d13db 100644 --- a/executor/show.go +++ b/executor/show.go @@ -60,8 +60,9 @@ type ShowExec struct { Column *ast.ColumnName // Used for `desc table column`. Flag int // Some flag parsed from sql, such as FULL. Full bool - User *auth.UserIdentity // Used for show grants. - IfNotExists bool // Used for `show create database if not exists` + User *auth.UserIdentity // Used for show grants. + Roles []*auth.RoleIdentity // Used for show grants. + IfNotExists bool // Used for `show create database if not exists` // GlobalScope is used by show variables GlobalScope bool @@ -908,7 +909,12 @@ func (e *ShowExec) fetchShowGrants() error { if checker == nil { return errors.New("miss privilege checker") } - gs, err := checker.ShowGrants(e.ctx, e.User) + for _, r := range e.Roles { + if !checker.FindEdge(e.ctx, r, e.User) { + return ErrRoleNotGranted.GenWithStackByArgs(r.String(), e.User.String()) + } + } + gs, err := checker.ShowGrants(e.ctx, e.User, e.Roles) if err != nil { return errors.Trace(err) } diff --git a/go.mod b/go.mod index ecbd19cb3b8c6..57eb3f4ba4a2e 100644 --- a/go.mod +++ b/go.mod @@ -90,3 +90,5 @@ require ( sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) + +replace github.com/pingcap/parser => github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c diff --git a/go.sum b/go.sum index a53c14279e76e..98378b5d66350 100644 --- a/go.sum +++ b/go.sum @@ -76,6 +76,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.1 h1:3scN4iuXkNOyP98jF55Lv8a9j1o/Iwv github.com/grpc-ecosystem/grpc-gateway v1.5.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c h1:QUDmm1Lnixo8z8TiWddnRamwyALjG+Yk4GTd+CDRYOo= +github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 15448100a4d61..8a1131e585666 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -318,13 +318,13 @@ type Show struct { Column *ast.ColumnName // Used for `desc table column`. Flag int // Some flag parsed from sql, such as FULL. Full bool - User *auth.UserIdentity // Used for show grants. - IfNotExists bool // Used for `show create database if not exists` + User *auth.UserIdentity // Used for show grants. + Roles []*auth.RoleIdentity // Used for show grants. + IfNotExists bool // Used for `show create database if not exists` Conditions []expression.Expression - // Used by show variables - GlobalScope bool + GlobalScope bool // Used by show variables } // Set represents a plan for set stmt. diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index ab8721a9f343e..5d24df0751b54 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -1026,6 +1026,7 @@ func (b *PlanBuilder) buildShow(show *ast.ShowStmt) (Plan, error) { Flag: show.Flag, Full: show.Full, User: show.User, + Roles: show.Roles, IfNotExists: show.IfNotExists, GlobalScope: show.GlobalScope, }.Init(b.ctx) diff --git a/privilege/privilege.go b/privilege/privilege.go index e94ae4e9f8dae..332e8229ea93a 100644 --- a/privilege/privilege.go +++ b/privilege/privilege.go @@ -29,7 +29,7 @@ func (k keyType) String() string { // Manager is the interface for providing privilege related operations. type Manager interface { // ShowGrants shows granted privileges for user. - ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity) ([]string, error) + ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity, roles []*auth.RoleIdentity) ([]string, error) // GetEncodedPassword shows the encoded password for user. GetEncodedPassword(user, host string) string diff --git a/privilege/privileges/cache.go b/privilege/privileges/cache.go index 07055813ae2e3..2b6cb4afeaf21 100644 --- a/privilege/privileges/cache.go +++ b/privilege/privileges/cache.go @@ -700,21 +700,30 @@ func (p *MySQLPrivilege) DBIsVisible(user, host, db string) bool { return false } -func (p *MySQLPrivilege) showGrants(user, host string) []string { +func (p *MySQLPrivilege) showGrants(user, host string, roles []*auth.RoleIdentity) []string { var gs []string var hasGlobalGrant bool = false - // Show global grants + // Show global grants. + var currentPriv mysql.PrivilegeType + var g string for _, record := range p.User { if record.User == user && record.Host == host { hasGlobalGrant = true - g := userPrivToString(record.Privileges) - if len(g) > 0 { - s := fmt.Sprintf(`GRANT %s ON *.* TO '%s'@'%s'`, g, record.User, record.Host) - gs = append(gs, s) + currentPriv |= record.Privileges + } else { + for _, r := range roles { + if record.User == r.Username && record.Host == r.Hostname { + hasGlobalGrant = true + currentPriv |= record.Privileges + } } - break // it's unique } } + g = userPrivToString(currentPriv) + if len(g) > 0 { + s := fmt.Sprintf(`GRANT %s ON *.* TO '%s'@'%s'`, g, user, host) + gs = append(gs, s) + } // This is a mysql convention. if len(gs) == 0 && hasGlobalGrant { @@ -722,28 +731,81 @@ func (p *MySQLPrivilege) showGrants(user, host string) []string { gs = append(gs, s) } - // Show db scope grants + // Show db scope grants. + dbPrivTable := make(map[string]mysql.PrivilegeType) for _, record := range p.DB { if record.User == user && record.Host == host { - g := dbPrivToString(record.Privileges) - if len(g) > 0 { - s := fmt.Sprintf(`GRANT %s ON %s.* TO '%s'@'%s'`, g, record.DB, record.User, record.Host) - gs = append(gs, s) + if _, ok := dbPrivTable[record.DB]; ok { + dbPrivTable[record.DB] |= record.Privileges + } else { + dbPrivTable[record.DB] = record.Privileges + } + } else { + for _, r := range roles { + if record.User == r.Username && record.Host == r.Hostname { + if _, ok := dbPrivTable[record.DB]; ok { + dbPrivTable[record.DB] |= record.Privileges + } else { + dbPrivTable[record.DB] = record.Privileges + } + } } } } + for dbName, priv := range dbPrivTable { + g := dbPrivToString(priv) + if len(g) > 0 { + s := fmt.Sprintf(`GRANT %s ON %s.* TO '%s'@'%s'`, g, dbName, user, host) + gs = append(gs, s) + } + } - // Show table scope grants + // Show table scope grants. + tablePrivTable := make(map[string]mysql.PrivilegeType) for _, record := range p.TablesPriv { + recordKey := record.DB + "." + record.TableName if record.User == user && record.Host == host { - g := tablePrivToString(record.TablePriv) - if len(g) > 0 { - s := fmt.Sprintf(`GRANT %s ON %s.%s TO '%s'@'%s'`, g, record.DB, record.TableName, record.User, record.Host) - gs = append(gs, s) + if _, ok := dbPrivTable[record.DB]; ok { + tablePrivTable[recordKey] |= record.TablePriv + } else { + tablePrivTable[recordKey] = record.TablePriv + } + } else { + for _, r := range roles { + if record.User == r.Username && record.Host == r.Hostname { + if _, ok := dbPrivTable[record.DB]; ok { + tablePrivTable[recordKey] |= record.TablePriv + } else { + tablePrivTable[recordKey] = record.TablePriv + } + } } } } + for k, priv := range tablePrivTable { + g := tablePrivToString(priv) + if len(g) > 0 { + s := fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s'`, g, k, user, host) + gs = append(gs, s) + } + } + // Show role grants. + graphKey := user + "@" + host + edgeTable, ok := p.RoleGraph[graphKey] + g = "" + if ok { + for k := range edgeTable.roleList { + role := strings.Split(k, "@") + roleName, roleHost := role[0], role[1] + if g != "" { + g += ", " + } + g += fmt.Sprintf("'%s'@'%s'", roleName, roleHost) + } + s := fmt.Sprintf(`GRANT %s TO '%s'@'%s'`, g, user, host) + gs = append(gs, s) + } return gs } diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index 98683977255cb..2e6bba13e5b00 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -175,7 +175,7 @@ func (p *UserPrivileges) UserPrivilegesTable() [][]types.Datum { } // ShowGrants implements privilege.Manager ShowGrants interface. -func (p *UserPrivileges) ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity) (grants []string, err error) { +func (p *UserPrivileges) ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity, roles []*auth.RoleIdentity) (grants []string, err error) { mysqlPrivilege := p.Handle.Get() u := user.Username h := user.Hostname @@ -183,7 +183,12 @@ func (p *UserPrivileges) ShowGrants(ctx sessionctx.Context, user *auth.UserIdent u = user.AuthUsername h = user.AuthHostname } - grants = mysqlPrivilege.showGrants(u, h) + for _, r := range roles { + if r.Hostname == "" { + r.Hostname = "%" + } + } + grants = mysqlPrivilege.showGrants(u, h, roles) if len(grants) == 0 { err = errNonexistingGrant.GenWithStackByArgs(u, h) } diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 1767d6bdd761e..d909f57c03b87 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -147,34 +147,34 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { mustExec(c, se, `GRANT Index ON *.* TO 'show'@'localhost';`) pc := privilege.GetPrivilegeManager(se) - gs, err := pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err := pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 1) c.Assert(gs[0], Equals, `GRANT Index ON *.* TO 'show'@'localhost'`) mustExec(c, se, `GRANT Select ON *.* TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 1) c.Assert(gs[0], Equals, `GRANT Select,Index ON *.* TO 'show'@'localhost'`) // The order of privs is the same with AllGlobalPrivs mustExec(c, se, `GRANT Update ON *.* TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 1) c.Assert(gs[0], Equals, `GRANT Select,Update,Index ON *.* TO 'show'@'localhost'`) // All privileges mustExec(c, se, `GRANT ALL ON *.* TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 1) c.Assert(gs[0], Equals, `GRANT ALL PRIVILEGES ON *.* TO 'show'@'localhost'`) // Add db scope privileges mustExec(c, se, `GRANT Select ON test.* TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 2) expected := []string{`GRANT ALL PRIVILEGES ON *.* TO 'show'@'localhost'`, @@ -182,7 +182,7 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) mustExec(c, se, `GRANT Index ON test1.* TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 3) expected = []string{`GRANT ALL PRIVILEGES ON *.* TO 'show'@'localhost'`, @@ -191,7 +191,7 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) mustExec(c, se, `GRANT ALL ON test1.* TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 3) expected = []string{`GRANT ALL PRIVILEGES ON *.* TO 'show'@'localhost'`, @@ -201,7 +201,7 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { // Add table scope privileges mustExec(c, se, `GRANT Update ON test.test TO 'show'@'localhost';`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 4) expected = []string{`GRANT ALL PRIVILEGES ON *.* TO 'show'@'localhost'`, @@ -215,7 +215,7 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { mustExec(c, se, `REVOKE Select on test.* FROM 'show'@'localhost'`) mustExec(c, se, `REVOKE ALL ON test1.* FROM 'show'@'localhost'`) mustExec(c, se, `REVOKE UPDATE on test.test FROM 'show'@'localhost'`) - gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 1) c.Assert(gs[0], Equals, `GRANT USAGE ON *.* TO 'show'@'localhost'`) @@ -226,11 +226,42 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { mustExec(c, se, `DROP USER 'show'@'localhost'`) // This should now return an error - _, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) + _, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}, nil) c.Assert(err, NotNil) // cant show grants for non-existent errNonexistingGrant := terror.ClassPrivilege.New(mysql.ErrNonexistingGrant, mysql.MySQLErrName[mysql.ErrNonexistingGrant]) c.Assert(terror.ErrorEqual(err, errNonexistingGrant), IsTrue) + + // Test SHOW GRANTS with USING roles. + mustExec(c, se, `CREATE ROLE 'r1', 'r2'`) + mustExec(c, se, `GRANT SELECT ON test.* TO 'r1'`) + mustExec(c, se, `GRANT INSERT, UPDATE, DELETE ON test.* TO 'r2'`) + mustExec(c, se, `CREATE USER 'testrole'@'localhost' IDENTIFIED BY 'u1pass'`) + mustExec(c, se, `GRANT 'r1', 'r2' TO 'testrole'@'localhost'`) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, nil) + c.Assert(err, IsNil) + c.Assert(gs, HasLen, 2) + expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, + `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} + c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) + roles := make([]*auth.RoleIdentity, 0, 10) + roles = append(roles, &auth.RoleIdentity{Username: "r2", Hostname: "%"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, roles) + c.Assert(err, IsNil) + c.Assert(gs, HasLen, 3) + expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, + `GRANT Insert,Update,Delete ON test.* TO 'testrole'@'localhost'`, + `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} + c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) + roles = append(roles, &auth.RoleIdentity{Username: "r1", Hostname: "%"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, roles) + c.Assert(err, IsNil) + c.Assert(gs, HasLen, 3) + expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, + `GRANT Select,Insert,Update,Delete ON test.* TO 'testrole'@'localhost'`, + `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} + mustExec(c, se, `DROP ROLE 'r1', 'r2'`) + mustExec(c, se, `DROP USER 'testrole'@'localhost'`) } func (s *testPrivilegeSuite) TestDropTablePriv(c *C) { From 21a7d8e2c543c369de9a062110a4a6eb26e8403e Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Thu, 4 Apr 2019 12:31:45 +0800 Subject: [PATCH 2/9] update parser --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 57eb3f4ba4a2e..ff84692703e31 100644 --- a/go.mod +++ b/go.mod @@ -91,4 +91,4 @@ require ( sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) -replace github.com/pingcap/parser => github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c +replace github.com/pingcap/parser => github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268 diff --git a/go.sum b/go.sum index 98378b5d66350..b216f76835d93 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c h1:QUDmm1Lnixo8z8TiWddnRamwyALjG+Yk4GTd+CDRYOo= github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= +github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268 h1:M6ozr8Rcdro0sHjuizNashwrUwwJNGGwIBGMlrcbHz0= +github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= From c3858d638398565ed605b6a9c202b9d758855be8 Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 12 Apr 2019 11:39:42 +0800 Subject: [PATCH 3/9] address comment --- executor/show.go | 3 +++ privilege/privileges/privileges.go | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/executor/show.go b/executor/show.go index 5e526da5d13db..3a8db9d11a26e 100644 --- a/executor/show.go +++ b/executor/show.go @@ -910,6 +910,9 @@ func (e *ShowExec) fetchShowGrants() error { return errors.New("miss privilege checker") } for _, r := range e.Roles { + if r.Hostname == "" { + r.Hostname = "%" + } if !checker.FindEdge(e.ctx, r, e.User) { return ErrRoleNotGranted.GenWithStackByArgs(r.String(), e.User.String()) } diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index 2e6bba13e5b00..25e35b17027e7 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -183,11 +183,6 @@ func (p *UserPrivileges) ShowGrants(ctx sessionctx.Context, user *auth.UserIdent u = user.AuthUsername h = user.AuthHostname } - for _, r := range roles { - if r.Hostname == "" { - r.Hostname = "%" - } - } grants = mysqlPrivilege.showGrants(u, h, roles) if len(grants) == 0 { err = errNonexistingGrant.GenWithStackByArgs(u, h) From 4a8387c8694d67a0ecf5dd7f6310bfcac76546ce Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 12 Apr 2019 11:49:24 +0800 Subject: [PATCH 4/9] update parser --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ff84692703e31..12fb27cd14517 100644 --- a/go.mod +++ b/go.mod @@ -91,4 +91,4 @@ require ( sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) -replace github.com/pingcap/parser => github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268 +replace github.com/pingcap/parser => github.com/imtbkcat/parser showgrant From 4252891d87efa49da60187d03511deeb5e1bb56b Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 12 Apr 2019 13:59:23 +0800 Subject: [PATCH 5/9] update go mod --- go.mod | 2 -- go.sum | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 12fb27cd14517..ecbd19cb3b8c6 100644 --- a/go.mod +++ b/go.mod @@ -90,5 +90,3 @@ require ( sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) - -replace github.com/pingcap/parser => github.com/imtbkcat/parser showgrant diff --git a/go.sum b/go.sum index b216f76835d93..38b66ffce6cb3 100644 --- a/go.sum +++ b/go.sum @@ -80,6 +80,8 @@ github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c h1:QUDmm1Lnixo8z8T github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268 h1:M6ozr8Rcdro0sHjuizNashwrUwwJNGGwIBGMlrcbHz0= github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= +github.com/imtbkcat/parser v0.0.0-20190412035857-e13e5e1ba3f6 h1:nlmD42DCidhlIF9gDmJJvKxIaWOABbiDAhgYHhWvke4= +github.com/imtbkcat/parser v0.0.0-20190412035857-e13e5e1ba3f6/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= From 464b119b644e787390dd17f11ba9f3a40b0900a5 Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 12 Apr 2019 19:36:30 +0800 Subject: [PATCH 6/9] fix ci --- go.sum | 6 ------ 1 file changed, 6 deletions(-) diff --git a/go.sum b/go.sum index 38b66ffce6cb3..a53c14279e76e 100644 --- a/go.sum +++ b/go.sum @@ -76,12 +76,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.1 h1:3scN4iuXkNOyP98jF55Lv8a9j1o/Iwv github.com/grpc-ecosystem/grpc-gateway v1.5.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c h1:QUDmm1Lnixo8z8TiWddnRamwyALjG+Yk4GTd+CDRYOo= -github.com/imtbkcat/parser v0.0.0-20190402033409-224a4061fe0c/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= -github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268 h1:M6ozr8Rcdro0sHjuizNashwrUwwJNGGwIBGMlrcbHz0= -github.com/imtbkcat/parser v0.0.0-20190404042555-21a967c26268/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= -github.com/imtbkcat/parser v0.0.0-20190412035857-e13e5e1ba3f6 h1:nlmD42DCidhlIF9gDmJJvKxIaWOABbiDAhgYHhWvke4= -github.com/imtbkcat/parser v0.0.0-20190412035857-e13e5e1ba3f6/go.mod h1:qupHD3o7J0aBb3bbVyXRnxe9kKy2MTY/6POS6NO/Ei8= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= From 0e645fdd265a2fada5d9dfd28ac53fb5ca2e6cac Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 12 Apr 2019 20:43:14 +0800 Subject: [PATCH 7/9] fix ci --- privilege/privileges/privileges_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index d909f57c03b87..74bda2aa5f3c5 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -260,6 +260,7 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, `GRANT Select,Insert,Update,Delete ON test.* TO 'testrole'@'localhost'`, `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} + c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) mustExec(c, se, `DROP ROLE 'r1', 'r2'`) mustExec(c, se, `DROP USER 'testrole'@'localhost'`) } From a6856df4d2e1f6838732e463537b5725799cf8c7 Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 12 Apr 2019 21:46:10 +0800 Subject: [PATCH 8/9] add more testcase --- privilege/privileges/privileges_test.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 74bda2aa5f3c5..0b2c4cac618ab 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -235,32 +235,27 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { // Test SHOW GRANTS with USING roles. mustExec(c, se, `CREATE ROLE 'r1', 'r2'`) mustExec(c, se, `GRANT SELECT ON test.* TO 'r1'`) - mustExec(c, se, `GRANT INSERT, UPDATE, DELETE ON test.* TO 'r2'`) + mustExec(c, se, `GRANT INSERT, UPDATE ON test.* TO 'r2'`) mustExec(c, se, `CREATE USER 'testrole'@'localhost' IDENTIFIED BY 'u1pass'`) mustExec(c, se, `GRANT 'r1', 'r2' TO 'testrole'@'localhost'`) gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, nil) c.Assert(err, IsNil) c.Assert(gs, HasLen, 2) - expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, - `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} - c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) - roles := make([]*auth.RoleIdentity, 0, 10) + roles := make([]*auth.RoleIdentity, 0) roles = append(roles, &auth.RoleIdentity{Username: "r2", Hostname: "%"}) + mustExec(c, se, `GRANT DELETE ON test.* TO 'testrole'@'localhost'`) gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, roles) c.Assert(err, IsNil) c.Assert(gs, HasLen, 3) - expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, - `GRANT Insert,Update,Delete ON test.* TO 'testrole'@'localhost'`, - `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} - c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) roles = append(roles, &auth.RoleIdentity{Username: "r1", Hostname: "%"}) gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, roles) c.Assert(err, IsNil) c.Assert(gs, HasLen, 3) - expected = []string{`GRANT USAGE ON *.* TO 'testrole'@'localhost'`, - `GRANT Select,Insert,Update,Delete ON test.* TO 'testrole'@'localhost'`, - `GRANT 'r1'@'%', 'r2'@'%' TO 'testrole'@'localhost'`} - c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue) + mustExec(c, se, `GRANT INSERT, DELETE ON test.test TO 'r2'`) + mustExec(c, se, `GRANT UPDATE ON a.b TO 'testrole'@'localhost'`) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, roles) + c.Assert(err, IsNil) + c.Assert(gs, HasLen, 5) mustExec(c, se, `DROP ROLE 'r1', 'r2'`) mustExec(c, se, `DROP USER 'testrole'@'localhost'`) } From dc0d7939987b82f7b5626ab26b54f8369005215b Mon Sep 17 00:00:00 2001 From: imtbkcat Date: Fri, 19 Apr 2019 10:57:19 +0800 Subject: [PATCH 9/9] add bfs search --- privilege/privileges/cache.go | 42 +++++++++++++++++++++---- privilege/privileges/privileges_test.go | 13 ++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/privilege/privileges/cache.go b/privilege/privileges/cache.go index 2b6cb4afeaf21..85121d30440e4 100644 --- a/privilege/privileges/cache.go +++ b/privilege/privileges/cache.go @@ -119,7 +119,7 @@ type defaultRoleRecord struct { // roleGraphEdgesTable is used to cache relationship between and role. type roleGraphEdgesTable struct { - roleList map[string]bool + roleList map[string]*auth.RoleIdentity } // Find method is used to find role from table @@ -145,6 +145,33 @@ type MySQLPrivilege struct { RoleGraph map[string]roleGraphEdgesTable } +// FindAllRole is used to find all roles grant to this user. +func (p *MySQLPrivilege) FindAllRole(activeRoles []*auth.RoleIdentity) []*auth.RoleIdentity { + queue, head := make([]*auth.RoleIdentity, 0, len(activeRoles)), 0 + for _, r := range activeRoles { + queue = append(queue, r) + } + // Using breadth first search to find all roles grant to this user. + visited, ret := make(map[string]bool), make([]*auth.RoleIdentity, 0) + for head < len(queue) { + role := queue[head] + if _, ok := visited[role.String()]; !ok { + visited[role.String()] = true + ret = append(ret, role) + key := role.Username + "@" + role.Hostname + if edgeTable, ok := p.RoleGraph[key]; ok { + for _, v := range edgeTable.roleList { + if _, ok := visited[v.String()]; !ok { + queue = append(queue, v) + } + } + } + } + head += 1 + } + return ret +} + // FindRole is used to detect whether there is edges between users and roles. func (p *MySQLPrivilege) FindRole(user string, host string, role *auth.RoleIdentity) bool { rec := p.matchUser(user, host) @@ -474,10 +501,10 @@ func (p *MySQLPrivilege) decodeRoleEdgesTable(row chunk.Row, fs []*ast.ResultFie toKey := toUser + "@" + toHost roleGraph, ok := p.RoleGraph[toKey] if !ok { - roleGraph = roleGraphEdgesTable{roleList: make(map[string]bool)} + roleGraph = roleGraphEdgesTable{roleList: make(map[string]*auth.RoleIdentity)} p.RoleGraph[toKey] = roleGraph } - roleGraph.roleList[fromKey] = true + roleGraph.roleList[fromKey] = &auth.RoleIdentity{Username: fromUser, Hostname: fromHost} return nil } @@ -703,6 +730,9 @@ func (p *MySQLPrivilege) DBIsVisible(user, host, db string) bool { func (p *MySQLPrivilege) showGrants(user, host string, roles []*auth.RoleIdentity) []string { var gs []string var hasGlobalGrant bool = false + // Some privileges may granted from role inheritance. + // We should find these inheritance relationship. + allRoles := p.FindAllRole(roles) // Show global grants. var currentPriv mysql.PrivilegeType var g string @@ -711,7 +741,7 @@ func (p *MySQLPrivilege) showGrants(user, host string, roles []*auth.RoleIdentit hasGlobalGrant = true currentPriv |= record.Privileges } else { - for _, r := range roles { + for _, r := range allRoles { if record.User == r.Username && record.Host == r.Hostname { hasGlobalGrant = true currentPriv |= record.Privileges @@ -741,7 +771,7 @@ func (p *MySQLPrivilege) showGrants(user, host string, roles []*auth.RoleIdentit dbPrivTable[record.DB] = record.Privileges } } else { - for _, r := range roles { + for _, r := range allRoles { if record.User == r.Username && record.Host == r.Hostname { if _, ok := dbPrivTable[record.DB]; ok { dbPrivTable[record.DB] |= record.Privileges @@ -771,7 +801,7 @@ func (p *MySQLPrivilege) showGrants(user, host string, roles []*auth.RoleIdentit tablePrivTable[recordKey] = record.TablePriv } } else { - for _, r := range roles { + for _, r := range allRoles { if record.User == r.Username && record.Host == r.Hostname { if _, ok := dbPrivTable[record.DB]; ok { tablePrivTable[recordKey] |= record.TablePriv diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 0b2c4cac618ab..85dc130203fc6 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -258,6 +258,19 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { c.Assert(gs, HasLen, 5) mustExec(c, se, `DROP ROLE 'r1', 'r2'`) mustExec(c, se, `DROP USER 'testrole'@'localhost'`) + mustExec(c, se, `CREATE ROLE 'r1', 'r2'`) + mustExec(c, se, `GRANT SELECT ON test.* TO 'r2'`) + mustExec(c, se, `CREATE USER 'testrole'@'localhost' IDENTIFIED BY 'u1pass'`) + mustExec(c, se, `GRANT 'r1' TO 'testrole'@'localhost'`) + mustExec(c, se, `GRANT 'r2' TO 'r1'`) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, nil) + c.Assert(err, IsNil) + c.Assert(gs, HasLen, 2) + roles = make([]*auth.RoleIdentity, 0) + roles = append(roles, &auth.RoleIdentity{Username: "r1", Hostname: "%"}) + gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "testrole", Hostname: "localhost"}, roles) + c.Assert(err, IsNil) + c.Assert(gs, HasLen, 3) } func (s *testPrivilegeSuite) TestDropTablePriv(c *C) {