diff --git a/executor/set_config.go b/executor/set_config.go index 4036963bd3c82..f7e2b824cc09f 100644 --- a/executor/set_config.go +++ b/executor/set_config.go @@ -23,11 +23,9 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util" @@ -46,12 +44,6 @@ type SetConfigExec struct { // Open implements the Executor Open interface. func (s *SetConfigExec) Open(ctx context.Context) error { - // TODO: create a new privilege for this operation instead of using the SuperPriv - checker := privilege.GetPrivilegeManager(s.ctx) - if checker != nil && !checker.RequestVerification(s.ctx.GetSessionVars().ActiveRoles, "", "", "", mysql.SuperPriv) { - return core.ErrSpecificAccessDenied.GenWithStackByArgs("SET CONFIG") - } - if s.p.Type != "" { s.p.Type = strings.ToLower(s.p.Type) if s.p.Type != "tikv" && s.p.Type != "tidb" && s.p.Type != "pd" { diff --git a/go.mod b/go.mod index e4c06903a76e3..48ca182d0ae79 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,11 @@ require ( github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200420075417-e0c6e8842f22 github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd +<<<<<<< HEAD github.com/pingcap/parser v0.0.0-20200422082501-7329d80eaf2c +======= + github.com/pingcap/parser v0.0.0-20200427031542-879c7bd4f27d +>>>>>>> 468b8c6... executor, privilege: introduce a new privilege for the `set config` statement (#16847) github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 github.com/pingcap/tidb-tools v4.0.0-beta.1.0.20200306084441-875bd09aa3d5+incompatible diff --git a/go.sum b/go.sum index 2c19548495d64..89f7d02f42557 100644 --- a/go.sum +++ b/go.sum @@ -282,8 +282,13 @@ github.com/pingcap/kvproto v0.0.0-20200420075417-e0c6e8842f22/go.mod h1:IOdRDPLy github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd h1:CV3VsP3Z02MVtdpTMfEgRJ4T9NGgGTxdHpJerent7rM= github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= +<<<<<<< HEAD github.com/pingcap/parser v0.0.0-20200422082501-7329d80eaf2c h1:eXC+xkHerLvR6+mceugr4e8ALAQHj25S5slt8A2f6Ho= github.com/pingcap/parser v0.0.0-20200422082501-7329d80eaf2c/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= +======= +github.com/pingcap/parser v0.0.0-20200427031542-879c7bd4f27d h1:iXKcmBOj5v8Vw4jbiWdY0LKyhJinSwqd1Hwyi0NvxBY= +github.com/pingcap/parser v0.0.0-20200427031542-879c7bd4f27d/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= +>>>>>>> 468b8c6... executor, privilege: introduce a new privilege for the `set config` statement (#16847) github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 4a04aebca1e79..13fa6d49faaf8 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -525,6 +525,8 @@ func (b *PlanBuilder) Build(ctx context.Context, node ast.Node) (Plan, error) { } func (b *PlanBuilder) buildSetConfig(ctx context.Context, v *ast.SetConfigStmt) (Plan, error) { + privErr := ErrSpecificAccessDenied.GenWithStackByArgs("CONFIG") + b.visitInfo = appendVisitInfo(b.visitInfo, mysql.ConfigPriv, "", "", "", privErr) mockTablePlan := LogicalTableDual{}.Init(b.ctx, b.getSelectOffset()) expr, _, err := b.rewrite(ctx, v.Value, mockTablePlan, nil, true) return &SetConfig{Name: v.Name, Type: v.Type, Instance: v.Instance, Value: expr}, err diff --git a/privilege/privileges/cache_test.go b/privilege/privileges/cache_test.go index bbc8a1462448b..aa3979f78ff02 100644 --- a/privilege/privileges/cache_test.go +++ b/privilege/privileges/cache_test.go @@ -384,6 +384,7 @@ func (s *testCacheSuite) TestAbnormalMySQLTable(c *C) { Shutdown_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', Process_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', File_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Config_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', Grant_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', References_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', Index_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', @@ -419,7 +420,7 @@ func (s *testCacheSuite) TestAbnormalMySQLTable(c *C) { password_expired enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', PRIMARY KEY (Host,User) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges';`) - mustExec(c, se, `INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'mysql_native_password','','N'); + mustExec(c, se, `INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'mysql_native_password','','N'); `) var p privileges.MySQLPrivilege err = p.LoadUserTable(se) diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index da250261145f8..0117db286141d 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -827,6 +827,24 @@ func (s *testPrivilegeSuite) TestCreateDropUser(c *C) { mustExec(c, se, `DROP USER tcd3`) } +func (s *testPrivilegeSuite) TestConfigPrivilege(c *C) { + se := newSession(c, s.store, s.dbName) + mustExec(c, se, `DROP USER IF EXISTS tcd1`) + mustExec(c, se, `CREATE USER tcd1`) + mustExec(c, se, `GRANT ALL ON *.* to tcd1`) + mustExec(c, se, `DROP USER IF EXISTS tcd2`) + mustExec(c, se, `CREATE USER tcd2`) + mustExec(c, se, `GRANT ALL ON *.* to tcd2`) + mustExec(c, se, `REVOKE CONFIG ON *.* FROM tcd2`) + + c.Assert(se.Auth(&auth.UserIdentity{Username: "tcd1", Hostname: "localhost", AuthHostname: "tcd1", AuthUsername: "%"}, nil, nil), IsTrue) + mustExec(c, se, `SET CONFIG TIKV testkey="testval"`) + c.Assert(se.Auth(&auth.UserIdentity{Username: "tcd2", Hostname: "localhost", AuthHostname: "tcd2", AuthUsername: "%"}, nil, nil), IsTrue) + _, err := se.Execute(context.Background(), `SET CONFIG TIKV testkey="testval"`) + c.Assert(err, ErrorMatches, ".*you need \\(at least one of\\) the CONFIG privilege\\(s\\) for this operation") + mustExec(c, se, `DROP USER tcd1, tcd2`) +} + func (s *testPrivilegeSuite) TestShowCreateTable(c *C) { se := newSession(c, s.store, s.dbName) mustExec(c, se, `CREATE USER tsct1, tsct2`) diff --git a/session/bootstrap.go b/session/bootstrap.go index 5a89b0c6037a1..67b1a285c07a7 100644 --- a/session/bootstrap.go +++ b/session/bootstrap.go @@ -77,6 +77,7 @@ const ( Shutdown_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Reload_priv ENUM('N','Y') NOT NULL DEFAULT 'N', FILE_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Config_priv ENUM('N','Y') NOT NULL DEFAULT 'N', PRIMARY KEY (Host, User));` // CreateGlobalPrivTable is the SQL statement creates Global scope privilege table in system db. CreateGlobalPrivTable = "CREATE TABLE if not exists mysql.global_priv (" + @@ -381,6 +382,8 @@ const ( version43 = 43 // version44 delete tidb_isolation_read_engines from mysql.global_variables to avoid unexpected behavior after upgrade. version44 = 44 + // version45 introduces CONFIG_PRIV for SET CONFIG statements. + version45 = 45 ) var ( @@ -428,6 +431,7 @@ var ( upgradeToVer42, upgradeToVer43, upgradeToVer44, + upgradeToVer45, } ) @@ -1033,6 +1037,14 @@ func upgradeToVer44(s Session, ver int64) { mustExecute(s, "DELETE FROM mysql.global_variables where variable_name = \"tidb_isolation_read_engines\"") } +func upgradeToVer45(s Session, ver int64) { + if ver >= version45 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Config_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Config_priv='Y' where Super_priv='Y'") +} + // updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. func updateBootstrapVer(s Session) { // Update bootstrap version. @@ -1105,7 +1117,7 @@ func doDMLWorks(s Session) { // Insert a default user with empty password. mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.user VALUES - ("%", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y")`) + ("%", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y")`) // Init global system variables table. values := make([]string, 0, len(variable.SysVars)) diff --git a/session/bootstrap_test.go b/session/bootstrap_test.go index 3c091c9745c3a..f03e66300ffb8 100644 --- a/session/bootstrap_test.go +++ b/session/bootstrap_test.go @@ -54,7 +54,7 @@ func (s *testBootstrapSuite) TestBootstrap(c *C) { c.Assert(err, IsNil) c.Assert(req.NumRows() == 0, IsFalse) datums := statistics.RowToDatums(req.GetRow(0), r.Fields()) - match(c, datums, `%`, "root", []byte(""), "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y") + match(c, datums, `%`, "root", []byte(""), "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y") c.Assert(se.Auth(&auth.UserIdentity{Username: "root", Hostname: "anyhost"}, []byte(""), []byte("")), IsTrue) mustExecSQL(c, se, "USE test;") @@ -159,7 +159,7 @@ func (s *testBootstrapSuite) TestBootstrapWithError(c *C) { c.Assert(req.NumRows() == 0, IsFalse) row := req.GetRow(0) datums := statistics.RowToDatums(row, r.Fields()) - match(c, datums, `%`, "root", []byte(""), "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y") + match(c, datums, `%`, "root", []byte(""), "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y") c.Assert(r.Close(), IsNil) mustExecSQL(c, se, "USE test;") diff --git a/session/session.go b/session/session.go index 43a93f40d7b3f..3b040c1ec1719 100644 --- a/session/session.go +++ b/session/session.go @@ -1829,7 +1829,7 @@ func CreateSessionWithDomain(store kv.Storage, dom *domain.Domain) (*session, er const ( notBootstrapped = 0 - currentBootstrapVersion = version44 + currentBootstrapVersion = version45 ) func getStoreBootstrapVersion(store kv.Storage) int64 {