Skip to content

Commit

Permalink
planner,privilege: requires extra privileges for REPLACE and INSERT O…
Browse files Browse the repository at this point in the history
…N DUPLICATE statements (#23911) (#23939)
  • Loading branch information
ti-srebot authored Apr 15, 2021
1 parent 47384bf commit 0d27c7e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 3 deletions.
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

0 comments on commit 0d27c7e

Please sign in to comment.