Skip to content

Commit a741d50

Browse files
committed
ApplicationAuth: handle authentication mode
1 parent cd7ffe5 commit a741d50

File tree

2 files changed

+501
-208
lines changed

2 files changed

+501
-208
lines changed

controllers/capabilities/applicationauth_controller.go

Lines changed: 119 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,12 @@ package controllers
1818

1919
import (
2020
"context"
21-
"crypto/sha256"
22-
"encoding/hex"
2321
"encoding/json"
2422
"fmt"
25-
"strconv"
26-
"time"
2723

2824
capabilitiesv1beta1 "github.com/3scale/3scale-operator/apis/capabilities/v1beta1"
2925
controllerhelper "github.com/3scale/3scale-operator/pkg/controller/helper"
26+
rand "github.com/3scale/3scale-operator/pkg/crypto/rand"
3027
"github.com/3scale/3scale-operator/pkg/helper"
3128
"github.com/3scale/3scale-operator/pkg/reconcilers"
3229
"github.com/3scale/3scale-operator/version"
@@ -133,6 +130,12 @@ func (r *ApplicationAuthReconciler) Reconcile(ctx context.Context, req ctrl.Requ
133130
return ctrl.Result{}, err
134131
}
135132

133+
authMode := product.Spec.AuthenticationMode()
134+
if authMode == nil {
135+
err := fmt.Errorf("unable to identify authentication mode from Product CR")
136+
return reconcileStatus(r.BaseReconciler, applicationAuth, err, reqLogger)
137+
}
138+
136139
// Retrieve providerAccountRef
137140
providerAccount, err := controllerhelper.LookupProviderAccount(r.Client(), applicationAuth.GetNamespace(), applicationAuth.Spec.ProviderAccountRef, r.Logger())
138141
if err != nil {
@@ -158,9 +161,14 @@ func (r *ApplicationAuthReconciler) Reconcile(ctx context.Context, req ctrl.Requ
158161
return ctrl.Result{}, err
159162
}
160163

161-
// populate authSecret struct
162-
authSecret := authSecretReferenceSource(r.Client(), applicationAuth.Namespace, applicationAuth.Spec.AuthSecretRef, reqLogger)
163-
err = r.applicationAuthReconciler(applicationAuth, *developerAccount.Status.ID, *application.Status.ID, product, *authSecret, threescaleAPIClient)
164+
// populate authSecret struct and make sure required fields are available
165+
shouldGenerateSecret := applicationAuth.Spec.GenerateSecret != nil && *applicationAuth.Spec.GenerateSecret
166+
authSecret, err := authSecretReferenceSource(r.Client(), applicationAuth.Namespace, applicationAuth.Spec.AuthSecretRef, shouldGenerateSecret, *authMode, reqLogger)
167+
if err != nil {
168+
return reconcileStatus(r.BaseReconciler, applicationAuth, err, reqLogger)
169+
}
170+
171+
err = syncApplicationAuth(*developerAccount.Status.ID, *application.Status.ID, *authMode, *authSecret, threescaleAPIClient)
164172
if err != nil {
165173
return reconcileStatus(r.BaseReconciler, applicationAuth, err, reqLogger)
166174
}
@@ -176,109 +184,112 @@ func (r *ApplicationAuthReconciler) SetupWithManager(mgr ctrl.Manager) error {
176184
Complete(r)
177185
}
178186

179-
func (r *ApplicationAuthReconciler) applicationAuthReconciler(
180-
applicationAuth *capabilitiesv1beta1.ApplicationAuth,
187+
func syncApplicationAuth(
181188
developerAccountID int64,
182189
applicationID int64,
183-
product *capabilitiesv1beta1.Product,
190+
authMode string,
184191
authSecret AuthSecret,
185192
threescaleClient *threescaleapi.ThreeScaleClient,
186193
) error {
187-
// generate sha base of timestamp
188-
timestamp := time.Now().Unix()
189-
// Write the timestamp string and encode to hash
190-
hash := sha256.New()
191-
hash.Write([]byte(strconv.FormatInt(timestamp, 10)))
192-
hashedBytes := hash.Sum(nil)
193-
hashedString := hex.EncodeToString(hashedBytes)
194-
195-
// Check the values if populated or the GenerateSecret field is true and make the api call to update
196-
// If UserKey is not populated generate random sha
197-
if authSecret.UserKey == "" && *applicationAuth.Spec.GenerateSecret {
198-
authSecret.UserKey = hashedString
199-
}
200-
if authSecret.UserKey != "" {
201-
params := make(map[string]string)
202-
params["user_key"] = authSecret.UserKey
203-
// edge case if the operator is stopped before reconcile finished need to nil check application.Status.ID
204-
_, err := threescaleClient.UpdateApplication(developerAccountID, applicationID, params)
194+
switch authMode {
195+
case "1":
196+
// get the existing value from the porta
197+
existingApplication, err := threescaleClient.Application(developerAccountID, applicationID)
205198
if err != nil {
206199
return err
207200
}
208-
}
209-
210-
if authSecret.ApplicationKey != "" {
211-
foundApplication, err := threescaleClient.CreateApplicationKey(developerAccountID, applicationID, authSecret.ApplicationKey)
201+
existingKey := existingApplication.UserKey
202+
203+
// user_key mismatch, update
204+
if existingKey != authSecret.UserKey {
205+
params := make(map[string]string)
206+
params["user_key"] = authSecret.UserKey
207+
if _, err := threescaleClient.UpdateApplication(developerAccountID, applicationID, params); err != nil {
208+
return err
209+
}
210+
}
211+
case "2":
212+
// get the existing value from the portal
213+
existingKeys, err := threescaleClient.ApplicationKeys(developerAccountID, applicationID)
212214
if err != nil {
213215
return err
214216
}
215217

216-
authSecret.ApplicationID = foundApplication.ApplicationId
217-
}
218+
// pre-existing keys
219+
if len(existingKeys) > 0 {
220+
// Nothing to do, return early
221+
if existingKeys[0].Value == authSecret.ApplicationKey {
222+
return nil
223+
}
218224

219-
if applicationAuth.Spec.GenerateSecret != nil && *applicationAuth.Spec.GenerateSecret {
220-
foundApplication, err := threescaleClient.CreateApplicationRandomKey(developerAccountID, applicationID)
221-
if err != nil {
222-
return err
225+
// if the key is not match, delete it
226+
if err := threescaleClient.DeleteApplicationKey(developerAccountID, applicationID, existingKeys[0].Value); err != nil {
227+
return err
228+
}
223229
}
224-
authSecret.ApplicationID = foundApplication.ApplicationId
225-
var foundApplicationKeys []threescaleapi.ApplicationKey
226-
foundApplicationKeys, err = threescaleClient.ApplicationKeys(developerAccountID, applicationID)
227-
if err != nil {
230+
231+
if _, err := threescaleClient.CreateApplicationKey(developerAccountID, applicationID, authSecret.ApplicationKey); err != nil {
228232
return err
229233
}
230-
lastKey := len(foundApplicationKeys) - 1
231-
authSecret.ApplicationKey = fmt.Sprint(foundApplicationKeys[lastKey].Value)
232-
}
233-
234-
// get the current values and update the secret
235-
ApplicationAuthSecret := &corev1.Secret{}
236-
err := r.Client().Get(r.Context(), types.NamespacedName{
237-
Name: applicationAuth.Spec.AuthSecretRef.Name,
238-
Namespace: applicationAuth.Namespace,
239-
}, ApplicationAuthSecret)
240-
if err != nil {
241-
// Handle errors gracefully, e.g., log and return or retry
242-
r.Logger().Error(err, "Failed to get existing ApplicationAuthSecret")
243-
return err
244-
}
245-
newData := ApplicationAuthSecret.Data
246-
newValues := map[string][]byte{
247-
UserKey: []byte(authSecret.UserKey),
248-
ApplicationID: []byte(authSecret.ApplicationID),
249-
ApplicationKey: []byte(authSecret.ApplicationKey),
250-
}
251-
for key, value := range newValues {
252-
newData[key] = value
253-
}
254-
255-
ApplicationAuthSecret.Data = newData
256-
err = r.Client().Update(r.Context(), ApplicationAuthSecret)
257-
if err != nil {
258-
r.Logger().Error(err, "Failed to update ApplicationAuthSecret")
259-
return err
260234
}
261235

262236
return nil
263237
}
264238

265-
func authSecretReferenceSource(cl client.Client, ns string, authSectretRef *corev1.LocalObjectReference, logger logr.Logger) *AuthSecret {
266-
if authSectretRef != nil {
267-
logger.Info("LookupAuthSecret", "ns", ns, "authSecretRef", authSectretRef)
268-
secretSource := helper.NewSecretSource(cl, ns)
239+
func authSecretReferenceSource(cl client.Client, ns string, authSectretRef *corev1.LocalObjectReference, generateSecret bool, authMode string, logger logr.Logger) (*AuthSecret, error) {
240+
logger.Info("LookupAuthSecret", "ns", ns, "authSecretRef", authSectretRef)
241+
secretSource := helper.NewSecretSource(cl, ns)
242+
243+
switch authMode {
244+
case "1":
269245
userKeyStr, err := secretSource.RequiredFieldValueFromRequiredSecret(authSectretRef.Name, UserKey)
270246
if err != nil {
271-
userKeyStr = ""
247+
return nil, err
248+
}
249+
250+
if userKeyStr == "" {
251+
if generateSecret {
252+
userKeyStr = rand.String(16)
253+
254+
newValues := map[string][]byte{
255+
UserKey: []byte(userKeyStr),
256+
}
257+
258+
if err := updateSecret(context.Background(), cl, authSectretRef.Name, ns, newValues); err != nil {
259+
return nil, err
260+
}
261+
} else {
262+
// Nothing available raise error now
263+
return nil, fmt.Errorf("no user_key available in secret and generate secret is set to false")
264+
}
272265
}
266+
return &AuthSecret{UserKey: userKeyStr}, nil
267+
case "2":
273268
applicationKeyStr, err := secretSource.RequiredFieldValueFromRequiredSecret(authSectretRef.Name, ApplicationKey)
274269
if err != nil {
275-
applicationKeyStr = ""
270+
return nil, err
276271
}
277272

278-
return &AuthSecret{UserKey: userKeyStr, ApplicationKey: applicationKeyStr}
279-
}
273+
if applicationKeyStr == "" {
274+
if generateSecret {
275+
applicationKeyStr = rand.String(16)
280276

281-
return nil
277+
newValues := map[string][]byte{
278+
ApplicationKey: []byte(applicationKeyStr),
279+
}
280+
281+
if err := updateSecret(context.Background(), cl, authSectretRef.Name, ns, newValues); err != nil {
282+
return nil, err
283+
}
284+
} else {
285+
// Nothing available raise error now
286+
return nil, fmt.Errorf("no user_key available in secret and generate secret is set to false")
287+
}
288+
}
289+
return &AuthSecret{ApplicationKey: applicationKeyStr}, nil
290+
default:
291+
return nil, fmt.Errorf("unknown authentication mode")
292+
}
282293
}
283294

284295
func reconcileStatus(b *reconcilers.BaseReconciler, resource *capabilitiesv1beta1.ApplicationAuth, err error, logger logr.Logger) (ctrl.Result, error) {
@@ -315,3 +326,30 @@ func checkApplicationResources(applicationAuthResource *capabilitiesv1beta1.Appl
315326

316327
return nil
317328
}
329+
330+
func updateSecret(ctx context.Context, client client.Client, name string, namespace string, values map[string][]byte) error {
331+
// get the current values and update the secret
332+
secret := &corev1.Secret{}
333+
err := client.Get(ctx, types.NamespacedName{
334+
Name: name,
335+
Namespace: namespace,
336+
}, secret)
337+
if err != nil {
338+
// Handle errors gracefully, e.g., log and return or retry
339+
return err
340+
}
341+
342+
newData := secret.Data
343+
344+
for key, value := range values {
345+
newData[key] = value
346+
}
347+
348+
secret.Data = newData
349+
350+
if err = client.Update(ctx, secret); err != nil {
351+
return err
352+
}
353+
354+
return nil
355+
}

0 commit comments

Comments
 (0)