From f1363e469b76c9eb7e6f14a371d7c896e269ced2 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 25 Apr 2019 10:33:29 +0800 Subject: [PATCH 1/2] privilege,executor: update DBIsVisible() function for RBAC `USE DB` check privileges using the DBIsVisible function, that function should take role into consideration CREATE DATABASE app_db CREATE ROLE 'app_developer' GRANT ALL ON app_db.* TO 'app_developer CREATE USER 'dev'@'localhost GRANT 'app_developer' TO 'dev'@'localhost' SET DEFAULT ROLE 'app_developer' TO 'dev'@'localhost' login as 'dev'@'localhost' Before: mysql> use app_db ERROR 1044 (42000): Access denied for user 'dev'@'localhost' to database 'app_db' After: mysql> use app_db; Database changed --- executor/show.go | 20 +++++++++++++------- executor/simple.go | 6 ++++-- privilege/privilege.go | 2 +- privilege/privileges/privileges.go | 12 ++++++++++-- privilege/privileges/privileges_test.go | 17 ++++++++++++++++- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/executor/show.go b/executor/show.go index 99b1aa7a51050..cd1564e06448f 100644 --- a/executor/show.go +++ b/executor/show.go @@ -234,7 +234,7 @@ func (e *ShowExec) fetchShowDatabases() error { // let information_schema be the first database moveInfoSchemaToFront(dbs) for _, d := range dbs { - if checker != nil && !checker.DBIsVisible(d) { + if checker != nil && !checker.DBIsVisible(e.ctx.GetSessionVars().ActiveRoles, d) { continue } e.appendRow([]interface{}{ @@ -279,8 +279,10 @@ func (e *ShowExec) fetchShowOpenTables() error { func (e *ShowExec) fetchShowTables() error { checker := privilege.GetPrivilegeManager(e.ctx) - if checker != nil && e.ctx.GetSessionVars().User != nil && !checker.DBIsVisible(e.DBName.O) { - return e.dbAccessDenied() + if checker != nil && e.ctx.GetSessionVars().User != nil { + if !checker.DBIsVisible(e.ctx.GetSessionVars().ActiveRoles, e.DBName.O) { + return e.dbAccessDenied() + } } if !e.is.SchemaExists(e.DBName) { return ErrBadDB.GenWithStackByArgs(e.DBName) @@ -315,8 +317,10 @@ func (e *ShowExec) fetchShowTables() error { func (e *ShowExec) fetchShowTableStatus() error { checker := privilege.GetPrivilegeManager(e.ctx) - if checker != nil && e.ctx.GetSessionVars().User != nil && !checker.DBIsVisible(e.DBName.O) { - return e.dbAccessDenied() + if checker != nil && e.ctx.GetSessionVars().User != nil { + if !checker.DBIsVisible(e.ctx.GetSessionVars().ActiveRoles, e.DBName.O) { + return e.dbAccessDenied() + } } if !e.is.SchemaExists(e.DBName) { return ErrBadDB.GenWithStackByArgs(e.DBName) @@ -879,8 +883,10 @@ func appendPartitionInfo(partitionInfo *model.PartitionInfo, buf *bytes.Buffer) // fetchShowCreateDatabase composes show create database result. func (e *ShowExec) fetchShowCreateDatabase() error { checker := privilege.GetPrivilegeManager(e.ctx) - if checker != nil && e.ctx.GetSessionVars().User != nil && !checker.DBIsVisible(fmt.Sprint(e.DBName)) { - return e.dbAccessDenied() + if checker != nil && e.ctx.GetSessionVars().User != nil { + if !checker.DBIsVisible(e.ctx.GetSessionVars().ActiveRoles, e.DBName.String()) { + return e.dbAccessDenied() + } } db, ok := e.is.SchemaByName(e.DBName) if !ok { diff --git a/executor/simple.go b/executor/simple.go index d4199c4950858..268678b113521 100644 --- a/executor/simple.go +++ b/executor/simple.go @@ -269,8 +269,10 @@ func (e *SimpleExec) executeUse(s *ast.UseStmt) error { dbname := model.NewCIStr(s.DBName) checker := privilege.GetPrivilegeManager(e.ctx) - if checker != nil && e.ctx.GetSessionVars().User != nil && !checker.DBIsVisible(fmt.Sprint(dbname)) { - return e.dbAccessDenied(dbname.O) + if checker != nil && e.ctx.GetSessionVars().User != nil { + if !checker.DBIsVisible(e.ctx.GetSessionVars().ActiveRoles, dbname.String()) { + return e.dbAccessDenied(dbname.O) + } } dbinfo, exists := e.is.SchemaByName(dbname) diff --git a/privilege/privilege.go b/privilege/privilege.go index 0b33276284024..2946a686c4ddb 100644 --- a/privilege/privilege.go +++ b/privilege/privilege.go @@ -48,7 +48,7 @@ type Manager interface { ConnectionVerification(user, host string, auth, salt []byte) (string, string, bool) // DBIsVisible returns true is the database is visible to current user. - DBIsVisible(db string) bool + DBIsVisible(activeRole []*auth.RoleIdentity, db string) bool // UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGE table. UserPrivilegesTable() [][]types.Datum diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index 3535e8c464ccf..d151f399809c9 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -160,12 +160,20 @@ func (p *UserPrivileges) ConnectionVerification(user, host string, authenticatio } // DBIsVisible implements the Manager interface. -func (p *UserPrivileges) DBIsVisible(db string) bool { +func (p *UserPrivileges) DBIsVisible(activeRoles []*auth.RoleIdentity, db string) bool { if SkipWithGrant { return true } mysqlPriv := p.Handle.Get() - return mysqlPriv.DBIsVisible(p.user, p.host, db) + if mysqlPriv.DBIsVisible(p.user, p.host, db) { + return true + } + for _, role := range activeRoles { + if mysqlPriv.DBIsVisible(role.Username, role.Hostname, db) { + return true + } + } + return false } // UserPrivilegesTable implements the Manager interface. diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 5a53d780652c1..42a9b083a16cd 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -434,7 +434,7 @@ func (s *testPrivilegeSuite) TestCheckAuthenticate(c *C) { mustExec(c, se1, "drop user 'r3@example.com'@'localhost'") } -func (s *testPrivilegeSuite) TestUseDb(c *C) { +func (s *testPrivilegeSuite) TestUseDB(c *C) { se := newSession(c, s.store, s.dbName) // high privileged user @@ -454,6 +454,21 @@ func (s *testPrivilegeSuite) TestUseDb(c *C) { c.Assert(se.Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil), IsTrue) _, err = se.Execute(context.Background(), "use mysql") c.Assert(err, IsNil) + + // test `use db` for role. + c.Assert(se.Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil), IsTrue) + mustExec(c, se, `CREATE DATABASE app_db`) + mustExec(c, se, `CREATE ROLE 'app_developer'`) + mustExec(c, se, `GRANT ALL ON app_db.* TO 'app_developer'`) + mustExec(c, se, `CREATE USER 'dev'@'localhost'`) + mustExec(c, se, `GRANT 'app_developer' TO 'dev'@'localhost'`) + mustExec(c, se, `SET DEFAULT ROLE 'app_developer' TO 'dev'@'localhost'`) + mustExec(c, se, `FLUSH PRIVILEGES`) + c.Assert(se.Auth(&auth.UserIdentity{Username: "dev", Hostname: "localhost", AuthUsername: "dev", AuthHostname: "localhost"}, nil, nil), IsTrue) + _, err = se.Execute(context.Background(), "use app_db") + c.Assert(err, IsNil) + _, err = se.Execute(context.Background(), "use mysql") + c.Assert(err, NotNil) } func (s *testPrivilegeSuite) TestSetGlobal(c *C) { From 9d2a77397b693a65ef915f6b4bf6c32fed260f12 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Wed, 8 May 2019 14:57:54 +0800 Subject: [PATCH 2/2] address comment --- privilege/privileges/privileges.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index d151f399809c9..446432cb2cc49 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -168,7 +168,8 @@ func (p *UserPrivileges) DBIsVisible(activeRoles []*auth.RoleIdentity, db string if mysqlPriv.DBIsVisible(p.user, p.host, db) { return true } - for _, role := range activeRoles { + allRoles := mysqlPriv.FindAllRole(activeRoles) + for _, role := range allRoles { if mysqlPriv.DBIsVisible(role.Username, role.Hostname, db) { return true }