diff --git a/pkg/planner/core/expression_rewriter.go b/pkg/planner/core/expression_rewriter.go index bf362c6af2212..3d9cdece9ea81 100644 --- a/pkg/planner/core/expression_rewriter.go +++ b/pkg/planner/core/expression_rewriter.go @@ -1681,7 +1681,7 @@ func (er *expressionRewriter) rewriteVariable(planCtx *exprRewriterPlanCtx, v *a } if sem.IsEnabled() && sem.IsInvisibleSysVar(sysVar.Name) { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("RESTRICTED_VARIABLES_ADMIN") - planCtx.builder.visitInfo = appendDynamicVisitInfo(planCtx.builder.visitInfo, "RESTRICTED_VARIABLES_ADMIN", false, err) + planCtx.builder.visitInfo = appendDynamicVisitInfo(planCtx.builder.visitInfo, []string{"RESTRICTED_VARIABLES_ADMIN"}, false, err) } if v.ExplicitScope && !sysVar.HasNoneScope() { if v.IsGlobal && !(sysVar.HasGlobalScope() || sysVar.HasInstanceScope()) { diff --git a/pkg/planner/core/logical_plan_builder.go b/pkg/planner/core/logical_plan_builder.go index 5a52cf5ff09eb..705d6e7334b4f 100644 --- a/pkg/planner/core/logical_plan_builder.go +++ b/pkg/planner/core/logical_plan_builder.go @@ -7288,10 +7288,10 @@ func collectTableName(node ast.ResultSetNode, updatableName *map[string]bool, in } } -func appendDynamicVisitInfo(vi []visitInfo, priv string, withGrant bool, err error) []visitInfo { +func appendDynamicVisitInfo(vi []visitInfo, privs []string, withGrant bool, err error) []visitInfo { return append(vi, visitInfo{ privilege: mysql.ExtendedPriv, - dynamicPriv: priv, + dynamicPrivs: privs, dynamicWithGrant: withGrant, err: err, }) diff --git a/pkg/planner/core/logical_plans_test.go b/pkg/planner/core/logical_plans_test.go index 7cc42eb2b5129..a2c5c8910493f 100644 --- a/pkg/planner/core/logical_plans_test.go +++ b/pkg/planner/core/logical_plans_test.go @@ -1097,27 +1097,27 @@ func TestVisitInfo(t *testing.T) { { sql: "insert into t (a) values (1)", ans: []visitInfo{ - {mysql.InsertPriv, "test", "t", "", nil, false, "", false}, + {mysql.InsertPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "delete from t where a = 1", ans: []visitInfo{ - {mysql.DeletePriv, "test", "t", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.DeletePriv, "test", "t", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "delete from t order by a", ans: []visitInfo{ - {mysql.DeletePriv, "test", "t", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.DeletePriv, "test", "t", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "delete from t", ans: []visitInfo{ - {mysql.DeletePriv, "test", "t", "", nil, false, "", false}, + {mysql.DeletePriv, "test", "t", "", nil, false, nil, false}, }, }, /* Not currently supported. See https://github.com/pingcap/tidb/issues/23644 @@ -1131,234 +1131,234 @@ func TestVisitInfo(t *testing.T) { { sql: "delete from a1 using t as a1 inner join t as a2 where a1.a = a2.a", ans: []visitInfo{ - {mysql.DeletePriv, "test", "t", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.DeletePriv, "test", "t", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "update t set a = 7 where a = 1", ans: []visitInfo{ - {mysql.UpdatePriv, "test", "t", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.UpdatePriv, "test", "t", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "update t, (select * from t) a1 set t.a = a1.a;", ans: []visitInfo{ - {mysql.UpdatePriv, "test", "t", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.UpdatePriv, "test", "t", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "update t a1 set a1.a = a1.a + 1", ans: []visitInfo{ - {mysql.UpdatePriv, "test", "t", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.UpdatePriv, "test", "t", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "select a, sum(e) from t group by a", ans: []visitInfo{ - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "truncate table t", ans: []visitInfo{ - {mysql.DropPriv, "test", "t", "", nil, false, "", false}, + {mysql.DropPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "drop table t", ans: []visitInfo{ - {mysql.DropPriv, "test", "t", "", nil, false, "", false}, + {mysql.DropPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "create table t (a int)", ans: []visitInfo{ - {mysql.CreatePriv, "test", "t", "", nil, false, "", false}, + {mysql.CreatePriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "create table t1 like t", ans: []visitInfo{ - {mysql.CreatePriv, "test", "t1", "", nil, false, "", false}, - {mysql.SelectPriv, "test", "t", "", nil, false, "", false}, + {mysql.CreatePriv, "test", "t1", "", nil, false, nil, false}, + {mysql.SelectPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "create database test", ans: []visitInfo{ - {mysql.CreatePriv, "test", "", "", nil, false, "", false}, + {mysql.CreatePriv, "test", "", "", nil, false, nil, false}, }, }, { sql: "drop database test", ans: []visitInfo{ - {mysql.DropPriv, "test", "", "", nil, false, "", false}, + {mysql.DropPriv, "test", "", "", nil, false, nil, false}, }, }, { sql: "create index t_1 on t (a)", ans: []visitInfo{ - {mysql.IndexPriv, "test", "t", "", nil, false, "", false}, + {mysql.IndexPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "drop index e on t", ans: []visitInfo{ - {mysql.IndexPriv, "test", "t", "", nil, false, "", false}, + {mysql.IndexPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: `grant all privileges on test.* to 'test'@'%'`, ans: []visitInfo{ - {mysql.SelectPriv, "test", "", "", nil, false, "", false}, - {mysql.InsertPriv, "test", "", "", nil, false, "", false}, - {mysql.UpdatePriv, "test", "", "", nil, false, "", false}, - {mysql.DeletePriv, "test", "", "", nil, false, "", false}, - {mysql.CreatePriv, "test", "", "", nil, false, "", false}, - {mysql.DropPriv, "test", "", "", nil, false, "", false}, - {mysql.GrantPriv, "test", "", "", nil, false, "", false}, - {mysql.ReferencesPriv, "test", "", "", nil, false, "", false}, - {mysql.LockTablesPriv, "test", "", "", nil, false, "", false}, - {mysql.CreateTMPTablePriv, "test", "", "", nil, false, "", false}, - {mysql.EventPriv, "test", "", "", nil, false, "", false}, - {mysql.CreateRoutinePriv, "test", "", "", nil, false, "", false}, - {mysql.AlterRoutinePriv, "test", "", "", nil, false, "", false}, - {mysql.AlterPriv, "test", "", "", nil, false, "", false}, - {mysql.ExecutePriv, "test", "", "", nil, false, "", false}, - {mysql.IndexPriv, "test", "", "", nil, false, "", false}, - {mysql.CreateViewPriv, "test", "", "", nil, false, "", false}, - {mysql.ShowViewPriv, "test", "", "", nil, false, "", false}, - {mysql.TriggerPriv, "test", "", "", nil, false, "", false}, + {mysql.SelectPriv, "test", "", "", nil, false, nil, false}, + {mysql.InsertPriv, "test", "", "", nil, false, nil, false}, + {mysql.UpdatePriv, "test", "", "", nil, false, nil, false}, + {mysql.DeletePriv, "test", "", "", nil, false, nil, false}, + {mysql.CreatePriv, "test", "", "", nil, false, nil, false}, + {mysql.DropPriv, "test", "", "", nil, false, nil, false}, + {mysql.GrantPriv, "test", "", "", nil, false, nil, false}, + {mysql.ReferencesPriv, "test", "", "", nil, false, nil, false}, + {mysql.LockTablesPriv, "test", "", "", nil, false, nil, false}, + {mysql.CreateTMPTablePriv, "test", "", "", nil, false, nil, false}, + {mysql.EventPriv, "test", "", "", nil, false, nil, false}, + {mysql.CreateRoutinePriv, "test", "", "", nil, false, nil, false}, + {mysql.AlterRoutinePriv, "test", "", "", nil, false, nil, false}, + {mysql.AlterPriv, "test", "", "", nil, false, nil, false}, + {mysql.ExecutePriv, "test", "", "", nil, false, nil, false}, + {mysql.IndexPriv, "test", "", "", nil, false, nil, false}, + {mysql.CreateViewPriv, "test", "", "", nil, false, nil, false}, + {mysql.ShowViewPriv, "test", "", "", nil, false, nil, false}, + {mysql.TriggerPriv, "test", "", "", nil, false, nil, false}, }, }, { sql: `grant all privileges on *.* to 'test'@'%'`, ans: []visitInfo{ - {mysql.SelectPriv, "", "", "", nil, false, "", false}, - {mysql.InsertPriv, "", "", "", nil, false, "", false}, - {mysql.UpdatePriv, "", "", "", nil, false, "", false}, - {mysql.DeletePriv, "", "", "", nil, false, "", false}, - {mysql.CreatePriv, "", "", "", nil, false, "", false}, - {mysql.DropPriv, "", "", "", nil, false, "", false}, - {mysql.ProcessPriv, "", "", "", nil, false, "", false}, - {mysql.ReferencesPriv, "", "", "", nil, false, "", false}, - {mysql.AlterPriv, "", "", "", nil, false, "", false}, - {mysql.ShowDBPriv, "", "", "", nil, false, "", false}, - {mysql.SuperPriv, "", "", "", nil, false, "", false}, - {mysql.ExecutePriv, "", "", "", nil, false, "", false}, - {mysql.IndexPriv, "", "", "", nil, false, "", false}, - {mysql.CreateUserPriv, "", "", "", nil, false, "", false}, - {mysql.CreateTablespacePriv, "", "", "", nil, false, "", false}, - {mysql.TriggerPriv, "", "", "", nil, false, "", false}, - {mysql.CreateViewPriv, "", "", "", nil, false, "", false}, - {mysql.ShowViewPriv, "", "", "", nil, false, "", false}, - {mysql.CreateRolePriv, "", "", "", nil, false, "", false}, - {mysql.DropRolePriv, "", "", "", nil, false, "", false}, - {mysql.CreateTMPTablePriv, "", "", "", nil, false, "", false}, - {mysql.LockTablesPriv, "", "", "", nil, false, "", false}, - {mysql.CreateRoutinePriv, "", "", "", nil, false, "", false}, - {mysql.AlterRoutinePriv, "", "", "", nil, false, "", false}, - {mysql.EventPriv, "", "", "", nil, false, "", false}, - {mysql.ShutdownPriv, "", "", "", nil, false, "", false}, - {mysql.ReloadPriv, "", "", "", nil, false, "", false}, - {mysql.FilePriv, "", "", "", nil, false, "", false}, - {mysql.ConfigPriv, "", "", "", nil, false, "", false}, - {mysql.ReplicationClientPriv, "", "", "", nil, false, "", false}, - {mysql.ReplicationSlavePriv, "", "", "", nil, false, "", false}, - {mysql.GrantPriv, "", "", "", nil, false, "", false}, + {mysql.SelectPriv, "", "", "", nil, false, nil, false}, + {mysql.InsertPriv, "", "", "", nil, false, nil, false}, + {mysql.UpdatePriv, "", "", "", nil, false, nil, false}, + {mysql.DeletePriv, "", "", "", nil, false, nil, false}, + {mysql.CreatePriv, "", "", "", nil, false, nil, false}, + {mysql.DropPriv, "", "", "", nil, false, nil, false}, + {mysql.ProcessPriv, "", "", "", nil, false, nil, false}, + {mysql.ReferencesPriv, "", "", "", nil, false, nil, false}, + {mysql.AlterPriv, "", "", "", nil, false, nil, false}, + {mysql.ShowDBPriv, "", "", "", nil, false, nil, false}, + {mysql.SuperPriv, "", "", "", nil, false, nil, false}, + {mysql.ExecutePriv, "", "", "", nil, false, nil, false}, + {mysql.IndexPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateUserPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateTablespacePriv, "", "", "", nil, false, nil, false}, + {mysql.TriggerPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateViewPriv, "", "", "", nil, false, nil, false}, + {mysql.ShowViewPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateRolePriv, "", "", "", nil, false, nil, false}, + {mysql.DropRolePriv, "", "", "", nil, false, nil, false}, + {mysql.CreateTMPTablePriv, "", "", "", nil, false, nil, false}, + {mysql.LockTablesPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateRoutinePriv, "", "", "", nil, false, nil, false}, + {mysql.AlterRoutinePriv, "", "", "", nil, false, nil, false}, + {mysql.EventPriv, "", "", "", nil, false, nil, false}, + {mysql.ShutdownPriv, "", "", "", nil, false, nil, false}, + {mysql.ReloadPriv, "", "", "", nil, false, nil, false}, + {mysql.FilePriv, "", "", "", nil, false, nil, false}, + {mysql.ConfigPriv, "", "", "", nil, false, nil, false}, + {mysql.ReplicationClientPriv, "", "", "", nil, false, nil, false}, + {mysql.ReplicationSlavePriv, "", "", "", nil, false, nil, false}, + {mysql.GrantPriv, "", "", "", nil, false, nil, false}, }, }, { sql: `grant select on test.ttt to 'test'@'%'`, ans: []visitInfo{ - {mysql.SelectPriv, "test", "ttt", "", nil, false, "", false}, - {mysql.GrantPriv, "test", "ttt", "", nil, false, "", false}, + {mysql.SelectPriv, "test", "ttt", "", nil, false, nil, false}, + {mysql.GrantPriv, "test", "ttt", "", nil, false, nil, false}, }, }, { sql: `grant select on ttt to 'test'@'%'`, ans: []visitInfo{ - {mysql.SelectPriv, "test", "ttt", "", nil, false, "", false}, - {mysql.GrantPriv, "test", "ttt", "", nil, false, "", false}, + {mysql.SelectPriv, "test", "ttt", "", nil, false, nil, false}, + {mysql.GrantPriv, "test", "ttt", "", nil, false, nil, false}, }, }, { sql: `revoke all privileges on test.* from 'test'@'%'`, ans: []visitInfo{ - {mysql.SelectPriv, "test", "", "", nil, false, "", false}, - {mysql.InsertPriv, "test", "", "", nil, false, "", false}, - {mysql.UpdatePriv, "test", "", "", nil, false, "", false}, - {mysql.DeletePriv, "test", "", "", nil, false, "", false}, - {mysql.CreatePriv, "test", "", "", nil, false, "", false}, - {mysql.DropPriv, "test", "", "", nil, false, "", false}, - {mysql.GrantPriv, "test", "", "", nil, false, "", false}, - {mysql.ReferencesPriv, "test", "", "", nil, false, "", false}, - {mysql.LockTablesPriv, "test", "", "", nil, false, "", false}, - {mysql.CreateTMPTablePriv, "test", "", "", nil, false, "", false}, - {mysql.EventPriv, "test", "", "", nil, false, "", false}, - {mysql.CreateRoutinePriv, "test", "", "", nil, false, "", false}, - {mysql.AlterRoutinePriv, "test", "", "", nil, false, "", false}, - {mysql.AlterPriv, "test", "", "", nil, false, "", false}, - {mysql.ExecutePriv, "test", "", "", nil, false, "", false}, - {mysql.IndexPriv, "test", "", "", nil, false, "", false}, - {mysql.CreateViewPriv, "test", "", "", nil, false, "", false}, - {mysql.ShowViewPriv, "test", "", "", nil, false, "", false}, - {mysql.TriggerPriv, "test", "", "", nil, false, "", false}, + {mysql.SelectPriv, "test", "", "", nil, false, nil, false}, + {mysql.InsertPriv, "test", "", "", nil, false, nil, false}, + {mysql.UpdatePriv, "test", "", "", nil, false, nil, false}, + {mysql.DeletePriv, "test", "", "", nil, false, nil, false}, + {mysql.CreatePriv, "test", "", "", nil, false, nil, false}, + {mysql.DropPriv, "test", "", "", nil, false, nil, false}, + {mysql.GrantPriv, "test", "", "", nil, false, nil, false}, + {mysql.ReferencesPriv, "test", "", "", nil, false, nil, false}, + {mysql.LockTablesPriv, "test", "", "", nil, false, nil, false}, + {mysql.CreateTMPTablePriv, "test", "", "", nil, false, nil, false}, + {mysql.EventPriv, "test", "", "", nil, false, nil, false}, + {mysql.CreateRoutinePriv, "test", "", "", nil, false, nil, false}, + {mysql.AlterRoutinePriv, "test", "", "", nil, false, nil, false}, + {mysql.AlterPriv, "test", "", "", nil, false, nil, false}, + {mysql.ExecutePriv, "test", "", "", nil, false, nil, false}, + {mysql.IndexPriv, "test", "", "", nil, false, nil, false}, + {mysql.CreateViewPriv, "test", "", "", nil, false, nil, false}, + {mysql.ShowViewPriv, "test", "", "", nil, false, nil, false}, + {mysql.TriggerPriv, "test", "", "", nil, false, nil, false}, }, }, { sql: `revoke connection_admin on *.* from u1`, ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", nil, false, "CONNECTION_ADMIN", true}, + {mysql.ExtendedPriv, "", "", "", nil, false, []string{"CONNECTION_ADMIN"}, true}, }, }, { sql: `revoke connection_admin, select on *.* from u1`, ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", nil, false, "CONNECTION_ADMIN", true}, - {mysql.SelectPriv, "", "", "", nil, false, "", false}, - {mysql.GrantPriv, "", "", "", nil, false, "", false}, + {mysql.ExtendedPriv, "", "", "", nil, false, []string{"CONNECTION_ADMIN"}, true}, + {mysql.SelectPriv, "", "", "", nil, false, nil, false}, + {mysql.GrantPriv, "", "", "", nil, false, nil, false}, }, }, { sql: `revoke all privileges on *.* FROM u1`, ans: []visitInfo{ - {mysql.SelectPriv, "", "", "", nil, false, "", false}, - {mysql.InsertPriv, "", "", "", nil, false, "", false}, - {mysql.UpdatePriv, "", "", "", nil, false, "", false}, - {mysql.DeletePriv, "", "", "", nil, false, "", false}, - {mysql.CreatePriv, "", "", "", nil, false, "", false}, - {mysql.DropPriv, "", "", "", nil, false, "", false}, - {mysql.ProcessPriv, "", "", "", nil, false, "", false}, - {mysql.ReferencesPriv, "", "", "", nil, false, "", false}, - {mysql.AlterPriv, "", "", "", nil, false, "", false}, - {mysql.ShowDBPriv, "", "", "", nil, false, "", false}, - {mysql.SuperPriv, "", "", "", nil, false, "", false}, - {mysql.ExecutePriv, "", "", "", nil, false, "", false}, - {mysql.IndexPriv, "", "", "", nil, false, "", false}, - {mysql.CreateUserPriv, "", "", "", nil, false, "", false}, - {mysql.CreateTablespacePriv, "", "", "", nil, false, "", false}, - {mysql.TriggerPriv, "", "", "", nil, false, "", false}, - {mysql.CreateViewPriv, "", "", "", nil, false, "", false}, - {mysql.ShowViewPriv, "", "", "", nil, false, "", false}, - {mysql.CreateRolePriv, "", "", "", nil, false, "", false}, - {mysql.DropRolePriv, "", "", "", nil, false, "", false}, - {mysql.CreateTMPTablePriv, "", "", "", nil, false, "", false}, - {mysql.LockTablesPriv, "", "", "", nil, false, "", false}, - {mysql.CreateRoutinePriv, "", "", "", nil, false, "", false}, - {mysql.AlterRoutinePriv, "", "", "", nil, false, "", false}, - {mysql.EventPriv, "", "", "", nil, false, "", false}, - {mysql.ShutdownPriv, "", "", "", nil, false, "", false}, - {mysql.ReloadPriv, "", "", "", nil, false, "", false}, - {mysql.FilePriv, "", "", "", nil, false, "", false}, - {mysql.ConfigPriv, "", "", "", nil, false, "", false}, - {mysql.ReplicationClientPriv, "", "", "", nil, false, "", false}, - {mysql.ReplicationSlavePriv, "", "", "", nil, false, "", false}, - {mysql.GrantPriv, "", "", "", nil, false, "", false}, + {mysql.SelectPriv, "", "", "", nil, false, nil, false}, + {mysql.InsertPriv, "", "", "", nil, false, nil, false}, + {mysql.UpdatePriv, "", "", "", nil, false, nil, false}, + {mysql.DeletePriv, "", "", "", nil, false, nil, false}, + {mysql.CreatePriv, "", "", "", nil, false, nil, false}, + {mysql.DropPriv, "", "", "", nil, false, nil, false}, + {mysql.ProcessPriv, "", "", "", nil, false, nil, false}, + {mysql.ReferencesPriv, "", "", "", nil, false, nil, false}, + {mysql.AlterPriv, "", "", "", nil, false, nil, false}, + {mysql.ShowDBPriv, "", "", "", nil, false, nil, false}, + {mysql.SuperPriv, "", "", "", nil, false, nil, false}, + {mysql.ExecutePriv, "", "", "", nil, false, nil, false}, + {mysql.IndexPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateUserPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateTablespacePriv, "", "", "", nil, false, nil, false}, + {mysql.TriggerPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateViewPriv, "", "", "", nil, false, nil, false}, + {mysql.ShowViewPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateRolePriv, "", "", "", nil, false, nil, false}, + {mysql.DropRolePriv, "", "", "", nil, false, nil, false}, + {mysql.CreateTMPTablePriv, "", "", "", nil, false, nil, false}, + {mysql.LockTablesPriv, "", "", "", nil, false, nil, false}, + {mysql.CreateRoutinePriv, "", "", "", nil, false, nil, false}, + {mysql.AlterRoutinePriv, "", "", "", nil, false, nil, false}, + {mysql.EventPriv, "", "", "", nil, false, nil, false}, + {mysql.ShutdownPriv, "", "", "", nil, false, nil, false}, + {mysql.ReloadPriv, "", "", "", nil, false, nil, false}, + {mysql.FilePriv, "", "", "", nil, false, nil, false}, + {mysql.ConfigPriv, "", "", "", nil, false, nil, false}, + {mysql.ReplicationClientPriv, "", "", "", nil, false, nil, false}, + {mysql.ReplicationSlavePriv, "", "", "", nil, false, nil, false}, + {mysql.GrantPriv, "", "", "", nil, false, nil, false}, }, }, { @@ -1368,122 +1368,122 @@ func TestVisitInfo(t *testing.T) { { sql: `show create table test.ttt`, ans: []visitInfo{ - {mysql.AllPrivMask, "test", "ttt", "", nil, false, "", false}, + {mysql.AllPrivMask, "test", "ttt", "", nil, false, nil, false}, }, }, { sql: "alter table t add column a int(4)", ans: []visitInfo{ - {mysql.AlterPriv, "test", "t", "", nil, false, "", false}, + {mysql.AlterPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "rename table t_old to t_new", ans: []visitInfo{ - {mysql.AlterPriv, "test", "t_old", "", nil, false, "", false}, - {mysql.DropPriv, "test", "t_old", "", nil, false, "", false}, - {mysql.CreatePriv, "test", "t_new", "", nil, false, "", false}, - {mysql.InsertPriv, "test", "t_new", "", nil, false, "", false}, + {mysql.AlterPriv, "test", "t_old", "", nil, false, nil, false}, + {mysql.DropPriv, "test", "t_old", "", nil, false, nil, false}, + {mysql.CreatePriv, "test", "t_new", "", nil, false, nil, false}, + {mysql.InsertPriv, "test", "t_new", "", nil, false, nil, false}, }, }, { sql: "alter table t_old rename to t_new", ans: []visitInfo{ - {mysql.AlterPriv, "test", "t_old", "", nil, false, "", false}, - {mysql.DropPriv, "test", "t_old", "", nil, false, "", false}, - {mysql.CreatePriv, "test", "t_new", "", nil, false, "", false}, - {mysql.InsertPriv, "test", "t_new", "", nil, false, "", false}, + {mysql.AlterPriv, "test", "t_old", "", nil, false, nil, false}, + {mysql.DropPriv, "test", "t_old", "", nil, false, nil, false}, + {mysql.CreatePriv, "test", "t_new", "", nil, false, nil, false}, + {mysql.InsertPriv, "test", "t_new", "", nil, false, nil, false}, }, }, { sql: "alter table t drop partition p0;", ans: []visitInfo{ - {mysql.AlterPriv, "test", "t", "", nil, false, "", false}, - {mysql.DropPriv, "test", "t", "", nil, false, "", false}, + {mysql.AlterPriv, "test", "t", "", nil, false, nil, false}, + {mysql.DropPriv, "test", "t", "", nil, false, nil, false}, }, }, { sql: "flush privileges", ans: []visitInfo{ - {mysql.ReloadPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "", false}, + {mysql.ReloadPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, nil, false}, }, }, { sql: "SET GLOBAL wait_timeout=12345", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "SYSTEM_VARIABLES_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"SYSTEM_VARIABLES_ADMIN"}, false}, }, }, { sql: "create placement policy x LEARNERS=1", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "PLACEMENT_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"PLACEMENT_ADMIN"}, false}, }, }, { sql: "drop placement policy if exists x", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "PLACEMENT_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"PLACEMENT_ADMIN"}, false}, }, }, { sql: "BACKUP DATABASE test TO 'local:///tmp/a'", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "BACKUP_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"BACKUP_ADMIN"}, false}, }, }, { sql: "RESTORE DATABASE test FROM 'local:///tmp/a'", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "RESTORE_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"RESTORE_ADMIN"}, false}, }, }, { sql: "SHOW BACKUPS", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "BACKUP_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"BACKUP_ADMIN"}, false}, }, }, { sql: "SHOW RESTORES", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "RESTORE_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"RESTORE_ADMIN"}, false}, }, }, { sql: "GRANT rolename TO user1", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "ROLE_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"ROLE_ADMIN"}, false}, }, }, { sql: "REVOKE rolename FROM user1", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "ROLE_ADMIN", false}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"ROLE_ADMIN"}, false}, }, }, { sql: "GRANT BACKUP_ADMIN ON *.* TO user1", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "BACKUP_ADMIN", true}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"BACKUP_ADMIN"}, true}, }, }, { sql: "GRANT BACKUP_ADMIN ON *.* TO user1 WITH GRANT OPTION", ans: []visitInfo{ - {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "BACKUP_ADMIN", true}, + {mysql.ExtendedPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, []string{"BACKUP_ADMIN"}, true}, }, }, { sql: "RENAME USER user1 to user1_tmp", ans: []visitInfo{ - {mysql.CreateUserPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "", false}, + {mysql.CreateUserPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, nil, false}, }, }, { sql: "SHOW CONFIG", ans: []visitInfo{ - {mysql.ConfigPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, "", false}, + {mysql.ConfigPriv, "", "", "", plannererrors.ErrSpecificAccessDenied, false, nil, false}, }, }, } @@ -1545,7 +1545,7 @@ func (v visitInfoArray) Swap(i, j int) { func unique(v []visitInfo) []visitInfo { repeat := 0 for i := 1; i < len(v); i++ { - if v[i] == v[i-1] { + if v[i].Equals(&v[i-1]) { repeat++ } else { v[i-repeat] = v[i] diff --git a/pkg/planner/core/optimizer.go b/pkg/planner/core/optimizer.go index f56ddc8a351ef..3816116e9052f 100644 --- a/pkg/planner/core/optimizer.go +++ b/pkg/planner/core/optimizer.go @@ -163,9 +163,16 @@ func BuildLogicalPlanForTest(ctx context.Context, sctx sessionctx.Context, node func CheckPrivilege(activeRoles []*auth.RoleIdentity, pm privilege.Manager, vs []visitInfo) error { for _, v := range vs { if v.privilege == mysql.ExtendedPriv { - if !pm.RequestDynamicVerification(activeRoles, v.dynamicPriv, v.dynamicWithGrant) { + hasPriv := false + for _, priv := range v.dynamicPrivs { + hasPriv = hasPriv || pm.RequestDynamicVerification(activeRoles, priv, v.dynamicWithGrant) + if hasPriv { + break + } + } + if !hasPriv { if v.err == nil { - return plannererrors.ErrPrivilegeCheckFail.GenWithStackByArgs(v.dynamicPriv) + return plannererrors.ErrPrivilegeCheckFail.GenWithStackByArgs(v.dynamicPrivs) } return v.err } diff --git a/pkg/planner/core/planbuilder.go b/pkg/planner/core/planbuilder.go index f38f42e572cdf..c7129d02a3351 100644 --- a/pkg/planner/core/planbuilder.go +++ b/pkg/planner/core/planbuilder.go @@ -19,6 +19,7 @@ import ( "encoding/binary" "fmt" "math" + "reflect" "strconv" "strings" @@ -72,16 +73,29 @@ import ( ) type visitInfo struct { - privilege mysql.PrivilegeType - db string - table string - column string - err error - alterWritable bool - dynamicPriv string + privilege mysql.PrivilegeType + db string + table string + column string + err error + alterWritable bool + // if multiple privileges is provided, user should + // have at least one privilege to pass the check. + dynamicPrivs []string dynamicWithGrant bool } +func (v *visitInfo) Equals(other *visitInfo) bool { + return v.privilege == other.privilege && + v.db == other.db && + v.table == other.table && + v.column == other.column && + v.err == other.err && + v.alterWritable == other.alterWritable && + reflect.DeepEqual(v.dynamicPrivs, other.dynamicPrivs) && + v.dynamicWithGrant == other.dynamicWithGrant +} + // clauseCode indicates in which clause the column is currently. type clauseCode int @@ -654,11 +668,11 @@ func (b *PlanBuilder) buildSet(ctx context.Context, v *ast.SetStmt) (base.Plan, for _, vars := range v.Variables { if vars.IsGlobal { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or SYSTEM_VARIABLES_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "SYSTEM_VARIABLES_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"SYSTEM_VARIABLES_ADMIN"}, false, err) } if sem.IsEnabled() && sem.IsInvisibleSysVar(strings.ToLower(vars.Name)) { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("RESTRICTED_VARIABLES_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESTRICTED_VARIABLES_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESTRICTED_VARIABLES_ADMIN"}, false, err) } assign := &expression.VarAssignment{ Name: vars.Name, @@ -3167,10 +3181,10 @@ func (b *PlanBuilder) buildShow(ctx context.Context, show *ast.ShowStmt) (base.P b.visitInfo = appendVisitInfo(b.visitInfo, mysql.ShowViewPriv, show.Table.Schema.L, show.Table.Name.L, "", err) case ast.ShowBackups: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or BACKUP_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "BACKUP_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"BACKUP_ADMIN"}, false, err) case ast.ShowRestores: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESTORE_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESTORE_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESTORE_ADMIN"}, false, err) case ast.ShowTableNextRowId: p := &ShowNextRowID{TableName: show.Table} p.setSchemaAndNames(buildShowNextRowID()) @@ -3288,28 +3302,28 @@ func (b *PlanBuilder) buildSimple(ctx context.Context, node ast.StmtNode) (base. p.setSchemaAndNames(buildBRIESchema(raw.Kind)) if raw.Kind == ast.BRIEKindRestore { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESTORE_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESTORE_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESTORE_ADMIN"}, false, err) } else { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or BACKUP_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "BACKUP_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"BACKUP_ADMIN"}, false, err) } case *ast.CalibrateResourceStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESOURCE_GROUP_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESOURCE_GROUP_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESOURCE_GROUP_ADMIN"}, false, err) p.setSchemaAndNames(buildCalibrateResourceSchema()) case *ast.AddQueryWatchStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESOURCE_GROUP_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESOURCE_GROUP_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESOURCE_GROUP_ADMIN"}, false, err) p.setSchemaAndNames(buildAddQueryWatchSchema()) case *ast.DropQueryWatchStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESOURCE_GROUP_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESOURCE_GROUP_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESOURCE_GROUP_ADMIN"}, false, err) case *ast.GrantRoleStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or ROLE_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "ROLE_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"ROLE_ADMIN"}, false, err) case *ast.RevokeRoleStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or ROLE_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "ROLE_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"ROLE_ADMIN"}, false, err) // Check if any of the users are RESTRICTED for _, user := range raw.Users { b.visitInfo = appendVisitInfoIsRestrictedUser(b.visitInfo, b.ctx, user, "RESTRICTED_USER_ADMIN") @@ -3330,13 +3344,13 @@ func (b *PlanBuilder) buildSimple(ctx context.Context, node ast.StmtNode) (base. loginUser := b.ctx.GetSessionVars().User if pi.User != loginUser.Username { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or CONNECTION_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "CONNECTION_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"CONNECTION_ADMIN"}, false, err) b.visitInfo = appendVisitInfoIsRestrictedUser(b.visitInfo, b.ctx, &auth.UserIdentity{Username: pi.User, Hostname: pi.Host}, "RESTRICTED_CONNECTION_ADMIN") } } else if raw.ConnectionID == domain.GetDomain(b.ctx).GetAutoAnalyzeProcID() { // Only the users with SUPER or CONNECTION_ADMIN privilege can kill auto analyze. err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or CONNECTION_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "CONNECTION_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"CONNECTION_ADMIN"}, false, err) } } case *ast.UseStmt: @@ -3381,6 +3395,9 @@ func (b *PlanBuilder) buildSimple(ctx context.Context, node ast.StmtNode) (base. } p.StaleTxnStartTS = startTS } + case *ast.SetResourceGroupStmt: + err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESOURCE_GROUP_ADMIN or RESOURCE_GROUP_USER") + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESOURCE_GROUP_ADMIN", "RESOURCE_GROUP_USER"}, false, err) } return p, nil } @@ -3402,7 +3419,7 @@ func collectVisitInfoFromRevokeStmt(sctx base.PlanContext, vi []visitInfo, stmt var allPrivs []mysql.PrivilegeType for _, item := range stmt.Privs { if item.Priv == mysql.ExtendedPriv { - vi = appendDynamicVisitInfo(vi, strings.ToUpper(item.Name), true, nil) // verified in MySQL: requires the dynamic grant option to revoke. + vi = appendDynamicVisitInfo(vi, []string{strings.ToUpper(item.Name)}, true, nil) // verified in MySQL: requires the dynamic grant option to revoke. continue } nonDynamicPrivilege = true @@ -3444,7 +3461,7 @@ func appendVisitInfoIsRestrictedUser(visitInfo []visitInfo, sctx base.PlanContex checker := privilege.GetPrivilegeManager(sctx) if checker != nil && checker.RequestDynamicVerificationWithUser("RESTRICTED_USER_ADMIN", false, user) { err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs(priv) - visitInfo = appendDynamicVisitInfo(visitInfo, priv, false, err) + visitInfo = appendDynamicVisitInfo(visitInfo, []string{priv}, false, err) } return visitInfo } @@ -3479,7 +3496,7 @@ func collectVisitInfoFromGrantStmt(sctx base.PlanContext, vi []visitInfo, stmt * // with dynamic privileges. err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("GRANT OPTION") - vi = appendDynamicVisitInfo(vi, item.Name, true, err) + vi = appendDynamicVisitInfo(vi, []string{item.Name}, true, err) continue } nonDynamicPrivilege = true @@ -4843,10 +4860,10 @@ func (b *PlanBuilder) buildDDL(ctx context.Context, node ast.DDLNode) (base.Plan b.visitInfo = appendVisitInfo(b.visitInfo, mysql.SuperPriv, "", "", "", nil) case *ast.DropPlacementPolicyStmt, *ast.CreatePlacementPolicyStmt, *ast.AlterPlacementPolicyStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or PLACEMENT_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "PLACEMENT_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"PLACEMENT_ADMIN"}, false, err) case *ast.CreateResourceGroupStmt, *ast.DropResourceGroupStmt, *ast.AlterResourceGroupStmt: err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESOURCE_GROUP_ADMIN") - b.visitInfo = appendDynamicVisitInfo(b.visitInfo, "RESOURCE_GROUP_ADMIN", false, err) + b.visitInfo = appendDynamicVisitInfo(b.visitInfo, []string{"RESOURCE_GROUP_ADMIN"}, false, err) case *ast.OptimizeTableStmt: return nil, dbterror.ErrGeneralUnsupportedDDL.GenWithStack("OPTIMIZE TABLE is not supported") } diff --git a/pkg/planner/optimize.go b/pkg/planner/optimize.go index 0ab8e0953dfe5..0f18f94b6f7ab 100644 --- a/pkg/planner/optimize.go +++ b/pkg/planner/optimize.go @@ -175,11 +175,23 @@ func Optimize(ctx context.Context, sctx sessionctx.Context, node ast.Node, is in // Override the resource group if the hint is set. if retErr == nil && sessVars.StmtCtx.StmtHints.HasResourceGroup { if variable.EnableResourceControl.Load() { - sessVars.StmtCtx.ResourceGroupName = sessVars.StmtCtx.StmtHints.ResourceGroup - // if we are in a txn, should update the txn resource name to let the txn - // commit with the hint resource group. - if txn, err := sctx.Txn(false); err == nil && txn != nil && txn.Valid() { - kv.SetTxnResourceGroup(txn, sessVars.StmtCtx.ResourceGroupName) + checker := privilege.GetPrivilegeManager(sctx) + hasPriv := true + if checker != nil { + hasRgAdminPriv := checker.RequestDynamicVerification(sctx.GetSessionVars().ActiveRoles, "RESOURCE_GROUP_ADMIN", false) + hasRgUserPriv := checker.RequestDynamicVerification(sctx.GetSessionVars().ActiveRoles, "RESOURCE_GROUP_USER", false) + hasPriv = hasRgAdminPriv || hasRgUserPriv + } + if hasPriv { + sessVars.StmtCtx.ResourceGroupName = sessVars.StmtCtx.StmtHints.ResourceGroup + // if we are in a txn, should update the txn resource name to let the txn + // commit with the hint resource group. + if txn, err := sctx.Txn(false); err == nil && txn != nil && txn.Valid() { + kv.SetTxnResourceGroup(txn, sessVars.StmtCtx.ResourceGroupName) + } + } else { + err := plannererrors.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER or RESOURCE_GROUP_ADMIN or RESOURCE_GROUP_USER") + sessVars.StmtCtx.AppendWarning(err) } } else { err := infoschema.ErrResourceGroupSupportDisabled diff --git a/pkg/privilege/privileges/cache.go b/pkg/privilege/privileges/cache.go index 5b6089279fafc..241e8ee0f96e3 100644 --- a/pkg/privilege/privileges/cache.go +++ b/pkg/privilege/privileges/cache.go @@ -1125,7 +1125,7 @@ func (p *MySQLPrivilege) HasExplicitlyGrantedDynamicPrivilege(activeRoles []*aut } // RequestDynamicVerification checks all roles for a specific DYNAMIC privilege. -func (p *MySQLPrivilege) RequestDynamicVerification(activeRoles []*auth.RoleIdentity, user, host, privName string, withGrant bool) bool { +func (p *MySQLPrivilege) RequestDynamicVerification(activeRoles []*auth.RoleIdentity, user, host string, privName string, withGrant bool) bool { privName = strings.ToUpper(privName) if p.HasExplicitlyGrantedDynamicPrivilege(activeRoles, user, host, privName, withGrant) { return true @@ -1135,6 +1135,7 @@ func (p *MySQLPrivilege) RequestDynamicVerification(activeRoles []*auth.RoleIden if sem.IsEnabled() && sem.IsRestrictedPrivilege(privName) { return false } + // For compatibility reasons, the SUPER privilege also has all DYNAMIC privileges granted to it (dynamic privs are a super replacement) // This may be changed in future, but will require a bootstrap task to assign all dynamic privileges // to users with SUPER, otherwise tasks such as BACKUP and ROLE_ADMIN will start to fail. diff --git a/pkg/privilege/privileges/privileges.go b/pkg/privilege/privileges/privileges.go index fb55a0b34a22f..aaa1cbeceb9ef 100644 --- a/pkg/privilege/privileges/privileges.go +++ b/pkg/privilege/privileges/privileges.go @@ -66,6 +66,7 @@ var dynamicPrivs = []string{ "RESTRICTED_CONNECTION_ADMIN", // Can not be killed by PROCESS/CONNECTION_ADMIN privilege "RESTRICTED_REPLICA_WRITER_ADMIN", // Can write to the sever even when tidb_restriced_read_only is turned on. "RESOURCE_GROUP_ADMIN", // Create/Drop/Alter RESOURCE GROUP + "RESOURCE_GROUP_USER", // Can change the resource group of current session. } var dynamicPrivLock sync.Mutex var defaultTokenLife = 15 * time.Minute diff --git a/tests/integrationtest/r/executor/executor.result b/tests/integrationtest/r/executor/executor.result index 848fa5fe92096..c69fb8e0541b0 100644 --- a/tests/integrationtest/r/executor/executor.result +++ b/tests/integrationtest/r/executor/executor.result @@ -3220,6 +3220,7 @@ RESTRICTED_USER_ADMIN Server Admin RESTRICTED_CONNECTION_ADMIN Server Admin RESTRICTED_REPLICA_WRITER_ADMIN Server Admin RESOURCE_GROUP_ADMIN Server Admin +RESOURCE_GROUP_USER Server Admin show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t InnoDB 10 Compact 0 0 0 0 0 0 NULL 0 NULL NULL utf8mb4_bin diff --git a/tests/integrationtest/r/privilege/privileges.result b/tests/integrationtest/r/privilege/privileges.result index d8b48e13d0ec3..f5c4a90e1f3ba 100644 --- a/tests/integrationtest/r/privilege/privileges.result +++ b/tests/integrationtest/r/privilege/privileges.result @@ -55,24 +55,52 @@ drop placement policy if exists x; create placement policy x PRIMARY_REGION="cn-east-1" REGIONS="cn-east-1"; drop placement policy if exists x; drop user placement_user; +CREATE USER resource_group_admin; CREATE USER resource_group_user; set @@global.tidb_enable_resource_control = 1; CREATE RESOURCE GROUP test RU_PER_SEC = 666; Error 1227 (42000): Access denied; you need (at least one of) the SUPER or RESOURCE_GROUP_ADMIN privilege(s) for this operation -GRANT RESOURCE_GROUP_ADMIN ON *.* TO resource_group_user; +CREATE DATABASE IF NOT EXISTS test_rc; +CREATE TABLE test_rc.t(id int); +INSERT INTO test_rc.t VALUES (1); +GRANT RESOURCE_GROUP_ADMIN ON *.* TO resource_group_admin; +SHOW GRANTS FOR resource_group_admin; +Grants for resource_group_admin@% +GRANT USAGE ON *.* TO 'resource_group_admin'@'%' +GRANT RESOURCE_GROUP_ADMIN ON *.* TO 'resource_group_admin'@'%' +GRANT RESOURCE_GROUP_USER ON *.* TO resource_group_user; SHOW GRANTS FOR resource_group_user; Grants for resource_group_user@% GRANT USAGE ON *.* TO 'resource_group_user'@'%' -GRANT RESOURCE_GROUP_ADMIN ON *.* TO 'resource_group_user'@'%' +GRANT RESOURCE_GROUP_USER ON *.* TO 'resource_group_user'@'%' +GRANT SELECT on test_rc.* TO resource_group_admin; +GRANT SELECT on test_rc.* TO resource_group_user; CREATE RESOURCE GROUP test RU_PER_SEC = 666; CREATE RESOURCE GROUP test2 RU_PER_SEC = 999; ALTER RESOURCE GROUP test2 RU_PER_SEC = 1000; DROP RESOURCE GROUP test2; -REVOKE RESOURCE_GROUP_ADMIN ON *.* FROM resource_group_user; +SELECT /*+ RESOURCE_GROUP(test) */ * from test_rc.t; +id +1 +SET RESOURCE GROUP test; +SELECT /*+ RESOURCE_GROUP(test) */ * from test_rc.t; +id +1 +SET RESOURCE GROUP test; +REVOKE RESOURCE_GROUP_ADMIN ON *.* FROM resource_group_admin; +REVOKE RESOURCE_GROUP_USER ON *.* FROM resource_group_user; ALTER RESOURCE GROUP test RU_PER_SEC = 667; Error 1227 (42000): Access denied; you need (at least one of) the SUPER or RESOURCE_GROUP_ADMIN privilege(s) for this operation DROP RESOURCE GROUP test; Error 1227 (42000): Access denied; you need (at least one of) the SUPER or RESOURCE_GROUP_ADMIN privilege(s) for this operation +SET RESOURCE GROUP test; +Error 1227 (42000): Access denied; you need (at least one of) the SUPER or RESOURCE_GROUP_ADMIN or RESOURCE_GROUP_USER privilege(s) for this operation +SELECT /*+ RESOURCE_GROUP(test) */ * from test_rc.t; +id +1 +REVOKE SELECT on test_rc.* FROM resource_group_admin; +REVOKE SELECT on test_rc.* FROM resource_group_user; +DROP DATABASE test_rc; CREATE SCHEMA IF NOT EXISTS privilege__privileges; USE privilege__privileges; CREATE TABLE reftest (a int); diff --git a/tests/integrationtest/t/privilege/privileges.test b/tests/integrationtest/t/privilege/privileges.test index 7815e14a09585..3dc1f24d76199 100644 --- a/tests/integrationtest/t/privilege/privileges.test +++ b/tests/integrationtest/t/privilege/privileges.test @@ -85,35 +85,60 @@ connection default; drop user placement_user; # TestResourceGroupAdminDynamicPriv +CREATE USER resource_group_admin; CREATE USER resource_group_user; # This should be the default value in the future, so we do not need to set if for testing? set @@global.tidb_enable_resource_control = 1; +connect (resource_group_admin,localhost,resource_group_admin,,); connect (resource_group_user,localhost,resource_group_user,,); -connection resource_group_user; + +connection resource_group_admin; --error 1227 CREATE RESOURCE GROUP test RU_PER_SEC = 666; connection default; -GRANT RESOURCE_GROUP_ADMIN ON *.* TO resource_group_user; +CREATE DATABASE IF NOT EXISTS test_rc; +CREATE TABLE test_rc.t(id int); +INSERT INTO test_rc.t VALUES (1); +GRANT RESOURCE_GROUP_ADMIN ON *.* TO resource_group_admin; +SHOW GRANTS FOR resource_group_admin; +GRANT RESOURCE_GROUP_USER ON *.* TO resource_group_user; SHOW GRANTS FOR resource_group_user; +GRANT SELECT on test_rc.* TO resource_group_admin; +GRANT SELECT on test_rc.* TO resource_group_user; -connection resource_group_user; +connection resource_group_admin; CREATE RESOURCE GROUP test RU_PER_SEC = 666; CREATE RESOURCE GROUP test2 RU_PER_SEC = 999; ALTER RESOURCE GROUP test2 RU_PER_SEC = 1000; DROP RESOURCE GROUP test2; +SELECT /*+ RESOURCE_GROUP(test) */ * from test_rc.t; +SET RESOURCE GROUP test; + +connection resource_group_user; +SELECT /*+ RESOURCE_GROUP(test) */ * from test_rc.t; +SET RESOURCE GROUP test; connection default; -REVOKE RESOURCE_GROUP_ADMIN ON *.* FROM resource_group_user; +REVOKE RESOURCE_GROUP_ADMIN ON *.* FROM resource_group_admin; +REVOKE RESOURCE_GROUP_USER ON *.* FROM resource_group_user; -connection resource_group_user; +connection resource_group_admin; --error 1227 ALTER RESOURCE GROUP test RU_PER_SEC = 667; --error 1227 DROP RESOURCE GROUP test; +--error 1227 +SET RESOURCE GROUP test; +SELECT /*+ RESOURCE_GROUP(test) */ * from test_rc.t; + +disconnect resource_group_admin; disconnect resource_group_user; connection default; +REVOKE SELECT on test_rc.* FROM resource_group_admin; +REVOKE SELECT on test_rc.* FROM resource_group_user; +DROP DATABASE test_rc; # TestGrantReferences CREATE SCHEMA IF NOT EXISTS privilege__privileges;