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

extension: add support for extension to custom table and sysvar privileges #39137

Merged
merged 3 commits into from
Nov 15, 2022
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
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