This commit adds and passes context around to (almost) every function. This allows cancelling (almost) every function call globally.
121 lines
4.3 KiB
Go
121 lines
4.3 KiB
Go
package sql
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/errorx"
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/sqle"
|
|
)
|
|
|
|
var errProvisionInvalidDatabaseParams = errors.New("Provision: Invalid parameters")
|
|
var errProvisionInvalidGrant = errors.New("Provision: Grant failed")
|
|
|
|
// Provision provisions sql-specific resource for the given instance
|
|
func (sql *SQL) Provision(ctx context.Context, instance models.Instance, domain string) error {
|
|
return sql.CreateDatabase(ctx, instance.SqlDatabase, instance.SqlUsername, instance.SqlPassword)
|
|
}
|
|
|
|
// Purge purges sql-specific resources for the given instance
|
|
func (sql *SQL) Purge(ctx context.Context, instance models.Instance, domain string) error {
|
|
return errorx.First(
|
|
sql.PurgeDatabase(instance.SqlDatabase),
|
|
sql.PurgeUser(ctx, instance.SqlUsername),
|
|
)
|
|
}
|
|
|
|
// CreateDatabase creates a new database with the given name.
|
|
// It then generates a new user, with the name 'user' and the password 'password', that is then granted access to this database.
|
|
//
|
|
// Provision internally waits for the database to become available.
|
|
func (sql *SQL) CreateDatabase(ctx context.Context, name, user, password string) error {
|
|
|
|
// NOTE(twiesing): We shouldn't use string concat to build sql queries.
|
|
// But the driver doesn't support using query params for this particular query.
|
|
// Apparently it's a "feature", see https://github.com/go-sql-driver/mysql/issues/398#issuecomment-169951763.
|
|
|
|
// quick and dirty check to make sure that all the names won't sql inject.
|
|
if !sqle.IsSafeDatabaseLiteral(name) || !sqle.IsSafeDatabaseSingleQuote(user) || !sqle.IsSafeDatabaseSingleQuote(password) {
|
|
return errProvisionInvalidDatabaseParams
|
|
}
|
|
|
|
// We use the sql shell here, because not only can we not use query params, but the driver outright rejects queries.
|
|
// Queries of the form "CREATE USER 'test'@'%' IDENTIFIED BY 'test'; FLUSH PRIVILEGES;" return error 1064 when using driver, but are fine with the shell.
|
|
// This should be fixed eventually, but I have no idea how.
|
|
|
|
if err := sql.unsafeWaitShell(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
query := "CREATE DATABASE `" + name + "`;" +
|
|
"CREATE USER '" + user + "'@'%' IDENTIFIED BY '" + password + "';" +
|
|
"GRANT ALL PRIVILEGES ON `" + name + "`.* TO `" + user + "`@`%`; FLUSH PRIVILEGES;"
|
|
if !sql.unsafeQueryShell(ctx, query) {
|
|
return errProvisionInvalidGrant
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var errCreateSuperuserGrant = errors.New("CreateSuperUser: Grant failed")
|
|
|
|
// CreateSuperuser createsa new user, with the name 'user' and the password 'password'.
|
|
// It then grants this user superuser status in the database.
|
|
//
|
|
// CreateSuperuser internally waits for the database to become available.
|
|
func (sql *SQL) CreateSuperuser(ctx context.Context, user, password string, allowExisting bool) error {
|
|
// NOTE(twiesing): This function unsafely uses the shell directly to create a superuser.
|
|
// This is for two reasons:
|
|
// (1) this is used during bootstraping
|
|
// (2) The underlying driver doesn't support "GRANT ALL PRIVILEGES"
|
|
// See also [sql.Provision].
|
|
|
|
if !sqle.IsSafeDatabaseSingleQuote(user) || !sqle.IsSafeDatabaseSingleQuote(password) {
|
|
return errProvisionInvalidDatabaseParams
|
|
}
|
|
|
|
if err := sql.unsafeWaitShell(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
var IfNotExists string
|
|
if allowExisting {
|
|
IfNotExists = "IF NOT EXISTS"
|
|
}
|
|
|
|
query := "CREATE USER " + IfNotExists + " '" + user + "'@'%' IDENTIFIED BY '" + password + "';" +
|
|
"GRANT ALL PRIVILEGES ON *.* TO '" + user + "'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;"
|
|
if !sql.unsafeQueryShell(ctx, query) {
|
|
return errCreateSuperuserGrant
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var errPurgeUser = errors.New("PurgeUser: Failed to drop user")
|
|
|
|
// SQLPurgeUser deletes the specified user from the database
|
|
func (sql *SQL) PurgeUser(ctx context.Context, user string) error {
|
|
if !sqle.IsSafeDatabaseSingleQuote(user) {
|
|
return errPurgeUser
|
|
}
|
|
|
|
query := "DROP USER IF EXISTS '" + user + "'@'%';" +
|
|
"FLUSH PRIVILEGES;"
|
|
if !sql.unsafeQueryShell(ctx, query) {
|
|
return errPurgeUser
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var errSQLPurgeDB = errors.New("unable to drop database: unsafe database name")
|
|
|
|
// SQLPurgeDatabase deletes the specified db from the database
|
|
func (sql *SQL) PurgeDatabase(db string) error {
|
|
if !sqle.IsSafeDatabaseLiteral(db) {
|
|
return errSQLPurgeDB
|
|
}
|
|
return sql.Exec("DROP DATABASE IF EXISTS `" + db + "`")
|
|
}
|