finished / abandoning

the default password mode in mysql 8 will not work with this approach, it requires the plaintext passwords be in proxysql
This commit is contained in:
2023-08-26 21:47:23 -07:00
parent 312a2c6fe7
commit 876069d735

View File

@ -13,8 +13,41 @@ type User struct {
Password string
}
type UserClient interface {
GetUsers() ([]User, error)
type MatchResult int
const (
MATCH_NONE MatchResult = iota
MATCH_USER
MATCH_ALL
)
// Matches describes if/how a User matches another user if the username and passwords match, MATCH_ALL is returned. If
// the username matches but not the password, MATCH_USER is returned. If the username doesn't match, MATCH_NONE.
func (u *User) Matches(b *User) MatchResult {
if u.User == b.User {
if u.Password == b.Password {
return MATCH_ALL
}
return MATCH_USER
}
return MATCH_NONE
}
// matchUser searches $users for a user matching $user, and returns the matched user
func matchUser(users []*User, user *User) *User {
for _, otheruser := range users {
if otheruser.Matches(user) != MATCH_NONE {
return otheruser
}
}
return nil
}
type UserFetcher interface {
GetUsers() ([]*User, error)
}
type UserSetter interface {
CreateUser(string, string, string) error
DeleteUser(string, string) error
}
@ -23,10 +56,10 @@ type MysqlClient struct {
con *sql.DB
}
func (m MysqlClient) GetUsers() ([]User, error) {
var users []User
func (m MysqlClient) GetUsers() ([]*User, error) {
var users []*User
rows, err := m.con.Query("select User,Host,authentication_string from mysql.user;")
rows, err := m.con.Query(`SELECT User, Host, authentication_string FROM mysql.user WHERE Host="%";`)
if err != nil {
return nil, fmt.Errorf("couldn't run query: %s", err)
}
@ -37,7 +70,7 @@ func (m MysqlClient) GetUsers() ([]User, error) {
if err := rows.Scan(&user.User, &user.Host, &user.Password); err != nil {
return nil, fmt.Errorf("couldn't read row: %s", err)
}
users = append(users, user)
users = append(users, &user)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("couldn't read rows: %s", err)
@ -49,12 +82,12 @@ type ProxysqlClient struct {
con *sql.DB
}
func (p ProxysqlClient) GetUsers() ([]User, error) {
func (p ProxysqlClient) GetUsers() ([]*User, error) {
// This differs from the mysql version above in that proxysql doesn't support by-host user filtering, so we just
// assume every user's host is %
var users []User
var users []*User
rows, err := p.con.Query("select username, password from mysql_users;")
rows, err := p.con.Query("SELECT username, password FROM mysql_users;")
if err != nil {
return nil, fmt.Errorf("couldn't run query: %s", err)
}
@ -66,7 +99,7 @@ func (p ProxysqlClient) GetUsers() ([]User, error) {
if err := rows.Scan(&user.User, &user.Password); err != nil {
return nil, fmt.Errorf("couldn't read row: %s", err)
}
users = append(users, user)
users = append(users, &user)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("couldn't read rows: %s", err)
@ -75,11 +108,21 @@ func (p ProxysqlClient) GetUsers() ([]User, error) {
}
func (p ProxysqlClient) CreateUser(username, host, password string) error {
// TODO
//_, err := p.con.Exec("REPLACE INTO mysql_users (username, password) VALUES (?, ?);", username, password)
_, err := p.con.Exec(`REPLACE INTO mysql_users (username, password) VALUES ("` + username + `", "` + password + `");`)
if err != nil {
ver, _ := getVersion(p.con)
log.Printf("server version: %s", ver)
return fmt.Errorf("add user query failed: %s", err)
}
return nil
}
func (p ProxysqlClient) DeleteUser(username, host string) error {
// TODO
_, err := p.con.Exec("DELETE FROM mysql_users WHERE username=?;", username)
if err != nil {
return fmt.Errorf("delete user query failed: %s", err)
}
return nil
}
@ -97,6 +140,26 @@ func getMysqlClient(dsn string) (*sql.DB, error) {
return db, nil
}
func getVersion(db *sql.DB) (string, error) {
var version string
rows, err := db.Query("SELECT version();")
if err != nil {
return "", fmt.Errorf("couldn't run query: %s", err)
}
defer rows.Close()
for rows.Next() {
if err := rows.Scan(&version); err != nil {
return "", fmt.Errorf("couldn't read row: %s", err)
}
}
if err := rows.Err(); err != nil {
return "", fmt.Errorf("couldn't read rows: %s", err)
}
return version, nil
}
func Sync(srcDSN, destDSN string) error {
// connect to both mysqls
srcdb, err := getMysqlClient(srcDSN)
@ -120,22 +183,73 @@ func Sync(srcDSN, destDSN string) error {
if err != nil {
return fmt.Errorf("couldn't retreive src users: %s", err)
}
for _, user := range srcUsers {
log.Printf("src: %v", user)
}
destUsers, err := dest.GetUsers()
if err != nil {
return fmt.Errorf("couldn't retreive dest users: %s", err)
}
for _, user := range destUsers {
log.Printf("dest: %v", user)
}
// build list of operations we'll do
// remove user
// add user
// update user password
// first, pair up users that exist on both sides
// and use it to check for users that need a password updated
// find users that need to be created on the dest side
// find users that need to be deleted on the dest side
var pairs [][]*User
var srcOnly []*User
var destOnly []*User
for _, srcUser := range srcUsers {
destUser := matchUser(destUsers, srcUser)
if destUser != nil {
if srcUser.Password != destUser.Password {
pairs = append(pairs, []*User{srcUser, destUser})
}
} else {
srcOnly = append(srcOnly, srcUser)
}
}
for _, destUser := range destUsers {
srcUser := matchUser(srcUsers, destUser)
if srcUser == nil {
destOnly = append(destOnly, destUser)
}
}
log.Printf("update password: ")
for _, user := range pairs {
log.Printf("\t%s", user[0].User)
err := dest.CreateUser(user[0].User, user[0].Host, user[0].Password)
if err != nil {
return fmt.Errorf("couldn't update password: %s", err)
}
}
log.Printf("create on destination: ")
for _, user := range srcOnly {
log.Printf("\t%s", user.User)
err := dest.CreateUser(user.User, user.Host, user.Password)
if err != nil {
return fmt.Errorf("couldn't create user: %s", err)
}
}
log.Printf("delete on destination:")
for _, user := range destOnly {
log.Printf("\t%s", user.User)
err := dest.DeleteUser(user.User, user.Host)
if err != nil {
return fmt.Errorf("couldn't delete user: %s", err)
}
}
if len(pairs) > 0 || len(srcOnly) > 0 || len(destOnly) > 0 {
_, err := destdb.Exec("LOAD MYSQL USERS TO RUNTIME;")
if err != nil {
return fmt.Errorf("couldn't update runtime users: %s", err)
}
}
// perform the operations