let isSystemUsername check all system users (#2489)

* let isSystemUsername check all system users
* extend robot user unit test
* reset system users for initSystemUser test
This commit is contained in:
Felix Kunde 2023-12-08 15:21:56 +01:00 committed by GitHub
parent 96077c47d6
commit 9ee14f26cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 21 deletions

View File

@ -26,6 +26,8 @@ import (
const (
superUserName = "postgres"
replicationUserName = "standby"
poolerUserName = "pooler"
adminUserName = "admin"
exampleSpiloConfig = `{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"test":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"100","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`
spiloConfigDiff = `{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"test":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`
)
@ -37,7 +39,7 @@ var cl = New(
Config{
OpConfig: config.Config{
PodManagementPolicy: "ordered_ready",
ProtectedRoles: []string{"admin", "cron_admin", "part_man"},
ProtectedRoles: []string{adminUserName, "cron_admin", "part_man"},
Auth: config.Auth{
SuperUsername: superUserName,
ReplicationUsername: replicationUserName,
@ -46,6 +48,9 @@ var cl = New(
Resources: config.Resources{
DownscalerAnnotations: []string{"downscaler/*"},
},
ConnectionPooler: config.ConnectionPooler{
User: poolerUserName,
},
},
},
k8sutil.NewMockKubernetesClient(),
@ -55,6 +60,20 @@ var cl = New(
Namespace: "test",
Annotations: map[string]string{"downscaler/downtime_replicas": "0"},
},
Spec: acidv1.PostgresSpec{
EnableConnectionPooler: util.True(),
Streams: []acidv1.Stream{
acidv1.Stream{
ApplicationId: "test-app",
Database: "test_db",
Tables: map[string]acidv1.StreamTable{
"test_table": acidv1.StreamTable{
EventType: "test-app.test",
},
},
},
},
},
},
logger,
eventRecorder,
@ -127,56 +146,85 @@ func TestStatefulSetUpdateWithEnv(t *testing.T) {
func TestInitRobotUsers(t *testing.T) {
tests := []struct {
testCase string
manifestUsers map[string]acidv1.UserFlags
infraRoles map[string]spec.PgUser
result map[string]spec.PgUser
err error
}{
{
testCase: "manifest user called like infrastructure role - latter should take percedence",
manifestUsers: map[string]acidv1.UserFlags{"foo": {"superuser", "createdb"}},
infraRoles: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginInfrastructure, Name: "foo", Namespace: cl.Namespace, Password: "bar"}},
result: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginInfrastructure, Name: "foo", Namespace: cl.Namespace, Password: "bar"}},
err: nil,
},
{
testCase: "manifest user with forbidden characters",
manifestUsers: map[string]acidv1.UserFlags{"!fooBar": {"superuser", "createdb"}},
err: fmt.Errorf(`invalid username: "!fooBar"`),
},
{
testCase: "manifest user with unknown privileges (should be catched by CRD, too)",
manifestUsers: map[string]acidv1.UserFlags{"foobar": {"!superuser", "createdb"}},
err: fmt.Errorf(`invalid flags for user "foobar": ` +
`user flag "!superuser" is not alphanumeric`),
},
{
testCase: "manifest user with unknown privileges - part 2 (should be catched by CRD, too)",
manifestUsers: map[string]acidv1.UserFlags{"foobar": {"superuser1", "createdb"}},
err: fmt.Errorf(`invalid flags for user "foobar": ` +
`user flag "SUPERUSER1" is not valid`),
},
{
testCase: "manifest user with conflicting flags",
manifestUsers: map[string]acidv1.UserFlags{"foobar": {"inherit", "noinherit"}},
err: fmt.Errorf(`invalid flags for user "foobar": ` +
`conflicting user flags: "NOINHERIT" and "INHERIT"`),
},
{
manifestUsers: map[string]acidv1.UserFlags{"admin": {"superuser"}, superUserName: {"createdb"}},
testCase: "manifest user called like Spilo system users",
manifestUsers: map[string]acidv1.UserFlags{superUserName: {"createdb"}, replicationUserName: {"replication"}},
infraRoles: map[string]spec.PgUser{},
result: map[string]spec.PgUser{},
err: nil,
},
{
testCase: "manifest user called like protected user name",
manifestUsers: map[string]acidv1.UserFlags{adminUserName: {"superuser"}},
infraRoles: map[string]spec.PgUser{},
result: map[string]spec.PgUser{},
err: nil,
},
{
testCase: "manifest user called like pooler system user",
manifestUsers: map[string]acidv1.UserFlags{poolerUserName: {}},
infraRoles: map[string]spec.PgUser{},
result: map[string]spec.PgUser{},
err: nil,
},
{
testCase: "manifest user called like stream system user",
manifestUsers: map[string]acidv1.UserFlags{"fes_user": {"replication"}},
infraRoles: map[string]spec.PgUser{},
result: map[string]spec.PgUser{},
err: nil,
},
}
cl.initSystemUsers()
for _, tt := range tests {
cl.Spec.Users = tt.manifestUsers
cl.pgUsers = tt.infraRoles
if err := cl.initRobotUsers(); err != nil {
if tt.err == nil {
t.Errorf("%s got an unexpected error: %v", t.Name(), err)
t.Errorf("%s - %s: got an unexpected error: %v", tt.testCase, t.Name(), err)
}
if err.Error() != tt.err.Error() {
t.Errorf("%s expected error %v, got %v", t.Name(), tt.err, err)
t.Errorf("%s - %s: expected error %v, got %v", tt.testCase, t.Name(), tt.err, err)
}
} else {
if !reflect.DeepEqual(cl.pgUsers, tt.result) {
t.Errorf("%s expected: %#v, got %#v", t.Name(), tt.result, cl.pgUsers)
t.Errorf("%s - %s: expected: %#v, got %#v", tt.testCase, t.Name(), tt.result, cl.pgUsers)
}
}
}
@ -269,7 +317,7 @@ func TestInitHumanUsers(t *testing.T) {
},
{
existingRoles: map[string]spec.PgUser{},
teamRoles: []string{"admin", replicationUserName},
teamRoles: []string{adminUserName, replicationUserName},
result: map[string]spec.PgUser{},
err: nil,
},
@ -896,6 +944,11 @@ func TestServiceAnnotations(t *testing.T) {
}
func TestInitSystemUsers(t *testing.T) {
// reset system users, pooler and stream section
cl.systemUsers = make(map[string]spec.PgUser)
cl.Spec.EnableConnectionPooler = boolToPointer(false)
cl.Spec.Streams = []acidv1.Stream{}
// default cluster without connection pooler and event streams
cl.initSystemUsers()
if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; exist {
@ -914,35 +967,35 @@ func TestInitSystemUsers(t *testing.T) {
// superuser is not allowed as connection pool user
cl.Spec.ConnectionPooler = &acidv1.ConnectionPooler{
User: "postgres",
User: superUserName,
}
cl.OpConfig.SuperUsername = "postgres"
cl.OpConfig.ConnectionPooler.User = "pooler"
cl.OpConfig.SuperUsername = superUserName
cl.OpConfig.ConnectionPooler.User = poolerUserName
cl.initSystemUsers()
if _, exist := cl.systemUsers["pooler"]; !exist {
if _, exist := cl.systemUsers[poolerUserName]; !exist {
t.Errorf("%s, Superuser is not allowed to be a connection pool user", t.Name())
}
// neither protected users are
delete(cl.systemUsers, "pooler")
delete(cl.systemUsers, poolerUserName)
cl.Spec.ConnectionPooler = &acidv1.ConnectionPooler{
User: "admin",
User: adminUserName,
}
cl.OpConfig.ProtectedRoles = []string{"admin"}
cl.OpConfig.ProtectedRoles = []string{adminUserName}
cl.initSystemUsers()
if _, exist := cl.systemUsers["pooler"]; !exist {
if _, exist := cl.systemUsers[poolerUserName]; !exist {
t.Errorf("%s, Protected user are not allowed to be a connection pool user", t.Name())
}
delete(cl.systemUsers, "pooler")
delete(cl.systemUsers, poolerUserName)
cl.Spec.ConnectionPooler = &acidv1.ConnectionPooler{
User: "standby",
User: replicationUserName,
}
cl.initSystemUsers()
if _, exist := cl.systemUsers["pooler"]; !exist {
if _, exist := cl.systemUsers[poolerUserName]; !exist {
t.Errorf("%s, System users are not allowed to be a connection pool user", t.Name())
}
@ -960,8 +1013,8 @@ func TestInitSystemUsers(t *testing.T) {
ApplicationId: "test-app",
Database: "test_db",
Tables: map[string]acidv1.StreamTable{
"data.test_table": {
EventType: "test_event",
"test_table": {
EventType: "test-app.test",
},
},
},
@ -1017,7 +1070,7 @@ func TestPreparedDatabases(t *testing.T) {
subTest: "Test admin role of owner",
role: "foo_owner",
memberOf: "",
admin: "admin",
admin: adminUserName,
},
{
subTest: "Test writer is a member of reader",

View File

@ -78,7 +78,14 @@ func (c *Cluster) isProtectedUsername(username string) bool {
}
func (c *Cluster) isSystemUsername(username string) bool {
return (username == c.OpConfig.SuperUsername || username == c.OpConfig.ReplicationUsername)
// is there a pooler system user defined
for _, systemUser := range c.systemUsers {
if username == systemUser.Name {
return true
}
}
return false
}
func isValidFlag(flag string) bool {