Skip to content

Commit 27f62bf

Browse files
committed
ApplicationAuth: support OIDC authentication mode
1 parent b787969 commit 27f62bf

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

controllers/capabilities/applicationauth_controller.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ type AuthSecret struct {
4747
UserKey string
4848
ApplicationKey string
4949
ApplicationID string
50+
ClientSecret string
5051
}
5152

5253
const (
5354
UserKey = "UserKey"
5455
ApplicationKey = "ApplicationKey"
5556
ApplicationID = "ApplicationID"
57+
ClientSecret = "ClientSecret"
5658
)
5759

5860
// +kubebuilder:rbac:groups=capabilities.3scale.net,resources=applicationauths,verbs=get;list;watch;create;update;patch;delete
@@ -202,6 +204,8 @@ func GetAuthController(mode string, logger logr.Logger) (AuthController, error)
202204
return &userKeyAuthMode{logger: logger}, nil
203205
case "2":
204206
return &appIDAuthMode{logger: logger}, nil
207+
case "oidc":
208+
return &oidcAuthMode{logger: logger}, nil
205209
default:
206210
return nil, fmt.Errorf("unknown authentication mode")
207211
}
@@ -328,6 +332,58 @@ func (a *appIDAuthMode) SecretReferenceSource(cl client.Client, ns string, authS
328332
return &AuthSecret{ApplicationKey: applicationKeyStr}, nil
329333
}
330334

335+
type oidcAuthMode struct {
336+
logger logr.Logger
337+
}
338+
339+
func (o *oidcAuthMode) Sync(threescaleClient *threescaleapi.ThreeScaleClient, developerAccountID int64, applicationID int64, authSecret AuthSecret) error {
340+
// get the existing value from the portal
341+
applicationKeys, err := threescaleClient.ApplicationKeys(developerAccountID, applicationID)
342+
if err != nil {
343+
return err
344+
}
345+
346+
// pre-existing keys
347+
if len(applicationKeys) > 0 {
348+
// Nothing to do, return early
349+
if applicationKeys[0].Value == authSecret.ClientSecret {
350+
return nil
351+
}
352+
353+
// if the key is not match, delete it
354+
if err := threescaleClient.DeleteApplicationKey(developerAccountID, applicationID, applicationKeys[0].Value); err != nil {
355+
return err
356+
}
357+
}
358+
359+
if _, err = threescaleClient.CreateApplicationKey(developerAccountID, applicationID, authSecret.ClientSecret); err != nil {
360+
return err
361+
}
362+
return nil
363+
}
364+
365+
func (o *oidcAuthMode) SecretReferenceSource(cl client.Client, ns string, authSectretRef *corev1.LocalObjectReference, generateSecret bool) (*AuthSecret, error) {
366+
secretSource := helper.NewSecretSource(cl, ns)
367+
clientSecretStr, err := secretSource.RequiredFieldValueFromRequiredSecret(authSectretRef.Name, ClientSecret)
368+
if err != nil {
369+
return nil, err
370+
}
371+
372+
if clientSecretStr == "" {
373+
if generateSecret {
374+
clientSecretStr = rand.String(16)
375+
}
376+
newValues := map[string][]byte{
377+
ClientSecret: []byte(clientSecretStr),
378+
}
379+
380+
if err := updateSecret(context.Background(), cl, authSectretRef.Name, ns, newValues); err != nil {
381+
return nil, err
382+
}
383+
}
384+
return &AuthSecret{ClientSecret: clientSecretStr}, nil
385+
}
386+
331387
func (r *ApplicationAuthReconciler) reconcileStatus(resource *capabilitiesv1beta1.ApplicationAuth, err error, logger logr.Logger) (ctrl.Result, error) {
332388
statusReconciler := NewApplicationAuthStatusReconciler(r.BaseReconciler, resource, err)
333389
statusResult, statusErr := statusReconciler.Reconcile()

controllers/capabilities/applicationauth_controller_test.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,45 @@ func TestApplicationAuthReconciler_syncApplicationAuth(t *testing.T) {
123123
expectedKey: "testkey",
124124
wantErr: false,
125125
},
126+
{
127+
name: "returns error with empty client_secret with empty secret",
128+
mockServer: &mockApplicationAuthServer{
129+
authMode: "oidc",
130+
keys: []string{},
131+
userAccountID: appID,
132+
appID: userAccountID,
133+
},
134+
authMode: "oidc",
135+
authSecret: getEmptyAuthSecret(),
136+
expectedKey: "",
137+
wantErr: true,
138+
},
139+
{
140+
name: "update existing client_secret with value from secret",
141+
mockServer: &mockApplicationAuthServer{
142+
authMode: "oidc",
143+
keys: []string{"initalkey"},
144+
userAccountID: appID,
145+
appID: userAccountID,
146+
},
147+
authMode: "oidc",
148+
authSecret: getAuthSecret(),
149+
expectedKey: "testkey",
150+
wantErr: false,
151+
},
152+
{
153+
name: "update existing client_secret with the same value should not return error",
154+
mockServer: &mockApplicationAuthServer{
155+
authMode: "oidc",
156+
keys: []string{"testkey"},
157+
userAccountID: appID,
158+
appID: userAccountID,
159+
},
160+
authMode: "oidc",
161+
authSecret: getAuthSecret(),
162+
expectedKey: "testkey",
163+
wantErr: false,
164+
},
126165
}
127166
for _, tt := range tests {
128167
t.Run(tt.name, func(t *testing.T) {
@@ -260,6 +299,30 @@ func TestApplicationAuthReconciler_authSecretReferenceSource(t *testing.T) {
260299
wantErr: false,
261300
err: "",
262301
},
302+
{
303+
name: "return error when secret is empty",
304+
authMode: "oidc",
305+
generateSecret: true,
306+
secretData: map[string][]byte{},
307+
wantErr: true,
308+
err: "secret field 'ClientSecret' is required in secret 'test'",
309+
},
310+
{
311+
name: "generate client_secret when secret is empty",
312+
authMode: "oidc",
313+
generateSecret: true,
314+
secretData: map[string][]byte{"ClientSecret": []byte("")},
315+
wantErr: false,
316+
err: "",
317+
},
318+
{
319+
name: "use client_secret value in secret",
320+
authMode: "oidc",
321+
generateSecret: true,
322+
secretData: map[string][]byte{"ClientSecret": []byte("testkey")},
323+
wantErr: false,
324+
err: "",
325+
},
263326
}
264327
for _, tt := range tests {
265328
t.Run(tt.name, func(t *testing.T) {
@@ -317,6 +380,10 @@ func TestApplicationAuthReconciler_authSecretReferenceSource(t *testing.T) {
317380
if authSecret.ApplicationKey != string(newSecret.Data["ApplicationKey"]) {
318381
t.Fatalf("mismatch user_key expected = '%s', got '%s'", authSecret.ApplicationKey, newSecret.Data["ApplicationKey"])
319382
}
383+
case "oidc":
384+
if authSecret.ClientSecret != string(newSecret.Data[ClientSecret]) {
385+
t.Fatalf("mismatch user_key expected = '%s', got '%s'", authSecret.ClientSecret, newSecret.Data[ClientSecret])
386+
}
320387
}
321388
}
322389
})
@@ -373,6 +440,7 @@ func getAuthSecret() AuthSecret {
373440
UserKey: "testkey",
374441
ApplicationKey: "testkey",
375442
ApplicationID: "",
443+
ClientSecret: "testkey",
376444
}
377445
return authSecret
378446
}
@@ -400,7 +468,7 @@ func (m *mockApplicationAuthServer) GetKey(mode string) string {
400468
switch mode {
401469
case "1":
402470
return m.userKey
403-
case "2":
471+
case "2", "oidc":
404472
return strings.Join(m.keys, ",")
405473
default:
406474
return ""
@@ -461,6 +529,8 @@ func (m *mockApplicationAuthServer) applicationKeysHandler(w http.ResponseWriter
461529

462530
if m.authMode == "2" {
463531
keyLimit = 5
532+
} else if m.authMode == "oidc" {
533+
keyLimit = 1
464534
}
465535

466536
// Check if the current length does not exceed 5 keys limit

0 commit comments

Comments
 (0)