github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/verify-install/keycloak/keycloak_test.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package keycloak 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "os/exec" 10 "path" 11 "strings" 12 "time" 13 14 . "github.com/onsi/ginkgo/v2" 15 . "github.com/onsi/gomega" 16 "github.com/verrazzano/verrazzano/pkg/constants" 17 "github.com/verrazzano/verrazzano/pkg/k8sutil" 18 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 19 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 20 corev1 "k8s.io/api/core/v1" 21 "k8s.io/apimachinery/pkg/api/errors" 22 ) 23 24 const ( 25 waitTimeout = 10 * time.Minute 26 pollingInterval = 30 * time.Second 27 keycloakNamespace = "keycloak" 28 vzUser = "verrazzano" 29 realmMgmt = "realm-management" 30 viewUsersRole = "view-users" 31 osdURI = "osd.vmi.system." 32 osURI = "opensearch.vmi.system." 33 grafanaURI = "grafana.vmi.system." 34 prometheusURI = "prometheus.vmi.system." 35 kialiURI = "kiali.vmi.system." 36 verrazzanoURI = "verrazzano." 37 rancherURI = "rancher." 38 kcAdminScript = "/opt/keycloak/bin/kcadm.sh" 39 argocdURI = "argocd." 40 alertmanagerURI = "alertmanager." 41 ) 42 43 // KeycloakClients represents an array of clients currently configured in Keycloak 44 type KeycloakClients []struct { 45 ID string `json:"id"` 46 ClientID string `json:"clientId"` 47 } 48 49 // keycloakRoles represents an array of role names configured in Keycloak 50 type keycloakRoles []struct { 51 Name string `json:"name"` 52 } 53 54 type Client struct { 55 ID string `json:"id"` 56 ClientID string `json:"clientId"` 57 SurrogateAuthRequired bool `json:"surrogateAuthRequired"` 58 Enabled bool `json:"enabled"` 59 AlwaysDisplayInConsole bool `json:"alwaysDisplayInConsole"` 60 ClientAuthenticatorType string `json:"clientAuthenticatorType"` 61 RedirectUris []string `json:"redirectUris"` 62 WebOrigins []string `json:"webOrigins"` 63 NotBefore int `json:"notBefore"` 64 BearerOnly bool `json:"bearerOnly"` 65 ConsentRequired bool `json:"consentRequired"` 66 StandardFlowEnabled bool `json:"standardFlowEnabled"` 67 ImplicitFlowEnabled bool `json:"implicitFlowEnabled"` 68 DirectAccessGrantsEnabled bool `json:"directAccessGrantsEnabled"` 69 ServiceAccountsEnabled bool `json:"serviceAccountsEnabled"` 70 PublicClient bool `json:"publicClient"` 71 FrontchannelLogout bool `json:"frontchannelLogout"` 72 Protocol string `json:"protocol"` 73 Attributes struct { 74 SamlAssertionSignature string `json:"saml.assertion.signature"` 75 SamlForcePostBinding string `json:"saml.force.post.binding"` 76 SamlMultivaluedRoles string `json:"saml.multivalued.roles"` 77 SamlEncrypt string `json:"saml.encrypt"` 78 SamlServerSignature string `json:"saml.server.signature"` 79 SamlServerSignatureKeyinfoExt string `json:"saml.server.signature.keyinfo.ext"` 80 ExcludeSessionStateFromAuthResponse string `json:"exclude.session.state.from.auth.response"` 81 SamlForceNameIDFormat string `json:"saml_force_name_id_format"` 82 SamlClientSignature string `json:"saml.client.signature"` 83 TLSClientCertificateBoundAccessTokens string `json:"tls.client.certificate.bound.access.tokens"` 84 SamlAuthnstatement string `json:"saml.authnstatement"` 85 DisplayOnConsentScreen string `json:"display.on.consent.screen"` 86 PkceCodeChallengeMethod string `json:"pkce.code.challenge.method"` 87 SamlOnetimeuseCondition string `json:"saml.onetimeuse.condition"` 88 } `json:"attributes"` 89 AuthenticationFlowBindingOverrides struct { 90 } `json:"authenticationFlowBindingOverrides"` 91 FullScopeAllowed bool `json:"fullScopeAllowed"` 92 NodeReRegistrationTimeout int `json:"nodeReRegistrationTimeout"` 93 ProtocolMappers []struct { 94 ID string `json:"id"` 95 Name string `json:"name"` 96 Protocol string `json:"protocol"` 97 ProtocolMapper string `json:"protocolMapper"` 98 ConsentRequired bool `json:"consentRequired"` 99 Config struct { 100 Multivalued string `json:"multivalued"` 101 UserinfoTokenClaim string `json:"userinfo.token.claim"` 102 UserAttribute string `json:"user.attribute"` 103 IDTokenClaim string `json:"id.token.claim"` 104 AccessTokenClaim string `json:"access.token.claim"` 105 ClaimName string `json:"claim.name"` 106 JSONTypeLabel string `json:"jsonType.label"` 107 } `json:"config,omitempty"` 108 } `json:"protocolMappers"` 109 DefaultClientScopes []string `json:"defaultClientScopes"` 110 OptionalClientScopes []string `json:"optionalClientScopes"` 111 Access struct { 112 View bool `json:"view"` 113 Configure bool `json:"configure"` 114 Manage bool `json:"manage"` 115 } `json:"access"` 116 } 117 118 var volumeClaims map[string]*corev1.PersistentVolumeClaim 119 120 var t = framework.NewTestFramework("keycloak") 121 122 var isMinVersion140 bool 123 var isMinVersion150 bool 124 var isKeycloakEnabled bool 125 var isArgoCDEnabled bool 126 127 var beforeSuite = t.BeforeSuiteFunc(func() { 128 Eventually(func() (map[string]*corev1.PersistentVolumeClaim, error) { 129 var err error 130 volumeClaims, err = pkg.GetPersistentVolumeClaims(keycloakNamespace) 131 return volumeClaims, err 132 }, waitTimeout, pollingInterval).ShouldNot(BeNil()) 133 134 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 135 if err != nil { 136 Fail(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error())) 137 } 138 isKeycloakEnabled = pkg.IsKeycloakEnabled(kubeconfigPath) 139 isMinVersion140, err = pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath) 140 isMinVersion150, err = pkg.IsVerrazzanoMinVersion("1.5.0", kubeconfigPath) 141 isArgoCDEnabled = pkg.IsArgoCDEnabled(kubeconfigPath) 142 if err != nil { 143 Fail(err.Error()) 144 } 145 }) 146 147 var _ = BeforeSuite(beforeSuite) 148 149 var _ = t.AfterEach(func() {}) 150 151 var _ = t.Describe("Test Keycloak configuration.", Label("f:platform-lcm.install"), func() { 152 var _ = t.Context("Verify", func() { 153 isManagedClusterProfile := pkg.IsManagedClusterProfile() 154 t.It("master realm password policy", func() { 155 if !isManagedClusterProfile { 156 // GIVEN the password policy setup for the master realm during installation 157 // WHEN valid and invalid password changes are attempted 158 // THEN verify valid passwords are accepted and invalid passwords are rejected. 159 Eventually(verifyKeycloakMasterRealmPasswordPolicyIsCorrect, waitTimeout, pollingInterval).Should(BeTrue()) 160 } 161 }) 162 t.It("verrazzano-system realm password policy", func() { 163 if !isManagedClusterProfile { 164 // GIVEN the password policy setup for the verrazzano-system realm during installation 165 // WHEN valid and invalid password changes are attempted 166 // THEN verify valid passwords are accepted and invalid passwords are rejected. 167 Eventually(verifyKeycloakVerrazzanoRealmPasswordPolicyIsCorrect, waitTimeout, pollingInterval).Should(BeTrue()) 168 } 169 }) 170 }) 171 }) 172 173 var _ = t.Describe("Verify", Label("f:platform-lcm.install"), func() { 174 var _ = t.Context("MySQL Persistent Volumes in namespace keycloak based on", func() { 175 kubeconfigPath, _ := k8sutil.GetKubeConfigLocation() 176 177 size := "8Gi" // based on values set in platform-operator/thirdparty/charts/mysql 178 if ok, _ := pkg.IsVerrazzanoMinVersion("1.5.0", kubeconfigPath); ok { 179 size = "2Gi" 180 } 181 override, _ := pkg.GetEffectiveKeyCloakPersistenceOverride(kubeconfigPath) 182 if override != nil { 183 size = override.Spec.Resources.Requests.Storage().String() 184 } 185 186 claimName := "mysql" 187 if ok, _ := pkg.IsVerrazzanoMinVersion("1.5.0", kubeconfigPath); ok { 188 claimName = "datadir-mysql-0" 189 } 190 191 if pkg.IsDevProfile() { 192 expectedKeyCloakPVCs := 0 193 is15, _ := pkg.IsVerrazzanoMinVersion("1.5.0", kubeconfigPath) 194 if is15 { 195 expectedKeyCloakPVCs = 1 196 } 197 if override != nil { 198 expectedKeyCloakPVCs = 1 199 } 200 t.It("Dev install profile", func() { 201 // There is no Persistent Volume for MySQL in a dev install 202 Expect(len(volumeClaims)).To(Equal(expectedKeyCloakPVCs)) 203 if expectedKeyCloakPVCs > 0 { 204 assertPersistentVolume(claimName, size) 205 } 206 }) 207 } else if pkg.IsManagedClusterProfile() { 208 t.It("Managed Cluster install profile and verify namespace keycloak doesn't exist", func() { 209 // There is no keycloak namespace in a managed cluster install 210 Eventually(func() bool { 211 _, err := pkg.GetNamespace(keycloakNamespace) 212 return err != nil && errors.IsNotFound(err) 213 }, waitTimeout, pollingInterval).Should(BeTrue()) 214 }) 215 } else if pkg.IsProdProfile() { 216 t.It("Prod install profile", func() { 217 // Expect the number of claims to be equal to the number of MySQL replicas 218 mysqlStatefulSet, err := pkg.GetStatefulSet("keycloak", "mysql") 219 Expect(err).ShouldNot(HaveOccurred(), "Unexpected error obtaining MySQL statefulset") 220 expectedClaims := int(mysqlStatefulSet.Status.Replicas) 221 Expect(len(volumeClaims)).To(Equal(expectedClaims)) 222 assertPersistentVolume(claimName, size) 223 }) 224 } 225 }) 226 }) 227 228 var _ = t.Describe("Verify Keycloak", Label("f:platform-lcm.install"), func() { 229 var _ = t.Context("redirect and weborigins URIs", func() { 230 pkg.MinVersionSpec("Verify redirect and weborigins URIs", "1.1.0", 231 func() { 232 isManagedClusterProfile := pkg.IsManagedClusterProfile() 233 if !isManagedClusterProfile { 234 // GIVEN installation/upgrade of Keycloak has happened 235 // THEN verify that the correct redirect and weborigins URIs are created for verrazzano 236 Eventually(verifyKeycloakClientURIs, waitTimeout, pollingInterval).Should(BeTrue()) 237 } 238 }) 239 }) 240 }) 241 242 var _ = t.Describe("Verify client role", Label("f:platform-lcm.install"), func() { 243 t.It("Verify clients role for verrazzano user", func() { 244 isManagedClusterProfile := pkg.IsManagedClusterProfile() 245 // verrazzano user has the view-user role, starting from v1.4.0 246 if isKeycloakEnabled && isMinVersion140 && !isManagedClusterProfile { 247 Eventually(func() bool { 248 return verifyUserClientRole(vzUser, viewUsersRole) 249 }, waitTimeout, pollingInterval).Should(BeTrue()) 250 } else { 251 t.Logs.Info("Skipping client role verification") 252 } 253 }) 254 }) 255 256 func verifyKeycloakVerrazzanoRealmPasswordPolicyIsCorrect() bool { 257 return verifyKeycloakRealmPasswordPolicyIsCorrect(constants.VerrazzanoOIDCSystemRealm) 258 } 259 260 func verifyKeycloakMasterRealmPasswordPolicyIsCorrect() bool { 261 return verifyKeycloakRealmPasswordPolicyIsCorrect("master") 262 } 263 264 func verifyKeycloakRealmPasswordPolicyIsCorrect(realm string) bool { 265 kc, err := pkg.NewKeycloakAdminRESTClient() 266 if err != nil { 267 t.Logs.Error(fmt.Printf("Failed to create Keycloak REST client: %v\n", err)) 268 return false 269 } 270 271 var realmData map[string]interface{} 272 realmData, err = kc.GetRealm(realm) 273 if err != nil { 274 t.Logs.Error(fmt.Printf("Failed to get realm %s\n", realm)) 275 return false 276 } 277 if realmData["passwordPolicy"] == nil { 278 t.Logs.Error(fmt.Printf("Failed to find password policy for realm: %s\n", realm)) 279 return false 280 } 281 policy := realmData["passwordPolicy"].(string) 282 if len(policy) == 0 || !strings.Contains(policy, "length") { 283 t.Logs.Error(fmt.Printf("Failed to find password policy for realm: %s\n", realm)) 284 return false 285 } 286 287 salt := time.Now().Format("20060102150405.000000000") 288 userName := fmt.Sprintf("test-user-%s", salt) 289 firstName := fmt.Sprintf("test-first-%s", salt) 290 lastName := fmt.Sprintf("test-last-%s", salt) 291 validPassword := fmt.Sprintf("test-password-12-!@-AB-%s", salt) 292 userURL, err := kc.CreateUser(realm, userName, firstName, lastName, validPassword) 293 if err != nil { 294 t.Logs.Error(fmt.Printf("Failed to create user %s/%s: %v\n", realm, userName, err)) 295 return false 296 } 297 userID := path.Base(userURL) 298 defer func() { 299 err = kc.DeleteUser(realm, userID) 300 if err != nil { 301 t.Logs.Info(fmt.Printf("Failed to delete user %s/%s: %v\n", realm, userID, err)) 302 } 303 }() 304 err = kc.SetPassword(realm, userID, "invalid") 305 if err == nil { 306 t.Logs.Error(fmt.Printf("Should not have been able to set password for %s/%s\n", realm, userID)) 307 return false 308 } 309 newValidPassword := fmt.Sprintf("test-new-password-12-!@-AB-%s", salt) 310 err = kc.SetPassword(realm, userID, newValidPassword) 311 if err != nil { 312 t.Logs.Error(fmt.Printf("Failed to set password for %s/%s: %v\n", realm, userID, err)) 313 return false 314 } 315 return true 316 } 317 318 func verifyKeycloakClientURIs() bool { 319 var keycloakClients KeycloakClients 320 321 // Login to Keycloak 322 if !loginKeycloak() { 323 return false 324 } 325 326 // Get the Client ID JSON array 327 cmd := exec.Command("kubectl", "exec", "keycloak-0", "-n", "keycloak", "-c", "keycloak", "--", kcAdminScript, "get", "clients", "-r", constants.VerrazzanoOIDCSystemRealm, "--fields", "id,clientId") //nolint:gosec //#nosec G204 328 out, err := cmd.Output() 329 if err != nil { 330 t.Logs.Error(fmt.Printf("Error retrieving ID for client ID, zero length: %s\n", err)) 331 return false 332 } 333 334 if len(string(out)) == 0 { 335 t.Logs.Error(fmt.Print("Error retrieving Clients JSON from Keycloak, zero length, zero length\n")) 336 return false 337 } 338 339 err = json.Unmarshal([]byte(out), &keycloakClients) 340 if err != nil { 341 t.Logs.Error(fmt.Sprintf("error unmarshalling keycloak client json %v", err.Error())) 342 return false 343 } 344 345 // Verify Num URIs per product endpoint 346 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 347 if err != nil { 348 t.Logs.Error(fmt.Printf("Error retrieving Kubeconfig Path: %s\n", err)) 349 return false 350 } 351 env, err := pkg.GetEnvName(kubeconfigPath) 352 if err != nil { 353 t.Logs.Error(fmt.Printf("Error retrieving Verrazzano Env: %s\n", err)) 354 return false 355 } 356 357 keycloakClient, err := getKeycloakClientByClientID(keycloakClients, "verrazzano-pkce") 358 if err != nil { 359 t.Logs.Error(fmt.Printf("Error retrieving Verrazzano pkce client: %s\n", err)) 360 return false 361 } 362 363 if !verifyVerrazzanoPKCEClientURIs(keycloakClient, env) { 364 return false 365 } 366 367 if isMinVersion140 { 368 keycloakClient, err = getKeycloakClientByClientID(keycloakClients, "rancher") 369 if err != nil { 370 t.Logs.Error(fmt.Printf("Error retrieving Verrazzano rancher client: %s\n", err)) 371 return false 372 } 373 374 if !verifyRancherClientURIs(keycloakClient, env) { 375 return false 376 } 377 } 378 379 if isArgoCDEnabled { 380 keycloakClient, err = getKeycloakClientByClientID(keycloakClients, "argocd") 381 if err != nil { 382 t.Logs.Error(fmt.Printf("Error retrieving Verrazzano argocd client: %s\n", err)) 383 return false 384 } 385 386 if !verifyArgoCDClientURIs(keycloakClient, env) { 387 return false 388 } 389 } 390 391 return true 392 } 393 394 func assertPersistentVolume(key string, size string) { 395 Expect(volumeClaims).To(HaveKey(key)) 396 pvc := volumeClaims[key] 397 Expect(pvc.Spec.Resources.Requests.Storage().String()).To(Equal(size)) 398 } 399 400 func verifyURIs(uriArray []string, name string, numToFind int) bool { 401 ctr := 0 402 for _, uri := range uriArray { 403 if strings.Contains(uri, name) { 404 ctr++ 405 } 406 } 407 return ctr == numToFind 408 } 409 410 func getKeycloakClientByClientID(keycloakClients KeycloakClients, clientID string) (*Client, error) { 411 // Extract the id associated with ClientID 412 var keycloakClient Client 413 var id = "" 414 for _, client := range keycloakClients { 415 if client.ClientID == clientID { 416 id = client.ID 417 t.Logs.Info(fmt.Printf("Keycloak Clients ID found = %s\n", id)) 418 } 419 } 420 if id == "" { 421 err := fmt.Errorf("error retrieving ID for Keycloak user, zero length") 422 t.Logs.Error(err.Error()) 423 return nil, err 424 } 425 426 // Get the client Info 427 client := "clients/" + id 428 cmd := exec.Command("kubectl", "exec", "keycloak-0", "-n", "keycloak", "-c", "keycloak", "--", kcAdminScript, "get", client, "-r", constants.VerrazzanoOIDCSystemRealm) //nolint:gosec //#nosec G204 429 out, err := cmd.Output() 430 if err != nil { 431 err := fmt.Errorf("error retrieving clientID json: %s", err) 432 t.Logs.Error(err.Error()) 433 return nil, err 434 } 435 436 if len(string(out)) == 0 { 437 err := fmt.Errorf("error retrieving client json from keycloak, zero length") 438 t.Logs.Error(err.Error()) 439 return nil, err 440 } 441 442 err = json.Unmarshal([]byte(out), &keycloakClient) 443 if err != nil { 444 err := fmt.Errorf("error unmarshalling keycloak client %s", err.Error()) 445 t.Logs.Error(err.Error()) 446 return nil, err 447 } 448 449 return &keycloakClient, nil 450 } 451 452 func verifyVerrazzanoPKCEClientURIs(keycloakClient *Client, env string) bool { 453 // Verify Correct number of RedirectURIs 454 // 25 redirect Uris for new installation 455 // 29 redirect Uris for upgrade from older versions. The urls are deprecated ingress hosts. 456 if isMinVersion150 { 457 if !(len(keycloakClient.RedirectUris) == 29 || len(keycloakClient.RedirectUris) == 25) { 458 t.Logs.Error(fmt.Printf("Incorrect Number of Redirect URIs returned for client %+v\n", keycloakClient.RedirectUris)) 459 return false 460 } 461 } else if !isMinVersion150 && len(keycloakClient.RedirectUris) != 29 { 462 t.Logs.Error(fmt.Printf("Incorrect Number of Redirect URIs returned for client %+v\n", keycloakClient.RedirectUris)) 463 return false 464 } 465 466 // Verify Correct number of WebOrigins 467 if isMinVersion150 { 468 if !(len(keycloakClient.WebOrigins) == 15 || len(keycloakClient.WebOrigins) == 13) { 469 t.Logs.Error(fmt.Printf("Incorrect Number of WebOrigins returned for client %+v\n", keycloakClient.WebOrigins)) 470 return false 471 } 472 } else if !isMinVersion150 && len(keycloakClient.WebOrigins) != 13 { 473 t.Logs.Error(fmt.Printf("Incorrect Number of WebOrigins returned for client %+v\n", keycloakClient.WebOrigins)) 474 return false 475 } 476 477 // Kiali 478 if !verifyURIs(keycloakClient.RedirectUris, kialiURI+env, 2) { 479 t.Logs.Error(fmt.Printf("Expected 2 Kiali redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 480 return false 481 } 482 483 if !verifyURIs(keycloakClient.WebOrigins, kialiURI+env, 1) { 484 t.Logs.Error(fmt.Printf("Expected 1 Kiali weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 485 return false 486 } 487 488 // Prometheus 489 if !verifyURIs(keycloakClient.RedirectUris, prometheusURI+env, 2) { 490 t.Logs.Error(fmt.Printf("Expected 2 Prometheus redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 491 return false 492 } 493 494 if !verifyURIs(keycloakClient.WebOrigins, prometheusURI+env, 1) { 495 t.Logs.Error(fmt.Printf("Expected 1 Prometheus weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 496 return false 497 } 498 499 // Grafana 500 if !verifyURIs(keycloakClient.RedirectUris, grafanaURI+env, 2) { 501 t.Logs.Error(fmt.Printf("Expected 2 Grafana redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 502 return false 503 } 504 505 if !verifyURIs(keycloakClient.WebOrigins, grafanaURI+env, 1) { 506 t.Logs.Error(fmt.Printf("Expected 1 Grafana weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 507 return false 508 } 509 510 // Opensearch 511 if !verifyURIs(keycloakClient.RedirectUris, osURI+env, 2) { 512 t.Logs.Error(fmt.Printf("Expected 2 Opensearch redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 513 return false 514 } 515 516 if !verifyURIs(keycloakClient.WebOrigins, osURI+env, 1) { 517 t.Logs.Error(fmt.Printf("Expected 1 Opensearch weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 518 return false 519 } 520 521 // Opensearchdashboards 522 if !(isMinVersion150 && verifyURIs(keycloakClient.RedirectUris, osdURI+env, 2)) { 523 t.Logs.Error(fmt.Printf("Expected 2 Opensearchdashboards redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 524 return false 525 } 526 527 if !verifyURIs(keycloakClient.WebOrigins, osdURI+env, 1) { 528 t.Logs.Error(fmt.Printf("Expected 1 Opensearchdashboards weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 529 return false 530 } 531 532 // Verrazzano 533 if !verifyURIs(keycloakClient.RedirectUris, verrazzanoURI+env, 2) { 534 t.Logs.Error(fmt.Printf("Expected 2 Verrazzano redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 535 return false 536 } 537 538 if !verifyURIs(keycloakClient.WebOrigins, verrazzanoURI+env, 1) { 539 t.Logs.Error(fmt.Printf("Expected 1 Verrazzano weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 540 return false 541 } 542 543 // Alertmanager 544 if !verifyURIs(keycloakClient.RedirectUris, alertmanagerURI+env, 2) { 545 t.Logs.Error(fmt.Printf("Expected 2 Alertmanager redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 546 return false 547 } 548 549 if !verifyURIs(keycloakClient.WebOrigins, alertmanagerURI+env, 1) { 550 t.Logs.Error(fmt.Printf("Expected 1 Alertmanager weborigin URIs. Found %+v\n", keycloakClient.WebOrigins)) 551 return false 552 } 553 554 return true 555 } 556 557 func verifyRancherClientURIs(keycloakClient *Client, env string) bool { 558 // Verify Correct number of RedirectURIs 559 if len(keycloakClient.RedirectUris) != 1 { 560 t.Logs.Error(fmt.Printf("Incorrect Number of Redirect URIs returned for client %+v\n", keycloakClient.RedirectUris)) 561 return false 562 } 563 564 // Verify Correct number of WebOrigins 565 if len(keycloakClient.WebOrigins) != 1 { 566 t.Logs.Error(fmt.Printf("Incorrect Number of WebOrigins returned for client %+v\n", keycloakClient.WebOrigins)) 567 return false 568 } 569 570 // Verify rancher redirectUI 571 if !verifyURIs(keycloakClient.RedirectUris, rancherURI+env, 1) { 572 t.Logs.Error(fmt.Printf("Expected 1 Rancher redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 573 return false 574 } 575 // Verify rancher web origin 576 if !verifyURIs(keycloakClient.WebOrigins, rancherURI+env, 1) { 577 t.Logs.Error(fmt.Printf("Expected 1 Rancher weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 578 return false 579 } 580 581 return true 582 } 583 584 func verifyArgoCDClientURIs(keycloakClient *Client, env string) bool { 585 // Verify Correct number of RedirectURIs 586 if len(keycloakClient.RedirectUris) != 1 { 587 t.Logs.Error(fmt.Printf("Incorrect Number of Redirect URIs returned for client %+v\n", keycloakClient.RedirectUris)) 588 return false 589 } 590 591 // Verify Correct number of WebOrigins 592 if len(keycloakClient.WebOrigins) != 1 { 593 t.Logs.Error(fmt.Printf("Incorrect Number of WebOrigins returned for client %+v\n", keycloakClient.WebOrigins)) 594 return false 595 } 596 597 // Verify Argo CD redirectUI 598 if !verifyURIs(keycloakClient.RedirectUris, argocdURI+env, 1) { 599 t.Logs.Error(fmt.Printf("Expected 1 ArgoCD redirect URIs. Found %+v\n", keycloakClient.RedirectUris)) 600 return false 601 } 602 // Verify Argo CD web origin 603 if !verifyURIs(keycloakClient.WebOrigins, argocdURI+env, 1) { 604 t.Logs.Error(fmt.Printf("Expected 1 ArgoCD weborigin URIs. Found %+v\n", keycloakClient.RedirectUris)) 605 return false 606 } 607 608 return true 609 } 610 611 // loginKeycloak logs into master realm, by calling kcadmin.sh config credentials 612 func loginKeycloak() bool { 613 // Get the Keycloak admin password 614 secret, err := pkg.GetSecret("keycloak", "keycloak-http") 615 if err != nil { 616 t.Logs.Error(fmt.Printf("Failed to get KeyCloak secret: %s\n", err)) 617 return false 618 } 619 pw := secret.Data["password"] 620 keycloakpw := string(pw) 621 if keycloakpw == "" { 622 t.Logs.Error(fmt.Print("Invalid Keycloak password. Empty String returned")) 623 return false 624 } 625 626 // Login to Keycloak 627 cmd := exec.Command("kubectl", "exec", "keycloak-0", "-n", "keycloak", "-c", "keycloak", "--", 628 kcAdminScript, "config", "credentials", "--server", "http://localhost:8080/auth", "--realm", "master", "--user", "keycloakadmin", "--password", keycloakpw) 629 _, err = cmd.Output() 630 if err != nil { 631 t.Logs.Error(fmt.Printf("Error logging into Keycloak: %s\n", err)) 632 return false 633 } 634 return true 635 } 636 637 // verifyUserClientRole verifies whether user has the specified client role 638 func verifyUserClientRole(user, userRole string) bool { 639 var kcRoles keycloakRoles 640 641 // Login to Keycloak 642 if !loginKeycloak() { 643 return false 644 } 645 646 // Get the roles for the user 647 cmd := exec.Command("kubectl", "exec", "keycloak-0", "-n", "keycloak", "-c", "keycloak", "--", kcAdminScript, "get-roles", "-r", constants.VerrazzanoOIDCSystemRealm, "--uusername", user, "--cclientid", realmMgmt, "--effective", "--fields", "name") //nolint:gosec //#nosec G204 648 out, err := cmd.Output() 649 if err != nil { 650 t.Logs.Error(fmt.Printf("Error retrieving client role for the user %s: %s\n", vzUser, err.Error())) 651 return false 652 } 653 654 if len(string(out)) == 0 { 655 t.Logs.Error(fmt.Print("Client roles retrieved from Keycloak is of zero length\n")) 656 return false 657 } 658 659 err = json.Unmarshal([]byte(out), &kcRoles) 660 if err != nil { 661 t.Logs.Error(fmt.Sprintf("Error unmarshalling Keycloak client role, received as JSON: %s\n", err.Error())) 662 return false 663 } 664 665 for _, role := range kcRoles { 666 if role.Name == userRole { 667 t.Logs.Info(fmt.Printf("Client role %s found\n", userRole)) 668 return true 669 } 670 } 671 return false 672 }