/
default_email.go
214 lines (179 loc) · 7.02 KB
/
default_email.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package common
import (
"fmt"
"strings"
"time"
goctx "context"
keycloakv1 "github.com/integr8ly/keycloak-client/apis/keycloak/v1alpha1"
userv1 "github.com/openshift/api/user/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
)
const (
usernameNoEmail = "autotest-user01"
usernameWithEmail = "autotest-user02"
usernameUsingEmail = "autotest-user03@test.com"
usernameRequireSanitiseEmail = "autotest$user04"
existingEmail = "autotest-nondefault-user02@hotmail.com"
defaultDomain = "@rhmi.io"
)
// TestDefaultUserEmail verifies that a user syncronized from the IDP have a
// default email adress if no email is present from the IDP.
//
// Verify that the email address is generated as <username>@rhmi.io
func TestDefaultUserEmail(t TestingTB, ctx *TestingContext) {
err := createTestingIDP(t, goctx.TODO(), ctx.Client, ctx.KubeConfig, ctx.SelfSignedCerts)
if err != nil {
t.Fatalf("Error occurred creating testing IDP: %v", err)
}
// Create user with no email
// the default email for this user will be <user name>@rhmi.io
userNoEmail, identityNoEmail, err := createUserTestingIDP(ctx, usernameNoEmail, nil)
if err != nil {
t.Fatalf("Unexpected error creating User: %v", err)
}
// Clean up the user resource
defer func(ctx *TestingContext, user *userv1.User, identity *userv1.Identity) {
if err := deleteUser(ctx, user, identity); err != nil {
t.Fatal(err)
}
}(ctx, userNoEmail, identityNoEmail)
// Create user with email
// different that the default generated email
userWithEmail, identityWithEmail, err := createUserTestingIDP(ctx, usernameWithEmail, func(identity *userv1.Identity) {
identity.Extra = map[string]string{
"email": existingEmail,
}
})
if err != nil {
t.Fatalf("Unexpected error creating User: %v", err)
}
// Cleanup the user resource
defer func(ctx *TestingContext, user *userv1.User, identity *userv1.Identity) {
if err := deleteUser(ctx, user, identity); err != nil {
t.Fatal(err)
}
}(ctx, userWithEmail, identityWithEmail)
// Create user that uses email as username
userUsingEmailAsUsername, identityWithEmailAsUserName, err := createUserTestingIDP(ctx, usernameUsingEmail, nil)
if err != nil {
t.Fatalf("Unexpected error creating User: %v", err)
}
// Cleanup the user resource
defer func(ctx *TestingContext, user *userv1.User, identity *userv1.Identity) {
if err := deleteUser(ctx, user, identity); err != nil {
t.Fatal(err)
}
}(ctx, userUsingEmailAsUsername, identityWithEmailAsUserName)
// Create user with username that requires sanitization
userRequireSanitiseEmail, identityRequireSanitiseEmail, err := createUserTestingIDP(ctx, usernameRequireSanitiseEmail, nil)
if err != nil {
t.Fatalf("Unexpected error creating User: %v", err)
}
// Cleanup the user resource
defer func(ctx *TestingContext, user *userv1.User, identity *userv1.Identity) {
if err := deleteUser(ctx, user, identity); err != nil {
t.Fatal(err)
}
}(ctx, userRequireSanitiseEmail, identityRequireSanitiseEmail)
rhssoNamespace := fmt.Sprintf("%srhsso", NamespacePrefix)
// Get the keycloak CR for each user
keycloakUser1, err := waitForKeycloakUser(ctx, 5*time.Minute, rhssoNamespace, usernameNoEmail)
if err != nil {
t.Fatalf("Unexpected error querying KeycloakUser %s: %v", usernameNoEmail, err)
}
keycloakUser2, err := waitForKeycloakUser(ctx, 5*time.Minute, rhssoNamespace, usernameWithEmail)
if err != nil {
t.Fatalf("Unexpected error querying KeycloakUser %s: %v", usernameWithEmail, err)
return
}
keycloakUser3, err := waitForKeycloakUser(ctx, 5*time.Minute, rhssoNamespace, usernameUsingEmail)
if err != nil {
t.Fatalf("Unexpected error querying KeycloakUser %s: %v", usernameUsingEmail, err)
return
}
keycloakUser4, err := waitForKeycloakUser(ctx, 5*time.Minute, rhssoNamespace, usernameRequireSanitiseEmail)
if err != nil {
t.Fatalf("Unexpected error querying KeycloakUser %s: %v", usernameRequireSanitiseEmail, err)
return
}
// Assert that the user with no email has the default generated email
expectedEmail := fmt.Sprintf("%s%s", usernameNoEmail, defaultDomain)
if keycloakUser1.Spec.User.Email != expectedEmail {
t.Errorf("Unexpected email for generated KeycloakUser: Expected %s, got %s", expectedEmail, keycloakUser1.Spec.User.Email)
}
// Assert that the user with email has its own email
if keycloakUser2.Spec.User.Email != existingEmail {
t.Errorf("Unexpected email for generated KeycloakUser: Expected %s, got %s", existingEmail, keycloakUser2.Spec.User.Email)
}
// Assert that the user using email as username does not get appended with default domain and just uses username
if keycloakUser3.Spec.User.Email != usernameUsingEmail {
t.Errorf("Unexpected email for generated KeycloakUser: Expected %s, got %s", usernameUsingEmail, keycloakUser3.Spec.User.Email)
}
// Assert that the username is sanitised when using as email
if keycloakUser4.Spec.User.Email != fmt.Sprintf("%s%s", strings.Replace(usernameRequireSanitiseEmail, "$", "-", -1), defaultDomain) {
t.Errorf("Unexpected email for generated KeycloakUser: Expected %s, got %s", usernameUsingEmail, keycloakUser4.Spec.User.Email)
}
}
func createUserTestingIDP(ctx *TestingContext, userName string, mutateIdentity func(*userv1.Identity)) (*userv1.User, *userv1.Identity, error) {
identityName := fmt.Sprintf("%s:%s", TestingIDPRealm, userName)
identity := &userv1.Identity{
ObjectMeta: v1.ObjectMeta{
Name: identityName,
},
ProviderName: TestingIDPRealm,
ProviderUserName: userName,
}
if mutateIdentity != nil {
mutateIdentity(identity)
}
if err := ctx.Client.Create(goctx.TODO(), identity); err != nil {
if !k8serr.IsAlreadyExists(err) {
return nil, nil, err
}
}
var user = &userv1.User{
ObjectMeta: v1.ObjectMeta{
Name: userName,
},
Identities: []string{
identityName,
},
}
if err := ctx.Client.Create(goctx.TODO(), user); err != nil {
return nil, nil, err
}
return user, identity, nil
}
func deleteUser(ctx *TestingContext, user *userv1.User, identity *userv1.Identity) error {
// Delete the user
if err := ctx.Client.Delete(goctx.TODO(), user); err != nil {
return err
}
// Delete the identity
return ctx.Client.Delete(goctx.TODO(), identity)
}
func waitForKeycloakUser(ctx *TestingContext, timeout time.Duration, namespace, userName string) (*keycloakv1.KeycloakUser, error) {
began := time.Now()
for {
// If it timed out, return an error
if time.Now().After(began.Add(timeout)) {
return nil, fmt.Errorf("Timeout after %v", timeout)
}
// Get the list of users in the RHSSO namespace
list := &keycloakv1.KeycloakUserList{}
err := ctx.Client.List(goctx.TODO(), list, k8sclient.InNamespace(namespace))
// If an error occurred, return the error
if err != nil {
return nil, err
}
// Look for the matching user in the user list and send it if it's
// found
for _, keycloakUser := range list.Items {
if keycloakUser.Spec.User.UserName == userName {
return &keycloakUser, nil
}
}
}
}