Skip to content

Commit 778c3f4

Browse files
author
Lingyu Song
authored
privilege, executor: add SET ROLE and CURRENT_ROLE support (#9581)
1 parent e829920 commit 778c3f4

File tree

14 files changed

+220
-5
lines changed

14 files changed

+220
-5
lines changed

executor/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ var (
4949
ErrTableaccessDenied = terror.ClassExecutor.New(mysql.ErrTableaccessDenied, mysql.MySQLErrName[mysql.ErrTableaccessDenied])
5050
ErrBadDB = terror.ClassExecutor.New(mysql.ErrBadDB, mysql.MySQLErrName[mysql.ErrBadDB])
5151
ErrWrongObject = terror.ClassExecutor.New(mysql.ErrWrongObject, mysql.MySQLErrName[mysql.ErrWrongObject])
52+
ErrRoleNotGranted = terror.ClassPrivilege.New(mysql.ErrRoleNotGranted, mysql.MySQLErrName[mysql.ErrRoleNotGranted])
5253
)
5354

5455
func init() {

executor/simple.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,34 @@ func (e *SimpleExec) Next(ctx context.Context, req *chunk.RecordBatch) (err erro
8181
return nil
8282
case *ast.DropStatsStmt:
8383
err = e.executeDropStats(x)
84+
case *ast.SetRoleStmt:
85+
err = e.executeSetRole(x)
8486
}
8587
e.done = true
8688
return errors.Trace(err)
8789
}
8890

91+
func (e *SimpleExec) executeSetRole(s *ast.SetRoleStmt) error {
92+
checkDup := make(map[string]*auth.RoleIdentity, len(s.RoleList))
93+
// Check whether RoleNameList contain duplicate role name.
94+
for _, r := range s.RoleList {
95+
key := r.String()
96+
checkDup[key] = r
97+
}
98+
roleList := make([]*auth.RoleIdentity, 0, 10)
99+
for _, v := range checkDup {
100+
roleList = append(roleList, v)
101+
}
102+
103+
checker := privilege.GetPrivilegeManager(e.ctx)
104+
ok, roleName := checker.ActiveRoles(e.ctx, roleList)
105+
if !ok {
106+
u := e.ctx.GetSessionVars().User
107+
return ErrRoleNotGranted.GenWithStackByArgs(roleName, u.String())
108+
}
109+
return nil
110+
}
111+
89112
func (e *SimpleExec) dbAccessDenied(dbname string) error {
90113
user := e.ctx.GetSessionVars().User
91114
u := user.Username

expression/builtin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ var funcs = map[string]functionClass{
470470
// information functions
471471
ast.ConnectionID: &connectionIDFunctionClass{baseFunctionClass{ast.ConnectionID, 0, 0}},
472472
ast.CurrentUser: &currentUserFunctionClass{baseFunctionClass{ast.CurrentUser, 0, 0}},
473+
ast.CurrentRole: &currentRoleFunctionClass{baseFunctionClass{ast.CurrentRole, 0, 0}},
473474
ast.Database: &databaseFunctionClass{baseFunctionClass{ast.Database, 0, 0}},
474475
// This function is a synonym for DATABASE().
475476
// See http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_schema

expression/builtin_info.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var (
3030
_ functionClass = &databaseFunctionClass{}
3131
_ functionClass = &foundRowsFunctionClass{}
3232
_ functionClass = &currentUserFunctionClass{}
33+
_ functionClass = &currentRoleFunctionClass{}
3334
_ functionClass = &userFunctionClass{}
3435
_ functionClass = &connectionIDFunctionClass{}
3536
_ functionClass = &lastInsertIDFunctionClass{}
@@ -156,6 +157,50 @@ func (b *builtinCurrentUserSig) evalString(row chunk.Row) (string, bool, error)
156157
return data.User.AuthIdentityString(), false, nil
157158
}
158159

160+
type currentRoleFunctionClass struct {
161+
baseFunctionClass
162+
}
163+
164+
func (c *currentRoleFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
165+
if err := c.verifyArgs(args); err != nil {
166+
return nil, err
167+
}
168+
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString)
169+
bf.tp.Flen = 64
170+
sig := &builtinCurrentRoleSig{bf}
171+
return sig, nil
172+
}
173+
174+
type builtinCurrentRoleSig struct {
175+
baseBuiltinFunc
176+
}
177+
178+
func (b *builtinCurrentRoleSig) Clone() builtinFunc {
179+
newSig := &builtinCurrentRoleSig{}
180+
newSig.cloneFrom(&b.baseBuiltinFunc)
181+
return newSig
182+
}
183+
184+
// evalString evals a builtinCurrentUserSig.
185+
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_current-user
186+
func (b *builtinCurrentRoleSig) evalString(row chunk.Row) (string, bool, error) {
187+
data := b.ctx.GetSessionVars()
188+
if data == nil || data.ActiveRoles == nil {
189+
return "", true, errors.Errorf("Missing session variable when eval builtin")
190+
}
191+
if len(data.ActiveRoles) == 0 {
192+
return "", false, nil
193+
}
194+
res := ""
195+
for i, r := range data.ActiveRoles {
196+
res += r.String()
197+
if i != len(data.ActiveRoles)-1 {
198+
res += ","
199+
}
200+
}
201+
return res, false, nil
202+
}
203+
159204
type userFunctionClass struct {
160205
baseFunctionClass
161206
}

expression/builtin_info_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,22 @@ func (s *testEvaluatorSuite) TestCurrentUser(c *C) {
9595
c.Assert(d.GetString(), Equals, "root@localhost")
9696
}
9797

98+
func (s *testEvaluatorSuite) TestCurrentRole(c *C) {
99+
defer testleak.AfterTest(c)()
100+
ctx := mock.NewContext()
101+
sessionVars := ctx.GetSessionVars()
102+
sessionVars.ActiveRoles = make([]*auth.RoleIdentity, 0, 10)
103+
sessionVars.ActiveRoles = append(sessionVars.ActiveRoles, &auth.RoleIdentity{Username: "r_1", Hostname: "%"})
104+
sessionVars.ActiveRoles = append(sessionVars.ActiveRoles, &auth.RoleIdentity{Username: "r_2", Hostname: "localhost"})
105+
106+
fc := funcs[ast.CurrentRole]
107+
f, err := fc.getFunction(ctx, nil)
108+
c.Assert(err, IsNil)
109+
d, err := evalBuiltinFunc(f, chunk.Row{})
110+
c.Assert(err, IsNil)
111+
c.Assert(d.GetString(), Equals, "`r_1`@`%`,`r_2`@`localhost`")
112+
}
113+
98114
func (s *testEvaluatorSuite) TestConnectionID(c *C) {
99115
defer testleak.AfterTest(c)()
100116
ctx := mock.NewContext()

expression/function_traits.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
var UnCacheableFunctions = map[string]struct{}{
2222
ast.Database: {},
2323
ast.CurrentUser: {},
24+
ast.CurrentRole: {},
2425
ast.User: {},
2526
ast.ConnectionID: {},
2627
ast.LastInsertId: {},

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ require (
5151
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e
5252
github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562
5353
github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596
54-
github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b
54+
github.com/pingcap/parser v0.0.0-20190320053247-fe243e3280cf
5555
github.com/pingcap/pd v2.1.0-rc.4+incompatible
5656
github.com/pingcap/tidb-tools v2.1.3-0.20190116051332-34c808eef588+incompatible
5757
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562 h1:32oF1/8lVnBR2JV
119119
github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY=
120120
github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596 h1:t2OQTpPJnrPDGlvA+3FwJptMTt6MEPdzK1Wt99oaefQ=
121121
github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596/go.mod h1:WpHUKhNZ18v116SvGrmjkA9CBhYmuUTKL+p8JC9ANEw=
122-
github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b h1:NlvTrxqezIJh6CD5Leky12IZ8E/GtpEEmzgNNb34wbw=
123-
github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
122+
github.com/pingcap/parser v0.0.0-20190320053247-fe243e3280cf h1:yxK78TmeSK3BIm8Z8SwdZLVzRpY80HZe1VMlA2dL648=
123+
github.com/pingcap/parser v0.0.0-20190320053247-fe243e3280cf/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
124124
github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE=
125125
github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E=
126126
github.com/pingcap/tidb-tools v2.1.3-0.20190116051332-34c808eef588+incompatible h1:e9Gi/LP9181HT3gBfSOeSBA+5JfemuE4aEAhqNgoE4k=

planner/core/planbuilder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ func (b *PlanBuilder) Build(node ast.Node) (Plan, error) {
212212
case *ast.AnalyzeTableStmt:
213213
return b.buildAnalyze(x)
214214
case *ast.BinlogStmt, *ast.FlushStmt, *ast.UseStmt,
215-
*ast.BeginStmt, *ast.CommitStmt, *ast.RollbackStmt, *ast.CreateUserStmt, *ast.SetPwdStmt,
216-
*ast.GrantStmt, *ast.DropUserStmt, *ast.AlterUserStmt, *ast.RevokeStmt, *ast.KillStmt, *ast.DropStatsStmt:
215+
*ast.BeginStmt, *ast.CommitStmt, *ast.RollbackStmt, *ast.CreateUserStmt, *ast.SetPwdStmt, *ast.GrantStmt,
216+
*ast.DropUserStmt, *ast.AlterUserStmt, *ast.RevokeStmt, *ast.KillStmt, *ast.DropStatsStmt, *ast.SetRoleStmt:
217217
return b.buildSimple(node.(ast.StmtNode))
218218
case ast.DDLNode:
219219
return b.buildDDL(x)

privilege/privilege.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ type Manager interface {
5252

5353
// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGE table.
5454
UserPrivilegesTable() [][]types.Datum
55+
56+
// ActiveRoles active roles for current session.
57+
// The first illegal role will be returned.
58+
ActiveRoles(ctx sessionctx.Context, roleList []*auth.RoleIdentity) (bool, string)
5559
}
5660

5761
const key keyType = 0

0 commit comments

Comments
 (0)