Skip to content

Commit 6180d3c

Browse files
committed
PGAdmin user passwords are now stored in secrets created by customer and secret is referenced in the user spec. Updated/added appropriate go and kuttl tests.
1 parent e5dd23f commit 6180d3c

24 files changed

+720
-63
lines changed

build/crd/pgadmins/todos.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@
1313
- op: copy
1414
from: /work
1515
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/config/properties/ldapBindPassword/properties/name/description
16+
- op: copy
17+
from: /work
18+
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/users/items/properties/passwordRef/properties/name/description
1619
- op: remove
1720
path: /work

config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml

+19
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,24 @@ spec:
14481448
not show up here.
14491449
items:
14501450
properties:
1451+
passwordRef:
1452+
description: A reference to the secret that holds the user's
1453+
password.
1454+
properties:
1455+
key:
1456+
description: The key of the secret to select from. Must
1457+
be a valid secret key.
1458+
type: string
1459+
name:
1460+
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
1461+
type: string
1462+
optional:
1463+
description: Specify whether the Secret or its key must
1464+
be defined
1465+
type: boolean
1466+
required:
1467+
- key
1468+
type: object
14511469
role:
14521470
description: Role determines whether the user has admin privileges
14531471
or not. Defaults to User. Valid options are Administrator
@@ -1461,6 +1479,7 @@ spec:
14611479
in the pgAdmin's users list.
14621480
type: string
14631481
required:
1482+
- passwordRef
14641483
- username
14651484
type: object
14661485
type: array

internal/controller/standalone_pgadmin/controller.go

+4-28
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,9 @@ import (
2222
corev1 "k8s.io/api/core/v1"
2323
"k8s.io/apimachinery/pkg/api/equality"
2424
"k8s.io/client-go/tools/record"
25-
"k8s.io/client-go/util/workqueue"
2625
ctrl "sigs.k8s.io/controller-runtime"
2726
"sigs.k8s.io/controller-runtime/pkg/client"
2827
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
29-
"sigs.k8s.io/controller-runtime/pkg/event"
30-
"sigs.k8s.io/controller-runtime/pkg/handler"
3128
"sigs.k8s.io/controller-runtime/pkg/source"
3229

3330
controllerruntime "github.com/crunchydata/postgres-operator/internal/controller/runtime"
@@ -75,34 +72,13 @@ func (r *PGAdminReconciler) SetupWithManager(mgr ctrl.Manager) error {
7572
&source.Kind{Type: v1beta1.NewPostgresCluster()},
7673
r.watchPostgresClusters(),
7774
).
75+
Watches(
76+
&source.Kind{Type: &corev1.Secret{}},
77+
r.watchForRelatedSecret(),
78+
).
7879
Complete(r)
7980
}
8081

81-
// watchPostgresClusters returns a [handler.EventHandler] for PostgresClusters.
82-
func (r *PGAdminReconciler) watchPostgresClusters() handler.Funcs {
83-
handle := func(cluster client.Object, q workqueue.RateLimitingInterface) {
84-
ctx := context.Background()
85-
for _, pgadmin := range r.findPGAdminsForPostgresCluster(ctx, cluster) {
86-
87-
q.Add(ctrl.Request{
88-
NamespacedName: client.ObjectKeyFromObject(pgadmin),
89-
})
90-
}
91-
}
92-
93-
return handler.Funcs{
94-
CreateFunc: func(e event.CreateEvent, q workqueue.RateLimitingInterface) {
95-
handle(e.Object, q)
96-
},
97-
UpdateFunc: func(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
98-
handle(e.ObjectNew, q)
99-
},
100-
DeleteFunc: func(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
101-
handle(e.Object, q)
102-
},
103-
}
104-
}
105-
10682
//+kubebuilder:rbac:groups="postgres-operator.crunchydata.com",resources="pgadmins",verbs={get}
10783
//+kubebuilder:rbac:groups="postgres-operator.crunchydata.com",resources="pgadmins/status",verbs={patch}
10884

internal/controller/standalone_pgadmin/users.go

+48-21
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ import (
2525

2626
"github.com/pkg/errors"
2727
corev1 "k8s.io/api/core/v1"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829
"sigs.k8s.io/controller-runtime/pkg/client"
2930

3031
"github.com/crunchydata/postgres-operator/internal/logging"
3132
"github.com/crunchydata/postgres-operator/internal/naming"
32-
"github.com/crunchydata/postgres-operator/internal/util"
3333
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
3434
)
3535

@@ -196,25 +196,45 @@ cd $PGADMIN_DIR
196196
isAdmin = true
197197
}
198198

199+
// Get password from secret
200+
userPasswordSecret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{
201+
Namespace: pgadmin.Namespace,
202+
Name: user.PasswordRef.LocalObjectReference.Name,
203+
}}
204+
err := errors.WithStack(
205+
r.Client.Get(ctx, client.ObjectKeyFromObject(userPasswordSecret), userPasswordSecret))
206+
if err != nil {
207+
log.Error(err, "Could not get user password secret")
208+
continue
209+
}
210+
211+
// Make sure the password isn't nil or empty
212+
password := userPasswordSecret.Data[user.PasswordRef.Key]
213+
if password == nil {
214+
log.Error(nil, `Could not retrieve password from secret. Make sure secret name and key are correct.`)
215+
continue
216+
}
217+
if len(password) == 0 {
218+
log.Error(nil, `Password must not be empty.`)
219+
continue
220+
}
221+
199222
// Assemble user that will be used in add/update command and in updating
200223
// the users.json file in the secret
201224
intentUser := pgAdminUserForJson{
202225
Username: user.Username,
203-
Password: "",
226+
Password: string(password),
204227
IsAdmin: isAdmin,
205228
}
206-
// If the user already exists in users.json, and isAdmin has changed, run
207-
// the update-user command. If the user already exists in users.json, but
208-
// it hasn't changed, do nothing. If the user doesn't exist in users.json,
209-
// run the add-user command.
229+
// If the user already exists in users.json and isAdmin or password has
230+
// changed, run the update-user command. If the user already exists in
231+
// users.json, but it hasn't changed, do nothing. If the user doesn't
232+
// exist in users.json, run the add-user command.
210233
if existingUser, present := existingUsersMap[user.Username]; present {
211-
// Set password for intentUser
212-
intentUser.Password = existingUser.Password
213-
214-
if intentUser.IsAdmin != existingUser.IsAdmin {
215-
// Attempt update-user command
216-
script := setupScript + fmt.Sprintf(`python3 setup.py update-user %s "%s"`,
217-
typeFlag, intentUser.Username) + "\n"
234+
// If Password or IsAdmin have changed, attempt update-user command
235+
if intentUser.IsAdmin != existingUser.IsAdmin || intentUser.Password != existingUser.Password {
236+
script := setupScript + fmt.Sprintf(`python3 setup.py update-user %s --password "%s" "%s"`,
237+
typeFlag, intentUser.Password, intentUser.Username) + "\n"
218238
err = exec(ctx, &stdin, &stdout, &stderr,
219239
[]string{"bash", "-ceu", "--", script}...)
220240

@@ -231,16 +251,23 @@ cd $PGADMIN_DIR
231251
intentUsers = append(intentUsers, existingUser)
232252
continue
233253
}
254+
// If update user fails due to user not found or password length:
255+
// https://github.com/pgadmin-org/pgadmin4/blob/REL-8_5/web/setup.py#L263
256+
// https://github.com/pgadmin-org/pgadmin4/blob/REL-8_5/web/setup.py#L246
257+
if strings.Contains(stdout.String(), "User not found") ||
258+
strings.Contains(stdout.String(), "Password must be") {
259+
260+
log.Info("Failed to update pgAdmin user", "user", intentUser.Username, "error", stdout.String())
261+
r.Recorder.Event(pgadmin,
262+
corev1.EventTypeWarning, "InvalidUserWarning",
263+
fmt.Sprintf("Failed to update pgAdmin user %s: %s",
264+
intentUser.Username, stdout.String()))
265+
intentUsers = append(intentUsers, existingUser)
266+
continue
267+
}
234268
}
235269
} else {
236-
// New user, so generate a password and set it on intentUser
237-
password, err := util.GenerateASCIIPassword(util.DefaultGeneratedPasswordLength)
238-
if err != nil {
239-
return err
240-
}
241-
intentUser.Password = password
242-
243-
// Attempt add-user command
270+
// New user, so attempt add-user command
244271
script := setupScript + fmt.Sprintf(`python3 setup.py add-user %s -- "%s" "%s"`,
245272
typeFlag, intentUser.Username, intentUser.Password) + "\n"
246273
err = exec(ctx, &stdin, &stdout, &stderr,

0 commit comments

Comments
 (0)