github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/pxc/users/users.go (about)

     1  package users
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  
     7  	"github.com/go-sql-driver/mysql"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  const (
    12  	Root         = "root"
    13  	Operator     = "operator"
    14  	Monitor      = "monitor"
    15  	Xtrabackup   = "xtrabackup"
    16  	Replication  = "replication"
    17  	ProxyAdmin   = "proxyadmin"
    18  	PMMServer    = "pmmserver"
    19  	PMMServerKey = "pmmserverkey"
    20  )
    21  
    22  var UserNames = []string{Root, Operator, Monitor, Xtrabackup,
    23  	Replication, ProxyAdmin, PMMServer, PMMServerKey}
    24  
    25  type Manager struct {
    26  	db *sql.DB
    27  }
    28  
    29  type SysUser struct {
    30  	Name  string   `yaml:"username"`
    31  	Pass  string   `yaml:"password"`
    32  	Hosts []string `yaml:"hosts"`
    33  }
    34  
    35  func NewManager(addr string, user, pass string, timeout int32) (Manager, error) {
    36  	var um Manager
    37  
    38  	timeoutStr := fmt.Sprintf("%ds", timeout)
    39  	config := mysql.NewConfig()
    40  	config.User = user
    41  	config.Passwd = pass
    42  	config.Net = "tcp"
    43  	config.Addr = addr
    44  	config.DBName = "mysql"
    45  	config.Params = map[string]string{
    46  		"interpolateParams": "true",
    47  		"timeout":           timeoutStr,
    48  		"readTimeout":       timeoutStr,
    49  		"writeTimeout":      timeoutStr,
    50  		"tls":               "preferred",
    51  	}
    52  
    53  	mysqlDB, err := sql.Open("mysql", config.FormatDSN())
    54  	if err != nil {
    55  		return um, errors.Wrap(err, "cannot connect to any host")
    56  	}
    57  
    58  	um.db = mysqlDB
    59  
    60  	return um, nil
    61  }
    62  
    63  func (u *Manager) Close() error {
    64  	return u.db.Close()
    65  }
    66  
    67  func (u *Manager) CreateOperatorUser(pass string) error {
    68  	_, err := u.db.Exec("CREATE USER IF NOT EXISTS 'operator'@'%' IDENTIFIED BY ?", pass)
    69  	if err != nil {
    70  		return errors.Wrap(err, "create operator user")
    71  	}
    72  
    73  	_, err = u.db.Exec("GRANT ALL ON *.* TO 'operator'@'%' WITH GRANT OPTION")
    74  	if err != nil {
    75  		return errors.Wrap(err, "grant operator user")
    76  	}
    77  
    78  	return nil
    79  }
    80  
    81  // UpdateUserPassWithoutDP updates user pass without Dual Password
    82  // feature introduced in MsSQL 8
    83  func (u *Manager) UpdateUserPassWithoutDP(user *SysUser) error {
    84  	if user == nil {
    85  		return nil
    86  	}
    87  
    88  	for _, host := range user.Hosts {
    89  		_, err := u.db.Exec("ALTER USER ?@? IDENTIFIED BY ?", user.Name, host, user.Pass)
    90  		if err != nil {
    91  			return errors.Wrap(err, "update password")
    92  		}
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  // UpdateUserPass updates user passwords but retains the current password
    99  // using Dual Password feature of MySQL 8.
   100  func (m *Manager) UpdateUserPass(user *SysUser) error {
   101  	if user == nil {
   102  		return nil
   103  	}
   104  
   105  	tx, err := m.db.Begin()
   106  	if err != nil {
   107  		return errors.Wrap(err, "begin transaction")
   108  	}
   109  
   110  	for _, host := range user.Hosts {
   111  		_, err = tx.Exec("ALTER USER ?@? IDENTIFIED BY ? RETAIN CURRENT PASSWORD", user.Name, host, user.Pass)
   112  		if err != nil {
   113  			err = errors.Wrap(err, "alter user")
   114  
   115  			if errT := tx.Rollback(); errT != nil {
   116  				return errors.Wrap(errors.Wrap(errT, "rollback"), err.Error())
   117  			}
   118  
   119  			return err
   120  		}
   121  	}
   122  
   123  	if err := tx.Commit(); err != nil {
   124  		return errors.Wrap(err, "commit transaction")
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  // DiscardOldPassword discards old passwords of given users
   131  func (m *Manager) DiscardOldPassword(user *SysUser) error {
   132  	if user == nil {
   133  		return nil
   134  	}
   135  
   136  	tx, err := m.db.Begin()
   137  	if err != nil {
   138  		return errors.Wrap(err, "begin transaction")
   139  	}
   140  
   141  	for _, host := range user.Hosts {
   142  		_, err = tx.Exec("ALTER USER ?@? DISCARD OLD PASSWORD", user.Name, host)
   143  		if err != nil {
   144  			err = errors.Wrap(err, "alter user")
   145  
   146  			if errT := tx.Rollback(); errT != nil {
   147  				return errors.Wrap(errors.Wrap(errT, "rollback"), err.Error())
   148  			}
   149  
   150  			return err
   151  		}
   152  	}
   153  
   154  	if err := tx.Commit(); err != nil {
   155  		return errors.Wrap(err, "commit transaction")
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  // DiscardOldPassword discards old passwords of given users
   162  func (m *Manager) IsOldPassDiscarded(user *SysUser) (bool, error) {
   163  	var attributes sql.NullString
   164  	r := m.db.QueryRow("SELECT User_attributes FROM mysql.user WHERE user=?", user.Name)
   165  
   166  	err := r.Scan(&attributes)
   167  	if err != nil {
   168  		if err == sql.ErrNoRows {
   169  			return true, nil
   170  		}
   171  		return false, errors.Wrap(err, "select User_attributes field")
   172  	}
   173  
   174  	if attributes.Valid {
   175  		return false, nil
   176  	}
   177  
   178  	return true, nil
   179  }
   180  
   181  func (u *Manager) UpdateProxyUser(user *SysUser) error {
   182  	switch user.Name {
   183  	case ProxyAdmin:
   184  		_, err := u.db.Exec("UPDATE global_variables SET variable_value=? WHERE variable_name='admin-admin_credentials'", "proxyadmin:"+user.Pass)
   185  		if err != nil {
   186  			return errors.Wrap(err, "update proxy admin password")
   187  		}
   188  		_, err = u.db.Exec("UPDATE global_variables SET variable_value=? WHERE variable_name='admin-cluster_password'", user.Pass)
   189  		if err != nil {
   190  			return errors.Wrap(err, "update proxy admin password")
   191  		}
   192  		_, err = u.db.Exec("LOAD ADMIN VARIABLES TO RUNTIME")
   193  		if err != nil {
   194  			return errors.Wrap(err, "load to runtime")
   195  		}
   196  
   197  		_, err = u.db.Exec("SAVE ADMIN VARIABLES TO DISK")
   198  		if err != nil {
   199  			return errors.Wrap(err, "save to disk")
   200  		}
   201  	case Monitor:
   202  		_, err := u.db.Exec("UPDATE global_variables SET variable_value=? WHERE variable_name='mysql-monitor_password'", user.Pass)
   203  		if err != nil {
   204  			return errors.Wrap(err, "update proxy monitor password")
   205  		}
   206  		_, err = u.db.Exec("LOAD MYSQL VARIABLES TO RUNTIME")
   207  		if err != nil {
   208  			return errors.Wrap(err, "load to runtime")
   209  		}
   210  
   211  		_, err = u.db.Exec("SAVE MYSQL VARIABLES TO DISK")
   212  		if err != nil {
   213  			return errors.Wrap(err, "save to disk")
   214  		}
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  // Update160MonitorUserGrant grants SERVICE_CONNECTION_ADMIN rights to the monitor user
   221  // if pxc version is 8 or more and sets the MAX_USER_CONNECTIONS parameter to 100 (empirically determined)
   222  func (u *Manager) Update160MonitorUserGrant(pass string) (err error) {
   223  
   224  	_, err = u.db.Exec("CREATE USER IF NOT EXISTS 'monitor'@'%' IDENTIFIED BY ?", pass)
   225  	if err != nil {
   226  		return errors.Wrap(err, "create operator user")
   227  	}
   228  
   229  	_, err = u.db.Exec("/*!80015 GRANT SERVICE_CONNECTION_ADMIN ON *.* TO 'monitor'@'%' */")
   230  	if err != nil {
   231  		return errors.Wrapf(err, "grant service_connection to user monitor")
   232  	}
   233  
   234  	_, err = u.db.Exec("ALTER USER 'monitor'@'%' WITH MAX_USER_CONNECTIONS 100")
   235  	if err != nil {
   236  		return errors.Wrapf(err, "set max connections to user monitor")
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  // Update170XtrabackupUser grants all needed rights to the xtrabackup user
   243  func (u *Manager) Update170XtrabackupUser(pass string) (err error) {
   244  
   245  	_, err = u.db.Exec("CREATE USER IF NOT EXISTS 'xtrabackup'@'%' IDENTIFIED BY ?", pass)
   246  	if err != nil {
   247  		return errors.Wrap(err, "create operator user")
   248  	}
   249  
   250  	_, err = u.db.Exec("GRANT ALL ON *.* TO 'xtrabackup'@'%'")
   251  	if err != nil {
   252  		return errors.Wrapf(err, "grant privileges to user xtrabackup")
   253  	}
   254  
   255  	return nil
   256  }
   257  
   258  // Update1100MonitorUserPrivilege grants system_user privilege for monitor
   259  func (u *Manager) Update1100MonitorUserPrivilege() (err error) {
   260  	if _, err := u.db.Exec("GRANT SYSTEM_USER ON *.* TO 'monitor'@'%'"); err != nil {
   261  		return errors.Wrap(err, "monitor user")
   262  	}
   263  
   264  	return nil
   265  }
   266  
   267  func (u *Manager) CreateReplicationUser(password string) error {
   268  
   269  	_, err := u.db.Exec("CREATE USER IF NOT EXISTS 'replication'@'%' IDENTIFIED BY ?", password)
   270  	if err != nil {
   271  		return errors.Wrap(err, "create replication user")
   272  	}
   273  
   274  	_, err = u.db.Exec("GRANT REPLICATION SLAVE ON *.* to 'replication'@'%'")
   275  	if err != nil {
   276  		return errors.Wrap(err, "grant replication user")
   277  	}
   278  
   279  	return nil
   280  }
   281  
   282  // UpdatePassExpirationPolicy sets user password expiration policy to never
   283  func (u *Manager) UpdatePassExpirationPolicy(user *SysUser) error {
   284  	if user == nil {
   285  		return nil
   286  	}
   287  
   288  	for _, host := range user.Hosts {
   289  		_, err := u.db.Exec("ALTER USER ?@? PASSWORD EXPIRE NEVER", user.Name, host)
   290  		if err != nil {
   291  			return err
   292  		}
   293  	}
   294  	return nil
   295  }