Skip to content

Commit

Permalink
extension: add support for extension to custom table and sysvar privi…
Browse files Browse the repository at this point in the history
…leges (#39137)

close #39136
  • Loading branch information
lcwangchao authored Nov 15, 2022
1 parent b03690a commit c354b8b
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 38 deletions.
19 changes: 19 additions & 0 deletions executor/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ import (
"github.com/pingcap/tidb/parser/ast"
"github.com/pingcap/tidb/parser/charset"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/plugin"
"github.com/pingcap/tidb/privilege"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/table/temptable"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/collate"
"github.com/pingcap/tidb/util/gcutil"
"github.com/pingcap/tidb/util/logutil"
"github.com/pingcap/tidb/util/sem"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -109,6 +112,22 @@ func (e *SetExecutor) setSysVariable(ctx context.Context, name string, v *expres
}
return variable.ErrUnknownSystemVar.GenWithStackByArgs(name)
}

if sysVar.RequireDynamicPrivileges != nil {
semEnabled := sem.IsEnabled()
pm := privilege.GetPrivilegeManager(e.ctx)
privs := sysVar.RequireDynamicPrivileges(v.IsGlobal, semEnabled)
for _, priv := range privs {
if !pm.RequestDynamicVerification(sessionVars.ActiveRoles, priv, false) {
msg := priv
if !semEnabled {
msg = "SUPER or " + msg
}
return core.ErrSpecificAccessDenied.GenWithStackByArgs(msg)
}
}
}

if sysVar.IsNoop && !variable.EnableNoopVariables.Load() {
// The variable is a noop. For compatibility we allow it to still
// be changed, but we append a warning since users might be expecting
Expand Down
10 changes: 6 additions & 4 deletions expression/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,13 @@ func (c *extensionFuncClass) getFunction(ctx sessionctx.Context, args []Expressi
}

func (c *extensionFuncClass) checkPrivileges(ctx sessionctx.Context) error {
privs := c.funcDef.RequireDynamicPrivileges
if semPrivs := c.funcDef.SemRequireDynamicPrivileges; len(semPrivs) > 0 && sem.IsEnabled() {
privs = semPrivs
fn := c.funcDef.RequireDynamicPrivileges
if fn == nil {
return nil
}

semEnabled := sem.IsEnabled()
privs := fn(semEnabled)
if len(privs) == 0 {
return nil
}
Expand All @@ -129,7 +131,7 @@ func (c *extensionFuncClass) checkPrivileges(ctx sessionctx.Context) error {
for _, priv := range privs {
if !manager.RequestDynamicVerification(activeRoles, priv, false) {
msg := priv
if !sem.IsEnabled() {
if !semEnabled {
msg = "SUPER or " + msg
}
return errSpecificAccessDenied.GenWithStackByArgs(msg)
Expand Down
1 change: 1 addition & 0 deletions extension/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ go_library(
"//parser",
"//parser/ast",
"//parser/auth",
"//parser/mysql",
"//sessionctx/stmtctx",
"//sessionctx/variable",
"//types",
Expand Down
15 changes: 15 additions & 0 deletions extension/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ func (es *Extensions) Bootstrap(ctx BootstrapContext) error {
return nil
}

// GetAccessCheckFuncs returns spec functions of the custom access check
func (es *Extensions) GetAccessCheckFuncs() (funcs []AccessCheckFunc) {
if es == nil {
return nil
}

for _, m := range es.manifests {
if m.accessCheckFunc != nil {
funcs = append(funcs, m.accessCheckFunc)
}
}

return funcs
}

// NewSessionExtensions creates a new ConnExtensions object
func (es *Extensions) NewSessionExtensions() *SessionExtensions {
if es == nil {
Expand Down
8 changes: 2 additions & 6 deletions extension/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,8 @@ type FunctionDef struct {
EvalStringFunc func(ctx FunctionContext, row chunk.Row) (string, bool, error)
// EvalIntFunc is the eval function when `EvalTp` is `types.ETInt`
EvalIntFunc func(ctx FunctionContext, row chunk.Row) (int64, bool, error)
// RequireDynamicPrivileges is the dynamic privileges needed to invoke the function
// If `RequireDynamicPrivileges` is empty, it means every one can invoke this function
RequireDynamicPrivileges []string
// SemRequireDynamicPrivileges is the dynamic privileges needed to invoke the function in sem mode
// If `SemRequireDynamicPrivileges` is empty, `DynamicPrivileges` will be used in sem mode
SemRequireDynamicPrivileges []string
// RequireDynamicPrivileges is a function to return a list of dynamic privileges to check.
RequireDynamicPrivileges func(sem bool) []string
}

// Validate validates the function definition
Expand Down
31 changes: 21 additions & 10 deletions extension/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,26 +283,37 @@ func TestExtensionFuncPrivilege(t *testing.T) {
},
},
{
Name: "custom_only_dyn_priv_func",
EvalTp: types.ETString,
RequireDynamicPrivileges: []string{"CUSTOM_DYN_PRIV_1"},
Name: "custom_only_dyn_priv_func",
EvalTp: types.ETString,
RequireDynamicPrivileges: func(sem bool) []string {
return []string{"CUSTOM_DYN_PRIV_1"}
},
EvalStringFunc: func(ctx extension.FunctionContext, row chunk.Row) (string, bool, error) {
return "abc", false, nil
},
},
{
Name: "custom_only_sem_dyn_priv_func",
EvalTp: types.ETString,
SemRequireDynamicPrivileges: []string{"RESTRICTED_CUSTOM_DYN_PRIV_2"},
Name: "custom_only_sem_dyn_priv_func",
EvalTp: types.ETString,
RequireDynamicPrivileges: func(sem bool) []string {
if sem {
return []string{"RESTRICTED_CUSTOM_DYN_PRIV_2"}
}
return nil
},
EvalStringFunc: func(ctx extension.FunctionContext, row chunk.Row) (string, bool, error) {
return "def", false, nil
},
},
{
Name: "custom_both_dyn_priv_func",
EvalTp: types.ETString,
RequireDynamicPrivileges: []string{"CUSTOM_DYN_PRIV_1"},
SemRequireDynamicPrivileges: []string{"RESTRICTED_CUSTOM_DYN_PRIV_2"},
Name: "custom_both_dyn_priv_func",
EvalTp: types.ETString,
RequireDynamicPrivileges: func(sem bool) []string {
if sem {
return []string{"RESTRICTED_CUSTOM_DYN_PRIV_2"}
}
return []string{"CUSTOM_DYN_PRIV_1"}
},
EvalStringFunc: func(ctx extension.FunctionContext, row chunk.Row) (string, bool, error) {
return "ghi", false, nil
},
Expand Down
12 changes: 12 additions & 0 deletions extension/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/ngaut/pools"
"github.com/pingcap/errors"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/util/chunk"
clientv3 "go.etcd.io/etcd/client/v3"
Expand Down Expand Up @@ -54,6 +55,16 @@ func WithCustomFunctions(funcs []*FunctionDef) Option {
}
}

// AccessCheckFunc is a function that returns a dynamic privilege list for db/tbl/column access
type AccessCheckFunc func(db, tbl, column string, priv mysql.PrivilegeType, sem bool) []string

// WithCustomAccessCheck specifies the custom db/tbl/column dynamic privilege check
func WithCustomAccessCheck(fn AccessCheckFunc) Option {
return func(m *Manifest) {
m.accessCheckFunc = fn
}
}

// WithSessionHandlerFactory specifies a factory function to handle session
func WithSessionHandlerFactory(factory func() *SessionHandler) Option {
return func(m *Manifest) {
Expand Down Expand Up @@ -106,6 +117,7 @@ type Manifest struct {
dynPrivs []string
bootstrap func(BootstrapContext) error
funcs []*FunctionDef
accessCheckFunc AccessCheckFunc
sessionHandlerFactory func() *SessionHandler
close func()
}
Expand Down
Loading

0 comments on commit c354b8b

Please sign in to comment.