diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 424c8e89a..7266965bd 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -957,10 +957,6 @@ func (c *Cluster) initSystemUsers() { Password: util.RandomPassword(constants.PasswordLength), } - if _, exists := c.pgUsers[username]; !exists { - c.pgUsers[username] = connectionPoolerUser - } - if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; !exists { c.systemUsers[constants.ConnectionPoolerUserKeyName] = connectionPoolerUser } diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index 760b68d72..f6a55a644 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -24,7 +24,8 @@ const ( JOIN pg_catalog.pg_authid b ON (m.roleid = b.oid) WHERE m.member = a.oid) as memberof FROM pg_catalog.pg_authid a LEFT JOIN pg_db_role_setting s ON (a.oid = s.setrole AND s.setdatabase = 0::oid) - WHERE a.rolname = ANY($1) + WHERE a.rolname != ALL($1) + AND (rolcanlogin OR (NOT rolcanlogin AND rolpassword IS NOT NULL)) ORDER BY 1;` getDatabasesSQL = `SELECT datname, pg_get_userbyid(datdba) AS owner FROM pg_database;` diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index c82e528fd..cea657fc7 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -528,6 +528,9 @@ func (c *Cluster) syncSecrets() error { } else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name { secretUsername = constants.ReplicationUserKeyName userMap = c.systemUsers + } else if secretUsername == c.systemUsers[constants.ConnectionPoolerUserName].Name { + secretUsername = constants.ConnectionPoolerUserName + userMap = c.systemUsers } else { userMap = c.pgUsers } @@ -557,8 +560,8 @@ func (c *Cluster) syncRoles() (err error) { c.setProcessName("syncing roles") var ( - dbUsers spec.PgUserMap - userNames []string + dbUsers spec.PgUserMap + systemUserNames []string ) err = c.initDbConn() @@ -576,20 +579,11 @@ func (c *Cluster) syncRoles() (err error) { } }() - for _, u := range c.pgUsers { - userNames = append(userNames, u.Name) + for _, u := range c.systemUsers { + systemUserNames = append(systemUserNames, u.Name) } - if needMasterConnectionPooler(&c.Spec) || needReplicaConnectionPooler(&c.Spec) { - connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName] - userNames = append(userNames, connectionPoolerUser.Name) - - if _, exists := c.pgUsers[connectionPoolerUser.Name]; !exists { - c.pgUsers[connectionPoolerUser.Name] = connectionPoolerUser - } - } - - dbUsers, err = c.readPgUsersFromDatabase(userNames) + dbUsers, err = c.readPgUsersFromDatabase(systemUserNames) if err != nil { return fmt.Errorf("error getting users from the database: %v", err) } diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 78c79e1b3..69f742e42 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -42,6 +42,7 @@ const ( PGSyncUserAdd = iota PGsyncUserAlter PGSyncAlterSet // handle ALTER ROLE SET parameter = value + PGSyncUserRename ) // PgUser contains information about a single user. diff --git a/pkg/util/constants/roles.go b/pkg/util/constants/roles.go index dd906fe80..4230028e1 100644 --- a/pkg/util/constants/roles.go +++ b/pkg/util/constants/roles.go @@ -18,5 +18,6 @@ const ( ReaderRoleNameSuffix = "_reader" WriterRoleNameSuffix = "_writer" UserRoleNameSuffix = "_user" + RoleRenameSuffix = "_delete_me" DefaultSearchPath = "\"$user\"" ) diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index 231cf2a89..1551ee437 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -9,11 +9,13 @@ import ( "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" + "github.com/zalando/postgres-operator/pkg/util/constants" ) const ( createUserSQL = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;` alterUserSQL = `ALTER ROLE "%s" %s` + alterUserRenameSQL = `ALTER ROLE "%s" RENAME TO "%s%s"` alterRoleResetAllSQL = `ALTER ROLE "%s" RESET ALL` alterRoleSetSQL = `ALTER ROLE "%s" SET %s TO %s` grantToUserSQL = `GRANT %s TO "%s"` @@ -36,7 +38,6 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM newUsers spec.PgUserMap) []spec.PgSyncUserRequest { var reqs []spec.PgSyncUserRequest - // No existing roles are deleted or stripped of role memebership/flags for name, newUser := range newUsers { dbUser, exists := dbUsers[name] if !exists { @@ -70,6 +71,16 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM } } + // No existing roles are deleted or stripped of role memebership/flags + // but they will be renamed acting as a simple blocker + for name, dbUser := range dbUsers { + _, exists := newUsers[name] + nameSuffixDiff := len(name) - len(constants.RoleRenameSuffix) + + if !exists && (nameSuffixDiff <= 0 || (nameSuffixDiff > 0 && name[nameSuffixDiff:] != constants.RoleRenameSuffix)) { + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserRename, User: dbUser}) + } + } return reqs } @@ -94,6 +105,11 @@ func (strategy DefaultUserSyncStrategy) ExecuteSyncRequests(requests []spec.PgSy reqretries = append(reqretries, request) errors = append(errors, fmt.Sprintf("could not set custom user %q parameters: %v", request.User.Name, err)) } + case spec.PGSyncUserRename: + if err := strategy.alterPgUserRename(request.User, db); err != nil { + reqretries = append(reqretries, request) + errors = append(errors, fmt.Sprintf("could not rename custom user %q: %v", request.User.Name, err)) + } default: return fmt.Errorf("unrecognized operation: %v", request.Kind) } @@ -124,6 +140,14 @@ func (strategy DefaultUserSyncStrategy) alterPgUserSet(user spec.PgUser, db *sql return nil } +func (strategy DefaultUserSyncStrategy) alterPgUserRename(user spec.PgUser, db *sql.DB) error { + query := fmt.Sprintf(alterUserRenameSQL, user.Name, user.Name, constants.RoleRenameSuffix) + if _, err := db.Exec(query); err != nil { + return err + } + return nil +} + func (strategy DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) error { var userFlags []string var userPassword string