Skip to content

Commit

Permalink
Allow dynamic configuration of vschema acl
Browse files Browse the repository at this point in the history
This allows configuring the users for the vschema dynamically at runtime
so you don't need to restart vtgate if this changes.

Signed-off-by: Dirkjan Bussink <[email protected]>
  • Loading branch information
dbussink committed Dec 9, 2024
1 parent f746c48 commit 3329f06
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 82 deletions.
2 changes: 1 addition & 1 deletion go/viperutil/internal/sync/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (v *Viper) Set(key string, value any) {
v.m.Lock()
defer v.m.Unlock()

// We must not update v.disk here; explicit calls to Set will supercede all
// We must not update v.disk here; explicit calls to Set will supersede all
// future config reloads.
v.live.Set(key, value)

Expand Down
1 change: 0 additions & 1 deletion go/vt/vtgate/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ func NewExecutor(
// setting the vcursor config.
e.initVConfig(warnOnShardedOnly, pv)

vschemaacl.Init()
// we subscribe to update from the VSchemaManager
e.vm = &VSchemaManager{
subscriber: e.SaveVSchema,
Expand Down
12 changes: 6 additions & 6 deletions go/vt/vtgate/executor_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,21 +401,21 @@ func TestExecutorSetMetadata(t *testing.T) {
})

t.Run("Session 2", func(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()

executor, _, _, _, ctx := createExecutorEnv(t)
session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true})

set := "set @@vitess_metadata.app_keyspace_v1= '1'"
_, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil)
assert.NoError(t, err, "%s error: %v", set, err)
require.NoError(t, err, "%s error: %v", set, err)

show := `show vitess_metadata variables like 'app\\_keyspace\\_v_'`
result, err := executor.Execute(ctx, nil, "TestExecute", session, show, nil)
assert.NoError(t, err)
require.NoError(t, err)

want := "1"
got := result.Rows[0][1].ToString()
Expand All @@ -424,11 +424,11 @@ func TestExecutorSetMetadata(t *testing.T) {
// Update metadata
set = "set @@vitess_metadata.app_keyspace_v2='2'"
_, err = executor.Execute(ctx, nil, "TestExecute", session, set, nil)
assert.NoError(t, err, "%s error: %v", set, err)
require.NoError(t, err, "%s error: %v", set, err)

show = `show vitess_metadata variables like 'app\\_keyspace\\_v%'`
gotqr, err := executor.Execute(ctx, nil, "TestExecute", session, show, nil)
assert.NoError(t, err)
require.NoError(t, err)

wantqr := &sqltypes.Result{
Fields: buildVarCharFields("Key", "Value"),
Expand Down
24 changes: 11 additions & 13 deletions go/vt/vtgate/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,9 @@ func TestExecutorTransactionsAutoCommitStreaming(t *testing.T) {
}

func TestExecutorDeleteMetadata(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()

executor, _, _, _, ctx := createExecutorEnv(t)
Expand Down Expand Up @@ -1318,9 +1318,9 @@ func TestExecutorDDLFk(t *testing.T) {
}

func TestExecutorAlterVSchemaKeyspace(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()

executor, _, _, _, ctx := createExecutorEnv(t)
Expand All @@ -1347,9 +1347,9 @@ func TestExecutorAlterVSchemaKeyspace(t *testing.T) {
}

func TestExecutorCreateVindexDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t)
ks := "TestExecutor"
Expand Down Expand Up @@ -1417,9 +1417,9 @@ func TestExecutorCreateVindexDDL(t *testing.T) {
}

func TestExecutorAddDropVschemaTableDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t)
ks := KsTestUnsharded
Expand Down Expand Up @@ -1486,8 +1486,7 @@ func TestExecutorVindexDDLACL(t *testing.T) {
require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`)

// test when all users are enabled
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.Init()
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
_, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil)
if err != nil {
t.Errorf("unexpected error '%v'", err)
Expand All @@ -1499,8 +1498,7 @@ func TestExecutorVindexDDLACL(t *testing.T) {
}

// test when only one user is enabled
vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser"
vschemaacl.Init()
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("orangeUser, blueUser, greenUser"))
_, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil)
require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`)

Expand All @@ -1511,7 +1509,7 @@ func TestExecutorVindexDDLACL(t *testing.T) {
}

// restore the disallowed state
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}

func TestExecutorUnrecognized(t *testing.T) {
Expand Down
40 changes: 19 additions & 21 deletions go/vt/vtgate/executor_vschema_ddl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ func waitForColVindexes(t *testing.T, ks, table string, names []string, executor
}

func TestPlanExecutorAlterVSchemaKeyspace(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, _, _, _, ctx := createExecutorEnv(t)
session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true})
Expand All @@ -163,9 +163,9 @@ func TestPlanExecutorAlterVSchemaKeyspace(t *testing.T) {
}

func TestPlanExecutorCreateVindexDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, _, _, _, ctx := createExecutorEnv(t)
ks := "TestExecutor"
Expand Down Expand Up @@ -205,9 +205,9 @@ func TestPlanExecutorCreateVindexDDL(t *testing.T) {
}

func TestPlanExecutorDropVindexDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, _, _, _, ctx := createExecutorEnv(t)
ks := "TestExecutor"
Expand Down Expand Up @@ -274,9 +274,9 @@ func TestPlanExecutorDropVindexDDL(t *testing.T) {
}

func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t)
ks := KsTestUnsharded
Expand Down Expand Up @@ -331,9 +331,9 @@ func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) {
}

func TestExecutorAddSequenceDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, _, _, _, ctx := createExecutorEnv(t)
ks := KsTestUnsharded
Expand Down Expand Up @@ -391,9 +391,9 @@ func TestExecutorAddSequenceDDL(t *testing.T) {
}

func TestExecutorDropSequenceDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, _, _, _, ctx := createExecutorEnv(t)
ks := KsTestUnsharded
Expand Down Expand Up @@ -442,9 +442,9 @@ func TestExecutorDropSequenceDDL(t *testing.T) {
}

func TestExecutorDropAutoIncDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, _, _, _, ctx := createExecutorEnv(t)
ks := KsTestUnsharded
Expand Down Expand Up @@ -484,9 +484,9 @@ func TestExecutorDropAutoIncDDL(t *testing.T) {
}

func TestExecutorAddDropVindexDDL(t *testing.T) {
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
defer func() {
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}()
executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t)
ks := "TestExecutor"
Expand Down Expand Up @@ -747,8 +747,7 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) {
require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`)

// test when all users are enabled
vschemaacl.AuthorizedDDLUsers = "%"
vschemaacl.Init()
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%"))
_, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil)
if err != nil {
t.Errorf("unexpected error '%v'", err)
Expand All @@ -760,8 +759,7 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) {
}

// test when only one user is enabled
vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser"
vschemaacl.Init()
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("orangeUser, blueUser, greenUser"))
_, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil)
require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`)

Expand All @@ -772,5 +770,5 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) {
}

// restore the disallowed state
vschemaacl.AuthorizedDDLUsers = ""
vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers(""))
}
91 changes: 57 additions & 34 deletions go/vt/vtgate/vschemaacl/vschemaacl.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,67 @@ package vschemaacl

import (
"strings"
"sync"

"github.com/spf13/pflag"
"github.com/spf13/viper"

"vitess.io/vitess/go/vt/servenv"

"vitess.io/vitess/go/viperutil"
querypb "vitess.io/vitess/go/vt/proto/query"
"vitess.io/vitess/go/vt/servenv"
)

var (
// AuthorizedDDLUsers specifies the users that can perform ddl operations
AuthorizedDDLUsers string

// ddlAllowAll is true if the special value of "*" was specified
type authorizedDDLUsers struct {
allowAll bool
acl map[string]struct{}
source string
}

func NewAuthorizedDDLUsers(users string) *authorizedDDLUsers {
acl := make(map[string]struct{})
allowAll := false

switch users {
case "":
case "%":
allowAll = true
default:
for _, user := range strings.Split(users, ",") {
user = strings.TrimSpace(user)
acl[user] = struct{}{}
}
}

return &authorizedDDLUsers{
allowAll: allowAll,
acl: acl,
source: users,
}
}

// ddlACL contains a set of allowed usernames
acl map[string]struct{}
func (a *authorizedDDLUsers) String() string {
return a.source
}

initMu sync.Mutex
var (
// AuthorizedDDLUsers specifies the users that can perform ddl operations
AuthorizedDDLUsers = viperutil.Configure(
"vschema_ddl_authorized_users",
viperutil.Options[*authorizedDDLUsers]{
FlagName: "vschema_ddl_authorized_users",
Default: &authorizedDDLUsers{},
Dynamic: true,
GetFunc: func(v *viper.Viper) func(key string) *authorizedDDLUsers {
return func(key string) *authorizedDDLUsers {
newVal := v.GetString(key)
curVal, ok := v.Get(key).(*authorizedDDLUsers)
if ok && newVal == curVal.source {
return curVal
}
return NewAuthorizedDDLUsers(newVal)
}
},
},
)
)

// RegisterSchemaACLFlags installs log flags on the given FlagSet.
Expand All @@ -46,7 +87,8 @@ var (
// calls this function, or call this function directly before parsing
// command-line arguments.
func RegisterSchemaACLFlags(fs *pflag.FlagSet) {
fs.StringVar(&AuthorizedDDLUsers, "vschema_ddl_authorized_users", AuthorizedDDLUsers, "List of users authorized to execute vschema ddl operations, or '%' to allow all users.")
fs.String("vschema_ddl_authorized_users", "", "List of users authorized to execute vschema ddl operations, or '%' to allow all users.")
viperutil.BindFlags(fs, AuthorizedDDLUsers)
}

func init() {
Expand All @@ -55,33 +97,14 @@ func init() {
}
}

// Init parses the users option and sets allowAll / acl accordingly
func Init() {
initMu.Lock()
defer initMu.Unlock()
acl = make(map[string]struct{})
allowAll = false

if AuthorizedDDLUsers == "%" {
allowAll = true
return
} else if AuthorizedDDLUsers == "" {
return
}

for _, user := range strings.Split(AuthorizedDDLUsers, ",") {
user = strings.TrimSpace(user)
acl[user] = struct{}{}
}
}

// Authorized returns true if the given caller is allowed to execute vschema operations
func Authorized(caller *querypb.VTGateCallerID) bool {
if allowAll {
users := AuthorizedDDLUsers.Get()
if users.allowAll {
return true
}

user := caller.GetUsername()
_, ok := acl[user]
_, ok := users.acl[user]
return ok
}
Loading

0 comments on commit 3329f06

Please sign in to comment.