@@ -40,6 +40,18 @@ const (
4040// +kubebuilder:rbac:groups=rabbitmq.com,resources=users/status,verbs=get;update;patch
4141// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create
4242
43+ // UserCredentials describes the credentials that can be provided in ImportCredentialsSecret for a User.
44+ // If the secret is not provided, a random username and password will be generated.
45+ type UserCredentials struct {
46+ // Must be present if ImportCredentialsSecret is provided.
47+ Username string
48+ // If PasswordHash is an empty string, a passwordless user is created.
49+ // If PasswordHash is nil, Password is used instead.
50+ PasswordHash * string
51+ // If Password is empty and PasswordHash is nil, a random password is generated.
52+ Password string
53+ }
54+
4355type UserReconciler struct {
4456 client.Client
4557 Scheme * runtime.Scheme
@@ -48,21 +60,31 @@ type UserReconciler struct {
4860func (r * UserReconciler ) declareCredentials (ctx context.Context , user * topology.User ) (string , error ) {
4961 logger := ctrl .LoggerFrom (ctx )
5062
51- username , password , err := r .generateCredentials (ctx , user )
63+ credentials , err := r .generateCredentials (ctx , user )
5264 if err != nil {
5365 logger .Error (err , "failed to generate credentials" )
5466 return "" , err
5567 }
56- // Password wasn't in the provided input secret we need to generate a random one
57- if password == "" {
58- password , err = internal .RandomEncodedString (24 )
68+ // Neither PasswordHash nor Password wasn't in the provided input secret we need to generate a random password
69+ if credentials . PasswordHash == nil && credentials . Password == "" {
70+ credentials . Password , err = internal .RandomEncodedString (24 )
5971 if err != nil {
6072 return "" , fmt .Errorf ("failed to generate random password: %w" , err )
6173 }
62-
6374 }
6475
65- logger .Info ("Credentials generated for User" , "user" , user .Name , "generatedUsername" , username )
76+ logger .Info ("Credentials generated for User" , "user" , user .Name , "generatedUsername" , credentials .Username )
77+
78+ credentialSecretData := map [string ][]byte {
79+ "username" : []byte (credentials .Username ),
80+ }
81+ if credentials .PasswordHash != nil {
82+ // Create `passwordHash` field only if necessary, to distinguish between an unset hash and an empty one
83+ credentialSecretData ["passwordHash" ] = []byte (* credentials .PasswordHash )
84+ } else {
85+ // Store password in the credential secret only if it will be used
86+ credentialSecretData ["password" ] = []byte (credentials .Password )
87+ }
6688
6789 credentialSecret := corev1.Secret {
6890 ObjectMeta : metav1.ObjectMeta {
@@ -72,10 +94,7 @@ func (r *UserReconciler) declareCredentials(ctx context.Context, user *topology.
7294 Type : corev1 .SecretTypeOpaque ,
7395 // The format of the generated Secret conforms to the Provisioned Service
7496 // type Spec. For more information, see https://k8s-service-bindings.github.io/spec/#provisioned-service.
75- Data : map [string ][]byte {
76- "username" : []byte (username ),
77- "password" : []byte (password ),
78- },
97+ Data : credentialSecretData ,
7998 }
8099
81100 var operationResult controllerutil.OperationResult
@@ -102,10 +121,10 @@ func (r *UserReconciler) declareCredentials(ctx context.Context, user *topology.
102121 }
103122
104123 logger .Info ("Successfully declared credentials secret" , "secret" , credentialSecret .Name , "namespace" , credentialSecret .Namespace )
105- return username , nil
124+ return credentials . Username , nil
106125}
107126
108- func (r * UserReconciler ) generateCredentials (ctx context.Context , user * topology.User ) (string , string , error ) {
127+ func (r * UserReconciler ) generateCredentials (ctx context.Context , user * topology.User ) (UserCredentials , error ) {
109128 logger := ctrl .LoggerFrom (ctx )
110129
111130 var err error
@@ -117,37 +136,48 @@ func (r *UserReconciler) generateCredentials(ctx context.Context, user *topology
117136 return r .importCredentials (ctx , user .Spec .ImportCredentialsSecret .Name , user .Namespace )
118137 }
119138
120- username , err := internal .RandomEncodedString (24 )
139+ credentials := UserCredentials {}
140+
141+ credentials .Username , err = internal .RandomEncodedString (24 )
121142 if err != nil {
122- return "" , "" , fmt .Errorf ("failed to generate random username: %w" , err )
143+ return credentials , fmt .Errorf ("failed to generate random username: %w" , err )
123144 }
124- password , err : = internal .RandomEncodedString (24 )
145+ credentials . Password , err = internal .RandomEncodedString (24 )
125146 if err != nil {
126- return "" , "" , fmt .Errorf ("failed to generate random password: %w" , err )
147+ return credentials , fmt .Errorf ("failed to generate random password: %w" , err )
127148 }
128- return username , password , nil
149+ return credentials , nil
129150}
130151
131- func (r * UserReconciler ) importCredentials (ctx context.Context , secretName , secretNamespace string ) (string , string , error ) {
152+ func (r * UserReconciler ) importCredentials (ctx context.Context , secretName , secretNamespace string ) (UserCredentials , error ) {
132153 logger := ctrl .LoggerFrom (ctx )
133154 logger .Info ("Importing user credentials from provided Secret" , "secretName" , secretName , "secretNamespace" , secretNamespace )
134155
156+ var credentials UserCredentials
135157 var credentialsSecret corev1.Secret
158+
136159 err := r .Client .Get (ctx , types.NamespacedName {Name : secretName , Namespace : secretNamespace }, & credentialsSecret )
137160 if err != nil {
138- return "" , "" , fmt .Errorf ("could not find password secret %s in namespace %s; Err: %w" , secretName , secretNamespace , err )
161+ return credentials , fmt .Errorf ("could not find password secret %s in namespace %s; Err: %w" , secretName , secretNamespace , err )
139162 }
163+
140164 username , ok := credentialsSecret .Data ["username" ]
141165 if ! ok {
142- return "" , "" , fmt .Errorf ("could not find username key in credentials secret: %s" , credentialsSecret .Name )
166+ return credentials , fmt .Errorf ("could not find username key in credentials secret: %s" , credentialsSecret .Name )
143167 }
144- password , ok := credentialsSecret .Data ["password" ]
145- if ! ok {
146- return string (username ), "" , nil
168+ credentials .Username = string (username )
169+
170+ password := credentialsSecret .Data ["password" ]
171+ credentials .Password = string (password )
172+
173+ passwordHash , ok := credentialsSecret .Data ["passwordHash" ]
174+ if ok {
175+ credentials .PasswordHash = new (string )
176+ * credentials .PasswordHash = string (passwordHash )
147177 }
148178
149179 logger .Info ("Retrieved credentials from Secret" , "secretName" , secretName , "retrievedUsername" , string (username ))
150- return string ( username ), string ( password ) , nil
180+ return credentials , nil
151181}
152182
153183func (r * UserReconciler ) setUserStatus (ctx context.Context , user * topology.User , username string ) error {
0 commit comments