diff --git a/infoschema/perfschema/tables.go b/infoschema/perfschema/tables.go index e06d2b15e29c3..fe8bde1000438 100644 --- a/infoschema/perfschema/tables.go +++ b/infoschema/perfschema/tables.go @@ -14,6 +14,8 @@ package perfschema import ( + "strings" + "github.com/pingcap/parser/model" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" @@ -36,6 +38,17 @@ const ( tableNameTiDBProfileGoroutines = "tidb_profile_goroutines" ) +var tableList = []string{ + tableNameEventsStatementsSummaryByDigest, + tableNameTiDBProfileCPU, + tableNameTiDBProfileMemory, + tableNameTiDBProfileMutex, + tableNameTiDBProfileAllocs, + tableNameTiDBProfileBlock, + tableNameTiDBProfileGoroutines, + tableNameEventsStatementsSummaryByDigestHistory, +} + // perfSchemaTable stands for the fake table all its data is in the memory. type perfSchemaTable struct { infoschema.VirtualTable @@ -45,6 +58,16 @@ type perfSchemaTable struct { var pluginTable = make(map[string]func(autoid.Allocators, *model.TableInfo) (table.Table, error)) +// IsPredefinedTable judges whether this table is predefined. +func IsPredefinedTable(tableName string) bool { + for _, name := range tableList { + if strings.EqualFold(tableName, name) { + return true + } + } + return false +} + // RegisterTable registers a new table into TiDB. func RegisterTable(tableName, sql string, tableFromMeta func(autoid.Allocators, *model.TableInfo) (table.Table, error)) { diff --git a/infoschema/perfschema/tables_test.go b/infoschema/perfschema/tables_test.go index 87371bd1c57c8..e34126252827c 100644 --- a/infoschema/perfschema/tables_test.go +++ b/infoschema/perfschema/tables_test.go @@ -20,6 +20,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/infoschema/perfschema" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" @@ -56,6 +57,11 @@ func (s *testTableSuite) TearDownSuite(c *C) { s.store.Close() } +func (s *testTableSuite) TestPredefinedTables(c *C) { + c.Assert(perfschema.IsPredefinedTable("EVENTS_statements_summary_by_digest"), IsTrue) + c.Assert(perfschema.IsPredefinedTable("statements"), IsFalse) +} + func (s *testTableSuite) TestPerfSchemaTables(c *C) { tk := testkit.NewTestKit(c, s.store) diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index 0aba853727e6f..ddaae0ff83726 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -21,6 +21,7 @@ import ( "github.com/pingcap/parser/auth" "github.com/pingcap/parser/mysql" + "github.com/pingcap/tidb/infoschema/perfschema" "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" @@ -52,15 +53,29 @@ func (p *UserPrivileges) RequestVerification(activeRoles []*auth.RoleIdentity, d return true } - // Skip check for INFORMATION_SCHEMA database. + // Skip check for system databases. // See https://dev.mysql.com/doc/refman/5.7/en/information-schema.html - if strings.EqualFold(db, "INFORMATION_SCHEMA") { + dbLowerName := strings.ToLower(db) + switch dbLowerName { + case util.InformationSchemaName.L: switch priv { case mysql.CreatePriv, mysql.AlterPriv, mysql.DropPriv, mysql.IndexPriv, mysql.CreateViewPriv, mysql.InsertPriv, mysql.UpdatePriv, mysql.DeletePriv: return false } return true + // We should be very careful of limiting privileges, so ignore `mysql` for now. + case util.PerformanceSchemaName.L: + // CREATE and DROP privileges are not limited in the older versions, so ignore them now. + // User may have created some tables in these schema, but predefined tables can't be altered or modified. + if dbLowerName == util.PerformanceSchemaName.L && perfschema.IsPredefinedTable(table) { + switch priv { + case mysql.AlterPriv, mysql.DropPriv, mysql.IndexPriv, mysql.InsertPriv, mysql.UpdatePriv, mysql.DeletePriv: + return false + case mysql.SelectPriv: + return true + } + } } mysqlPriv := p.Handle.Get() diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index ceb569fa123e1..20684571e63d5 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -880,8 +880,7 @@ func (s *testPrivilegeSuite) TestAnalyzeTable(c *C) { } -func (s *testPrivilegeSuite) TestInformationSchema(c *C) { - +func (s *testPrivilegeSuite) TestSystemSchema(c *C) { // This test tests no privilege check for INFORMATION_SCHEMA database. se := newSession(c, s.store, s.dbName) mustExec(c, se, `CREATE USER 'u1'@'localhost';`) @@ -894,6 +893,15 @@ func (s *testPrivilegeSuite) TestInformationSchema(c *C) { c.Assert(strings.Contains(err.Error(), "denied to user"), IsTrue) _, err = se.Execute(context.Background(), "update information_schema.tables set table_name = 'tst' where table_name = 'mysql'") c.Assert(strings.Contains(err.Error(), "privilege check fail"), IsTrue) + + // Test performance_schema. + mustExec(c, se, `select * from performance_schema.events_statements_summary_by_digest`) + _, err = se.Execute(context.Background(), "drop table performance_schema.events_statements_summary_by_digest") + c.Assert(strings.Contains(err.Error(), "denied to user"), IsTrue) + _, err = se.Execute(context.Background(), "update performance_schema.events_statements_summary_by_digest set table_names = 'tst'") + c.Assert(strings.Contains(err.Error(), "privilege check fail"), IsTrue) + _, err = se.Execute(context.Background(), "delete from performance_schema.events_statements_summary_by_digest") + c.Assert(strings.Contains(err.Error(), "privilege check fail"), IsTrue) } func (s *testPrivilegeSuite) TestAdminCommand(c *C) {