Skip to content
This repository was archived by the owner on Mar 2, 2024. It is now read-only.

Commit 9125863

Browse files
authored
🐛 Fix setup MFA breaking due to grafana client ssm parameter (#1005)
1 parent ce6c9ad commit 9125863

File tree

2 files changed

+110
-11
lines changed

2 files changed

+110
-11
lines changed

Diff for: pkg/cognito/mfa.go

+28-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"runtime"
1515
"strings"
1616

17+
"github.com/aws/aws-sdk-go/aws/request"
18+
1719
"github.com/google/uuid"
1820
qrcode "github.com/skip2/go-qrcode"
1921

@@ -32,11 +34,13 @@ const (
3234
// MFAOutputFormatQRCode represents showing the MFA details as a QR code
3335
MFAOutputFormatQRCode = "qrcode"
3436
// MFAOutputFormatText represents showing the MFA details as text
35-
MFAOutputFormatText = "text"
36-
defaultOneTimePasswordType = "TOTP"
37-
defaultOneTimePasswordDigits = 6
38-
defaultOneTimePasswordAlgorithm = "SHA1"
39-
defaultOneTimePasswordInterval = 30
37+
MFAOutputFormatText = "text"
38+
defaultOneTimePasswordType = "TOTP"
39+
defaultOneTimePasswordDigits = 6
40+
defaultOneTimePasswordAlgorithm = "SHA1"
41+
defaultOneTimePasswordInterval = 30
42+
defaultRelevantUserPoolClientKeyword = "argocd"
43+
defaultQRCodePixelSize = 256
4044
)
4145

4246
type userPoolClient struct {
@@ -127,24 +131,29 @@ func getRelevantUserPoolID(ctx context.Context, provider cognitoidentityprovider
127131
return "", fmt.Errorf("no user pool found for cluster %s", cluster.Metadata.Name)
128132
}
129133

130-
func getRelevantUserPoolClient(ctx context.Context, provider cognitoidentityprovideriface.CognitoIdentityProviderAPI, userPoolClientID string) (
134+
func getRelevantUserPoolClient(ctx context.Context, provider userpoolClientsLister, userPoolID string) (
131135
cognitoidentityprovider.UserPoolClientDescription,
132136
error,
133137
) {
134138
var nextToken *string
135139

136140
for {
137141
listUserPoolsResult, err := provider.ListUserPoolClientsWithContext(ctx, &cognitoidentityprovider.ListUserPoolClientsInput{
138-
MaxResults: aws.Int64(1),
142+
MaxResults: aws.Int64(maximumMaximumUserPoolResults),
139143
NextToken: nextToken,
140-
UserPoolId: aws.String(userPoolClientID),
144+
UserPoolId: aws.String(userPoolID),
141145
})
142146
if err != nil {
143147
return cognitoidentityprovider.UserPoolClientDescription{}, fmt.Errorf("listing user pools: %w", err)
144148
}
145149

146150
for _, client := range listUserPoolsResult.UserPoolClients {
147-
return *client, nil
151+
// This should be any of the clients provisioned by okctl, but due to inconsistent naming of the Grafana client
152+
// secret SSM parameter and the situation regarding golden path we'll settle on picking the ArgoCD client for
153+
// now. This breaks MFA for environments without ArgoCD.
154+
if strings.Contains(*client.ClientName, defaultRelevantUserPoolClientKeyword) {
155+
return *client, nil
156+
}
148157
}
149158

150159
if listUserPoolsResult.NextToken == nil {
@@ -154,7 +163,7 @@ func getRelevantUserPoolClient(ctx context.Context, provider cognitoidentityprov
154163
nextToken = listUserPoolsResult.NextToken
155164
}
156165

157-
return cognitoidentityprovider.UserPoolClientDescription{}, fmt.Errorf("no clients found for user pool %s", userPoolClientID)
166+
return cognitoidentityprovider.UserPoolClientDescription{}, fmt.Errorf("no clients found for user pool %s", userPoolID)
158167
}
159168

160169
func getCognitoClientSecretForClient(ctx context.Context, provider ssmiface.SSMAPI, clientName string) (string, error) {
@@ -226,7 +235,7 @@ func generateDeviceSecretQRCode(cluster v1alpha1.Cluster, userEmail string, secr
226235
defaultOneTimePasswordInterval,
227236
)
228237

229-
err := qrcode.WriteFile(qrCodeURI, qrcode.Medium, 256, qrCodePath)
238+
err := qrcode.WriteFile(qrCodeURI, qrcode.Medium, defaultQRCodePixelSize, qrCodePath)
230239
if err != nil {
231240
return "", fmt.Errorf("writing QR code: %w", err)
232241
}
@@ -253,3 +262,11 @@ func openbrowser(url string) {
253262
log.Fatal(err)
254263
}
255264
}
265+
266+
type userpoolClientsLister interface {
267+
ListUserPoolClientsWithContext(
268+
context.Context,
269+
*cognitoidentityprovider.ListUserPoolClientsInput,
270+
...request.Option,
271+
) (*cognitoidentityprovider.ListUserPoolClientsOutput, error)
272+
}

Diff for: pkg/cognito/mfa_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package cognito
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/aws/aws-sdk-go/aws"
8+
"github.com/aws/aws-sdk-go/aws/request"
9+
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestGetRelevantUserPoolClient(t *testing.T) {
14+
testCases := []struct {
15+
name string
16+
withClients []string
17+
expectClient string
18+
}{
19+
{
20+
name: "Should return the argocd client in an expected okctl setup",
21+
withClients: []string{
22+
"okctl-mock-cluster-argocd",
23+
"okctl-mock-cluster-grafana",
24+
},
25+
expectClient: "okctl-mock-cluster-argocd",
26+
},
27+
{
28+
name: "Should return the argocd client in a strange setup with a gazzilion clients",
29+
withClients: []string{
30+
"okctl-mock-cluster-client1",
31+
"okctl-mock-cluster-grafana",
32+
"okctl-mock-cluster-client2",
33+
"okctl-mock-cluster-client3",
34+
"okctl-mock-cluster-argocd",
35+
"okctl-mock-cluster-client4",
36+
"okctl-mock-cluster-client5",
37+
},
38+
expectClient: "okctl-mock-cluster-argocd",
39+
},
40+
}
41+
42+
for _, tc := range testCases {
43+
tc := tc
44+
45+
t.Run(tc.name, func(t *testing.T) {
46+
t.Parallel()
47+
48+
result, err := getRelevantUserPoolClient(
49+
context.Background(),
50+
&mockCognitoIdentityProviderAPI{clients: tc.withClients},
51+
"mock-userpool-id",
52+
)
53+
assert.NoError(t, err)
54+
55+
assert.Equal(t, tc.expectClient, *result.ClientName)
56+
})
57+
}
58+
}
59+
60+
type mockCognitoIdentityProviderAPI struct {
61+
clients []string
62+
}
63+
64+
func (m mockCognitoIdentityProviderAPI) ListUserPoolClientsWithContext(
65+
_ aws.Context,
66+
_ *cognitoidentityprovider.ListUserPoolClientsInput,
67+
_ ...request.Option,
68+
) (*cognitoidentityprovider.ListUserPoolClientsOutput, error) {
69+
clients := make([]*cognitoidentityprovider.UserPoolClientDescription, len(m.clients))
70+
71+
for index, name := range m.clients {
72+
clients[index] = &cognitoidentityprovider.UserPoolClientDescription{
73+
ClientId: aws.String(name),
74+
ClientName: aws.String(name),
75+
UserPoolId: aws.String("mock-user-pool-id"),
76+
}
77+
}
78+
79+
return &cognitoidentityprovider.ListUserPoolClientsOutput{
80+
UserPoolClients: clients,
81+
}, nil
82+
}

0 commit comments

Comments
 (0)