Changing to persist a root superuser that gets created on first launch of sql-server when there are no existing privileges.

This commit is contained in:
Jason Fulghum
2024-12-18 16:19:14 -08:00
parent cabf880711
commit eccaa4f56b
13 changed files with 322 additions and 116 deletions

View File

@@ -63,22 +63,23 @@ type contextFactory func(ctx context.Context, session sql.Session) (*sql.Context
type SystemVariables map[string]interface{}
type SqlEngineConfig struct {
IsReadOnly bool
IsServerLocked bool
DoltCfgDirPath string
PrivFilePath string
BranchCtrlFilePath string
ServerUser string
ServerPass string
ServerHost string
Autocommit bool
DoltTransactionCommit bool
Bulk bool
JwksConfig []servercfg.JwksConfig
SystemVariables SystemVariables
ClusterController *cluster.Controller
BinlogReplicaController binlogreplication.BinlogReplicaController
EventSchedulerStatus eventscheduler.SchedulerStatus
IsReadOnly bool
IsServerLocked bool
DoltCfgDirPath string
PrivFilePath string
BranchCtrlFilePath string
ServerUser string
ServerPass string
ServerHost string
SkipRootUserInitialization bool
Autocommit bool
DoltTransactionCommit bool
Bulk bool
JwksConfig []servercfg.JwksConfig
SystemVariables SystemVariables
ClusterController *cluster.Controller
BinlogReplicaController binlogreplication.BinlogReplicaController
EventSchedulerStatus eventscheduler.SchedulerStatus
}
// NewSqlEngine returns a SqlEngine

View File

@@ -29,32 +29,33 @@ import (
)
type commandLineServerConfig struct {
host string
port int
user string
password string
timeout uint64
readOnly bool
logLevel servercfg.LogLevel
dataDir string
cfgDir string
autoCommit bool
doltTransactionCommit bool
maxConnections uint64
tlsKey string
tlsCert string
requireSecureTransport bool
maxLoggedQueryLen int
shouldEncodeLoggedQuery bool
privilegeFilePath string
branchControlFilePath string
allowCleartextPasswords bool
socket string
remotesapiPort *int
remotesapiReadOnly *bool
goldenMysqlConn string
eventSchedulerStatus string
valuesSet map[string]struct{}
host string
port int
user string
password string
skipRootUserInitialization bool
timeout uint64
readOnly bool
logLevel servercfg.LogLevel
dataDir string
cfgDir string
autoCommit bool
doltTransactionCommit bool
maxConnections uint64
tlsKey string
tlsCert string
requireSecureTransport bool
maxLoggedQueryLen int
shouldEncodeLoggedQuery bool
privilegeFilePath string
branchControlFilePath string
allowCleartextPasswords bool
socket string
remotesapiPort *int
remotesapiReadOnly *bool
goldenMysqlConn string
eventSchedulerStatus string
valuesSet map[string]struct{}
}
var _ servercfg.ServerConfig = (*commandLineServerConfig)(nil)
@@ -116,6 +117,10 @@ func NewCommandLineConfig(creds *cli.UserPassword, apr *argparser.ArgParseResult
config.withPassword(creds.Password)
}
if apr.Contains(skipRootUserInitialization) {
config.skipRootUserInitialization = true
}
if port, ok := apr.GetInt(remotesapiPortFlag); ok {
config.WithRemotesapiPort(&port)
}
@@ -202,11 +207,22 @@ func (cfg *commandLineServerConfig) User() string {
return cfg.user
}
// UserIsSpecified returns true if the configuration explicitly specified a user.
func (cfg *commandLineServerConfig) UserIsSpecified() bool {
return cfg.user != ""
}
// Password returns the password that connecting clients must use.
func (cfg *commandLineServerConfig) Password() string {
return cfg.password
}
// SkipRootUserInitialization returns whether the server should skip creating the implicit root
// superuser the first time a sql-server instance is launched.
func (cfg *commandLineServerConfig) SkipRootUserInitialization() bool {
return cfg.skipRootUserInitialization
}
// ReadTimeout returns the read and write timeouts.
func (cfg *commandLineServerConfig) ReadTimeout() uint64 {
return cfg.timeout

View File

@@ -22,6 +22,7 @@ import (
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@@ -56,6 +57,7 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/sqlserver"
"github.com/dolthub/dolt/go/libraries/events"
"github.com/dolthub/dolt/go/libraries/utils/config"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
"github.com/dolthub/dolt/go/libraries/utils/svcs"
)
@@ -219,19 +221,20 @@ func ConfigureServices(
InitSqlEngineConfig := &svcs.AnonService{
InitF: func(context.Context) error {
config = &engine.SqlEngineConfig{
IsReadOnly: serverConfig.ReadOnly(),
PrivFilePath: serverConfig.PrivilegeFilePath(),
BranchCtrlFilePath: serverConfig.BranchControlFilePath(),
DoltCfgDirPath: serverConfig.CfgDir(),
ServerUser: serverConfig.User(),
ServerPass: serverConfig.Password(),
ServerHost: serverConfig.Host(),
Autocommit: serverConfig.AutoCommit(),
DoltTransactionCommit: serverConfig.DoltTransactionCommit(),
JwksConfig: serverConfig.JwksConfig(),
SystemVariables: serverConfig.SystemVars(),
ClusterController: clusterController,
BinlogReplicaController: binlogreplication.DoltBinlogReplicaController,
IsReadOnly: serverConfig.ReadOnly(),
PrivFilePath: serverConfig.PrivilegeFilePath(),
BranchCtrlFilePath: serverConfig.BranchControlFilePath(),
DoltCfgDirPath: serverConfig.CfgDir(),
ServerUser: serverConfig.User(),
ServerPass: serverConfig.Password(),
ServerHost: serverConfig.Host(),
Autocommit: serverConfig.AutoCommit(),
DoltTransactionCommit: serverConfig.DoltTransactionCommit(),
JwksConfig: serverConfig.JwksConfig(),
SystemVariables: serverConfig.SystemVars(),
ClusterController: clusterController,
BinlogReplicaController: binlogreplication.DoltBinlogReplicaController,
SkipRootUserInitialization: serverConfig.SkipRootUserInitialization(),
}
return nil
},
@@ -363,30 +366,75 @@ func ConfigureServices(
}
controller.Register(InitBinlogging)
// Add superuser if specified user exists; add root superuser if no user specified and no existing privileges
InitSuperUser := &svcs.AnonService{
InitF: func(context.Context) error {
userSpecified := config.ServerUser != ""
// MySQL creates a root superuser when the mysql install is first initialized. Depending on the options
// specified, the root superuser is created without a password, or with a random password. This varies
// slightly in some OS-specific installers. Dolt initializes the root superuser the first time a
// sql-server is started and initializes its privileges database. We do this on sql-server initialization,
// instead of dolt db initialization, because we only want to create the privileges database when it's
// used for a server, and because we want the same root initialization logic when a sql-server is started
// for a clone. More details: https://dev.mysql.com/doc/mysql-security-excerpt/8.0/en/default-privileges.html
//
// NOTE: The MySQL root user is created for host 'localhost', not any host ('%'). We could do the same here,
// but it seems like it would cause problems for users who want to connect from outside of Docker.
InitImplicitRootSuperUser := &svcs.AnonService{
InitF: func(ctx context.Context) error {
// If privileges.db has already been initialized, indicating that this is NOT the
// first time sql-server has been launched, then don't initialize the root superuser.
if permissionDbExists, err := doesPrivilegesDbExist(dEnv, serverConfig.PrivilegeFilePath()); err != nil {
return err
} else if permissionDbExists {
logrus.Debug("privileges.db already exists, not creating root superuser")
return nil
}
// We always persist the privileges.db file, to signal that the privileges system has been initialized
mysqlDb := sqlEngine.GetUnderlyingEngine().Analyzer.Catalog.MySQLDb
ed := mysqlDb.Editor()
var numUsers int
ed.VisitUsers(func(*mysql_db.User) { numUsers += 1 })
privsExist := numUsers != 0
defer ed.Close()
// If no ephemeral superuser has been configured and root user initialization wasn't skipped,
// then create a root@localhost superuser.
if !serverConfig.UserIsSpecified() && !config.SkipRootUserInitialization {
logrus.Info("Creating root@localhost superuser")
mysqlDb.AddSuperUser(ed, servercfg.DefaultUser, "localhost", servercfg.DefaultPass)
}
// TODO: The in-memory filesystem doesn't work with the GMS API
// for persisting the privileges database. The filesys API
// is in the Dolt layer, so when the file path is passed to
// GMS, it expects it to be a path on disk, and errors out.
if _, isInMemFs := dEnv.FS.(*filesys.InMemFS); isInMemFs {
return nil
} else {
sqlCtx, err := sqlEngine.NewDefaultContext(context.Background())
if err != nil {
return err
}
return mysqlDb.Persist(sqlCtx, ed)
}
},
}
controller.Register(InitImplicitRootSuperUser)
// Add an ephemeral superuser if one was requested
InitEphemeralSuperUser := &svcs.AnonService{
InitF: func(context.Context) error {
mysqlDb := sqlEngine.GetUnderlyingEngine().Analyzer.Catalog.MySQLDb
ed := mysqlDb.Editor()
userSpecified := config.ServerUser != ""
if userSpecified {
superuser := mysqlDb.GetUser(ed, config.ServerUser, "%", false)
if userSpecified && superuser == nil {
mysqlDb.AddSuperUser(ed, config.ServerUser, "%", config.ServerPass)
if superuser == nil {
mysqlDb.AddEphemeralSuperUser(ed, config.ServerUser, "%", config.ServerPass)
}
} else if !privsExist {
mysqlDb.AddSuperUser(ed, servercfg.DefaultUser, "%", servercfg.DefaultPass)
}
ed.Close()
return nil
},
}
controller.Register(InitSuperUser)
controller.Register(InitEphemeralSuperUser)
var metListener *metricsListener
InitMetricsListener := &svcs.AnonService{
@@ -406,7 +454,7 @@ func ConfigureServices(
InitF: func(context.Context) error {
mysqlDb := sqlEngine.GetUnderlyingEngine().Analyzer.Catalog.MySQLDb
ed := mysqlDb.Editor()
mysqlDb.AddSuperUser(ed, LocalConnectionUser, "localhost", localCreds.Secret)
mysqlDb.AddEphemeralSuperUser(ed, LocalConnectionUser, "localhost", localCreds.Secret)
ed.Close()
return nil
},
@@ -859,6 +907,29 @@ func (r *remotesapiAuth) ApiAuthorize(ctx context.Context, superUserRequired boo
return true, nil
}
// doesPrivilegesDbExist looks for an existing privileges database as the specified |privilegeFilePath|. If
// |privilegeFilePath| is an absolute path, it is used directly. If it is a relative path, then it is resolved
// relative to the root of the specified |dEnv|.
func doesPrivilegesDbExist(dEnv *env.DoltEnv, privilegeFilePath string) (exists bool, err error) {
if !filepath.IsAbs(privilegeFilePath) {
privilegeFilePath, err = dEnv.FS.Abs(privilegeFilePath)
if err != nil {
return false, err
}
}
_, err = os.Stat(privilegeFilePath)
if err != nil {
if os.IsNotExist(err) {
return false, nil
} else {
return false, err
}
}
return true, nil
}
func LoadClusterTLSConfig(cfg servercfg.ClusterConfig) (*tls.Config, error) {
rcfg := cfg.RemotesAPIConfig()
if rcfg.TLSKey() == "" && rcfg.TLSCert() == "" {

View File

@@ -38,6 +38,7 @@ import (
const (
hostFlag = "host"
portFlag = "port"
skipRootUserInitialization = "skip-root-user-initialization"
passwordFlag = "password"
timeoutFlag = "timeout"
readonlyFlag = "readonly"
@@ -83,7 +84,7 @@ SUPPORTED CONFIG FILE FIELDS:
{{.EmphasisLeft}}log_level{{.EmphasisRight}}: Level of logging provided. Options are: {{.EmphasisLeft}}trace{{.EmphasisRight}}, {{.EmphasisLeft}}debug{{.EmphasisRight}}, {{.EmphasisLeft}}info{{.EmphasisRight}}, {{.EmphasisLeft}}warning{{.EmphasisRight}}, {{.EmphasisLeft}}error{{.EmphasisRight}}, and {{.EmphasisLeft}}fatal{{.EmphasisRight}}.
{{.EmphasisLeft}}privilege_file{{.EmphasisRight}}: "Path to a file to load and store users and grants. Defaults to {{.EmphasisLeft}}$doltcfg-dir/privileges.db{{.EmphasisRight}}. Will be created as needed.
{{.EmphasisLeft}}privilege_file{{.EmphasisRight}}: "Path to a file to load and store users and grants. Defaults to {{.EmphasisLeft}}$doltcfg-dir/privileges.db{{.EmphasisRight}}. Will be created automatically if it doesn't exist.
{{.EmphasisLeft}}branch_control_file{{.EmphasisRight}}: Path to a file to load and store branch control permissions. Defaults to {{.EmphasisLeft}}$doltcfg-dir/branch_control.db{{.EmphasisRight}}. Will be created as needed.
@@ -95,9 +96,9 @@ SUPPORTED CONFIG FILE FIELDS:
{{.EmphasisLeft}}behavior.dolt_transaction_commit{{.EmphasisRight}}: If true all SQL transaction commits will automatically create a Dolt commit, with a generated commit message. This is useful when a system working with Dolt wants to create versioned data, but doesn't want to directly use Dolt features such as dolt_commit().
{{.EmphasisLeft}}user.name{{.EmphasisRight}}: The username that connections should use for authentication
{{.EmphasisLeft}}user.name{{.EmphasisRight}}: The username for an ephemeral superuser that will exist for the lifetime of the sql-server process. This user will not be persisted to the privileges database.
{{.EmphasisLeft}}user.password{{.EmphasisRight}}: The password that connections should use for authentication.
{{.EmphasisLeft}}user.password{{.EmphasisRight}}: The password for an ephemeral superuser that will exist for the lifetime of the sql-server process. This user will not be persisted to the privileges database.
{{.EmphasisLeft}}listener.host{{.EmphasisRight}}: The host address that the server will run on. This may be {{.EmphasisLeft}}localhost{{.EmphasisRight}} or an IPv4 or IPv6 address
@@ -163,6 +164,7 @@ func (cmd SqlServerCmd) ArgParserWithName(name string) *argparser.ArgParser {
ap.SupportsString(hostFlag, "H", "host address", fmt.Sprintf("Defines the host address that the server will run on. Defaults to `%v`.", serverConfig.Host()))
ap.SupportsUint(portFlag, "P", "port", fmt.Sprintf("Defines the port that the server will run on. Defaults to `%v`.", serverConfig.Port()))
ap.SupportsString(commands.UserFlag, "u", "user", fmt.Sprintf("Defines the server user. Defaults to `%v`. This should be explicit if desired.", serverConfig.User()))
ap.SupportsFlag(skipRootUserInitialization, "", "Skips the automatic creation of a default root super user on the first launch of a SQL server.")
ap.SupportsString(passwordFlag, "p", "password", fmt.Sprintf("Defines the server password. Defaults to `%v`.", serverConfig.Password()))
ap.SupportsInt(timeoutFlag, "t", "connection timeout", fmt.Sprintf("Defines the timeout, in seconds, used for connections\nA value of `0` represents an infinite timeout. Defaults to `%v`.", serverConfig.ReadTimeout()))
ap.SupportsFlag(readonlyFlag, "r", "Disable modification of the database.")
@@ -381,6 +383,12 @@ func getServerConfig(cwdFS filesys.Filesys, apr *argparser.ArgParseResults, data
}
}
if apr.Contains(skipRootUserInitialization) {
if wcfg, ok := cfg.(servercfg.WritableServerConfig); ok {
wcfg.SetSkipRootUserInitialization(true)
}
}
if connStr, ok := apr.GetValue(goldenMysqlConn); ok {
if yamlCfg, ok := cfg.(servercfg.YAMLConfig); ok {
cli.Println(connStr)

View File

@@ -291,7 +291,7 @@ func newLateBindingEngine(
dbUser = DefaultUser
ed := rawDb.Editor()
defer ed.Close()
rawDb.AddSuperUser(ed, dbUser, config.ServerHost, "")
rawDb.AddEphemeralSuperUser(ed, dbUser, config.ServerHost, "")
}
// Set client to specified user

View File

@@ -125,8 +125,13 @@ type ServerConfig interface {
Port() int
// User returns the username that connecting clients must use.
User() string
// UserIsSpecified returns true if a user was explicitly provided in the configuration.
UserIsSpecified() bool
// Password returns the password that connecting clients must use.
Password() string
// SkipRootUserInitialization returns whether the server should skip initializing the default root superuser.
// This option is only valid the first time a sql-server is started, when the root user is being initialized.
SkipRootUserInitialization() bool
// ReadTimeout returns the read timeout in milliseconds
ReadTimeout() uint64
// WriteTimeout returns the write timeout in milliseconds
@@ -239,6 +244,9 @@ type WritableServerConfig interface {
SetUserName(string)
// SetPassword sets the password for servers with no other auth established
SetPassword(string)
// SetSkipRootUserInitialization controls whether the default root superuser should be initialized,
// or if it should be skipped and not created.
SetSkipRootUserInitialization(bool)
}
type ValidatingServerConfig interface {

View File

@@ -14,6 +14,7 @@ BehaviorConfig servercfg.BehaviorYAMLConfig 0.0.0 behavior,omitempty
UserConfig servercfg.UserYAMLConfig 0.0.0 user,omitempty
-Name *string 0.0.0 name,omitempty
-Password *string 0.0.0 password,omitempty
SkipRootUserInit *bool 0.0.0 skip_root_user_initialization,omitempty
ListenerConfig servercfg.ListenerYAMLConfig 0.0.0 listener,omitempty
-HostStr *string 0.0.0 host,omitempty
-PortNumber *int 0.0.0 port,omitempty

View File

@@ -129,6 +129,7 @@ type YAMLConfig struct {
EncodeLoggedQuery *bool `yaml:"encode_logged_query,omitempty"`
BehaviorConfig BehaviorYAMLConfig `yaml:"behavior,omitempty"`
UserConfig UserYAMLConfig `yaml:"user,omitempty"`
SkipRootUserInit *bool `yaml:"skip_root_user_initialization,omitempty"`
ListenerConfig ListenerYAMLConfig `yaml:"listener,omitempty"`
PerformanceConfig *PerformanceYAMLConfig `yaml:"performance,omitempty"`
DataDirStr *string `yaml:"data_dir,omitempty"`
@@ -584,6 +585,27 @@ func (cfg YAMLConfig) User() string {
return *cfg.UserConfig.Name
}
// UserIsSpecified returns true if the configuration explicitly specified a user.
func (cfg YAMLConfig) UserIsSpecified() bool {
return cfg.UserConfig.Name != nil
}
// SkipRootUserInitialization returns whether the server should skip initializing the default, root
// superuser.
func (cfg YAMLConfig) SkipRootUserInitialization() bool {
if cfg.SkipRootUserInit == nil {
return false
}
return *cfg.SkipRootUserInit
}
// SetSkipRootUserInitialization sets whether the server should skip initializing the default, root
// superuser.
func (cfg YAMLConfig) SetSkipRootUserInitialization(b bool) {
cfg.SkipRootUserInit = &b
}
func (cfg *YAMLConfig) SetUserName(s string) {
cfg.UserConfig.Name = &s
}

View File

@@ -325,6 +325,7 @@ func TestYAMLConfigDefaults(t *testing.T) {
assert.Equal(t, DefaultHost, cfg.Host())
assert.Equal(t, DefaultPort, cfg.Port())
assert.Equal(t, DefaultUser, cfg.User())
assert.False(t, cfg.UserIsSpecified())
assert.Equal(t, DefaultPass, cfg.Password())
assert.Equal(t, uint64(DefaultTimeout), cfg.WriteTimeout())
assert.Equal(t, uint64(DefaultTimeout), cfg.ReadTimeout())

View File

@@ -263,7 +263,7 @@ SQL
[ $status -eq 0 ]
[[ $output =~ '| repo1 | eventTest1 | `__dolt_local_user__`@`localhost` | SYSTEM | RECURRING | NULL | 1 | SECOND | 2020-02-20 00:00:00 | NULL | ENABLED | 0 | utf8mb4 | utf8mb4_0900_bin | utf8mb4_0900_bin |' ]] || false
# Sleep for a few seconds to give the scheduler timer to run this event and verify that it executed
# Sleep for a few seconds to give the scheduler time to run this event and verify that it executed
sleep 2
run dolt sql -q "SELECT (SELECT COUNT(*) FROM totals) > 0;"
[ $status -eq 0 ]
@@ -286,7 +286,7 @@ SQL
run dolt sql -q "SHOW EVENTS;"
[[ $output =~ '| repo1 | eventTest1 | `__dolt_local_user__`@`localhost` | SYSTEM | RECURRING | NULL | 1 | SECOND | 2020-02-20 00:00:00 | NULL | ENABLED | 0 | utf8mb4 | utf8mb4_0900_bin | utf8mb4_0900_bin |' ]] || false
# Sleep for a few seconds to give the scheduler timer to run this event and verify that it is still enabled
# Sleep for a few seconds to give the scheduler time to run this event and verify that it is still enabled
sleep 2
run dolt sql -q "SHOW EVENTS"
[ $status -eq 0 ]

View File

@@ -145,17 +145,17 @@ get_commit_hash_at() {
stop_sql_server 1
run dolt --verbose-engine-setup --data-dir="$ROOT_DIR" --user dolt --password "" --use-db altDB sql -q "show tables"
run dolt --verbose-engine-setup --data-dir="$ROOT_DIR" --use-db altDB sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "starting local mode" ]] || false
[[ "$output" =~ "altDB_tbl" ]] || false
run dolt --verbose-engine-setup --data-dir="$ROOT_DIR" --user dolt --password "" --use-db defaultDB sql -q "show tables"
run dolt --verbose-engine-setup --data-dir="$ROOT_DIR" --use-db defaultDB sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "starting local mode" ]] || false
[[ "$output" =~ "defaultDB_tbl" ]] || false
run dolt --verbose-engine-setup --data-dir="$ROOT_DIR" --user dolt --password "" sql -q "show tables"
run dolt --verbose-engine-setup --data-dir="$ROOT_DIR" sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "starting local mode" ]] || false
[[ "$output" =~ "altDB_tbl" ]] || false
@@ -310,7 +310,7 @@ get_commit_hash_at() {
[ "$status" -eq 0 ] || false
localOutput=$output
run dolt --user dolt --password "" status --ignored
run dolt status --ignored
[ "$status" -eq 0 ] || false
localIgnoredOutput=$output
@@ -376,11 +376,11 @@ get_commit_hash_at() {
stop_sql_server 1
run dolt --verbose-engine-setup --user dolt --password "" branch b2
run dolt --verbose-engine-setup branch b2
[ "$status" -eq 0 ]
[[ "$output" =~ "starting local mode" ]] || false
run dolt --verbose-engine-setup --user dolt --password "" branch
run dolt --verbose-engine-setup branch
[ "$status" -eq 0 ]
[[ "$output" =~ "starting local mode" ]] || false
[[ "$output" =~ "main" ]] || false

View File

@@ -53,35 +53,106 @@ teardown() {
teardown_common
}
@test "sql-privs: default user is root. create new user destroys default user." {
make_test_repo
# Asserts that the root@% superuser is automatically created when a sql-server is started
# for the first time and no users are defined yet. As additional users are created, the
# root@% superuser remains and can be manually removed without coming back.
@test "sql-privs: implicit root superuser doesn't disappear after adding users" {
PORT=$( definePORT )
dolt sql-server --host 0.0.0.0 --port=$PORT &
SERVER_PID=$! # will get killed by teardown_common
SQL_USER='root'
wait_for_connection $PORT 8500
dolt sql-server --port $PORT &
SERVER_PID=$!
sleep 1
run dolt sql -q "select user from mysql.user order by user"
# Assert that the root user can log in and run a query
run dolt -u root sql -q "select user, host from mysql.user where user='root';"
[ $status -eq 0 ]
[[ $output =~ "root" ]] || false
[[ $output =~ "| root | localhost |" ]] || false
dolt sql -q "create user new_user"
run dolt sql -q "select user from mysql.user order by user"
# Create a new user
dolt -u root sql -q "CREATE USER user1@localhost; GRANT ALL PRIVILEGES on *.* to user1@localhost;"
# Restart the SQL server
stop_sql_server 1 && sleep 0.5
dolt sql-server --port $PORT &
SERVER_PID=$!
sleep 1
# Assert that both users are still present
run dolt -u root sql -q "select user, host from mysql.user where user in ('root', 'user1');"
[ $status -eq 0 ]
[[ $output =~ "root" ]] || false
[[ $output =~ "new_user" ]] || false
[[ $output =~ "| root | localhost |" ]] || false
[[ $output =~ "| user1 | localhost |" ]] || false
stop_sql_server
# Delete the root user
dolt -u root sql -q "DROP USER root@localhost;"
# Restart the SQL server
stop_sql_server 1 && sleep 0.5
dolt sql-server --port $PORT &
SERVER_PID=$!
sleep 1
# Assert that the root user is gone
run dolt -u user1 sql -q "select user, host from mysql.user where user in ('root', 'user1');"
[ $status -eq 0 ]
! [[ $output =~ "root" ]] || false
[[ $output =~ "| user1 | localhost |" ]] || false
}
# Asserts that creating users via 'dolt sql' before starting a sql-server causes the privileges.db to be
# initialized and prevents the root superuser from being created, since the customer has already started
# manually managing user accounts.
@test "sql-privs: implicit root superuser doesn't get created when users are created before the server starts" {
dolt sql -q "CREATE USER user1@localhost; GRANT ALL PRIVILEGES on *.* to user1@localhost;"
# restarting server
PORT=$( definePORT )
dolt sql-server --host 0.0.0.0 --port=$PORT &
SERVER_PID=$! # will get killed by teardown_common
SQL_USER='new_user'
wait_for_connection $PORT 8500
dolt sql-server --port $PORT &
SERVER_PID=$!
sleep 1
run dolt -u root sql -q "select user from mysql.user order by user"
# Assert that the root superuser was not automatically created
run dolt -u user1 sql -q "select user, host from mysql.user where user in ('root', 'user1');"
echo "OUTPUT: $output"
[ $status -eq 0 ]
! [[ $output =~ "root" ]] || false
[[ $output =~ "| user1 | localhost |" ]] || false
}
# Asserts that the root@% superuser does not get created when a temporary superuser is specified the
# first time a sql-server is started and privileges.db is initialized.
@test "sql-privs: implicit root superuser doesn't get created when specifying a temporary superuser" {
PORT=$( definePORT )
dolt sql-server --port $PORT -u temp1 &
SERVER_PID=$!
sleep 1
# Assert that there is no root user
run dolt -u temp1 sql -q "select user, host from mysql.user where user='root';"
[ $status -eq 0 ]
! [[ $output =~ "root" ]] || false
}
# Asserts that the root@% superuser is not created when the --skip-default-root-user flag is specified
# when first running sql-server and initializing privileges.db.
@test "sql-privs: implicit root superuser doesn't get created when skipped" {
PORT=$( definePORT )
dolt sql-server --port $PORT --skip-root-user-initialization &
SERVER_PID=$!
sleep 1
# Assert that the root user cannot log in
run dolt -u root sql -q "select user, host from mysql.user where user='root';"
[ $status -ne 0 ]
# Restart the SQL server with a temporary superuser
stop_sql_server 1 && sleep 0.5
dolt sql-server --port $PORT --u user1 &
SERVER_PID=$!
sleep 1
# Assert that there is no root user
run dolt -u user1 sql -q "select user, host from mysql.user where user='root';"
[ $status -eq 0 ]
! [[ $output =~ "root" ]] || false
}
# Asserts that `dolt sql` can always be used to access the database as a superuser. For example, if the root
@@ -258,6 +329,7 @@ behavior:
[[ $output =~ dolt ]] || false
[[ $output =~ new_user ]] || false
[[ $output =~ privs_user ]] || false
[[ $output =~ __dolt_local_user__ ]] || false
}
@test "sql-privs: errors instead of panic when reading badly formatted privilege file" {
@@ -276,7 +348,7 @@ behavior:
start_sql_server test_db
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
[[ "$output" =~ ".doltcfg" ]] || false
run dolt sql -q "select user from mysql.user"
[ $status -eq 0 ]
@@ -334,7 +406,7 @@ behavior:
! [[ "$output" =~ "privileges.db" ]] || false
run ls -a db_dir
! [[ "$output" =~ ".doltcfg" ]] || false
[[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "privileges.db" ]] || false
run dolt -u dolt --port $PORT --host 0.0.0.0 --no-tls --use-db db1 sql -q "show databases"
@@ -375,7 +447,7 @@ behavior:
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "doltcfgdir" ]] || false
[[ "$output" =~ "doltcfgdir" ]] || false
run dolt sql -q "select user from mysql.user"
[ $status -eq 0 ]
@@ -402,8 +474,12 @@ behavior:
start_sql_server_with_args --host 0.0.0.0 --user=dolt --privilege-file=privs.db
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "privs.db" ]] || false
[[ "$output" =~ ".doltcfg" ]] || false
[[ "$output" =~ "privs.db" ]] || false
! [[ "$output" =~ "privileges.db" ]] || false
run ls .doltcfg
! [[ "$output" =~ "privileges.db" ]] || false
run dolt sql -q "select user from mysql.user"
[ $status -eq 0 ]
@@ -415,6 +491,7 @@ behavior:
[ $status -eq 0 ]
[[ $output =~ dolt ]] || false
[[ $output =~ new_user ]] || false
! [[ $output =~ root ]] || false
run ls -a
[[ "$output" =~ ".doltcfg" ]] || false
@@ -428,7 +505,7 @@ behavior:
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "doltcfgdir" ]] || false
[[ "$output" =~ "doltcfgdir" ]] || false
! [[ "$output" =~ "privileges.db" ]] || false
run ls -a db_dir
@@ -474,10 +551,10 @@ behavior:
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "privs.db" ]] || false
[[ "$output" =~ "privs.db" ]] || false
run ls -a db_dir
! [[ "$output" =~ ".doltcfg" ]] || false
[[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "privs.db" ]] || false
run dolt -u dolt --port $PORT --host 0.0.0.0 --no-tls --use-db db1 sql -q "show databases"
@@ -518,8 +595,9 @@ behavior:
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "doltcfgdir" ]] || false
! [[ "$output" =~ "privs.db" ]] || false
[[ "$output" =~ "doltcfgdir" ]] || false
[[ "$output" =~ "privs.db" ]] || false
! [[ "$output" =~ "privileges.db" ]] || false
run dolt sql -q "select user from mysql.user"
[ $status -eq 0 ]
@@ -549,9 +627,9 @@ behavior:
run ls -a
! [[ "$output" =~ ".doltcfg" ]] || false
! [[ "$output" =~ "doltcfgdir" ]] || false
[[ "$output" =~ "doltcfgdir" ]] || false
! [[ "$output" =~ "privileges.db" ]] || false
! [[ "$output" =~ "privs.db" ]] || false
[[ "$output" =~ "privs.db" ]] || false
run dolt -u dolt --port $PORT --host 0.0.0.0 --no-tls --use-db db1 sql -q "show databases"
[ $status -eq 0 ]

View File

@@ -711,7 +711,7 @@ SQL
# verify changes outside the session
cd newdb
run dolt --user=dolt sql -q "show tables"
run dolt sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "test" ]] || false
}
@@ -1095,7 +1095,7 @@ END""")
[ "$status" -eq 0 ]
[[ "$output" =~ "new table a" ]] || false
run dolt --user=dolt sql -q "show tables"
run dolt sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "a" ]] || false
@@ -1104,7 +1104,7 @@ END""")
[ "$status" -eq 0 ]
[[ "$output" =~ "new table b" ]] || false
run dolt --user=dolt sql -q "show tables"
run dolt sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "b" ]] || false
@@ -1337,7 +1337,7 @@ END""")
[ "$status" -eq 0 ]
[[ "$output" =~ "new table a" ]] || false
run dolt --user=dolt sql -q "show tables"
run dolt sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "a" ]] || false
@@ -1346,7 +1346,7 @@ END""")
[ "$status" -eq 0 ]
[[ "$output" =~ "new table b" ]] || false
run dolt --user=dolt sql -q "show tables"
run dolt sql -q "show tables"
[ "$status" -eq 0 ]
[[ "$output" =~ "b" ]] || false