Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner,privilege: requires extra privileges for REPLACE and INSERT ON DUPLICATE statements (#23911) #23939

Merged
merged 5 commits into from
Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2442,15 +2442,31 @@ func (b *PlanBuilder) buildInsert(ctx context.Context, insert *ast.InsertStmt) (
return nil, ErrPartitionClauseOnNonpartitioned
}

user := b.ctx.GetSessionVars().User
var authErr error
if b.ctx.GetSessionVars().User != nil {
authErr = ErrTableaccessDenied.GenWithStackByArgs("INSERT", b.ctx.GetSessionVars().User.AuthUsername,
b.ctx.GetSessionVars().User.AuthHostname, tableInfo.Name.L)
if user != nil {
authErr = ErrTableaccessDenied.GenWithStackByArgs("INSERT", user.AuthUsername, user.AuthHostname, tableInfo.Name.L)
}

b.visitInfo = appendVisitInfo(b.visitInfo, mysql.InsertPriv, tn.DBInfo.Name.L,
tableInfo.Name.L, "", authErr)

// `REPLACE INTO` requires both INSERT + DELETE privilege
// `ON DUPLICATE KEY UPDATE` requires both INSERT + UPDATE privilege
var extraPriv mysql.PrivilegeType
if insert.IsReplace {
extraPriv = mysql.DeletePriv
} else if insert.OnDuplicate != nil {
extraPriv = mysql.UpdatePriv
}
if extraPriv != 0 {
if user != nil {
cmd := strings.ToUpper(mysql.Priv2Str[extraPriv])
authErr = ErrTableaccessDenied.GenWithStackByArgs(cmd, user.AuthUsername, user.AuthHostname, tableInfo.Name.L)
}
b.visitInfo = appendVisitInfo(b.visitInfo, extraPriv, tn.DBInfo.Name.L, tableInfo.Name.L, "", authErr)
}

mockTablePlan := LogicalTableDual{}.Init(b.ctx, b.getSelectOffset())
mockTablePlan.SetSchema(insertPlan.tableSchema)
mockTablePlan.names = insertPlan.tableColNames
Expand Down
39 changes: 39 additions & 0 deletions privilege/privileges/privileges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,45 @@ func (s *testPrivilegeSuite) TestShowCreateTable(c *C) {
mustExec(c, se, `SHOW CREATE TABLE mysql.user`)
}

func (s *testPrivilegeSuite) TestReplaceAndInsertOnDuplicate(c *C) {
se := newSession(c, s.store, s.dbName)
mustExec(c, se, `CREATE USER tr_insert`)
mustExec(c, se, `CREATE USER tr_update`)
mustExec(c, se, `CREATE USER tr_delete`)
mustExec(c, se, `CREATE TABLE t1 (a int primary key, b int)`)
mustExec(c, se, `GRANT INSERT ON t1 TO tr_insert`)
mustExec(c, se, `GRANT UPDATE ON t1 TO tr_update`)
mustExec(c, se, `GRANT DELETE ON t1 TO tr_delete`)

// Restrict the permission to INSERT only.
c.Assert(se.Auth(&auth.UserIdentity{Username: "tr_insert", Hostname: "localhost", AuthUsername: "tr_insert", AuthHostname: "%"}, nil, nil), IsTrue)

// REPLACE requires INSERT + DELETE privileges, having INSERT alone is insufficient.
_, err := se.ExecuteInternal(context.Background(), `REPLACE INTO t1 VALUES (1, 2)`)
c.Assert(terror.ErrorEqual(err, core.ErrTableaccessDenied), IsTrue)
c.Assert(err.Error(), Equals, "[planner:1142]DELETE command denied to user 'tr_insert'@'%' for table 't1'")

// INSERT ON DUPLICATE requires INSERT + UPDATE privileges, having INSERT alone is insufficient.
_, err = se.ExecuteInternal(context.Background(), `INSERT INTO t1 VALUES (3, 4) ON DUPLICATE KEY UPDATE b = 5`)
c.Assert(terror.ErrorEqual(err, core.ErrTableaccessDenied), IsTrue)
c.Assert(err.Error(), Equals, "[planner:1142]UPDATE command denied to user 'tr_insert'@'%' for table 't1'")

// Plain INSERT should work.
mustExec(c, se, `INSERT INTO t1 VALUES (6, 7)`)

// Also check that having DELETE alone is insufficient for REPLACE.
c.Assert(se.Auth(&auth.UserIdentity{Username: "tr_delete", Hostname: "localhost", AuthUsername: "tr_delete", AuthHostname: "%"}, nil, nil), IsTrue)
_, err = se.ExecuteInternal(context.Background(), `REPLACE INTO t1 VALUES (8, 9)`)
c.Assert(terror.ErrorEqual(err, core.ErrTableaccessDenied), IsTrue)
c.Assert(err.Error(), Equals, "[planner:1142]INSERT command denied to user 'tr_delete'@'%' for table 't1'")

// Also check that having UPDATE alone is insufficient for INSERT ON DUPLICATE.
c.Assert(se.Auth(&auth.UserIdentity{Username: "tr_update", Hostname: "localhost", AuthUsername: "tr_update", AuthHostname: "%"}, nil, nil), IsTrue)
_, err = se.ExecuteInternal(context.Background(), `INSERT INTO t1 VALUES (10, 11) ON DUPLICATE KEY UPDATE b = 12`)
c.Assert(terror.ErrorEqual(err, core.ErrTableaccessDenied), IsTrue)
c.Assert(err.Error(), Equals, "[planner:1142]INSERT command denied to user 'tr_update'@'%' for table 't1'")
}

func (s *testPrivilegeSuite) TestAnalyzeTable(c *C) {

se := newSession(c, s.store, s.dbName)
Expand Down