From 629b3cfa147fbfd0738ae9740444ac44fca4a840 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 19 Aug 2026 09:06:14 +0200 Subject: [PATCH 1/9] Moving files to the new test suite Signed-off-by: Erik Jan de Wit --- .../broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java (100%) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java similarity index 100% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java From c29f30eb0c4633143e376dca23d99eb18cbe52c1 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 19 Aug 2026 09:06:18 +0200 Subject: [PATCH 2/9] Migrate broker package tests to the new testsuite Signed-off-by: Erik Jan de Wit --- .../broker/AbstractAdvancedBrokerTest.java | 32 ++++++- .../AbstractAdvancedRoleMapperTest.java | 11 ++- .../tests}/broker/AbstractBaseBrokerTest.java | 92 +++++++++++-------- .../tests}/broker/AbstractBrokerTest.java | 27 +++++- .../tests}/broker/AbstractDefaultIdpTest.java | 30 +++++- .../broker/AbstractFirstBrokerLoginTest.java | 46 +++++++--- .../broker/AbstractGroupBrokerMapperTest.java | 11 ++- .../broker/AbstractGroupMapperTest.java | 13 ++- .../AbstractIdentityProviderMapperTest.java | 13 ++- .../AbstractInitializedBaseBrokerTest.java | 13 ++- .../AbstractKcOidcBrokerJWEEcdhEsTest.java | 9 +- .../AbstractKcOidcBrokerLogoutTest.java | 17 +++- .../AbstractKcSamlEncryptedElementsTest.java | 15 ++- .../AbstractKcSamlMetadataBrokerTest.java | 9 +- .../broker/AbstractNestedBrokerTest.java | 22 ++++- .../tests}/broker/AbstractRoleMapperTest.java | 15 ++- .../broker/AbstractSamlLoginHintTest.java | 21 ++++- .../AbstractUserAttributeMapperTest.java | 11 ++- .../AbstractUsernameTemplateMapperTest.java | 16 +++- .../tests}/broker/AccountLinkTest.java | 30 +++--- .../broker/AttributeToRoleMapperTest.java | 11 ++- .../tests}/broker/BrokerWithLegacyIdTest.java | 11 ++- .../ExternalKeycloakRoleToRoleMapperTest.java | 15 ++- .../broker/HardcodedRoleMapperTest.java | 15 ++- .../HardcodedUserAttributeMapperTest.java | 11 ++- .../broker/JsonUserAttributeMapperTest.java | 11 ++- .../tests}/broker/KcCustomOidcBrokerTest.java | 21 ++++- .../broker/KcOIDCBrokerWithSignatureTest.java | 34 +++++-- ...okenOnlyClaimsUserAttributeMapperTest.java | 9 +- .../broker/KcOidcBrokerAcrParameterTest.java | 19 +++- ...KcOidcBrokerClientSecretBasicAuthTest.java | 9 +- ...rokerClientSecretJwtCustomSignAlgTest.java | 9 +- .../KcOidcBrokerClientSecretJwtTest.java | 9 +- ...erColonAliasClientSecretBasicAuthTest.java | 9 +- .../tests}/broker/KcOidcBrokerEventTest.java | 27 +++++- .../broker/KcOidcBrokerFrontendUrlTest.java | 21 ++++- .../broker/KcOidcBrokerHiddenIdpHintTest.java | 21 ++++- .../broker/KcOidcBrokerIdpHintTest.java | 21 ++++- .../broker/KcOidcBrokerIdpLinkActionTest.java | 42 +++++++-- ...cOidcBrokerIdpPublicKeyMissingUseTest.java | 9 +- ...erJWEEcdhEsA128KwP256A128CbcHs256Test.java | 9 +- ...cBrokerJWEEcdhEsA128KwP256A128GcmTest.java | 9 +- ...erJWEEcdhEsA192KwP384A192CbcHs384Test.java | 9 +- ...cBrokerJWEEcdhEsA192KwP384A192GcmTest.java | 9 +- ...erJWEEcdhEsA256KwP521A256CbcHs512Test.java | 9 +- ...cBrokerJWEEcdhEsA256KwP521A256GcmTest.java | 9 +- ...dcBrokerJWEEcdhEsP384A192CbcHs384Test.java | 9 +- .../KcOidcBrokerJWEEcdhEsP384A192GcmTest.java | 9 +- .../tests}/broker/KcOidcBrokerJWETest.java | 11 ++- ...idcBrokerJWEUserInfoJustEncryptedTest.java | 9 +- .../broker/KcOidcBrokerLdapReadOnlyTest.java | 35 +++++-- .../tests}/broker/KcOidcBrokerLdapTest.java | 22 ++++- .../broker/KcOidcBrokerLoginHintTest.java | 21 ++++- .../KcOidcBrokerLogoutFrontChannelTest.java | 30 +++++- .../tests}/broker/KcOidcBrokerLogoutTest.java | 32 ++++++- .../broker/KcOidcBrokerNoLoginHintTest.java | 19 +++- .../KcOidcBrokerNonceParameterTest.java | 16 +++- .../KcOidcBrokerParameterForwardTest.java | 19 +++- .../broker/KcOidcBrokerPassMaxAgeTest.java | 26 +++++- .../tests}/broker/KcOidcBrokerPkceTest.java | 9 +- ...okerPrivateKeyJwtClientIdRequiredTest.java | 13 ++- ...BrokerPrivateKeyJwtCustomAudienceTest.java | 9 +- ...cBrokerPrivateKeyJwtCustomSignAlgTest.java | 11 ++- ...OidcBrokerPrivateKeyJwtMissingUseTest.java | 11 ++- .../broker/KcOidcBrokerPrivateKeyJwtTest.java | 11 ++- ...BrokerPrivateKeyJwtUnsupportedKeyTest.java | 11 ++- ...rokerPrivateKeyJwtWithX509HeadersTest.java | 11 ++- .../KcOidcBrokerPromptNoneRedirectTest.java | 26 +++++- .../KcOidcBrokerPromptParameterTest.java | 19 +++- .../KcOidcBrokerStateParameterTest.java | 33 +++++-- ...KcOidcBrokerSubMatchIntrospectionTest.java | 14 ++- .../tests}/broker/KcOidcBrokerTest.java | 30 +++++- .../broker/KcOidcBrokerTokenExchangeTest.java | 30 +++++- .../KcOidcBrokerTransientSessionsTest.java | 47 ++++++++-- .../KcOidcBrokerUiLocalesDisabledTest.java | 19 +++- .../KcOidcBrokerUiLocalesEnabledTest.java | 19 +++- .../KcOidcBrokerUiLocalesWithIdpHintTest.java | 19 +++- .../tests}/broker/KcOidcBrokerVaultTest.java | 9 +- .../broker/KcOidcBrokerWithConsentTest.java | 31 ++++++- .../tests}/broker/KcOidcDefaultIdpTest.java | 11 ++- ...irstBrokerLoginDetectExistingUserTest.java | 25 ++++- .../KcOidcFirstBrokerLoginNewAuthTest.java | 32 +++++-- .../broker/KcOidcFirstBrokerLoginTest.java | 36 ++++++-- .../broker/KcOidcMultipleTabsBrokerTest.java | 32 ++++++- .../broker/KcOidcPostBrokerLoginTest.java | 33 +++++-- .../KcOidcUsernameTemplateMapperTest.java | 9 +- ...amlAdvancedAttributeToGroupMapperTest.java | 11 ++- ...SamlAdvancedAttributeToRoleMapperTest.java | 11 ++- ...amlAttributeConsumingServiceIndexTest.java | 11 ++- .../KcSamlBrokerAllowedClockSkewTest.java | 11 ++- .../KcSamlBrokerArtifactBindingTest.java | 16 +++- .../broker/KcSamlBrokerDestinationTest.java | 17 +++- .../broker/KcSamlBrokerFrontendUrlTest.java | 27 +++++- ...BrokerLoginHintWithOptionDisabledTest.java | 9 +- ...lBrokerLoginHintWithOptionEnabledTest.java | 21 ++++- .../KcSamlBrokerSessionNotOnOrAfterTest.java | 11 ++- .../tests}/broker/KcSamlBrokerTest.java | 21 ++++- .../KcSamlCustomEntityIdBrokerTest.java | 11 ++- .../tests}/broker/KcSamlDefaultIdpTest.java | 11 ++- .../broker/KcSamlEncryptedAssertionTest.java | 9 +- .../tests}/broker/KcSamlEncryptedIdTest.java | 9 +- .../broker/KcSamlFirstBrokerLoginTest.java | 11 ++- .../broker/KcSamlForceAuthnBrokerTest.java | 11 ++- .../broker/KcSamlIdPInitiatedSsoTest.java | 43 +++++---- .../tests}/broker/KcSamlLogoutTest.java | 11 ++- ...lMetadataSignedAndEncryptedBrokerTest.java | 22 ++++- .../KcSamlMetadataSignedBrokerTest.java | 16 +++- ...amlMultipleAttributeToRoleMappersTest.java | 9 +- .../broker/KcSamlMultipleTabsBrokerTest.java | 32 ++++++- .../broker/KcSamlParseIdPDescriptorTest.java | 14 ++- ...KcSamlRequestedAuthnContextBrokerTest.java | 11 ++- .../tests}/broker/KcSamlSignedBrokerTest.java | 16 +++- .../KcSamlSignedDocumentOnlyBrokerTest.java | 9 +- .../tests}/broker/KcSamlSpDescriptorTest.java | 11 ++- .../KcSamlUsernameTemplateMapperTest.java | 16 +++- .../KcSamlXPathAttributeMapperTest.java | 11 ++- .../tests}/broker/OAuth2BrokerTest.java | 21 ++++- ...okenOnlyClaimsUserAttributeMapperTest.java | 9 +- .../OidcAdvancedClaimToGroupMapperTest.java | 11 ++- .../OidcAdvancedClaimToRoleMapperTest.java | 9 +- .../broker/OidcClaimToRoleMapperTest.java | 11 ++- .../OidcClaimToUserSessionNoteMapperTest.java | 20 +++- .../broker/OidcHardcodedGroupMapperTest.java | 9 +- .../OidcMultipleClaimToRoleMappersTest.java | 9 +- .../broker/OidcUserAttributeMapperTest.java | 9 +- .../OidcUserInfoClaimToRoleMapperTest.java | 11 ++- .../broker/SamlUserAttributeMapperTest.java | 9 +- .../tests}/broker/SocialLoginTest.java | 73 +++++++++------ .../broker/UsernameTemplateMapperTest.java | 19 ++-- 129 files changed, 1912 insertions(+), 416 deletions(-) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractAdvancedBrokerTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractAdvancedRoleMapperTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractBaseBrokerTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractBrokerTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractDefaultIdpTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractFirstBrokerLoginTest.java (98%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractGroupBrokerMapperTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractGroupMapperTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractIdentityProviderMapperTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractInitializedBaseBrokerTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractKcOidcBrokerLogoutTest.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractKcSamlEncryptedElementsTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractKcSamlMetadataBrokerTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractNestedBrokerTest.java (73%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractRoleMapperTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractSamlLoginHintTest.java (81%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractUserAttributeMapperTest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AbstractUsernameTemplateMapperTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AccountLinkTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/AttributeToRoleMapperTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/BrokerWithLegacyIdTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/ExternalKeycloakRoleToRoleMapperTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/HardcodedRoleMapperTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/HardcodedUserAttributeMapperTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/JsonUserAttributeMapperTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcCustomOidcBrokerTest.java (76%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOIDCBrokerWithSignatureTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java (81%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerAcrParameterTest.java (80%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerClientSecretBasicAuthTest.java (81%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerClientSecretJwtTest.java (88%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerEventTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerFrontendUrlTest.java (81%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerHiddenIdpHintTest.java (81%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerIdpHintTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerIdpLinkActionTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java (84%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java (78%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java (77%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWETest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java (80%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerLdapReadOnlyTest.java (85%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerLdapTest.java (85%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerLoginHintTest.java (88%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerLogoutFrontChannelTest.java (83%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerLogoutTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerNoLoginHintTest.java (84%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerNonceParameterTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerParameterForwardTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPassMaxAgeTest.java (87%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPkceTest.java (75%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPromptNoneRedirectTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerPromptParameterTest.java (84%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerStateParameterTest.java (87%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerSubMatchIntrospectionTest.java (85%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerTest.java (98%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerTokenExchangeTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerTransientSessionsTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerUiLocalesDisabledTest.java (85%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerUiLocalesEnabledTest.java (85%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java (86%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerVaultTest.java (53%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcBrokerWithConsentTest.java (80%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcDefaultIdpTest.java (79%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcFirstBrokerLoginNewAuthTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcFirstBrokerLoginTest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcMultipleTabsBrokerTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcPostBrokerLoginTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcOidcUsernameTemplateMapperTest.java (85%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlAdvancedAttributeToGroupMapperTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlAdvancedAttributeToRoleMapperTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlAttributeConsumingServiceIndexTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerAllowedClockSkewTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerArtifactBindingTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerDestinationTest.java (88%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerFrontendUrlTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java (65%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerSessionNotOnOrAfterTest.java (90%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlBrokerTest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlCustomEntityIdBrokerTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlDefaultIdpTest.java (62%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlEncryptedAssertionTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlEncryptedIdTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlFirstBrokerLoginTest.java (59%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlForceAuthnBrokerTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlIdPInitiatedSsoTest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlLogoutTest.java (96%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlMetadataSignedBrokerTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlMultipleAttributeToRoleMappersTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlMultipleTabsBrokerTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlParseIdPDescriptorTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlRequestedAuthnContextBrokerTest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlSignedBrokerTest.java (98%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlSignedDocumentOnlyBrokerTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlSpDescriptorTest.java (98%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlUsernameTemplateMapperTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/KcSamlXPathAttributeMapperTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OAuth2BrokerTest.java (91%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java (89%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcAdvancedClaimToGroupMapperTest.java (97%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcAdvancedClaimToRoleMapperTest.java (86%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcClaimToRoleMapperTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcClaimToUserSessionNoteMapperTest.java (94%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcHardcodedGroupMapperTest.java (88%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcMultipleClaimToRoleMappersTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcUserAttributeMapperTest.java (92%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/OidcUserInfoClaimToRoleMapperTest.java (95%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/SamlUserAttributeMapperTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/SocialLoginTest.java (93%) rename {testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite => tests/base/src/test/java/org/keycloak/tests}/broker/UsernameTemplateMapperTest.java (89%) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractAdvancedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractAdvancedBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java index e76bd39144bc..7fcb214143b6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractAdvancedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.time.Duration; import java.util.Arrays; @@ -23,8 +23,19 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.storage.UserStorageProvider; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; import org.keycloak.testsuite.util.AccountHelper; @@ -32,7 +43,7 @@ import org.keycloak.testsuite.util.WaitUtils; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.TimeoutException; @@ -63,10 +74,23 @@ * - Disabled user * - etc */ +@KeycloakIntegrationTest public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectEvents + Events events; protected void createRoleMappersForConsumerRealm() { createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode.FORCE); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractAdvancedRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractAdvancedRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java index 86b0612fdf05..2951239c7c10 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractAdvancedRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -8,7 +8,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -19,8 +22,12 @@ * Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public abstract class AbstractAdvancedRoleMapperTest extends AbstractRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String CLAIMS_OR_ATTRIBUTES = "[\n" + " {\n" + " \"key\": \"" + KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME + "\",\n" + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java index d2d45b1b7185..fdd7a564e533 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.net.URI; import java.util.Collections; @@ -34,23 +34,29 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.resources.RealmsResource; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; -import org.keycloak.testsuite.AbstractKeycloakTest; -import org.keycloak.testsuite.pages.ErrorPage; -import org.keycloak.testsuite.pages.IdpConfirmLinkPage; -import org.keycloak.testsuite.pages.IdpConfirmOverrideLinkPage; -import org.keycloak.testsuite.pages.IdpLinkEmailPage; -import org.keycloak.testsuite.pages.InfoPage; -import org.keycloak.testsuite.pages.LoginConfigTotpPage; -import org.keycloak.testsuite.pages.LoginExpiredPage; -import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.pages.LoginPasswordResetPage; -import org.keycloak.testsuite.pages.LoginTotpPage; -import org.keycloak.testsuite.pages.LogoutConfirmPage; -import org.keycloak.testsuite.pages.OAuthGrantPage; -import org.keycloak.testsuite.pages.ProceedPage; -import org.keycloak.testsuite.pages.UpdateAccountInformationPage; -import org.keycloak.testsuite.pages.VerifyEmailPage; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testframework.ui.page.ErrorPage; +import org.keycloak.testframework.ui.page.IdpConfirmLinkPage; +import org.keycloak.testframework.ui.page.IdpConfirmOverrideLinkPage; +import org.keycloak.testframework.ui.page.IdpLinkEmailPage; +import org.keycloak.testframework.ui.page.InfoPage; +import org.keycloak.testframework.ui.page.LoginConfigTotpPage; +import org.keycloak.testframework.ui.page.LoginExpiredPage; +import org.keycloak.testframework.ui.page.LoginPage; +import org.keycloak.testframework.ui.page.LoginPasswordResetPage; +import org.keycloak.testframework.ui.page.LoginTotpPage; +import org.keycloak.testframework.ui.page.LogoutConfirmPage; +import org.keycloak.testframework.ui.page.OAuthGrantPage; +import org.keycloak.testframework.ui.page.ProceedPage; +import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; +import org.keycloak.testframework.ui.page.VerifyEmailPage; import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import org.keycloak.testsuite.util.oauth.LogoutUrlBuilder; @@ -58,9 +64,9 @@ import org.keycloak.testsuite.util.userprofile.UserProfileUtil; import org.hamcrest.Matchers; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.After; -import org.junit.Before; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.TimeoutException; @@ -81,53 +87,63 @@ /** * No test methods there. Just some useful common functionality */ -public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest { +@KeycloakIntegrationTest +public abstract class AbstractBaseBrokerTest { + + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; protected static final String ATTRIBUTE_VALUE = "attribute.value"; - @Page + @InjectPage protected LoginPage loginPage; - @Page + @InjectPage protected UpdateAccountInformationPage updateAccountInformationPage; - @Page + @InjectPage protected ErrorPage errorPage; - @Page + @InjectPage protected IdpConfirmLinkPage idpConfirmLinkPage; - @Page + @InjectPage protected IdpConfirmOverrideLinkPage idpConfirmOverrideLinkPage; - @Page + @InjectPage protected ProceedPage proceedPage; - @Page + @InjectPage protected LogoutConfirmPage logoutConfirmPage; - @Page + @InjectPage protected InfoPage infoPage; - @Page + @InjectPage protected IdpLinkEmailPage idpLinkEmailPage; - @Page + @InjectPage protected LoginExpiredPage loginExpiredPage; - @Page + @InjectPage protected LoginTotpPage loginTotpPage; - @Page + @InjectPage protected LoginConfigTotpPage totpPage; - @Page + @InjectPage protected LoginPasswordResetPage loginPasswordResetPage; - @Page + @InjectPage protected VerifyEmailPage verifyEmailPage; - @Page + @InjectPage protected OAuthGrantPage grantPage; protected TimeBasedOTP totp = new TimeBasedOTP(); @@ -182,7 +198,7 @@ protected void addClientsToProviderAndConsumer() { } } - @Before + @BeforeEach public void beforeBrokerTest() { RealmRepresentation consumerRealm = bc.createConsumerRealm(); RealmRepresentation providerRealm = bc.createProviderRealm(); @@ -193,7 +209,7 @@ public void beforeBrokerTest() { UserProfileUtil.enableUnmanagedAttributes(adminClient.realm(providerRealm.getRealm()).users().userProfile()); } - @After + @AfterEach public void cleanupUsers() { deleteAllCookiesForRealm(bc.consumerRealmName()); adminClient.realm(bc.consumerRealmName()).remove(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java index b30d4bf916f8..72f6b5a9fe85 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Iterator; import java.util.List; @@ -22,11 +22,18 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; -import org.keycloak.testsuite.pages.ConsentPage; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testframework.ui.page.ConsentPage; import org.keycloak.testsuite.util.AccountHelper; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Test; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.models.utils.DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS; @@ -45,15 +52,25 @@ * Contains just few basic tests. This is good class to override if you're testing custom IDP configuration and you need * to verify if login with IDP works as expected */ +@KeycloakIntegrationTest public abstract class AbstractBrokerTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + public static final String ROLE_USER = "user"; public static final String ROLE_MANAGER = "manager"; public static final String ROLE_FRIENDLY_MANAGER = "friendly-manager"; public static final String ROLE_USER_DOT_GUIDE = "user.guide"; public static final String EMPTY_ATTRIBUTE_ROLE = "empty.attribute.role"; - @Page + @InjectPage ConsentPage consentPage; @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractDefaultIdpTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractDefaultIdpTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java index 61cd0958c8b3..bb092fbdb24c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractDefaultIdpTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java @@ -17,7 +17,7 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -29,10 +29,19 @@ import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticatorConfigModel; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.FlowUtil; import org.keycloak.testsuite.util.UIUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -47,8 +56,21 @@ /** * Test for scenarios where "Identity Provider Authenticator" is set with "default identity provider" directly redirecting to provider realm */ +@KeycloakIntegrationTest public abstract class AbstractDefaultIdpTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override public void beforeBrokerTest() { super.beforeBrokerTest(); @@ -169,8 +191,8 @@ protected void configureFlow(String defaultIdpValue) { else newFlowAlias = "Browser - Default IdP OFF"; - testingClient.server("consumer").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias)); - testingClient.server("consumer").run(session -> + runOnServer.run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias)); + runOnServer.run(session -> { List executions = FlowUtil.inCurrentRealm(session) .selectFlow(newFlowAlias) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java similarity index 98% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java index fca2b00cb4c8..98e8a354fe46 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; @@ -26,14 +26,26 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.storage.UserStorageProvider; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.mail.annotations.InjectMailServer; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.FederatedIdentityBuilder; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.admin.AdminApiUtil; import org.keycloak.testsuite.federation.UserMapStorageFactory; import org.keycloak.testsuite.pages.LoginPasswordUpdatePage; import org.keycloak.testsuite.util.AccountHelper; -import org.keycloak.testsuite.util.MailServer; +import org.keycloak.testframework.mail.MailServer; import org.keycloak.testsuite.util.MailServerConfiguration; import org.keycloak.testsuite.util.SecondBrowser; import org.keycloak.userprofile.UserProfileContext; @@ -43,7 +55,7 @@ import org.hamcrest.Matchers; import org.jboss.arquillian.drone.api.annotation.Drone; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -73,17 +85,29 @@ * * @author Marek Posolda */ +@KeycloakIntegrationTest public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBaseBrokerTest { - @Drone - @SecondBrowser - protected WebDriver driver2; + @InjectRealm + ManagedRealm managedRealm; - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRunOnServer + RunOnServerClient runOnServer; - @Rule - public MailServer mail = new MailServer(); + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectWebDriver(ref = "secondDriver") + protected ManagedWebDriver driver2; + + @InjectEvents + Events events; + + @InjectMailServer + MailServer mail; /** * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOn @@ -786,7 +810,7 @@ public void testHardcodedUserSessionNoteIsSetAfterFirstBrokerLogin() { updateAccountInformationPage.assertCurrent(); updateAccountInformationPage.updateAccountInformation("FirstName", "LastName"); - testingClient.server().run(assertHardCodedSessionNote()); + runOnServer.run(assertHardCodedSessionNote()); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractGroupBrokerMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractGroupBrokerMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java index 14af7b7701a3..1a05f54fcd1b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractGroupBrokerMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.List; @@ -27,12 +27,19 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; +@KeycloakIntegrationTest public abstract class AbstractGroupBrokerMapperTest extends AbstractGroupMapperTest { + @InjectRealm + ManagedRealm managedRealm; + protected static final String CLAIMS_OR_ATTRIBUTES = "[\n" + " {\n" + " \"key\": \"" + KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME + "\",\n" + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractGroupMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java index d86e32b02c9d..dab9eefe105a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.List; @@ -15,9 +15,12 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Assertions; import static org.hamcrest.MatcherAssert.assertThat; @@ -30,8 +33,12 @@ * @author Artur Baltabayev, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public abstract class AbstractGroupMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + public static final String MAPPER_TEST_GROUP_NAME = "mapper-test"; public static final String MAPPER_TEST_GROUP_PATH = buildGroupPath(MAPPER_TEST_GROUP_NAME); @@ -53,7 +60,7 @@ protected abstract String createMapperInIdp( protected void updateUser() { } - @Before + @BeforeEach public void addMapperTestGroupToConsumerRealm() { GroupRepresentation mapperTestGroup = new GroupRepresentation(); mapperTestGroup.setName(MAPPER_TEST_GROUP_NAME); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java index fba32f56c599..7c52c9e8b1ba 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -11,9 +11,12 @@ import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import static org.keycloak.testsuite.admin.AdminApiUtil.createUserAndResetPasswordWithAdminClient; @@ -26,11 +29,15 @@ * Benjamin Weimer, * Martin Idel, */ +@KeycloakIntegrationTest public abstract class AbstractIdentityProviderMapperTest extends AbstractBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + protected RealmResource realm; - @Before + @BeforeEach public void addClients() { addClientsToProviderAndConsumer(); realm = adminClient.realm(bc.consumerRealmName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java index bbd40b00a841..4649eab6b6b7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.function.BiConsumer; @@ -25,7 +25,10 @@ import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; @@ -33,14 +36,18 @@ /** * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. */ +@KeycloakIntegrationTest public abstract class AbstractInitializedBaseBrokerTest extends AbstractBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + protected IdentityProviderResource identityProviderResource; protected void postInitializeUser(UserRepresentation user) {} @Override - @Before + @BeforeEach public void beforeBrokerTest() { super.beforeBrokerTest(); log.debug("creating user for realm " + bc.providerRealmName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java index b55516011d12..7046fa657993 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java @@ -14,14 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.utils.DefaultKeyProviders; import org.keycloak.representations.idm.ComponentExportRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public abstract class AbstractKcOidcBrokerJWEEcdhEsTest extends KcOidcBrokerJWETest { + @InjectRealm + ManagedRealm managedRealm; + private final String crv; private final String encAlg; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcOidcBrokerLogoutTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcOidcBrokerLogoutTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java index 4cdf47644193..168f2f1dfcd0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcOidcBrokerLogoutTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java @@ -1,16 +1,23 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.UserRepresentation; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; +@KeycloakIntegrationTest public abstract class AbstractKcOidcBrokerLogoutTest extends AbstractBaseBrokerTest { - @Before + @InjectRealm + ManagedRealm managedRealm; + + @BeforeEach public void createUser() { log.debug("creating user for realm " + bc.providerRealmName()); @@ -26,7 +33,7 @@ public void createUser() { resetUserPassword(realmResource.users().get(userId), bc.getUserPassword(), false); } - @Before + @BeforeEach public void addIdentityProviderToProviderRealm() { log.debug("adding identity provider to realm " + bc.consumerRealmName()); @@ -34,7 +41,7 @@ public void addIdentityProviderToProviderRealm() { realm.identityProviders().create(bc.setUpIdentityProvider()).close(); } - @Before + @BeforeEach public void addClients() { addClientsToProviderAndConsumer(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcSamlEncryptedElementsTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcSamlEncryptedElementsTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java index 2a54a5f9c659..9e697d8f7a4a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcSamlEncryptedElementsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.security.PublicKey; import java.util.concurrent.atomic.AtomicReference; @@ -34,6 +34,9 @@ import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; @@ -42,8 +45,8 @@ import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.utils.EncryptionConstants; import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; @@ -56,8 +59,12 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; +@KeycloakIntegrationTest public abstract class AbstractKcSamlEncryptedElementsTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + private String encProviderId; private String sigProviderId; @@ -66,7 +73,7 @@ protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; } - @Before + @BeforeEach public void setupKeys() { sigProviderId = KeyUtils.findActiveSigningKey(adminClient.realm(bc.consumerRealmName())).getProviderId(); encProviderId = KeyUtils.findActiveEncryptingKey(adminClient.realm(bc.consumerRealmName()), Algorithm.RSA_OAEP).getProviderId(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcSamlMetadataBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcSamlMetadataBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java index 2980cfe8d462..a9ab357b845b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractKcSamlMetadataBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; @@ -32,6 +32,9 @@ import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; @@ -51,8 +54,12 @@ * * @author rmartinc */ +@KeycloakIntegrationTest public abstract class AbstractKcSamlMetadataBrokerTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + protected Document identityDocument(Document doc) { return doc; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java similarity index 73% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java index 5b1af70343c5..9d1b7b51ba19 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java @@ -1,12 +1,24 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +@KeycloakIntegrationTest public abstract class AbstractNestedBrokerTest extends AbstractBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + protected NestedBrokerConfiguration nbc = getNestedBrokerConfiguration(); protected abstract NestedBrokerConfiguration getNestedBrokerConfiguration(); @@ -16,12 +28,12 @@ protected BrokerConfiguration getBrokerConfiguration() { return getNestedBrokerConfiguration(); } - @Before + @BeforeEach public void createSubConsumerRealm() { importRealm(nbc.createSubConsumerRealm()); } - @After + @AfterEach public void removeSubConsumerRealm() { adminClient.realm(nbc.subConsumerRealmName()).remove(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java index 488dee6fabc2..bf8a0f70a302 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.Collections; @@ -17,13 +17,16 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RoleBuilder; import org.keycloak.testsuite.util.AccountHelper; import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; @@ -41,8 +44,12 @@ * Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public abstract class AbstractRoleMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + protected static final String CLIENT_ID = "mapper-test-client"; protected static final String CLIENT_ROLE = "test-role"; protected static final String CLIENT_ROLE_MAPPER_REPRESENTATION = createClientRoleString(CLIENT_ID, CLIENT_ROLE); @@ -65,7 +72,7 @@ protected static String createClientRoleString(final String clientId, final Stri protected void updateUser() { } - @Before + @BeforeEach public void init() { mapperIdsWithName = new ConcurrentHashMap<>(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractSamlLoginHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java similarity index 81% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractSamlLoginHintTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java index 1ef70a1255b1..5081b57c02db 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractSamlLoginHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java @@ -1,9 +1,16 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebElement; @@ -14,8 +21,18 @@ /** * Test of various scenarios related to the use of login hint */ +@KeycloakIntegrationTest public abstract class AbstractSamlLoginHintTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + // KEYCLOAK-13950 @Test public void testPassLoginHintShouldSendSubjectAndPrefillUsername() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java index d7af2734ce90..996a96d28371 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.List; @@ -11,12 +11,15 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; @@ -30,8 +33,12 @@ * * @author hmlnarik */ +@KeycloakIntegrationTest public abstract class AbstractUserAttributeMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + protected static final String MAPPED_ATTRIBUTE_NAME = "mapped-user-attribute"; protected static final String MAPPED_ATTRIBUTE_FRIENDLY_NAME = "mapped-user-attribute-friendly"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUsernameTemplateMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java index 83c5b4b3dd77..fbe5dbe77415 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java @@ -1,15 +1,20 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.AccountHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; @@ -21,8 +26,15 @@ /** * @author Martin Idel, */ +@KeycloakIntegrationTest public abstract class AbstractUsernameTemplateMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + protected abstract String getMapperTemplate(); protected abstract void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java index e5f77909a5ed..d6680cfab832 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.List; @@ -34,21 +34,23 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.storage.StorageId; import org.keycloak.storage.UserStorageProvider; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.FederatedIdentityBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.providers.runonserver.RunOnServer; -import org.keycloak.testsuite.AbstractKeycloakTest; -import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testframework.util.ApiUtil; import org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProvider; import org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory; import org.keycloak.testsuite.federation.UserMapStorageFactory; -import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.pages.UpdateAccountInformationPage; +import org.keycloak.testframework.ui.page.LoginPage; +import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testsuite.util.AccountHelper; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Before; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.BeforeEach; import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED; @@ -64,15 +66,19 @@ * @author Bill Burke * @version $Revision: 1 $ */ -public class AccountLinkTest extends AbstractKeycloakTest { +@KeycloakIntegrationTest +public class AccountLinkTest { + + @InjectRealm + ManagedRealm managedRealm; public static final String CHILD_IDP = "child"; public static final String PARENT_IDP = "parent-idp"; public static final String PARENT_USERNAME = "parent"; - @Page + @InjectPage protected UpdateAccountInformationPage profilePage; - @Page + @InjectPage protected LoginPage loginPage; @Override @@ -90,7 +96,7 @@ public void addTestRealms(List testRealms) { } - @Before + @BeforeEach public void beforeBrokerTest() { if (testContext.isInitialized()) { return; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AttributeToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AttributeToRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java index ecc8be893168..b34b1b89e0cd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AttributeToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -12,7 +12,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY; @@ -20,8 +23,12 @@ /** * @author Martin Idel */ +@KeycloakIntegrationTest public class AttributeToRoleMapperTest extends AbstractRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String ROLE_ATTR_NAME = "Role"; @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerWithLegacyIdTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerWithLegacyIdTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java index eb0689f1ef58..8ce77ae59967 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerWithLegacyIdTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; @@ -25,12 +25,15 @@ import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.FederatedIdentityBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; import org.keycloak.testsuite.broker.oidc.LegacyIdIdentityProviderFactory; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; @@ -42,7 +45,11 @@ /** * @author Vaclav Muzikar */ +@KeycloakIntegrationTest public class BrokerWithLegacyIdTest extends AbstractInitializedBaseBrokerTest { + + @InjectRealm + ManagedRealm managedRealm; private static final UserRepresentation consumerUser = UserBuilder.create() .username("anakin") .firstName("Darth") diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ExternalKeycloakRoleToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ExternalKeycloakRoleToRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java index 1aa01927f128..2ddc5717b3f5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ExternalKeycloakRoleToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.List; @@ -13,8 +13,11 @@ import org.keycloak.representations.idm.RoleRepresentation; import com.google.common.collect.ImmutableMap; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -24,7 +27,11 @@ * @author Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class ExternalKeycloakRoleToRoleMapperTest extends AbstractRoleMapperTest { + + @InjectRealm + ManagedRealm managedRealm; private boolean deleteRoleFromUser = true; @Override @@ -32,7 +39,7 @@ protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration(); } - @Before + @BeforeEach public void setupRealm() { super.addClients(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/HardcodedRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/HardcodedRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java index 0ab7f22aa3bb..b35d19c953b1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/HardcodedRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.List; @@ -11,8 +11,11 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import com.google.common.collect.ImmutableMap; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -22,14 +25,18 @@ * @author Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class HardcodedRoleMapperTest extends AbstractRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration(); } - @Before + @BeforeEach public void setupRealm() { super.addClients(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/HardcodedUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/HardcodedUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java index 04d1eb224540..0dc9e7ad9900 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/HardcodedUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; @@ -9,10 +9,13 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -24,8 +27,12 @@ /** * Martin Idel, */ +@KeycloakIntegrationTest public class HardcodedUserAttributeMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String USER_ATTRIBUTE = "user-attribute"; private static final String USER_ATTRIBUTE_VALUE = "user-attribute"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/JsonUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/JsonUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java index 71638997c77f..44cef1e9bf0f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/JsonUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; @@ -14,10 +14,13 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.social.github.GitHubUserAttributeMapper; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -34,8 +37,12 @@ /** * @author Martin Idel */ +@KeycloakIntegrationTest public class JsonUserAttributeMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + public static final String USER_ATTRIBUTE = "user-attribute"; @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcCustomOidcBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java similarity index 76% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcCustomOidcBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java index 5c56fab8d7da..4084caa31921 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcCustomOidcBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java @@ -14,13 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; @@ -31,8 +38,18 @@ /** * Test methods for testing a custom OIDC broker */ +@KeycloakIntegrationTest public class KcCustomOidcBrokerTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcCustomBrokerConfiguration(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java index ea1751f49f47..0c6b791c3d3c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.io.IOException; @@ -44,6 +44,15 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testsuite.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; @@ -52,8 +61,8 @@ import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.testsuite.util.runonserver.CacheHelper; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; @@ -66,14 +75,27 @@ /** * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; } - @Before + @BeforeEach public void createUser() { log.debug("creating user for realm " + bc.providerRealmName()); @@ -90,7 +112,7 @@ public void createUser() { } // TODO: Possibly move to parent superclass - @Before + @BeforeEach public void addIdentityProviderToProviderRealm() { log.debug("adding identity provider to realm " + bc.consumerRealmName()); @@ -100,7 +122,7 @@ public void addIdentityProviderToProviderRealm() { } - @Before + @BeforeEach public void addClients() { addClientsToProviderAndConsumer(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java similarity index 81% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java index a0be618f4574..aa241490fbac 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java @@ -1,13 +1,20 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest extends OidcUserAttributeMapperTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerAcrParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java similarity index 80% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerAcrParameterTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java index 4de31a6d999d..f9f736369107 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerAcrParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; @@ -6,11 +6,28 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +@KeycloakIntegrationTest public class KcOidcBrokerAcrParameterTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + private static final String ACR_VALUES = "acr_values"; private static final String ACR_3 = "3"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretBasicAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java similarity index 81% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretBasicAuthTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java index ebe74c0f5e55..c2c6356d1d94 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretBasicAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java @@ -1,17 +1,24 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerClientSecretBasicAuthTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithBasicAuthAuthentication(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java index c7ee351a48a3..cedf7c7c6441 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -12,13 +12,20 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerClientSecretJwtCustomSignAlgTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretJwtTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java similarity index 88% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretJwtTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java index 937b4b4db1b1..305dd39db762 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerClientSecretJwtTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -8,13 +8,20 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerClientSecretJwtTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + // BCFIPS approved mode requires at least 112 bits (14 characters) long SecretKey for "client-secret-jwt" authentication private static final String CLIENT_SECRET = "atleast-14chars-password"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java index 41132c975733..32e78030e0c6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; @@ -24,6 +24,9 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.broker.oidc.OAuth2IdentityProviderConfig.TOKEN_ENDPOINT_URL; import static org.keycloak.testsuite.broker.BrokerTestConstants.CLIENT_SECRET; @@ -33,8 +36,12 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; +@KeycloakIntegrationTest public class KcOidcBrokerColonAliasClientSecretBasicAuthTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerColonAliasClientSecretBasicAuthTest.KcOidcBrokerColonAliasConfigurationWithBasicAuthAuthentication(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerEventTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerEventTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java index 00db72f31964..997331c78115 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerEventTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; @@ -24,12 +24,21 @@ import org.keycloak.events.EventType; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.userprofile.UserProfileContext; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -41,10 +50,20 @@ * * @author rmartinc */ +@KeycloakIntegrationTest public final class KcOidcBrokerEventTest extends AbstractBrokerTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerFrontendUrlTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java similarity index 81% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerFrontendUrlTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java index fdfc935bb306..cb9bdd7bc7ea 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerFrontendUrlTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -9,11 +9,18 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.ReverseProxy; import org.junit.Ignore; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -22,8 +29,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +@KeycloakIntegrationTest public final class KcOidcBrokerFrontendUrlTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Rule public ReverseProxy proxy = new ReverseProxy(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerHiddenIdpHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java similarity index 81% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerHiddenIdpHintTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java index 2a74c5393bfe..49e81582d328 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerHiddenIdpHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java @@ -14,15 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; @@ -35,8 +42,18 @@ * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. * @author pedroigor */ +@KeycloakIntegrationTest public class KcOidcBrokerHiddenIdpHintTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcHiddenBrokerConfiguration(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpHintTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java index 0f06cd04c842..fb02e3e769c0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java @@ -14,15 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; @@ -32,8 +39,18 @@ * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. * @author pedroigor */ +@KeycloakIntegrationTest public class KcOidcBrokerIdpHintTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpLinkActionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpLinkActionTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java index 9edfa202648e..5fc73163021e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpLinkActionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java @@ -17,7 +17,7 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.net.URI; import java.net.URL; @@ -43,20 +43,31 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.messages.Messages; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.admin.AdminApiUtil; -import org.keycloak.testsuite.pages.IdpLinkActionPage; +import org.keycloak.testframework.ui.page.IdpLinkActionPage; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.testsuite.util.runonserver.RunHelpers; import org.keycloak.utils.BrokerUtil; import org.hamcrest.MatcherAssert; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Before; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.BeforeEach; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -68,8 +79,21 @@ * * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcBrokerIdpLinkActionTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + private static final BrokerConfiguration BROKER_CONFIG_INSTANCE = new KcOidcBrokerConfiguration() { @Override @@ -80,10 +104,10 @@ public List createProviderClients() { } }; - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectEvents + Events events; - @Page + @InjectPage private IdpLinkActionPage idpLinkActionPage; @Override @@ -91,7 +115,7 @@ protected BrokerConfiguration getBrokerConfiguration() { return BROKER_CONFIG_INSTANCE; } - @Before + @BeforeEach public void recreateConsumerUser() { RealmResource providerRealmResource = realmsResouce().realm(bc.providerRealmName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java similarity index 84% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java index 27681e55681f..50b3cc311a29 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; @@ -6,6 +6,9 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; @@ -13,8 +16,12 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; +@KeycloakIntegrationTest public class KcOidcBrokerIdpPublicKeyMissingUseTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithIdpPublicKeyMissingUse(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java index 1d514f9624cc..f5eedb928445 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test() { super("P-256", JWEConstants.ECDH_ES_A128KW, JWEConstants.A128CBC_HS256, Algorithm.ES256); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java index cc6229caceea..e4a3eed5db52 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest() { super("P-256", JWEConstants.ECDH_ES_A128KW, JWEConstants.A128GCM, Algorithm.ES256); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java index ba8f2f5a893a..949e33233928 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test() { super("P-384", JWEConstants.ECDH_ES_A192KW, JWEConstants.A192CBC_HS384, Algorithm.ES384); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java index eac7af5a10d5..2fdba96ea32d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest() { super("P-384", JWEConstants.ECDH_ES_A192KW, JWEConstants.A192GCM, Algorithm.ES384); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java similarity index 78% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java index 4163027ed105..791db30bb74d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test() { super("P-521", JWEConstants.ECDH_ES_A256KW, JWEConstants.A256CBC_HS512, Algorithm.ES512); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java index eb1dcd6616a6..9211c51be5a6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest() { super("P-521", JWEConstants.ECDH_ES_A256KW, JWEConstants.A256GCM, Algorithm.ES512); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java index bd753385522c..bb8603412ef9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test() { super("P-384", JWEConstants.ECDH_ES, JWEConstants.A192CBC_HS384, Algorithm.ES384); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java similarity index 77% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java index 3721b0345d7e..3fd5a82b2643 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerJWEEcdhEsP384A192GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { + + @InjectRealm + ManagedRealm managedRealm; public KcOidcBrokerJWEEcdhEsP384A192GcmTest() { super("P-384", JWEConstants.ECDH_ES, JWEConstants.A192GCM, Algorithm.ES384); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWETest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWETest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java index 8c42e6bccd94..ec7855f1e0da 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWETest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -49,7 +49,10 @@ import org.keycloak.representations.idm.UserRepresentation; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static java.util.Optional.ofNullable; @@ -64,8 +67,12 @@ * * @author rmartinc */ +@KeycloakIntegrationTest public class KcOidcBrokerJWETest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + private final String encAlg; private final String encEnc; private final String sigAlg; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java similarity index 80% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java index bd9c5897fe88..b8a96bce9900 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java @@ -14,9 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.jose.jwe.JWEConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** *

Extension of the KcOidcBrokerJWETest test to use a different key algorithm (RSA1_5), @@ -25,8 +28,12 @@ * * @author rmartinc */ +@KeycloakIntegrationTest public class KcOidcBrokerJWEUserInfoJustEncryptedTest extends KcOidcBrokerJWETest { + @InjectRealm + ManagedRealm managedRealm; + public KcOidcBrokerJWEUserInfoJustEncryptedTest() { super(JWEConstants.RSA_OAEP_256, null, null); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapReadOnlyTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java similarity index 85% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapReadOnlyTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java index 1199f9ee18a2..a75113146d3b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapReadOnlyTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; @@ -17,28 +17,45 @@ import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProviderFactory; import org.keycloak.storage.ldap.idm.model.LDAPObject; -import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.util.ApiUtil; import org.keycloak.testsuite.federation.ldap.LDAPTestContext; -import org.keycloak.testsuite.pages.IdpConfirmLinkPage; +import org.keycloak.testframework.ui.page.IdpConfirmLinkPage; import org.keycloak.testsuite.util.LDAPRule; import org.keycloak.testsuite.util.LDAPTestUtils; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Before; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.BeforeEach; import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.models.utils.ModelToRepresentation.toRepresentationWithoutConfig; import static org.junit.jupiter.api.Assertions.assertEquals; +@KeycloakIntegrationTest public final class KcOidcBrokerLdapReadOnlyTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectOAuthClient + OAuthClient oauth; + @ClassRule public static LDAPRule ldapRule = new LDAPRule(); - @Page + @InjectPage private IdpConfirmLinkPage confirmLinkPage; @Override @@ -51,7 +68,7 @@ public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSync }; } - @Before + @BeforeEach public void onBefore() { createLdapStorageProvider(); addLdapUser(bc.getUserLogin(), bc.getUserEmail()); @@ -133,7 +150,7 @@ private MultivaluedHashMap toComponentConfig(Map private void addLdapUser(String username, String email) { String realmName = bc.consumerRealmName(); - testingClient.server().run(session -> { + runOnServer.run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session, realmName, null); RealmModel appRealm = ctx.getRealm(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java similarity index 85% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java index 32d622a81e97..8f0d749e2f14 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; @@ -28,16 +28,28 @@ import org.keycloak.storage.UserStorageProvider.EditMode; import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProviderFactory; -import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.util.ApiUtil; import org.keycloak.testsuite.util.LDAPRule; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +@KeycloakIntegrationTest public final class KcOidcBrokerLdapTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; @@ -46,7 +58,7 @@ protected BrokerConfiguration getBrokerConfiguration() { @ClassRule public static LDAPRule ldapRule = new LDAPRule(); - @Before + @BeforeEach public void onBefore() { ComponentRepresentation rep = getUserStorageConfiguration(); Response resp = adminClient.realm(bc.consumerRealmName()).components().add(rep); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLoginHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java similarity index 88% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLoginHintTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java index 24dcbad45c48..69f705c19ad4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLoginHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -9,10 +9,17 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.updaters.Creator; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -24,8 +31,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +@KeycloakIntegrationTest public class KcOidcBrokerLoginHintTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithLoginHint(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLogoutFrontChannelTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java similarity index 83% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLogoutFrontChannelTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java index 8c7cc9ec2ae3..a3b017035d81 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLogoutFrontChannelTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java @@ -1,4 +1,13 @@ -package org.keycloak.testsuite.broker; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.Map; @@ -7,6 +16,17 @@ import org.keycloak.common.VerificationException; import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.IDToken; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.keycloak.testsuite.util.oauth.IntrospectionResponse; @@ -14,7 +34,7 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; @@ -24,8 +44,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@KeycloakIntegrationTest public class KcOidcBrokerLogoutFrontChannelTest extends AbstractKcOidcBrokerLogoutTest { - @Rule public AssertEvents events = new AssertEvents(this); + + @InjectRealm + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLogoutTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLogoutTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java index 1a478173b1a3..112eaa1d7072 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLogoutTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.Map; @@ -11,13 +11,24 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.representations.IDToken; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; @@ -27,10 +38,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +@KeycloakIntegrationTest public class KcOidcBrokerLogoutTest extends AbstractKcOidcBrokerLogoutTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerNoLoginHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java similarity index 84% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerNoLoginHintTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java index 661a9dbbd602..b83ba4c61620 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerNoLoginHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -11,6 +11,13 @@ import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; @@ -18,8 +25,18 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +@KeycloakIntegrationTest public class KcOidcBrokerNoLoginHintTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithNoLoginHint(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerNonceParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerNonceParameterTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java index 986879dde9cc..dec646157ae3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerNonceParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.ArrayList; import java.util.Arrays; @@ -17,18 +17,30 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +@KeycloakIntegrationTest public class KcOidcBrokerNonceParameterTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerParameterForwardTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerParameterForwardTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java index 32e2e2f51e58..3afe9c5561f4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerParameterForwardTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -13,6 +13,13 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; @@ -23,8 +30,18 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcOidcBrokerParameterForwardTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + private static final String FORWARDED_PARAMETER = "forwarded_parameter"; private static final String FORWARDED_PARAMETER_VALUE = "forwarded_value"; private static final String PARAMETER_NOT_SET = "parameter_not_set"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPassMaxAgeTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java similarity index 87% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPassMaxAgeTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java index d94bfbffdfc8..ef6a57a16af8 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPassMaxAgeTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Map; @@ -7,10 +7,19 @@ import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -23,8 +32,21 @@ * * see https://issues.redhat.com/browse/KEYCLOAK-18499 */ +@KeycloakIntegrationTest public class KcOidcBrokerPassMaxAgeTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithPassMaxAge(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPkceTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java similarity index 75% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPkceTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java index 5c6d069146f4..d3b4132440fe 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPkceTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java @@ -1,12 +1,19 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.OAuth2Constants; import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class KcOidcBrokerPkceTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java index ac03c403a6bf..bc7ab89888ef 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -33,12 +33,15 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation.KeyMetadataRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.AuthenticationExecutionBuilder; import org.keycloak.testframework.realm.AuthenticationFlowBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.broker.oidc.ClientIdRequiredJWTClientAuthenticator; import org.keycloak.testsuite.util.KeyUtils; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import static org.keycloak.testsuite.AbstractAuthenticationTest.findFlowByAlias; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -50,10 +53,14 @@ * * @author Justin Tay */ +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtClientIdRequiredTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override - @Before + @BeforeEach public void beforeBrokerTest() { super.beforeBrokerTest(); RealmResource realmResource = adminClient.realm(bc.providerRealmName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java index 970e25b5eaef..246989e5432a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -27,6 +27,9 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation.KeyMetadataRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -34,8 +37,12 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST2; +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtCustomAudienceTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java index 621c8f8175ad..63407cb0f7d2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -18,6 +18,9 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.TokenSignatureUtil; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -26,8 +29,12 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtCustomSignAlgTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); @@ -89,4 +96,4 @@ protected ComponentRepresentation createRep(String name, String realmId, String return rep; } } -} \ No newline at end of file +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java index a8f3e357fde5..32baf033cab4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -31,13 +31,20 @@ import org.keycloak.representations.idm.ComponentExportRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtMissingUseTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); @@ -105,4 +112,4 @@ public RealmRepresentation createConsumerRealm() { } -} \ No newline at end of file +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java index 8842e4bd37de..c14e28b1f600 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -27,14 +27,21 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation.KeyMetadataRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); @@ -69,4 +76,4 @@ public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSync } -} \ No newline at end of file +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java index adb5bc5a25fc..decb4127264f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -31,13 +31,20 @@ import org.keycloak.representations.idm.ComponentExportRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); @@ -103,4 +110,4 @@ public RealmRepresentation createConsumerRealm() { } -} \ No newline at end of file +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java index 4c3b42f50019..527c7011c820 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -27,14 +27,21 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation.KeyMetadataRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class KcOidcBrokerPrivateKeyJwtWithX509HeadersTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithJWTAuthentication(); @@ -70,4 +77,4 @@ public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSync } -} \ No newline at end of file +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPromptNoneRedirectTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPromptNoneRedirectTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java index 39f367045f54..b6cdc4bc9176 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPromptNoneRedirectTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -23,11 +23,20 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.AccountHelper; import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; @@ -48,8 +57,21 @@ */ // Remove @Ignore when closing Github issue 20642 @Ignore +@KeycloakIntegrationTest public class KcOidcBrokerPromptNoneRedirectTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerPromptNoneConfiguration(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPromptParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java similarity index 84% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPromptParameterTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java index d00b144d795c..d47bbfff6168 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPromptParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -9,11 +9,28 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +@KeycloakIntegrationTest public class KcOidcBrokerPromptParameterTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + private static final String PROMPT_CONSENT = "consent"; private static final String PROMPT_LOGIN = "login"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerStateParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java similarity index 87% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerStateParameterTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java index c5249b121191..1b1ba3eee495 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerStateParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java @@ -16,20 +16,29 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.OAuth2Constants; import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.UriUtils; import org.keycloak.events.EventType; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.pages.LoginExpiredPage; +import org.keycloak.testframework.ui.page.LoginExpiredPage; import org.hamcrest.Matchers; -import org.jboss.arquillian.graphene.page.Page; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; @@ -41,13 +50,23 @@ * * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcBrokerStateParameterTest extends AbstractInitializedBaseBrokerTest { - @Page + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectPage protected LoginExpiredPage loginExpiredPage; - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerSubMatchIntrospectionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java similarity index 85% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerSubMatchIntrospectionTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java index 208c6b012624..95e5c1515c4e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerSubMatchIntrospectionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.ArrayList; import java.util.List; @@ -6,16 +6,28 @@ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ClientBuilder; import org.junit.Ignore; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; +@KeycloakIntegrationTest public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java similarity index 98% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java index a94c88ec13f8..0b42b1e7f097 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.ArrayList; @@ -47,6 +47,15 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.admin.AdminApiUtil; import org.keycloak.testsuite.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.util.AccountHelper; @@ -59,8 +68,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -82,7 +91,20 @@ /** * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ +@KeycloakIntegrationTest public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest { + + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; private final static String USER_ATTRIBUTE_NAME = "user-attribute"; private final static String USER_ATTRIBUTE_VALUE = "attribute-value"; private final static String CLAIM_FILTER_REGEXP = ".*-value"; @@ -92,7 +114,7 @@ protected BrokerConfiguration getBrokerConfiguration() { return BROKER_CONFIG_INSTANCE; } - @Before + @BeforeEach public void setUpTotp() { totp = new TimeBasedOTP(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java index 3b5438c13c52..00b22b11a327 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.io.IOException; @@ -58,6 +58,15 @@ import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation; import org.keycloak.services.resources.admin.fgap.AdminPermissionManagement; import org.keycloak.services.resources.admin.fgap.AdminPermissions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testsuite.admin.AdminApiUtil; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeatures; @@ -68,7 +77,7 @@ import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.util.BasicAuthHelper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -87,8 +96,21 @@ * Test for identity-provider token exchange scenarios. Base for tests of token-exchange V1 */ @EnableFeatures({@EnableFeature(Profile.Feature.TOKEN_EXCHANGE), @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)}) +@KeycloakIntegrationTest public class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; @@ -243,7 +265,7 @@ public void testWithLinkedFederationProviderTokenExchangeInternalToExternal(bool String userName = bc.getUserLogin(); String idpAlias = bc.getIDPAlias(); - String oldTokenFromDatabase = testingClient.server().fetch(session -> { + String oldTokenFromDatabase = runOnServer.fetch(session -> { RealmModel realm = session.realms().getRealmByName(realmName); UserModel user = session.users().getUserByUsername(realm, userName); return session.getProvider(UserProvider.class, JpaRealmProviderFactory.PROVIDER_ID).getFederatedIdentity(realm, user, idpAlias).getToken(); @@ -259,7 +281,7 @@ public void testWithLinkedFederationProviderTokenExchangeInternalToExternal(bool if (storeToken) { /* Ensure that the tokens are persisted to the database as they would otherwise not be present on other nodes or on server restart */ - String newTokenFromDatabase = testingClient.server().fetch(session -> { + String newTokenFromDatabase = runOnServer.fetch(session -> { RealmModel realm = session.realms().getRealmByName(realmName); UserModel user = session.users().getUserByUsername(realm, userName); return session.getProvider(UserProvider.class, JpaRealmProviderFactory.PROVIDER_ID).getFederatedIdentity(realm, user, idpAlias).getToken(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTransientSessionsTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTransientSessionsTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java index bf31d97c1755..694db3b971a9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTransientSessionsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.ArrayList; @@ -56,13 +56,26 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.providers.runonserver.RunOnServerException; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.client.KeycloakTestingClient; -import org.keycloak.testsuite.pages.UpdateAccountInformationPage; +import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.Creator; import org.keycloak.testsuite.util.AccountHelper; @@ -79,10 +92,10 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.hamcrest.Matchers; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Before; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.BeforeEach; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; @@ -112,7 +125,23 @@ * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ @EnableFeature(value = Profile.Feature.TRANSIENT_USERS, skipRestart = true) +@KeycloakIntegrationTest public final class KcOidcBrokerTransientSessionsTest extends AbstractAdvancedBrokerTest { + + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; private final static String USER_ATTRIBUTE_NAME = "user-attribute"; private final static String USER_ATTRIBUTE_VALUE = "attribute-value"; private final static String CLAIM_FILTER_REGEXP = ".*-value"; @@ -122,10 +151,10 @@ protected BrokerConfiguration getBrokerConfiguration() { return BROKER_CONFIG_INSTANCE; } - @Page + @InjectPage UpdateAccountInformationPage updateAccountInformationPage; - @Before + @BeforeEach public void setUpTotp() { totp = new TimeBasedOTP(); } @@ -604,8 +633,8 @@ private EventRepresentation loginWithBrokerUsingOAuthClient(String consumerClien return loginEvent; } - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectEvents + Events events; @Test // Based on OfflineTokenTest.offlineTokenBrowserFlow() public void offlineTokenBrowserFlow() throws Exception { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesDisabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java similarity index 85% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesDisabledTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java index a4bffcab8344..865b06ffc704 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesDisabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -9,6 +9,13 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static java.util.Locale.ENGLISH; @@ -22,8 +29,18 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcOidcBrokerUiLocalesDisabledTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithUiLocalesDisabled(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesEnabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java similarity index 85% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesEnabledTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java index d82865eb29c8..11a6b8140cf4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesEnabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -9,6 +9,13 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static java.util.Locale.ENGLISH; @@ -21,8 +28,18 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcOidcBrokerUiLocalesEnabledTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationWithUiLocalesEnabled(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java similarity index 86% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java index 18bbf782d5ce..f372b3f24dde 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Locale; @@ -10,6 +10,13 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.OAuth2Constants.UI_LOCALES_PARAM; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -20,8 +27,18 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcOidcBrokerUiLocalesWithIdpHintTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + private static final Locale HUNGARIAN = Locale.forLanguageTag("hu"); @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerVaultTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java similarity index 53% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerVaultTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java index 9ad69b6535a6..de36e7593969 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerVaultTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java @@ -1,13 +1,20 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.arquillian.annotation.EnableVault; /** * @author Martin Kanis */ @EnableVault +@KeycloakIntegrationTest public class KcOidcBrokerVaultTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerVaultConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerWithConsentTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java similarity index 80% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerWithConsentTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java index 068d968a3269..7b03f9cdc713 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerWithConsentTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java @@ -1,14 +1,25 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.removeBrokerExpiredSessions; @@ -16,8 +27,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +@KeycloakIntegrationTest public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Rule public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcDefaultIdpTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java similarity index 79% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcDefaultIdpTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java index 77c750aca585..927a5a2b480c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcDefaultIdpTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java @@ -17,15 +17,22 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcDefaultIdpTest extends AbstractDefaultIdpTest { + @InjectRealm + ManagedRealm managedRealm; + // Issue 17441 @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java index 5e79263cb7d7..b34613797ebc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.admin.client.resource.AuthenticationManagementResource; import org.keycloak.admin.client.resource.IdentityProviderResource; @@ -12,25 +12,42 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.AuthenticationExecutionBuilder; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +@KeycloakIntegrationTest public class KcOidcFirstBrokerLoginDetectExistingUserTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration(); } @Override - @Before + @BeforeEach public void beforeBrokerTest() { super.beforeBrokerTest(); log.debug("creating detect existing user flow for realm " + bc.providerRealmName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginNewAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginNewAuthTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java index f118f0efa7d6..538b032676f4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginNewAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java @@ -1,19 +1,24 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.models.UserModel; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; -import org.keycloak.testsuite.Assert; +import org.keycloak.tests.utils.Assert; import org.keycloak.testsuite.admin.AdminApiUtil; -import org.keycloak.testsuite.pages.PasswordPage; -import org.keycloak.testsuite.pages.SelectAuthenticatorPage; +import org.keycloak.testframework.ui.page.PasswordPage; +import org.keycloak.testframework.ui.page.SelectAuthenticatorPage; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Before; -import org.junit.Test; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; @@ -28,12 +33,19 @@ * * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcFirstBrokerLoginNewAuthTest extends AbstractInitializedBaseBrokerTest { - @Page + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectPage PasswordPage passwordPage; - @Page + @InjectPage protected SelectAuthenticatorPage selectAuthenticatorPage; @Override @@ -41,7 +53,7 @@ protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; } - @Before + @BeforeEach public void disableReviewProfileBeforeTest() { updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java index 1d2994c698f6..086a29cb1214 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.time.Duration; import java.util.List; @@ -16,21 +16,30 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ClientScopeBuilder; import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; import org.keycloak.testsuite.forms.RegisterWithUserProfileTest; import org.keycloak.testsuite.forms.VerifyProfileTest; -import org.keycloak.testsuite.pages.LoginUpdateProfilePage; -import org.keycloak.testsuite.pages.RegisterPage; +import org.keycloak.testframework.ui.page.LoginUpdateProfilePage; +import org.keycloak.testframework.ui.page.RegisterPage; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.MailServerConfiguration; import org.keycloak.testsuite.util.userprofile.UserProfileUtil; import org.keycloak.util.JsonSerialization; import org.apache.commons.lang3.StringUtils; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Test; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -59,14 +68,27 @@ /** * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + protected ManagedRealm managedRealm = new ManagedRealm(this, bc.consumerRealmName()); - @Page + @InjectPage protected LoginUpdateProfilePage loginUpdateProfilePage; - @Page + @InjectPage protected RegisterPage registerPage; @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcMultipleTabsBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcMultipleTabsBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java index e9571c0a7f57..eadd17d593b7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcMultipleTabsBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java @@ -17,7 +17,7 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.OAuthErrorException; import org.keycloak.admin.client.resource.IdentityProviderResource; @@ -29,7 +29,18 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseMode; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.BrowserTabUtil; @@ -39,7 +50,7 @@ import org.hamcrest.Matchers; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.htmlunit.HtmlUnitDriver; @@ -53,10 +64,23 @@ /** * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectEvents + Events events; @Rule public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcPostBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcPostBrokerLoginTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java index 1244c5265cb6..36192d95470a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcPostBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java @@ -17,7 +17,7 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.Map; @@ -34,15 +34,24 @@ import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.FlowUtil; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.models.utils.TimeBasedOTP.DEFAULT_INTERVAL_SECONDS; @@ -54,19 +63,29 @@ /** * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcOidcPostBrokerLoginTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectOAuthClient + OAuthClient oauth; + private static final KcOidcBrokerConfiguration BROKER_CONFIG_INSTANCE = new KcOidcBrokerConfiguration(); - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { return BROKER_CONFIG_INSTANCE; } - @Before + @BeforeEach public void setUpTotp() { totp = new TimeBasedOTP(); } @@ -297,7 +316,7 @@ public void testPostBrokerLoginFlowWithOTP() { } static void configurePostBrokerLoginWithClientScopeConditionAndOTP(KeycloakTestingClient testingClient, String consumerRealmName, String idpAlias, String clientScopeName, boolean negate) { - testingClient.server(consumerRealmName).run(session -> { + runOnServer.run(session -> { AuthenticationFlowModel flowModel = session.getContext().getRealm().getFlowByAlias("post-broker"); if (flowModel == null) { flowModel = FlowUtil.createFlowModel("post-broker", "basic-flow", "post-broker flow with client-scope condition and OTP", true, false); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcUsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java similarity index 85% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcUsernameTemplateMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java index 8511cf3144b1..81417b57f55b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcUsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper; @@ -8,11 +8,18 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import com.google.common.collect.ImmutableMap; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * @author Martin Idel */ +@KeycloakIntegrationTest public class KcOidcUsernameTemplateMapperTest extends AbstractUsernameTemplateMapperTest { + + @InjectRealm + ManagedRealm managedRealm; @Override protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) { IdentityProviderMapperRepresentation usernameTemplateMapper = new IdentityProviderMapperRepresentation(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAdvancedAttributeToGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAdvancedAttributeToGroupMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java index 58926c28f910..dc6d68d2db28 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAdvancedAttributeToGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -15,11 +15,14 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; @@ -27,8 +30,12 @@ * @author Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class KcSamlAdvancedAttributeToGroupMapperTest extends AbstractGroupBrokerMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String ATTRIBUTES = "[\n" + " {\n" + " \"key\": \"" + ATTRIBUTE_TO_MAP_FRIENDLY_NAME + "\",\n" + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAdvancedAttributeToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAdvancedAttributeToRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java index 5bbcf8782268..432c435708bd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAdvancedAttributeToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -12,7 +12,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; @@ -20,8 +23,12 @@ * @author Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class KcSamlAdvancedAttributeToRoleMapperTest extends AbstractAdvancedRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String ATTRIBUTES = "[\n" + " {\n" + " \"key\": \"" + ATTRIBUTE_TO_MAP_FRIENDLY_NAME + "\",\n" + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAttributeConsumingServiceIndexTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAttributeConsumingServiceIndexTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java index d8d6d7dc4260..57c7a06b8d22 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAttributeConsumingServiceIndexTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; @@ -6,13 +6,16 @@ import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -22,8 +25,12 @@ /** * Final class as it's not intended to be overriden. */ +@KeycloakIntegrationTest public final class KcSamlAttributeConsumingServiceIndexTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerAllowedClockSkewTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerAllowedClockSkewTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java index 2dadc49bb063..68ca1e255795 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerAllowedClockSkewTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; @@ -25,13 +25,16 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; @@ -44,8 +47,12 @@ * * @author rmartinc */ +@KeycloakIntegrationTest public class KcSamlBrokerAllowedClockSkewTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerArtifactBindingTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerArtifactBindingTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java index 235dfb8fef5e..c133ee7ce038 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerArtifactBindingTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.io.IOException; @@ -6,17 +6,29 @@ import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.crypto.Algorithm; import org.keycloak.protocol.saml.SamlConfigAttributes; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.KeyUtils; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +@KeycloakIntegrationTest public final class KcSamlBrokerArtifactBindingTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerDestinationTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java similarity index 88% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerDestinationTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java index 150cef7dc078..98455bdad966 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerDestinationTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; @@ -8,14 +8,19 @@ import org.keycloak.events.EventType; import org.keycloak.protocol.saml.SamlConfigAttributes; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.w3c.dom.Element; @@ -28,10 +33,14 @@ import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcSamlBrokerDestinationTest extends AbstractBrokerTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerFrontendUrlTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerFrontendUrlTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java index 0dbc1abea439..75a053809071 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerFrontendUrlTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.net.URI; import java.net.URISyntaxException; @@ -22,7 +22,16 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.ReverseProxy; @@ -35,7 +44,7 @@ import org.apache.http.ssl.SSLContextBuilder; import org.junit.Ignore; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; @@ -50,13 +59,23 @@ import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public final class KcSamlBrokerFrontendUrlTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Rule public ReverseProxy proxy = new ReverseProxy(); - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectEvents + Events events; @Override protected BrokerConfiguration getBrokerConfiguration() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java index 44dfee1c41f3..2e3f095b5eff 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java @@ -1,6 +1,13 @@ -package org.keycloak.testsuite.broker; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +package org.keycloak.tests.broker; +@KeycloakIntegrationTest public class KcSamlBrokerLoginHintWithOptionDisabledTest extends AbstractSamlLoginHintTest { + + @InjectRealm + ManagedRealm managedRealm; @Override boolean isLoginHintOptionEnabled() { return false; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java similarity index 65% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java index 4b5ca5139629..0344195b273f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java @@ -1,15 +1,32 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; +@KeycloakIntegrationTest public class KcSamlBrokerLoginHintWithOptionEnabledTest extends AbstractSamlLoginHintTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + // KEYCLOAK-13950 @Test public void testPassLoginHintWithXmlCharShouldEncodeIt() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerSessionNotOnOrAfterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java similarity index 90% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerSessionNotOnOrAfterTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java index d0bbd0152898..8c19c77a883f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerSessionNotOnOrAfterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Set; @@ -8,11 +8,14 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; @@ -24,8 +27,12 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +@KeycloakIntegrationTest public class KcSamlBrokerSessionNotOnOrAfterTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java index 7bf9b280ff61..0cd1eb223352 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.net.URI; @@ -38,6 +38,13 @@ import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLProtocolQNames; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; @@ -50,7 +57,7 @@ import org.apache.http.NameValuePair; import org.apache.http.client.methods.HttpUriRequest; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -71,8 +78,18 @@ /** * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ +@KeycloakIntegrationTest public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlCustomEntityIdBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlCustomEntityIdBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java index bd699d9ed455..411ab5297f0f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlCustomEntityIdBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; @@ -6,13 +6,16 @@ import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -23,8 +26,12 @@ /** * Final class as it's not intended to be overriden. */ +@KeycloakIntegrationTest public final class KcSamlCustomEntityIdBrokerTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlDefaultIdpTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java similarity index 62% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlDefaultIdpTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java index b0094ffef5fe..45de21593e0a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlDefaultIdpTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java @@ -1,13 +1,20 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * Test of various scenarios related to the use of default IdP option * in the Identity Provider Redirector authenticator */ +@KeycloakIntegrationTest public class KcSamlDefaultIdpTest extends AbstractDefaultIdpTest { + @InjectRealm + ManagedRealm managedRealm; + // KEYCLOAK-17368 @Test public void testDefaultIdpSetTriedAndReturnedError() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlEncryptedAssertionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlEncryptedAssertionTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java index 531ff1b0d42e..fc82b6e3abe7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlEncryptedAssertionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.security.PublicKey; import javax.crypto.SecretKey; @@ -28,6 +28,9 @@ import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.processing.core.util.XMLEncryptionUtil; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.saml.SamlDocumentStepBuilder; import org.w3c.dom.Node; @@ -38,8 +41,12 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcSamlEncryptedAssertionTest extends AbstractKcSamlEncryptedElementsTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected SamlDocumentStepBuilder.Saml2DocumentTransformer encryptDocument(PublicKey publicKey, String keyEncryptionAlgorithm, String keyEncryptionDigestMethod, String keyEncryptionMgfAlgorithm) { return document -> { // Replace Assertion with EncryptedAssertion diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlEncryptedIdTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlEncryptedIdTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java index b61b6d7d6763..74ef9aa62ab8 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlEncryptedIdTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.security.PublicKey; import javax.crypto.SecretKey; @@ -10,6 +10,9 @@ import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.processing.core.util.XMLEncryptionUtil; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.saml.SamlDocumentStepBuilder; import org.hamcrest.CoreMatchers; @@ -22,8 +25,12 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; +@KeycloakIntegrationTest public class KcSamlEncryptedIdTest extends AbstractKcSamlEncryptedElementsTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected SamlDocumentStepBuilder.Saml2DocumentTransformer encryptDocument(PublicKey publicKey, String keyEncryptionAlgorithm, String keyEncryptionDigestMethod, String keyEncryptionMgfAlgorithm) { return document -> { // Replace Subject -> NameID with EncryptedId diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlFirstBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java similarity index 59% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlFirstBrokerLoginTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java index cfcaed8f1b8e..ac5f87347dcc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlFirstBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java @@ -1,12 +1,19 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcSamlFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlForceAuthnBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlForceAuthnBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java index 3c21becc0a47..c5e5a729ab28 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlForceAuthnBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java @@ -16,20 +16,23 @@ * limitations under the License. * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.w3c.dom.Document; @@ -38,8 +41,12 @@ /** * @author phamann */ +@KeycloakIntegrationTest public final class KcSamlForceAuthnBrokerTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlIdPInitiatedSsoTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlIdPInitiatedSsoTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java index ccf907fa8100..aaad891a8392 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlIdPInitiatedSsoTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -41,19 +41,23 @@ import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants; import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil; -import org.keycloak.testsuite.AbstractKeycloakTest; -import org.keycloak.testsuite.pages.ErrorPage; -import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.pages.PageUtils; -import org.keycloak.testsuite.pages.UpdateAccountInformationPage; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testframework.ui.page.ErrorPage; +import org.keycloak.testframework.ui.page.LoginPage; +import org.keycloak.testframework.ui.page.PageUtils; +import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; import org.keycloak.testsuite.utils.io.IOUtil; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Before; -import org.junit.Test; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; @@ -80,20 +84,27 @@ * * @author hmlnarik */ -public class KcSamlIdPInitiatedSsoTest extends AbstractKeycloakTest { +@KeycloakIntegrationTest +public class KcSamlIdPInitiatedSsoTest { + + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; private static final String PROVIDER_REALM_USER_NAME = "test"; private static final String PROVIDER_REALM_USER_PASSWORD = "test"; private static final String CONSUMER_CHOSEN_USERNAME = "mytest"; - @Page + @InjectPage protected LoginPage accountLoginPage; - @Page + @InjectPage protected UpdateAccountInformationPage updateAccountInformationPage; - @Page + @InjectPage protected ErrorPage errorPage; private String urlRealmConsumer2; @@ -115,7 +126,7 @@ private RealmRepresentation loadFromClasspath(String fileName, Properties proper } } - @Before + @BeforeEach public void cleanupTestUserInConsumerRealm() { final UsersResource users = adminClient.realm(REALM_CONS_NAME).users(); users.search(CONSUMER_CHOSEN_USERNAME).stream() @@ -124,14 +135,14 @@ public void cleanupTestUserInConsumerRealm() { .forEach(Response::close); } - @Before + @BeforeEach public void initRealmUrls() { urlRealmProvider = getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME; urlRealmConsumer = getAuthRoot() + "/auth/realms/" + REALM_CONS_NAME; urlRealmConsumer2 = getAuthRoot() + "/auth/realms/" + REALM_CONS_NAME + "-2"; } - @Before + @BeforeEach public void resetPrincipalType() { IdentityProviderResource idp = adminClient.realm(REALM_CONS_NAME).identityProviders().get("saml-leaf"); IdentityProviderRepresentation rep = idp.toRepresentation(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlLogoutTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java similarity index 96% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlLogoutTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java index 9151af9edf02..223acd4bb875 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlLogoutTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.util.List; @@ -17,7 +17,10 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; @@ -27,7 +30,7 @@ import org.keycloak.testsuite.util.saml.SamlMessageReceiver; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; @@ -44,8 +47,12 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class KcSamlLogoutTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String PROVIDER_SAML_CLIENT_ID = getProviderRoot() + "/sales-post/"; @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java index f9a430a4628b..4b537b3d8e67 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.util.HashMap; @@ -38,7 +38,14 @@ import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; @@ -50,7 +57,7 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.w3c.dom.Document; @@ -58,10 +65,17 @@ * * @author rmartinc */ +@KeycloakIntegrationTest public class KcSamlMetadataSignedAndEncryptedBrokerTest extends AbstractKcSamlMetadataBrokerTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectEvents + Events events; public class KcSamlMetadataSignedAndEncryptedBrokerConfiguration extends KcSamlBrokerConfiguration { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMetadataSignedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMetadataSignedBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java index 34494814f988..76afee6f4d0d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMetadataSignedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.util.HashMap; @@ -30,6 +30,11 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.saml.SignatureAlgorithm; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; @@ -37,15 +42,22 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; /** * * @author rmartinc */ +@KeycloakIntegrationTest public class KcSamlMetadataSignedBrokerTest extends AbstractKcSamlMetadataBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + public class KcSamlMetadataSignedBrokerConfiguration extends KcSamlBrokerConfiguration { @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMultipleAttributeToRoleMappersTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMultipleAttributeToRoleMappersTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java index 46304c7dba02..bf6c12b033fa 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMultipleAttributeToRoleMappersTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.broker.provider.ConfigConstants; import org.keycloak.broker.saml.mappers.AdvancedAttributeToRoleMapper; @@ -25,6 +25,9 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import com.google.common.collect.ImmutableMap; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * Runs the same tests as {@link AttributeToRoleMapperTest} but using multiple SAML mappers that map different IDP attributes @@ -47,8 +50,12 @@ * @author Stefan Guilhen, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class KcSamlMultipleAttributeToRoleMappersTest extends AttributeToRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String ATTRIBUTES_TO_MATCH = "[\n" + " {\n" + " \"key\": \"test attribute\",\n" + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMultipleTabsBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMultipleTabsBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java index 9951e045ff90..c832a924407d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlMultipleTabsBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java @@ -17,12 +17,23 @@ * */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.EventType; +import org.keycloak.testframework.annotations.InjectEvents; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; +import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.BrowserTabUtil; @@ -32,7 +43,7 @@ import org.hamcrest.Matchers; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.htmlunit.HtmlUnitDriver; @@ -45,10 +56,23 @@ * * @author Marek Posolda */ +@KeycloakIntegrationTest public class KcSamlMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerTest { - @Rule - public AssertEvents events = new AssertEvents(this); + @InjectRealm + ManagedRealm managedRealm; + + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + + @InjectEvents + Events events; @Rule public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlParseIdPDescriptorTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlParseIdPDescriptorTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java index 8591d68fc24e..710a2f08fae7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlParseIdPDescriptorTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.IOException; import java.util.List; @@ -25,8 +25,10 @@ import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; -import org.keycloak.testsuite.AbstractKeycloakTest; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -34,14 +36,18 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; /** * * @author rmartinc */ -public class KcSamlParseIdPDescriptorTest extends AbstractKeycloakTest { +@KeycloakIntegrationTest +public class KcSamlParseIdPDescriptorTest { + + @InjectRealm + ManagedRealm managedRealm; @Override public void addTestRealms(List testRealms) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlRequestedAuthnContextBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlRequestedAuthnContextBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java index 37305252edb4..021b94e01a0a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlRequestedAuthnContextBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; @@ -7,6 +7,9 @@ import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; @@ -14,7 +17,7 @@ import org.keycloak.testsuite.util.SamlClientBuilder; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -28,8 +31,12 @@ /** * Final class as it's not intended to be overriden. */ +@KeycloakIntegrationTest public final class KcSamlRequestedAuthnContextBrokerTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java similarity index 98% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java index e9487402f1a8..7631464164d3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.util.Arrays; @@ -30,6 +30,11 @@ import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionQNames; import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLProtocolQNames; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; @@ -44,7 +49,7 @@ import org.apache.http.HttpResponse; import org.hamcrest.Matcher; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -61,8 +66,15 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; +@KeycloakIntegrationTest public class KcSamlSignedBrokerTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + public void withSignedEncryptedAssertions(Runnable testBody, boolean signedDocument, boolean signedAssertion, boolean encryptedAssertion) throws Exception { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java index 771a9aeb0afe..23f546768cb8 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -8,14 +8,21 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_SIGN_CERT; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_SIGN_KEY; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PRIVATE_KEY; import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PUBLIC_KEY; +@KeycloakIntegrationTest public class KcSamlSignedDocumentOnlyBrokerTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + public static class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration { @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSpDescriptorTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java similarity index 98% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSpDescriptorTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java index afa26b40a50b..3d285a18f941 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSpDescriptorTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.Closeable; import java.io.IOException; @@ -32,11 +32,14 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.processing.core.parsers.saml.SAMLParser; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import com.google.common.collect.ImmutableMap; import org.apache.tools.ant.filters.StringInputStream; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.testsuite.util.KeyUtils.generateNewRealmKey; @@ -49,8 +52,12 @@ import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertTrue; +@KeycloakIntegrationTest public class KcSamlSpDescriptorTest extends AbstractBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlUsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlUsernameTemplateMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java index e18650445c53..75343c5bba69 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlUsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.List; @@ -9,10 +9,15 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.WaitUtils; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.keycloak.broker.saml.mappers.UsernameTemplateMapper.PROVIDER_ID; @@ -20,8 +25,15 @@ /** * @author Martin Idel */ +@KeycloakIntegrationTest public class KcSamlUsernameTemplateMapperTest extends AbstractUsernameTemplateMapperTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + @Override protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) { IdentityProviderMapperRepresentation usernameTemplateMapper = new IdentityProviderMapperRepresentation(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlXPathAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlXPathAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java index 6f72a80e9543..71b6fb223072 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlXPathAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; @@ -14,12 +14,15 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; @@ -37,8 +40,12 @@ * This contains only the happy path. Have a look at org.keycloak.test.broker.saml.XPathAttributeMapperTest * for unit style tests and handling parsing errors. */ +@KeycloakIntegrationTest public class KcSamlXPathAttributeMapperTest extends AbstractInitializedBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + @Override public void beforeBrokerTest() { super.beforeBrokerTest(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OAuth2BrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java similarity index 91% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OAuth2BrokerTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java index b083cbb02535..ae9454eb3dc2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OAuth2BrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.ArrayList; import java.util.List; @@ -19,14 +19,31 @@ import org.keycloak.representations.idm.UserRepresentation; import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; /** * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ +@KeycloakIntegrationTest public final class OAuth2BrokerTest extends AbstractAdvancedBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java index 503879b411fa..3d477b1a7db2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -10,11 +10,18 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +@KeycloakIntegrationTest public class OidcAccessTokenOnlyClaimsUserAttributeMapperTest extends OidcUserAttributeMapperTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAdvancedClaimToGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java similarity index 97% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAdvancedClaimToGroupMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java index 365790093701..565d25709f83 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAdvancedClaimToGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.List; @@ -19,7 +19,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -28,7 +31,11 @@ * @author Artur Baltabayev, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class OidcAdvancedClaimToGroupMapperTest extends AbstractGroupBrokerMapperTest { + + @InjectRealm + ManagedRealm managedRealm; protected boolean isHardcodedGroup() { return false; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAdvancedClaimToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java similarity index 86% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAdvancedClaimToRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java index dd0e4c84e806..e5a67696dee6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcAdvancedClaimToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.HashMap; import java.util.Map; @@ -8,13 +8,20 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * Benjamin Weimer, * Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class OidcAdvancedClaimToRoleMapperTest extends AbstractAdvancedRoleMapperTest { + + @InjectRealm + ManagedRealm managedRealm; @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcClaimToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcClaimToRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java index 5ffe7bb261dd..9e73921a96bb 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcClaimToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.List; import java.util.Map; @@ -12,7 +12,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY; @@ -21,8 +24,12 @@ * @author Martin Idel, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class OidcClaimToRoleMapperTest extends AbstractRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + protected static final String CLAIM = KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME; protected static final String CLAIM_VALUE = "value 1"; private String claimOnSecondLogin = ""; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcClaimToUserSessionNoteMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcClaimToUserSessionNoteMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java index 27abbd50da1b..00daecd3e972 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcClaimToUserSessionNoteMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.HashMap; @@ -21,21 +21,33 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import com.google.common.collect.ImmutableMap; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; +@KeycloakIntegrationTest public class OidcClaimToUserSessionNoteMapperTest extends AbstractIdentityProviderMapperTest { + @InjectRealm + ManagedRealm managedRealm; + + @InjectOAuthClient + OAuthClient oauth; + private static final String CLAIM_NAME = "sessionNoteTest"; private static final String CLAIM_VALUE = "foo"; private static final String CONFIG_PROPERTY_CLAIMS = "claims"; @@ -51,7 +63,7 @@ protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfiguration(); } - @Before + @BeforeEach public void setup() { RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcHardcodedGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java similarity index 88% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcHardcodedGroupMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java index e22cc06b6296..a99963514016 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcHardcodedGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import jakarta.ws.rs.core.Response; @@ -12,6 +12,9 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import com.google.common.collect.ImmutableMap; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * @author DanieleMartinoli @@ -22,7 +25,11 @@ * Since this mapper does not cause leaving the group when the claims do not match, an isHardcodedGroup * method is introduced to customize the behavior in the super class. */ +@KeycloakIntegrationTest public class OidcHardcodedGroupMapperTest extends OidcAdvancedClaimToGroupMapperTest { + + @InjectRealm + ManagedRealm managedRealm; @Override protected boolean isHardcodedGroup() { return true; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcMultipleClaimToRoleMappersTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcMultipleClaimToRoleMappersTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java index 18ed272a4999..4f23f93b801c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcMultipleClaimToRoleMappersTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.broker.oidc.mappers.AdvancedClaimToRoleMapper; import org.keycloak.broker.oidc.mappers.ClaimToRoleMapper; @@ -25,6 +25,9 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import com.google.common.collect.ImmutableMap; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * Runs the same tests as {@link OidcClaimToRoleMapperTest} but using multiple OIDC mappers that map different IDP claims @@ -47,8 +50,12 @@ * @author Stefan Guilhen, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class OidcMultipleClaimToRoleMappersTest extends OidcClaimToRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + private static final String CLAIMS_OR_ATTRIBUTES = "[\n" + " {\n" + " \"key\": \"test attribute\",\n" + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java similarity index 92% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java index 119e74cfdf7a..3c8768b25562 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.broker.oidc.mappers.UserAttributeMapper; import org.keycloak.models.IdentityProviderMapperModel; @@ -7,9 +7,16 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; +@KeycloakIntegrationTest public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserInfoClaimToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserInfoClaimToRoleMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java index 940f1be48eb0..65c3728e4bc9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserInfoClaimToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.util.Collections; import java.util.List; @@ -21,14 +21,21 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; /** * @author Dashaylan Naidoo, * Daniel Fesenmeyer */ +@KeycloakIntegrationTest public class OidcUserInfoClaimToRoleMapperTest extends AbstractRoleMapperTest { + @InjectRealm + ManagedRealm managedRealm; + protected static final String ATTRIBUTE_TO_MAP_USER_INFO = "user-attribute-info"; private static final String USER_INFO_CLAIM = ATTRIBUTE_TO_MAP_USER_INFO; private static final String USER_INFO_CLAIM_VALUE = "value 1"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java index ef498638aef8..a4948c89421b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.broker.saml.mappers.UserAttributeMapper; import org.keycloak.models.IdentityProviderMapperModel; @@ -7,11 +7,18 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; +@KeycloakIntegrationTest public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest { + @InjectRealm + ManagedRealm managedRealm; + @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java similarity index 93% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java index c8d489fc2c3d..eb286340571d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import java.io.FileInputStream; import java.util.List; @@ -33,27 +33,35 @@ import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.services.resources.admin.fgap.AdminPermissionManagement; import org.keycloak.services.resources.admin.fgap.AdminPermissions; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.IdentityProviderBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; -import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; +import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected; import org.keycloak.testsuite.auth.page.login.UpdateAccount; -import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.pages.social.AbstractSocialLoginPage; -import org.keycloak.testsuite.pages.social.BitbucketLoginPage; -import org.keycloak.testsuite.pages.social.FacebookLoginPage; -import org.keycloak.testsuite.pages.social.GitHubLoginPage; -import org.keycloak.testsuite.pages.social.GitLabLoginPage; -import org.keycloak.testsuite.pages.social.GoogleLoginPage; -import org.keycloak.testsuite.pages.social.InstagramLoginPage; -import org.keycloak.testsuite.pages.social.LinkedInLoginPage; -import org.keycloak.testsuite.pages.social.MicrosoftLoginPage; -import org.keycloak.testsuite.pages.social.OpenShiftLoginPage; -import org.keycloak.testsuite.pages.social.PayPalLoginPage; -import org.keycloak.testsuite.pages.social.StackOverflowLoginPage; -import org.keycloak.testsuite.pages.social.TwitterConsentLoginPage; +import org.keycloak.testframework.ui.page.LoginPage; +import org.keycloak.testframework.ui.page.social.AbstractSocialLoginPage; +import org.keycloak.testframework.ui.page.social.BitbucketLoginPage; +import org.keycloak.testframework.ui.page.social.FacebookLoginPage; +import org.keycloak.testframework.ui.page.social.GitHubLoginPage; +import org.keycloak.testframework.ui.page.social.GitLabLoginPage; +import org.keycloak.testframework.ui.page.social.GoogleLoginPage; +import org.keycloak.testframework.ui.page.social.InstagramLoginPage; +import org.keycloak.testframework.ui.page.social.LinkedInLoginPage; +import org.keycloak.testframework.ui.page.social.MicrosoftLoginPage; +import org.keycloak.testframework.ui.page.social.OpenShiftLoginPage; +import org.keycloak.testframework.ui.page.social.PayPalLoginPage; +import org.keycloak.testframework.ui.page.social.StackOverflowLoginPage; +import org.keycloak.testframework.ui.page.social.TwitterConsentLoginPage; import org.keycloak.testsuite.util.AdminClientUtil; import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.URLUtils; @@ -63,12 +71,12 @@ import com.google.common.collect.ImmutableMap; import org.jboss.arquillian.graphene.Graphene; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.After; -import org.junit.Before; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.BeforeClass; import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; @@ -102,7 +110,20 @@ * @author Vaclav Muzikar */ @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) -public class SocialLoginTest extends AbstractKeycloakTest { +@KeycloakIntegrationTest +public class SocialLoginTest { + + @InjectRealm + ManagedRealm managedRealm; + + @InjectRunOnServer + RunOnServerClient runOnServer; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; public static final String SOCIAL_CONFIG = "social.config"; public static final String REALM = "social"; @@ -110,10 +131,10 @@ public class SocialLoginTest extends AbstractKeycloakTest { private static final Properties config = new Properties(); - @Page + @InjectPage private LoginPage loginPage; - @Page + @InjectPage private UpdateAccount updateAccountPage; public enum Provider { @@ -173,13 +194,13 @@ public static void loadConfig() throws Exception { config.load(new FileInputStream(System.getProperty(SOCIAL_CONFIG))); } - @Before + @BeforeEach public void beforeSocialLoginTest() { oauth.realm(REALM); createAppClientInRealm(REALM); } - @After + @AfterEach public void afterSocialLoginTest() { currentTestProvider = null; oauth.realm("test"); @@ -586,7 +607,7 @@ private AccessTokenResponse checkFeature(int expectedStatusCode, String username Client httpClient = AdminClientUtil.createResteasyClient(); Response response = null; try { - testingClient.server(REALM).run(SocialLoginTest::setupClientExchangePermissions); + runOnServer.run(SocialLoginTest::setupClientExchangePermissions); WebTarget exchangeUrl = getExchangeUrl(httpClient); response = exchangeUrl.request() diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/UsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java similarity index 89% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/UsernameTemplateMapperTest.java rename to tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java index 737815042924..a09672b230db 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/UsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java @@ -1,4 +1,4 @@ -package org.keycloak.testsuite.broker; +package org.keycloak.tests.broker; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.RealmResource; @@ -10,15 +10,22 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; import static org.junit.jupiter.api.Assertions.assertEquals; +@KeycloakIntegrationTest public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest { + @InjectRealm + ManagedRealm managedRealm; + private String idpUserId; @Override @@ -26,12 +33,12 @@ protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; } - @Before + @BeforeEach public void addClients() { addClientsToProviderAndConsumer(); } - @Before + @BeforeEach public void addIdentityProviderToConsumerRealm() { log.debug("adding identity provider to realm " + bc.consumerRealmName()); @@ -47,7 +54,7 @@ public void addIdentityProviderToConsumerRealm() { } } - @Before + @BeforeEach public void createUserInIdp() { idpUserId = createUser(bc.providerRealmName(), bc.getUserLogin(), bc.getUserPassword(), "First", "Last", bc.getUserEmail()); } From 4421fd541a2beac0bf63ba0525cf84b0e41472f3 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 19 Aug 2026 14:34:13 +0200 Subject: [PATCH 3/9] Migrate broker package tests to the new testsuite Port broker tests from integration-arquillian into tests/base with compatibility shims in test-framework and tests/base utilities, and register migrated custom broker providers in tests/custom-providers. This removes legacy dependency assumptions while keeping migrated broker flows compiling under the new framework. Closes #50754 Signed-off-by: Erik Jan de Wit --- .../testframework/oauth/OAuthClient.java | 54 +- .../ui/page/AbstractLoginPage.java | 13 +- .../ui/page/LoginConfigTotpPage.java | 60 ++ .../testframework/ui/page/LoginPage.java | 34 + .../ui/page/LoginPasswordResetPage.java | 8 + .../ui/page/LoginPasswordUpdatePage.java | 4 + .../testframework/ui/page/PasswordPage.java | 14 + .../ui/page/SelectAuthenticatorPage.java | 5 + .../ui/webdriver/ManagedWebDriver.java | 23 + .../ui/webdriver/NavigateUtils.java | 12 + tests/base/pom.xml | 27 + .../ui/page/EnterRecoveryAuthnCodePage.java | 64 ++ .../ui/page/IdpConfirmLinkPage.java | 48 + .../ui/page/IdpConfirmOverrideLinkPage.java | 32 + .../ui/page/IdpLinkActionPage.java | 40 + .../ui/page/IdpLinkEmailPage.java | 39 + .../testframework/ui/page/LoginTotpPage.java | 62 ++ .../ui/page/UpdateAccountInformationPage.java | 31 + .../broker/AbstractAdvancedBrokerTest.java | 32 +- .../AbstractAdvancedRoleMapperTest.java | 8 +- .../tests/broker/AbstractBaseBrokerTest.java | 267 +++++- .../tests/broker/AbstractBrokerTest.java | 12 +- .../tests/broker/AbstractDefaultIdpTest.java | 6 +- .../broker/AbstractFirstBrokerLoginTest.java | 103 ++- .../broker/AbstractGroupBrokerMapperTest.java | 8 +- .../tests/broker/AbstractGroupMapperTest.java | 4 +- .../AbstractIdentityProviderMapperTest.java | 4 +- .../AbstractInitializedBaseBrokerTest.java | 24 +- .../AbstractKcOidcBrokerJWEEcdhEsTest.java | 2 +- .../AbstractKcOidcBrokerLogoutTest.java | 10 +- .../AbstractKcSamlEncryptedElementsTest.java | 6 +- .../AbstractKcSamlMetadataBrokerTest.java | 4 +- .../broker/AbstractNestedBrokerTest.java | 9 +- .../tests/broker/AbstractRoleMapperTest.java | 2 +- .../broker/AbstractSamlLoginHintTest.java | 10 +- .../AbstractUserAttributeMapperTest.java | 4 +- .../AbstractUsernameTemplateMapperTest.java | 8 +- .../tests/broker/AccountLinkTest.java | 52 +- .../broker/AttributeToRoleMapperTest.java | 8 +- .../tests/broker/BrokerConfiguration.java | 76 ++ .../tests/broker/BrokerRunOnServerUtil.java | 238 +++++ .../tests/broker/BrokerServerConfig.java | 13 + .../tests/broker/BrokerTestConstants.java | 86 ++ .../tests/broker/BrokerTestTools.java | 165 ++++ .../tests/broker/BrokerWithLegacyIdTest.java | 12 +- .../ExternalKeycloakRoleToRoleMapperTest.java | 8 +- .../tests/broker/HardcodedRoleMapperTest.java | 8 +- .../HardcodedUserAttributeMapperTest.java | 2 +- .../IdentityProviderStoreTokenV1Test.java | 2 +- .../IdentityProviderStoreTokenV2Test.java | 3 +- ...dpCreateUserIfUniqueAuthenticatorTest.java | 2 +- .../broker/JsonUserAttributeMapperTest.java | 8 +- .../tests/broker/KcCustomOidcBrokerTest.java | 8 +- .../broker/KcOIDCBrokerWithSignatureTest.java | 13 +- ...okenOnlyClaimsUserAttributeMapperTest.java | 2 +- .../broker/KcOidcBrokerAcrParameterTest.java | 8 +- ...KcOidcBrokerClientSecretBasicAuthTest.java | 8 +- ...rokerClientSecretJwtCustomSignAlgTest.java | 8 +- .../KcOidcBrokerClientSecretJwtTest.java | 8 +- ...erColonAliasClientSecretBasicAuthTest.java | 14 +- .../broker/KcOidcBrokerConfiguration.java | 290 ++++++ .../tests/broker/KcOidcBrokerEventTest.java | 8 +- .../broker/KcOidcBrokerFrontendUrlTest.java | 10 +- .../broker/KcOidcBrokerHiddenIdpHintTest.java | 16 +- .../tests/broker/KcOidcBrokerIdpHintTest.java | 10 +- .../broker/KcOidcBrokerIdpLinkActionTest.java | 15 +- ...cOidcBrokerIdpPublicKeyMissingUseTest.java | 12 +- ...erJWEEcdhEsA128KwP256A128CbcHs256Test.java | 2 +- ...cBrokerJWEEcdhEsA128KwP256A128GcmTest.java | 2 +- ...erJWEEcdhEsA192KwP384A192CbcHs384Test.java | 2 +- ...cBrokerJWEEcdhEsA192KwP384A192GcmTest.java | 2 +- ...erJWEEcdhEsA256KwP521A256CbcHs512Test.java | 2 +- ...cBrokerJWEEcdhEsA256KwP521A256GcmTest.java | 2 +- ...dcBrokerJWEEcdhEsP384A192CbcHs384Test.java | 2 +- .../KcOidcBrokerJWEEcdhEsP384A192GcmTest.java | 2 +- .../tests/broker/KcOidcBrokerJWETest.java | 8 +- ...idcBrokerJWEUserInfoJustEncryptedTest.java | 2 +- .../broker/KcOidcBrokerLdapReadOnlyTest.java | 10 +- .../tests/broker/KcOidcBrokerLdapTest.java | 6 +- .../broker/KcOidcBrokerLoginHintTest.java | 14 +- .../KcOidcBrokerLogoutFrontChannelTest.java | 31 +- .../tests/broker/KcOidcBrokerLogoutTest.java | 14 +- .../broker/KcOidcBrokerNoLoginHintTest.java | 18 +- .../KcOidcBrokerNonceParameterTest.java | 6 +- .../KcOidcBrokerParameterForwardTest.java | 14 +- .../broker/KcOidcBrokerPassMaxAgeTest.java | 14 +- .../tests/broker/KcOidcBrokerPkceTest.java | 2 +- ...okerPrivateKeyJwtClientIdRequiredTest.java | 10 +- ...BrokerPrivateKeyJwtCustomAudienceTest.java | 8 +- ...cBrokerPrivateKeyJwtCustomSignAlgTest.java | 12 +- ...OidcBrokerPrivateKeyJwtMissingUseTest.java | 10 +- .../broker/KcOidcBrokerPrivateKeyJwtTest.java | 8 +- ...BrokerPrivateKeyJwtUnsupportedKeyTest.java | 10 +- ...rokerPrivateKeyJwtWithX509HeadersTest.java | 8 +- .../KcOidcBrokerPromptNoneRedirectTest.java | 18 +- .../KcOidcBrokerPromptParameterTest.java | 8 +- .../KcOidcBrokerStateParameterTest.java | 12 +- ...KcOidcBrokerSubMatchIntrospectionTest.java | 8 +- .../tests/broker/KcOidcBrokerTest.java | 21 +- .../broker/KcOidcBrokerTokenExchangeTest.java | 9 +- .../KcOidcBrokerTransientSessionsTest.java | 29 +- .../KcOidcBrokerUiLocalesDisabledTest.java | 14 +- .../KcOidcBrokerUiLocalesEnabledTest.java | 14 +- .../KcOidcBrokerUiLocalesWithIdpHintTest.java | 14 +- .../KcOidcBrokerVaultConfiguration.java | 23 + .../tests/broker/KcOidcBrokerVaultTest.java | 2 +- .../broker/KcOidcBrokerWithConsentTest.java | 14 +- .../tests/broker/KcOidcDefaultIdpTest.java | 5 +- ...irstBrokerLoginDetectExistingUserTest.java | 2 +- .../KcOidcFirstBrokerLoginNewAuthTest.java | 12 +- .../broker/KcOidcFirstBrokerLoginTest.java | 31 +- .../broker/KcOidcMultipleTabsBrokerTest.java | 30 +- .../broker/KcOidcPostBrokerLoginTest.java | 16 +- .../KcOidcUsernameTemplateMapperTest.java | 6 +- ...amlAdvancedAttributeToGroupMapperTest.java | 4 +- ...SamlAdvancedAttributeToRoleMapperTest.java | 10 +- ...amlAttributeConsumingServiceIndexTest.java | 8 +- .../KcSamlBrokerAllowedClockSkewTest.java | 6 +- .../KcSamlBrokerArtifactBindingTest.java | 4 +- .../broker/KcSamlBrokerConfiguration.java | 292 ++++++ .../broker/KcSamlBrokerDestinationTest.java | 16 +- .../broker/KcSamlBrokerFrontendUrlTest.java | 19 +- ...BrokerLoginHintWithOptionDisabledTest.java | 6 +- ...lBrokerLoginHintWithOptionEnabledTest.java | 9 +- .../KcSamlBrokerSessionNotOnOrAfterTest.java | 12 +- .../tests/broker/KcSamlBrokerTest.java | 8 +- .../KcSamlCustomEntityIdBrokerTest.java | 8 +- .../tests/broker/KcSamlDefaultIdpTest.java | 5 +- .../broker/KcSamlEncryptedAssertionTest.java | 2 +- .../tests/broker/KcSamlEncryptedIdTest.java | 2 +- .../broker/KcSamlFirstBrokerLoginTest.java | 5 +- .../broker/KcSamlForceAuthnBrokerTest.java | 8 +- .../broker/KcSamlIdPInitiatedSsoTest.java | 50 +- .../tests/broker/KcSamlLogoutTest.java | 14 +- ...lMetadataSignedAndEncryptedBrokerTest.java | 8 +- .../KcSamlMetadataSignedBrokerTest.java | 4 +- ...amlMultipleAttributeToRoleMappersTest.java | 6 +- .../broker/KcSamlMultipleTabsBrokerTest.java | 16 +- .../broker/KcSamlParseIdPDescriptorTest.java | 15 +- ...KcSamlRequestedAuthnContextBrokerTest.java | 6 +- .../tests/broker/KcSamlSignedBrokerTest.java | 17 +- .../KcSamlSignedDocumentOnlyBrokerTest.java | 10 +- .../tests/broker/KcSamlSpDescriptorTest.java | 17 +- .../KcSamlUsernameTemplateMapperTest.java | 4 +- .../KcSamlXPathAttributeMapperTest.java | 8 +- .../broker/NestedBrokerConfiguration.java | 15 + .../tests/broker/OAuth2BrokerTest.java | 10 +- ...okenOnlyClaimsUserAttributeMapperTest.java | 4 +- .../OidcAdvancedClaimToGroupMapperTest.java | 8 +- .../OidcAdvancedClaimToRoleMapperTest.java | 2 +- ...cBackchannelLogoutBrokerConfiguration.java | 325 +++++++ .../broker/OidcClaimToRoleMapperTest.java | 8 +- .../OidcClaimToUserSessionNoteMapperTest.java | 4 +- .../broker/OidcHardcodedGroupMapperTest.java | 6 +- .../OidcMultipleClaimToRoleMappersTest.java | 6 +- .../broker/OidcUserAttributeMapperTest.java | 8 +- .../OidcUserInfoClaimToRoleMapperTest.java | 8 +- .../SamlIdentityProviderStoreTokenV1Test.java | 2 +- .../SamlIdentityProviderStoreTokenV2Test.java | 3 +- .../broker/SamlUserAttributeMapperTest.java | 10 +- .../tests/broker/SocialLoginTest.java | 153 +++- .../broker/UsernameTemplateMapperTest.java | 10 +- ...lientIdRequiredJWTClientAuthenticator.java | 64 ++ .../broker/oidc/LegacyIdIdentityProvider.java | 41 + .../oidc/LegacyIdIdentityProviderFactory.java | 47 + ...stKeycloakOidcIdentityProviderFactory.java | 118 +++ .../TrustMaterialIdentityProviderTest.java | 3 +- .../tests/broker/util/SimpleHttpDefault.java | 52 ++ ...ssThroughFederatedUserStorageProvider.java | 170 ++++ ...ghFederatedUserStorageProviderFactory.java | 40 + .../testsuite/AbstractAuthenticationTest.java | 36 + .../org/keycloak/testsuite/ProfileAssume.java | 52 ++ .../org/keycloak/testsuite/admin/ApiUtil.java | 47 + .../org/keycloak/testsuite/admin/Users.java | 70 ++ .../arquillian/annotation/EnableFeature.java | 36 + .../arquillian/annotation/EnableFeatures.java | 16 + .../arquillian/annotation/EnableVault.java | 51 ++ .../annotation/SetDefaultProvider.java | 44 + .../UncaughtServerErrorExpected.java | 35 + .../testsuite/auth/page/AccountFields.java | 52 ++ .../testsuite/auth/page/AuthRealm.java | 10 + .../auth/page/login/UpdateAccount.java | 34 + .../client/KeycloakTestingClient.java | 240 +++++ .../resources/TestApplicationResource.java | 74 ++ .../TestApplicationResourceUrls.java | 68 ++ .../resources/TestExampleCompanyResource.java | 56 ++ .../TestOIDCEndpointsApplicationResource.java | 181 ++++ .../TestSamlApplicationResource.java | 54 ++ .../client/resources/TestingResource.java | 181 ++++ .../CompanyRepresentation.java | 51 ++ .../domainextension/jpa/Company.java | 66 ++ .../federation/ldap/LDAPTestContext.java | 70 ++ .../forms/RegisterWithUserProfileTest.java | 35 + .../testsuite/forms/VerifyProfileTest.java | 17 + .../keycloak/testsuite/page/AbstractPage.java | 113 +++ .../page/AbstractPageWithInjectedUrl.java | 46 + .../page/AbstractPatternFlyAlert.java | 104 +++ .../org/keycloak/testsuite/page/Form.java | 67 ++ .../pages/social/AbstractSocialLoginPage.java | 40 + .../pages/social/BitbucketLoginPage.java | 68 ++ .../pages/social/FacebookLoginPage.java | 64 ++ .../pages/social/GitHubLoginPage.java | 58 ++ .../pages/social/GitLabLoginPage.java | 56 ++ .../pages/social/GoogleLoginPage.java | 62 ++ .../pages/social/InstagramLoginPage.java | 80 ++ .../pages/social/LinkedInLoginPage.java | 55 ++ .../pages/social/MicrosoftLoginPage.java | 79 ++ .../pages/social/OpenShiftLoginPage.java | 81 ++ .../pages/social/PayPalLoginPage.java | 71 ++ .../pages/social/StackOverflowLoginPage.java | 48 + .../pages/social/TwitterConsentLoginPage.java | 47 + .../pages/social/TwitterLoginPage.java | 68 ++ .../TestAuthenticationChannelRequest.java | 54 ++ .../updaters/ClientAttributeUpdater.java | 187 ++++ .../keycloak/testsuite/updaters/Creator.java | 195 ++++ .../IdentityProviderAttributeUpdater.java | 61 ++ .../updaters/IdentityProviderCreator.java | 66 ++ .../OrganizationAttributeUpdater.java | 37 + .../updaters/ProtocolMappersUpdater.java | 93 ++ .../updaters/RealmAttributeUpdater.java | 253 +++++ .../testsuite/updaters/RoleScopeUpdater.java | 76 ++ .../updaters/ServerResourceUpdater.java | 151 +++ .../testsuite/updaters/SetSystemProperty.java | 60 ++ .../updaters/UserAttributeUpdater.java | 138 +++ .../testsuite/util/AdminClientUtil.java | 180 ++++ .../testsuite/util/BrowserDriverUtil.java | 20 + .../testsuite/util/BrowserTabUtil.java | 151 +++ .../keycloak/testsuite/util/DroneUtils.java | 47 + .../testsuite/util/HttpClientUtils.java | 46 + .../util/InfinispanTestTimeServiceRule.java | 43 + .../org/keycloak/testsuite/util/KeyUtils.java | 170 ++++ .../org/keycloak/testsuite/util/LDAPRule.java | 359 ++++++++ .../testsuite/util/LDAPTestConfiguration.java | 175 ++++ .../keycloak/testsuite/util/MailAssert.java | 76 ++ .../org/keycloak/testsuite/util/Matchers.java | 176 ++++ .../testsuite/util/MutualTLSUtils.java | 25 + .../keycloak/testsuite/util/ReverseProxy.java | 43 + .../keycloak/testsuite/util/SamlClient.java | 861 ++++++++++++++++++ .../testsuite/util/SamlClientBuilder.java | 298 ++++++ .../keycloak/testsuite/util/SamlStreams.java | 58 ++ .../keycloak/testsuite/util/SamlUtils.java | 56 ++ .../keycloak/testsuite/util/ServerURLs.java | 66 ++ .../org/keycloak/testsuite/util/SqlUtils.java | 125 +++ .../testsuite/util/SystemInfoHelper.java | 53 ++ .../testsuite/util/TestAppHelper.java | 82 ++ .../testsuite/util/TextFileChecker.java | 80 ++ .../testsuite/util/TokenSignatureUtil.java | 22 + .../org/keycloak/testsuite/util/UIUtils.java | 332 +++++++ .../org/keycloak/testsuite/util/URLUtils.java | 160 ++++ .../keycloak/testsuite/util/VaultUtils.java | 35 + .../keycloak/testsuite/util/WaitUtils.java | 252 +++++ .../matchers/HttpResponseBodyMatcher.java | 67 ++ .../HttpResponseStatusCodeMatcher.java | 62 ++ .../util/matchers/ResponseBodyMatcher.java | 53 ++ .../util/matchers/ResponseHeaderMatcher.java | 49 + .../matchers/ResponseStatusCodeMatcher.java | 47 + .../SamlLogoutRequestTypeMatcher.java | 49 + .../matchers/SamlResponseTypeMatcher.java | 52 ++ .../SamlStatusResponseTypeMatcher.java | 70 ++ .../CreateArtifactMessageStepBuilder.java | 84 ++ .../saml/CreateAuthnRequestStepBuilder.java | 184 ++++ .../saml/CreateLogoutRequestStepBuilder.java | 152 ++++ .../util/saml/HandleArtifactStepBuilder.java | 250 +++++ .../util/saml/IdPInitiatedLoginBuilder.java | 73 ++ .../testsuite/util/saml/LoginBuilder.java | 174 ++++ .../saml/ModifySamlResponseStepBuilder.java | 295 ++++++ .../testsuite/util/saml/OtpLoginBuilder.java | 94 ++ .../util/saml/RequiredConsentBuilder.java | 127 +++ ...amlBackchannelArtifactResolveReceiver.java | 153 ++++ .../saml/SamlBackchannelLogoutReceiver.java | 124 +++ .../util/saml/SamlDocumentStepBuilder.java | 197 ++++ .../util/saml/SamlMessageReceiver.java | 83 ++ .../util/saml/SessionStateChecker.java | 173 ++++ .../testsuite/util/saml/StepWithCheckers.java | 10 + .../util/saml/UpdateProfileBuilder.java | 146 +++ .../DeploymentArchiveProcessorUtils.java | 185 ++++ .../keycloak/testsuite/utils/io/IOUtil.java | 351 +++++++ ...lientIdRequiredJWTClientAuthenticator.java | 46 + .../broker/oidc/LegacyIdIdentityProvider.java | 22 + .../oidc/LegacyIdIdentityProviderFactory.java | 27 + ...stKeycloakOidcIdentityProviderFactory.java | 101 ++ ...ssThroughFederatedUserStorageProvider.java | 170 ++++ ...ghFederatedUserStorageProviderFactory.java | 40 + ....authentication.ClientAuthenticatorFactory | 1 + ...ak.broker.provider.IdentityProviderFactory | 4 +- .../keycloak/test/migration/TestRewrite.java | 4 +- .../tests/utils/admin/AdminApiUtil.java | 4 + 287 files changed, 15564 insertions(+), 735 deletions(-) create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/EnterRecoveryAuthnCodePage.java create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmLinkPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmOverrideLinkPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkActionPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkEmailPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/LoginTotpPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/BrokerConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/BrokerRunOnServerUtil.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/BrokerServerConfig.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestConstants.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/NestedBrokerConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProvider.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProviderFactory.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/util/SimpleHttpDefault.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthenticationTest.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/ProfileAssume.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/admin/Users.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeature.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeatures.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableVault.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/SetDefaultProvider.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/UncaughtServerErrorExpected.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/auth/page/AccountFields.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/auth/page/AuthRealm.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/KeycloakTestingClient.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestSamlApplicationResource.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestingResource.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/domainextension/jpa/Company.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPatternFlyAlert.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/page/Form.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/BitbucketLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitLabLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/InstagramLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/OpenShiftLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/PayPalLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterConsentLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/rest/representation/TestAuthenticationChannelRequest.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/Creator.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderCreator.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/OrganizationAttributeUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/ProtocolMappersUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/RoleScopeUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/ServerResourceUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/SetSystemProperty.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/updaters/UserAttributeUpdater.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/BrowserDriverUtil.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/BrowserTabUtil.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/HttpClientUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/InfinispanTestTimeServiceRule.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/LDAPRule.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/SamlClientBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/SamlStreams.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/SamlUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/ServerURLs.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/SqlUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/SystemInfoHelper.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/TestAppHelper.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/TextFileChecker.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/TokenSignatureUtil.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/UIUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/URLUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/VaultUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/WaitUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseBodyMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseHeaderMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseStatusCodeMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateArtifactMessageStepBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateAuthnRequestStepBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateLogoutRequestStepBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/HandleArtifactStepBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/IdPInitiatedLoginBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/OtpLoginBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelArtifactResolveReceiver.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelLogoutReceiver.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlMessageReceiver.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/SessionStateChecker.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/StepWithCheckers.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java create mode 100644 tests/base/src/test/java/org/keycloak/testsuite/utils/io/IOUtil.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProvider.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProviderFactory.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java create mode 100644 tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory diff --git a/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java b/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java index 8851ec90b96b..42e60f07decf 100644 --- a/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java +++ b/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java @@ -19,6 +19,15 @@ */ public class OAuthClient extends AbstractOAuthClient { + public static String SERVER_ROOT; + public static String AUTH_SERVER_ROOT; + public static String APP_ROOT; + public static String APP_AUTH_ROOT; + + static { + updateURLs(defaultServerRoot()); + } + private final ManagedWebDriver managedWebDriver; private final ClientResource clientResource; @@ -27,14 +36,51 @@ public OAuthClient(String baseUrl, CloseableHttpClient httpClient, ManagedWebDri this.managedWebDriver = managedWebDriver; this.clientResource = clientResource; - config = new OAuthClientConfig() - .responseType(OAuth2Constants.CODE); + init(); } public OAuthClient(String baseUrl, CloseableHttpClient httpClient, ManagedWebDriver managedWebDriver) { this(baseUrl, httpClient, managedWebDriver, null); } + private static String defaultServerRoot() { + boolean ssl = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required", "true")); + String scheme = ssl ? "https" : "http"; + String host = System.getProperty("auth.server.host", "localhost"); + String port = ssl ? System.getProperty("auth.server.https.port", "8543") : System.getProperty("auth.server.http.port", "8180"); + return removeDefaultPorts(String.format("%s://%s:%s", scheme, host, port)); + } + + private static String removeDefaultPorts(String url) { + return url != null + ? url.replaceFirst("(.*)(:80)(\\/.*)?$", "$1$3").replaceFirst("(.*)(:443)(\\/.*)?$", "$1$3") + : null; + } + + public static void updateURLs(String serverRoot) { + SERVER_ROOT = removeDefaultPorts(serverRoot); + AUTH_SERVER_ROOT = SERVER_ROOT + "/auth"; + updateAppRootRealm("master"); + } + + public static void updateAppRootRealm(String realm) { + APP_ROOT = AUTH_SERVER_ROOT + "/realms/" + realm + "/app"; + APP_AUTH_ROOT = APP_ROOT + "/auth"; + } + + public static void resetAppRootRealm() { + updateAppRootRealm("master"); + } + + public void init() { + config = new OAuthClientConfig() + .realm("test") + .client("test-app", "password") + .redirectUri(APP_ROOT + "/auth") + .postLogoutRedirectUri(APP_ROOT + "/auth") + .responseType(OAuth2Constants.CODE); + } + @Override public void fillLoginForm(String username, String password) { LoginPage loginPage = new LoginPage(managedWebDriver); @@ -63,6 +109,10 @@ public ClientResource clientResource() { return clientResource; } + public org.openqa.selenium.WebDriver getDriver() { + return managedWebDriver.driver(); + } + public void close() { if (clientResource != null) { clientResource.remove(); diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java index 3943ef5546a2..87bafb581126 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java @@ -86,7 +86,18 @@ public String getAttemptedUsername() { public Optional getInfoMessage() { try { - return Optional.of(loginInfoMessage.getText()); + String text = loginInfoMessage.getText(); + if (text != null && !text.isBlank()) { + return Optional.of(text); + } + } catch (NoSuchElementException e) { + // fall through to legacy/base theme selectors + } + + try { + WebElement instruction = driver.driver().findElement(By.cssSelector("#instruction1, p.instruction")); + String text = instruction.getText(); + return (text == null || text.isBlank()) ? Optional.empty() : Optional.of(text); } catch (NoSuchElementException e) { return Optional.empty(); } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java index e23ea9557649..b43895a234f3 100755 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java @@ -61,10 +61,70 @@ public LoginConfigTotpPage(ManagedWebDriver driver) { super(driver); } + public void configure(String totp) { + totpInput.clear(); + if (totp != null) { + totpInput.sendKeys(totp); + } + submit(); + } + + public void configure(String totp, String userLabel) { + totpInput.clear(); + if (totp != null) { + totpInput.sendKeys(totp); + } + totpLabelInput.clear(); + if (userLabel != null) { + totpLabelInput.sendKeys(userLabel); + } + submit(); + } + + public void submit() { + submitButton.click(); + } + + public void cancel() { + cancelAIAButton.click(); + } + public String getTotpSecret() { return totpSecret.getAttribute("value"); } + public void clickManual() { + manualLink.click(); + } + + public void clickBarcode() { + barcodeLink.click(); + } + + public String getInputCodeError() { + try { + return totpInputCodeError.getText(); + } catch (NoSuchElementException e) { + return null; + } + } + + public String getInputLabelError() { + try { + return totpInputLabelError.getText(); + } catch (NoSuchElementException e) { + return null; + } + } + + public String getAlertError() { + try { + return loginAlertErrorMessage.getText(); + } catch (NoSuchElementException e) { + return null; + } + } + public boolean isCancelDisplayed() { try { return cancelAIAButton.isDisplayed(); diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java index 292a396020df..e30a949b0a0d 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java @@ -54,6 +54,16 @@ public void fillPassword(String password) { passwordInput.sendKeys(password); } + public void login(String username, String password) { + fillLogin(username, password); + submit(); + } + + public void login(String password) { + fillPassword(password); + submit(); + } + public void submit() { submitButton.click(); } @@ -145,6 +155,30 @@ public boolean isRememberMeCheckboxPresent() { } } + public boolean isRegisterLinkPresent() { + return !driver.driver().findElements(By.linkText("Register")).isEmpty(); + } + + public boolean isUsernameInputEnabled() { + return isUsernameInputPresent() && usernameInput.isEnabled(); + } + + public String getInputError() { + String usernameError = getUsernameInputError(); + if (usernameError != null && !usernameError.isBlank()) { + return usernameError; + } + return getPasswordInputError().orElse(null); + } + + public String getError() { + return getErrorMessage().orElse(null); + } + + public String getInstruction() { + return getInfoMessage().orElse(null); + } + public boolean isSwitchOrganizationPresent() { return !driver.driver().findElements(By.id("switch-organization")).isEmpty(); } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java index 8c4dc911d9df..72a3af9217a3 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java @@ -30,6 +30,14 @@ public void changePassword(String username) { submitButton.click(); } + public void changePassword() { + submitButton.click(); + } + + public String getUsername() { + return usernameInput.getAttribute("value"); + } + public void backToLogin() { backToLogin.click(); } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java index 73531c0edac3..6499931a78c5 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java @@ -55,6 +55,10 @@ public void changePassword(String newPassword, String passwordConfirm) { submitButton.click(); } + public void updatePasswords(String newPassword, String passwordConfirm) { + changePassword(newPassword, passwordConfirm); + } + public void cancel() { cancelAIAButton.click(); } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/PasswordPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/PasswordPage.java index 7c654145f3d4..a72f719e2718 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/PasswordPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/PasswordPage.java @@ -2,6 +2,7 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.junit.jupiter.api.Assertions; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -44,6 +45,11 @@ public void submit() { submitButton.click(); } + public void login(String password) { + fillPassword(password); + submit(); + } + public String getPassword() { return passwordInput.getAttribute("value"); } @@ -68,6 +74,14 @@ public void clickTryAnotherWayLink() { tryAnotherWayLink.click(); } + public void assertTryAnotherWayLinkAvailability(boolean expectedAvailability) { + try { + Assertions.assertEquals(expectedAvailability, tryAnotherWayLink.isDisplayed()); + } catch (NoSuchElementException e) { + Assertions.assertFalse(expectedAvailability); + } + } + @Override public String getExpectedPageId() { return "login-login-password"; diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/SelectAuthenticatorPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/SelectAuthenticatorPage.java index 96942d680330..9459e88347ff 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/SelectAuthenticatorPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/SelectAuthenticatorPage.java @@ -17,6 +17,7 @@ public class SelectAuthenticatorPage extends AbstractLoginPage { // Corresponds to the PasswordForm public static final String PASSWORD = "Password"; + public static final String AUTHENTICATOR_APPLICATION = "Authenticator Application"; // Corresponds to the WebAuthn authenticators public static final String SECURITY_KEY = "Passkey"; @@ -46,6 +47,10 @@ public String getLoginMethodHelpText(String loginMethodName) { return getLoginMethodRowByName(loginMethodName).findElement(By.className("select-auth-box-desc")).getText(); } + public List getAvailableLoginMethods() { + return getLoginMethodsRows().stream().map(this::getLoginMethodNameFromRow).toList(); + } + private List getLoginMethodsRows() { return driver.driver().findElements(By.className("select-auth-box-parent")); diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java index 82a62e6acdbd..fb66e7d78144 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java @@ -13,6 +13,8 @@ public class ManagedWebDriver extends ManagedTestResource { + private static final ThreadLocal CURRENT_DRIVER = new ThreadLocal<>(); + private WebDriver driver; private AssertionUtils assertionUtils = new AssertionUtils(this); @@ -24,6 +26,7 @@ public class ManagedWebDriver extends ManagedTestResource { public ManagedWebDriver(WebDriver driver) { this.driver = driver; + CURRENT_DRIVER.set(driver); this.tabUtils = new BrowserTabUtils(this); } @@ -31,6 +34,10 @@ public WebDriver driver() { return driver; } + public static WebDriver currentDriver() { + return CURRENT_DRIVER.get(); + } + public BrowserType getBrowserType() { if (driver instanceof HtmlUnitDriver) { return BrowserType.HTML_UNIT; @@ -46,10 +53,26 @@ public String getCurrentUrl() { return driver.getCurrentUrl(); } + public String getTitle() { + return driver.getTitle(); + } + + public String getPageSource() { + return driver.getPageSource(); + } + + public WebDriver.Options manage() { + return driver.manage(); + } + public WebElement findElement(By by) { return driver.findElement(by); } + public java.util.List findElements(By by) { + return driver.findElements(by); + } + public void open(String url) { driver.navigate().to(url); } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java index 1aa69ac5c0da..2c27a8780fb4 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java @@ -14,6 +14,18 @@ public void refresh() { driver.driver().navigate().refresh(); } + public static void to(String url) { + ManagedWebDriver.currentDriver().navigate().to(url); + } + + public static void back() { + ManagedWebDriver.currentDriver().navigate().back(); + } + + public static void forward() { + ManagedWebDriver.currentDriver().navigate().forward(); + } + public void backWithRefresh(AbstractPage expectedPage) { driver.driver().navigate().back(); diff --git a/tests/base/pom.xml b/tests/base/pom.xml index fe56d6a9e680..637e4fe259e7 100755 --- a/tests/base/pom.xml +++ b/tests/base/pom.xml @@ -153,6 +153,33 @@ keycloak-model-jpa test + + org.keycloak + keycloak-util-embedded-ldap + test + + + org.apache.ant + ant + 1.10.11 + test + + + org.keycloak + keycloak-saml-adapter-core + test + + + jakarta.xml.ws + jakarta.xml.ws-api + test + + + org.jsoup + jsoup + 1.23.1 + test + diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/EnterRecoveryAuthnCodePage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/EnterRecoveryAuthnCodePage.java new file mode 100644 index 000000000000..4b1e574b8724 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/EnterRecoveryAuthnCodePage.java @@ -0,0 +1,64 @@ +package org.keycloak.testframework.ui.page; + + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * Signing In Page with required action "Enter Backup Code for authentication" + * + * @author Venkata Nukala + */ +public class EnterRecoveryAuthnCodePage extends AbstractLoginPage { + + @FindBy(xpath = "//label[@for='recoveryCodeInput']") + private WebElement recoveryAuthnCodeLabel; + + @FindBy(id = "recoveryCodeInput") + private WebElement recoveryAuthnCodeTextField; + + @FindBy(id = "kc-login") + private WebElement signInButton; + + public EnterRecoveryAuthnCodePage(ManagedWebDriver driver) { + super(driver); + } + + public int getRecoveryAuthnCodeToEnterNumber() { + String [] recoveryAuthnCodeLabelParts = recoveryAuthnCodeLabel.getText().split("#"); + return Integer.parseInt(recoveryAuthnCodeLabelParts[1]) - 1; // Recovery Authn Code 1 is at element 0 in the list + } + + public void enterRecoveryAuthnCode(String recoveryCode) { + recoveryAuthnCodeTextField.sendKeys(recoveryCode); + } + + public void clickSignInButton() { + signInButton.click(); + } + + public void waitUntilReloaded() { + driver.waiting().until((WebDriver d) -> { + try { + return recoveryAuthnCodeTextField.getAttribute("value").isEmpty() && !getFeedbackText().isEmpty(); + } catch (StaleElementReferenceException | NoSuchElementException expected) { + return false; + } + }); + } + + @Override + public String getExpectedPageId() { + return "login-login-recovery-authn-code-input"; + } + + public String getFeedbackText() { + return driver.findElement(By.className("kc-feedback-text")).getText().trim(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmLinkPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmLinkPage.java new file mode 100644 index 000000000000..3fb11fa89663 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmLinkPage.java @@ -0,0 +1,48 @@ +package org.keycloak.testframework.ui.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class IdpConfirmLinkPage extends AbstractLoginPage { + + @FindBy(id = "updateProfile") + private WebElement updateProfileButton; + + @FindBy(id = "linkAccount") + private WebElement linkAccountButton; + + @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']") + private WebElement message; + + public IdpConfirmLinkPage(ManagedWebDriver driver) { + super(driver); + } + + @Override + public String getExpectedPageId() { + return "login-login-idp-link-confirm"; + } + + public String getMessage() { + return message.getText(); + } + + public void clickReviewProfile() { + updateProfileButton.click(); + } + + public boolean isReviewProfileDisplayed() { + try { + return updateProfileButton.isDisplayed(); + } catch (NoSuchElementException e) { + return false; + } + } + + public void clickLinkAccount() { + linkAccountButton.click(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmOverrideLinkPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmOverrideLinkPage.java new file mode 100644 index 000000000000..3ea5c7760070 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpConfirmOverrideLinkPage.java @@ -0,0 +1,32 @@ +package org.keycloak.testframework.ui.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class IdpConfirmOverrideLinkPage extends AbstractLoginPage { + + @FindBy(id = "confirmOverride") + private WebElement confirmOverrideButton; + + @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']") + private WebElement message; + + public IdpConfirmOverrideLinkPage(ManagedWebDriver driver) { + super(driver); + } + + @Override + public String getExpectedPageId() { + return "login-login-idp-link-confirm-override"; + } + + public String getMessage() { + return message.getText(); + } + + public void clickConfirmOverride() { + confirmOverrideButton.click(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkActionPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkActionPage.java new file mode 100644 index 000000000000..5c760d598614 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkActionPage.java @@ -0,0 +1,40 @@ +package org.keycloak.testframework.ui.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class IdpLinkActionPage extends AbstractLoginPage { + + @FindBy(id = "kc-continue") + private WebElement submitButton; + + @FindBy(id = "kc-cancel") + private WebElement cancelButton; + + @FindBy(id = "kc-link-text") + private WebElement message; + + public IdpLinkActionPage(ManagedWebDriver driver) { + super(driver); + } + + @Override + public String getExpectedPageId() { + return "login-link-idp-action"; + } + + public void confirm() { + submitButton.click(); + } + + public void cancel() { + cancelButton.click(); + } + + public void assertIdpInMessage(String expectedIdpDisplayName) { + Assertions.assertEquals("Do you want to link your account with " + expectedIdpDisplayName + "?", message.getText()); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkEmailPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkEmailPage.java new file mode 100644 index 000000000000..af2fa49d1ac6 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/IdpLinkEmailPage.java @@ -0,0 +1,39 @@ +package org.keycloak.testframework.ui.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class IdpLinkEmailPage extends AbstractLoginPage { + + @FindBy(id = "instruction1") + private WebElement message; + + @FindBy(xpath = "//p[@id='instruction2']/a[text() = 'Click here']") + private WebElement resendEmailLink; + + @FindBy(xpath = "//p[@id='instruction3']/a[text() = 'Click here']") + private WebElement continueLink; + + public IdpLinkEmailPage(ManagedWebDriver driver) { + super(driver); + } + + @Override + public String getExpectedPageId() { + return "login-login-idp-link-email"; + } + + public String getMessage() { + return message.getText(); + } + + public void resendEmail() { + resendEmailLink.click(); + } + + public void continueLink() { + continueLink.click(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/LoginTotpPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/LoginTotpPage.java new file mode 100644 index 000000000000..97c0f3011116 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/LoginTotpPage.java @@ -0,0 +1,62 @@ +package org.keycloak.testframework.ui.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class LoginTotpPage extends AbstractLoginPage { + + @FindBy(id = "otp") + private WebElement otpInput; + + @FindBy(css = "[type=\"submit\"]") + private WebElement submitButton; + + @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']") + private WebElement loginErrorMessage; + + @FindBy(id = "input-error-otp") + private WebElement totpInputCodeError; + + @FindBy(id = "input-error-otp-code") + private WebElement otpInputCodeError; + + public LoginTotpPage(ManagedWebDriver driver) { + super(driver); + } + + public void login(String totp) { + otpInput.clear(); + if (totp != null) { + otpInput.sendKeys(totp); + } + submitButton.click(); + } + + public String getAlertError() { + try { + return loginErrorMessage.getText(); + } catch (NoSuchElementException e) { + return null; + } + } + + public String getInputError() { + try { + return totpInputCodeError.getText(); + } catch (NoSuchElementException e) { + try { + return otpInputCodeError.getText(); + } catch (NoSuchElementException ex) { + return null; + } + } + } + + @Override + public String getExpectedPageId() { + return "login-login-otp"; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java new file mode 100644 index 000000000000..9456f81d1a00 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java @@ -0,0 +1,31 @@ +package org.keycloak.testframework.ui.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +public class UpdateAccountInformationPage extends LoginUpdateProfilePage { + + public UpdateAccountInformationPage(ManagedWebDriver driver) { + super(driver); + } + + public void updateAccountInformation(String userName, String email, String firstName, String lastName) { + prepareUpdate().username(userName).email(email).firstName(firstName).lastName(lastName).submit(); + } + + public void updateAccountInformation(String userName, String email, String firstName, String lastName, String department) { + prepareUpdate().username(userName).email(email).firstName(firstName).lastName(lastName).department(department).submit(); + } + + public void updateAccountInformation(String email, String firstName, String lastName) { + prepareUpdate().email(email).firstName(firstName).lastName(lastName).submit(); + } + + public void updateAccountInformation(String firstName, String lastName) { + prepareUpdate().firstName(firstName).lastName(lastName).submit(); + } + + @Override + public String getExpectedPageId() { + return "login-idp-review-user-profile"; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java index 7fcb214143b6..bd4a101889a8 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java @@ -36,23 +36,21 @@ import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; +import org.keycloak.tests.providers.federation.DummyUserFederationProviderFactory; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.TestAppHelper; import org.keycloak.testsuite.util.WaitUtils; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.TimeoutException; -import static org.keycloak.testsuite.admin.AdminApiUtil.removeUserByUsername; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.disablePostBrokerLoginFlow; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForElementEnabled; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.disablePostBrokerLoginFlow; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForElementEnabled; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.utils.admin.AdminApiUtil.removeUserByUsername; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; @@ -74,7 +72,7 @@ * - Disabled user * - etc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest { @InjectRealm @@ -114,7 +112,7 @@ protected void createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode protected abstract void createAdditionalMapperWithCustomSyncMode(IdentityProviderMapperSyncMode syncMode); /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testAccountManagementLinkIdentity + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testAccountManagementLinkIdentity */ @Test public void testAccountManagementLinkIdentity() { @@ -173,7 +171,7 @@ public void testAccountManagementLinkIdentity() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testAccountManagementLinkedIdentityAlreadyExists + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testAccountManagementLinkedIdentityAlreadyExists */ @Test public void testAccountManagementLinkedIdentityAlreadyExists() { @@ -285,7 +283,7 @@ public void loginWithExistingUserWithErrorFromProviderIdP() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest.testDisabledUser + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest.testDisabledUser */ @Test public void testDisabledUser() { @@ -455,7 +453,7 @@ public void testExpiredCode() { } /** - * Refers to in old testsuite: org.keycloak.testsuite.broker.PostBrokerFlowTest#testPostBrokerLoginWithOTP() + * Refers to in old testsuite: org.keycloak.tests.broker.PostBrokerFlowTest#testPostBrokerLoginWithOTP() */ @Test public void testPostBrokerLoginFlowWithOTP() { @@ -503,7 +501,7 @@ public void testPostBrokerLoginFlowWithOTP() { } /** - * Refers to in old testsuite: org.keycloak.testsuite.broker.OIDCKeyCloakServerBrokerBasicTest#testLogoutWorksWithTokenTimeout() + * Refers to in old testsuite: org.keycloak.tests.broker.OIDCKeyCloakServerBrokerBasicTest#testLogoutWorksWithTokenTimeout() */ @Test public void testLogoutWorksWithTokenTimeout() { @@ -542,7 +540,7 @@ public void testLogoutWorksWithTokenTimeout() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testWithLinkedFederationProvider + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testWithLinkedFederationProvider */ @Test public void testWithLinkedFederationProvider() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java index 2951239c7c10..3f9240db3380 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedRoleMapperTest.java @@ -5,13 +5,13 @@ import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -22,7 +22,7 @@ * Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractAdvancedRoleMapperTest extends AbstractRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java index fdd7a564e533..11014669a1b3 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java @@ -18,14 +18,24 @@ package org.keycloak.tests.broker; import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilderException; +import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.util.Retry; import org.keycloak.models.utils.TimeBasedOTP; @@ -34,14 +44,15 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.resources.RealmsResource; +import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; -import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testframework.ui.page.ErrorPage; import org.keycloak.testframework.ui.page.IdpConfirmLinkPage; import org.keycloak.testframework.ui.page.IdpConfirmOverrideLinkPage; @@ -57,27 +68,29 @@ import org.keycloak.testframework.ui.page.ProceedPage; import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testframework.ui.page.VerifyEmailPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import org.keycloak.testsuite.util.oauth.LogoutUrlBuilder; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.testsuite.util.userprofile.UserProfileUtil; import org.hamcrest.Matchers; -import org.keycloak.testframework.ui.annotations.InjectPage; +import org.jboss.logging.Logger; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.openqa.selenium.By; import org.openqa.selenium.TimeoutException; - -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; -import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.openqa.selenium.support.ui.WebDriverWait; + +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.encodeUrl; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.resetUserPassword; import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; import static org.keycloak.testsuite.util.ServerURLs.removeDefaultPorts; @@ -87,18 +100,29 @@ /** * No test methods there. Just some useful common functionality */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractBaseBrokerTest { + protected final Logger log = Logger.getLogger(getClass()); + @InjectRealm ManagedRealm managedRealm; + @InjectAdminClient + Keycloak adminClient; + @InjectWebDriver ManagedWebDriver driver; @InjectOAuthClient OAuthClient oauth; + protected KeycloakTestingClient testingClient; + protected final CompatibilitySuiteContext suiteContext = new CompatibilitySuiteContext(); + protected final CompatibilityTestContext testContext = new CompatibilityTestContext(); + protected final AtomicInteger timeOffSet = new AtomicInteger(); + private final Map cleanups = new HashMap<>(); + protected static final String ATTRIBUTE_VALUE = "attribute.value"; @InjectPage @@ -158,11 +182,23 @@ public abstract class AbstractBaseBrokerTest { */ protected abstract BrokerConfiguration getBrokerConfiguration(); - @Override public void addTestRealms(List testRealms) { } + protected void importRealm(RealmRepresentation realmRepresentation) { + try { + adminClient.realm(realmRepresentation.getRealm()).remove(); + } catch (NotFoundException ignored) { + // expected if realm does not exist yet + } + adminClient.realms().create(realmRepresentation); + } + + protected void deleteAllCookiesForRealm(String realm) { + driver.driver().manage().deleteAllCookies(); + } + protected void configureSMTPServer() { RealmResource realm = adminClient.realm(bc.consumerRealmName()); RealmRepresentation master = realm.toRepresentation(); @@ -200,6 +236,14 @@ protected void addClientsToProviderAndConsumer() { @BeforeEach public void beforeBrokerTest() { + if (testingClient != null) { + testingClient.close(); + } + String authServerRoot = resolveAuthServerContextRoot(); + testingClient = KeycloakTestingClient.getInstance(authServerRoot); + OAuthClient.updateURLs(authServerRoot); + oauth.init(); + RealmRepresentation consumerRealm = bc.createConsumerRealm(); RealmRepresentation providerRealm = bc.createProviderRealm(); importRealm(consumerRealm); @@ -211,6 +255,12 @@ public void beforeBrokerTest() { @AfterEach public void cleanupUsers() { + cleanups.values().forEach(cleanup -> cleanup.execute(adminClient)); + cleanups.clear(); + if (testingClient != null) { + testingClient.close(); + testingClient = null; + } deleteAllCookiesForRealm(bc.consumerRealmName()); adminClient.realm(bc.consumerRealmName()).remove(); adminClient.realm(bc.providerRealmName()).remove(); @@ -227,6 +277,50 @@ protected String createUser(String username) { return createUser(username, USER_EMAIL); } + public String createUser(String realm, String username, String password, String... requiredActions) { + UserRepresentation user = UserBuilder.create().username(username).enabled(true).build(); + user.setRequiredActions(Arrays.asList(requiredActions)); + String createdUserId = createUserWithAdminClient(adminClient.realm(realm), user); + resetUserPassword(adminClient.realm(realm).users().get(createdUserId), password, false); + return createdUserId; + } + + public String createUser(String realm, String username, String password, String firstName) { + return createUser(realm, username, password, firstName, null, null); + } + + public String createUser(String realm, String username, String password, String firstName, String lastName) { + return createUser(realm, username, password, firstName, lastName, null); + } + + protected String createUser(String realm, String username, String password, String firstName, String lastName, String email) { + UserRepresentation newUser = UserBuilder.create() + .username(username) + .email(email) + .firstName(firstName) + .lastName(lastName) + .enabled(true) + .build(); + String createdUserId = createUserWithAdminClient(adminClient.realm(realm), newUser); + resetUserPassword(adminClient.realm(realm).users().get(createdUserId), password, false); + return createdUserId; + } + + public String createUser(String realm, String username, String password, String firstName, String lastName, String email, + Consumer customizer) { + UserRepresentation newUser = UserBuilder.create() + .username(username) + .email(email) + .firstName(firstName) + .lastName(lastName) + .enabled(true) + .build(); + customizer.accept(newUser); + String createdUserId = createUserWithAdminClient(adminClient.realm(realm), newUser); + resetUserPassword(adminClient.realm(realm).users().get(createdUserId), password, false); + return createdUserId; + } + protected void assertNumFederatedIdentities(String userId, int expected) { assertEquals(expected, adminClient.realm(bc.consumerRealmName()).users().get(userId).getFederatedIdentity().size()); @@ -268,9 +362,20 @@ protected void logInWithIdp(String idpAlias, String username, String password) { waitForPage(driver, "sign in to", true); log.debug("Clicking social " + idpAlias); loginPage.clickSocial(idpAlias); - waitForPage(driver, "sign in to", true); - log.debug("Logging in"); - loginPage.login(username, password); + new WebDriverWait(driver.driver(), Duration.ofSeconds(10)).until(webDriver -> + !webDriver.findElements(By.id("username")).isEmpty() + || !webDriver.findElements(By.id("password")).isEmpty() + || webDriver.getCurrentUrl().contains("/broker/" + idpAlias + "/endpoint") + || webDriver.getCurrentUrl().contains("/login-actions/first-broker-login")); + if (loginPage.isUsernameInputPresent()) { + log.debug("Logging in with username and password"); + loginPage.login(username, password); + } else if (loginPage.isPasswordInputPresent()) { + log.debug("Logging in with password-only form"); + loginPage.login(password); + } else { + log.debugf("No login form present after social redirect (URL: %s). Continuing.", driver.getCurrentUrl()); + } } protected AuthorizationEndpointResponse doLoginSocial(OAuthClient oauth, String brokerId, String username, String password) { @@ -462,4 +567,132 @@ protected URI getSamlEndpoint(String fromUri, String realm) { .protocolUrl(UriBuilder.fromUri(fromUri).path("auth")) .build(realm, SamlProtocol.LOGIN_PROTOCOL); } + + public org.keycloak.admin.client.resource.RealmsResource realmsResouce() { + return adminClient.realms(); + } + + public URI getAuthServerRoot() { + return URI.create(resolveAuthServerContextRoot() + "/auth/"); + } + + protected TestCleanupSupport getCleanup(String realmName) { + return cleanups.computeIfAbsent(realmName, TestCleanupSupport::new); + } + + protected TestCleanupSupport getCleanup() { + return getCleanup("test"); + } + + protected void setOtpTimeOffset(int offsetSeconds, TimeBasedOTP otp) { + timeOffSet.set(offsetSeconds); + java.util.Calendar calendar = java.util.Calendar.getInstance(); + calendar.add(java.util.Calendar.SECOND, offsetSeconds); + otp.setCalendar(calendar); + } + + public Logger getLogger() { + return log; + } + + protected String resolveAuthServerContextRoot() { + if (managedRealm != null && managedRealm.getBaseUrl() != null) { + int realmsIndex = managedRealm.getBaseUrl().indexOf("/realms/"); + if (realmsIndex > 0) { + String root = managedRealm.getBaseUrl().substring(0, realmsIndex); + return root.endsWith("/auth") ? root.substring(0, root.length() - "/auth".length()) : root; + } + } + if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { + return OAuthClient.SERVER_ROOT.replaceAll("/+$", ""); + } + return getAuthServerContextRoot(); + } + + protected static class CompatibilityTestContext { + + private boolean initialized; + + public boolean isInitialized() { + return initialized; + } + + public void setInitialized(boolean initialized) { + this.initialized = initialized; + } + } + + protected class CompatibilitySuiteContext { + + private final AuthServerInfo authServerInfo = new AuthServerInfo(); + + public Map getSmtpServer() { + Map smtp = new HashMap<>(); + smtp.put("host", "localhost"); + smtp.put("port", "3025"); + smtp.put("from", "auto@keycloak.org"); + return smtp; + } + + public AuthServerInfo getAuthServerInfo() { + return authServerInfo; + } + } + + protected class AuthServerInfo { + public URI getContextRoot() { + try { + return new URI(resolveAuthServerContextRoot()); + } catch (URISyntaxException e) { + throw new IllegalStateException("Invalid auth server context root", e); + } + } + } + + protected static class TestCleanupSupport { + + private final String realmName; + private final List cleanups = new ArrayList<>(); + private final List userIds = new ArrayList<>(); + private final List componentIds = new ArrayList<>(); + + private TestCleanupSupport(String realmName) { + this.realmName = realmName; + } + + public TestCleanupSupport addCleanup(AutoCloseable closeable) { + cleanups.add(() -> { + try { + closeable.close(); + } catch (Exception ignored) { + } + }); + return this; + } + + public void addUserId(String userId) { + userIds.add(userId); + } + + public void addComponentId(String componentId) { + componentIds.add(componentId); + } + + private void execute(Keycloak adminClient) { + RealmResource realm = adminClient.realm(realmName); + cleanups.forEach(Runnable::run); + for (String userId : userIds) { + try { + realm.users().get(userId).remove(); + } catch (Exception ignored) { + } + } + for (String componentId : componentIds) { + try { + realm.components().component(componentId).remove(); + } catch (Exception ignored) { + } + } + } + } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java index 72f6b5a9fe85..154205f4752a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java @@ -27,18 +27,18 @@ import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; -import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testframework.ui.page.ConsentPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.AccountHelper; -import org.keycloak.testframework.ui.annotations.InjectPage; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import static org.keycloak.models.utils.DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -52,7 +52,7 @@ * Contains just few basic tests. This is good class to override if you're testing custom IDP configuration and you need * to verify if login with IDP works as expected */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractBrokerTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java index bb092fbdb24c..abcdaaa6a595 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractDefaultIdpTest.java @@ -41,13 +41,13 @@ import org.keycloak.testsuite.util.FlowUtil; import org.keycloak.testsuite.util.UIUtils; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -56,7 +56,7 @@ /** * Test for scenarios where "Identity Provider Authenticator" is set with "default identity provider" directly redirecting to provider realm */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractDefaultIdpTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java index 98e8a354fe46..61d0d57637ef 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractFirstBrokerLoginTest.java @@ -31,6 +31,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; import org.keycloak.testframework.events.Events; +import org.keycloak.testframework.mail.MailServer; import org.keycloak.testframework.mail.annotations.InjectMailServer; import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; @@ -38,40 +39,35 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.page.LoginPasswordUpdatePage; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.admin.AdminApiUtil; -import org.keycloak.testsuite.federation.UserMapStorageFactory; -import org.keycloak.testsuite.pages.LoginPasswordUpdatePage; +import org.keycloak.tests.providers.federation.UserMapStorageFactory; +import org.keycloak.tests.utils.admin.AdminApiUtil; import org.keycloak.testsuite.util.AccountHelper; -import org.keycloak.testframework.mail.MailServer; import org.keycloak.testsuite.util.MailServerConfiguration; -import org.keycloak.testsuite.util.SecondBrowser; import org.keycloak.userprofile.UserProfileContext; import com.google.common.collect.ImmutableMap; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.jboss.arquillian.drone.api.annotation.Drone; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.PageFactory; import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED; -import static org.keycloak.testsuite.admin.AdminApiUtil.removeUserByUsername; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.assertHardCodedSessionNote; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configureAutoLinkFlow; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configureConfirmOverrideLinkFlow; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.grantReadTokenRole; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.assertHardCodedSessionNote; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configureAutoLinkFlow; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configureConfirmOverrideLinkFlow; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.grantReadTokenRole; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.utils.admin.AdminApiUtil.removeUserByUsername; import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl; import static org.hamcrest.MatcherAssert.assertThat; @@ -85,7 +81,7 @@ * * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBaseBrokerTest { @InjectRealm @@ -109,8 +105,11 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa @InjectMailServer MailServer mail; + @InjectPage + LoginPasswordUpdatePage passwordUpdatePage; + /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOn + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOn */ @Test public void testErrorExistingUserWithUpdateProfile() { @@ -136,7 +135,7 @@ public void testErrorExistingUserWithUpdateProfile() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthenticationWithPassword + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthenticationWithPassword */ @Test public void testLinkAccountByReauthenticationWithPassword() { @@ -154,7 +153,7 @@ public void testLinkAccountByReauthenticationWithPassword() { assertEquals("User with email user@localhost.com already exists. How do you want to continue?", idpConfirmLinkPage.getMessage()); idpConfirmLinkPage.clickLinkAccount(); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -190,7 +189,7 @@ public void testLinkAccountByReauthenticationWithUsernameAndPassword() { assertEquals("User with email user@localhost.com already exists. How do you want to continue?", idpConfirmLinkPage.getMessage()); idpConfirmLinkPage.clickLinkAccount(); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -222,7 +221,7 @@ public void testLinkAccountByReauthenticationNoExistingUser() { logInWithBroker(bc); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -253,7 +252,7 @@ public void testLinkAccountByReauthenticationWithWrongPassword() { logInWithBroker(bc); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -295,7 +294,7 @@ public void testLinkAccountByReauthenticationResetPassword() { assertEquals("User with email user@localhost.com already exists. How do you want to continue?", idpConfirmLinkPage.getMessage()); idpConfirmLinkPage.clickLinkAccount(); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -325,7 +324,7 @@ public void testLinkAccountByReauthenticationResetPasswordNoExistingUser() { logInWithBroker(bc); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -341,7 +340,7 @@ public void testLinkAccountByReauthenticationResetPasswordNoExistingUser() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthenticationWithPassword_browserButtons + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthenticationWithPassword_browserButtons */ @Test public void testLinkAccountByLogInAsUserUsingBrowserButtons() { @@ -399,7 +398,7 @@ public void testLinkAccountByLogInAsUserUsingBrowserButtons() { assertEquals("consumer", loginPage.getUsername()); assertTrue(loginPage.isUsernameInputEnabled()); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), this.loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), this.loginPage.getInstruction()); try { loginPage.findSocialButton(bc.getIDPAlias()); @@ -464,7 +463,7 @@ public void testUpdateEmailReviewProfileLinkingAccount() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthentication_forgetPassword + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthentication_forgetPassword */ @Test public void testLinkAccountByLogInAsUserAfterResettingPassword() throws InterruptedException { @@ -545,7 +544,7 @@ public void testResetPasswordDuringFirstBrokerFlowWithoutExistingUserAuthenticat logInWithBroker(bc); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -578,7 +577,7 @@ public void testResetPasswordDuringFirstBrokerFlowWithoutExistingUserAuthenticat /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthentication_forgetPassword_differentBrowser + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthentication_forgetPassword_differentBrowser */ @Test public void testLinkAccountByLogInAsUserAfterResettingPasswordUsingDifferentBrowsers() throws InterruptedException { @@ -625,7 +624,7 @@ public void testLinkAccountByLogInAsUserAfterResettingPasswordUsingDifferentBrow removeSMTPConfiguration(realm); // Need to update password now - LoginPasswordUpdatePage passwordUpdatePage = PageFactory.initElements(driver2, LoginPasswordUpdatePage.class); + LoginPasswordUpdatePage passwordUpdatePage = PageFactory.initElements(driver2.driver(), LoginPasswordUpdatePage.class); passwordUpdatePage.changePassword("password", "password"); assertNumFederatedIdentities(existingUser, 0); @@ -650,7 +649,7 @@ public void testLinkAccountByLogInAsUserAfterResettingPasswordUsingDifferentBrow /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOff + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOff */ @Test public void testUserExistsFirstBrokerLoginFlowUpdateProfileOff() { @@ -674,7 +673,7 @@ public void testUserExistsFirstBrokerLoginFlowUpdateProfileOff() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOff + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOff */ @Test public void testUserExistsFirstBrokerLoginFlowUpdateProfileOn() { @@ -701,7 +700,7 @@ public void testUserExistsFirstBrokerLoginFlowUpdateProfileOn() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testRegistrationWithPasswordUpdateRequired + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testRegistrationWithPasswordUpdateRequired */ @Test public void testRequiredUpdatedPassword() { @@ -721,14 +720,14 @@ public void testRequiredUpdatedPassword() { updateAccountInformationPage.updateAccountInformation("FirstName", "LastName"); waitForPage(driver, "update password", false); - updatePasswordPage.updatePasswords("password", "password"); + passwordUpdatePage.updatePasswords("password", "password"); Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testFixDuplicationsByReviewProfile + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testFixDuplicationsByReviewProfile */ @Test public void testFixDuplicationsByReviewProfile() { @@ -777,7 +776,7 @@ public void testFixDuplicationsByReviewProfile() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testHardcodedUserSessionNoteIsSetAfterFristBrokerLogin() + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testHardcodedUserSessionNoteIsSetAfterFristBrokerLogin() */ @Test public void testHardcodedUserSessionNoteIsSetAfterFirstBrokerLogin() { @@ -815,8 +814,8 @@ public void testHardcodedUserSessionNoteIsSetAfterFirstBrokerLogin() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testRegistrationWithEmailAsUsername - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testRegistrationWithEmailAsUsername + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() */ @Test public void testRequiredRegistrationEmailAsUserName() { @@ -938,7 +937,7 @@ public void testLinkAccountModifyingEmailLinkingByEmailNotAllowed() { idpConfirmLinkPage.clickLinkAccount(); //it should start the link using username and password as email has been changed - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); loginPage.login("password"); Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); @@ -977,7 +976,7 @@ public void testLinkAccountReviewDisabled() throws Exception { idpConfirmLinkPage.clickLinkAccount(); //linking the account using password as email not configured - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); loginPage.login("password"); Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); @@ -986,7 +985,7 @@ public void testLinkAccountReviewDisabled() throws Exception { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled */ @Test public void testLinkAccountWithUntrustedEmailVerified() { @@ -1024,7 +1023,7 @@ public void testLinkAccountWithUntrustedEmailVerified() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled * */ @Test @@ -1058,7 +1057,7 @@ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_em /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled */ @Test public void testVerifyEmailNotRequiredActionWhenEmailIsTrustedByProvider() { @@ -1093,7 +1092,7 @@ public void testVerifyEmailNotRequiredActionWhenEmailIsTrustedByProvider() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest#testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin */ @Test public void testVerifyEmailRequiredActionWhenChangingEmailDuringFirstLogin() { @@ -1137,7 +1136,7 @@ public void testVerifyEmailRequiredActionWhenChangingEmailDuringFirstLogin() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByEmailVerificationTwice + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testLinkAccountByEmailVerificationTwice */ @Test public void testLinkAccountByEmailVerificationTwice() { @@ -1284,7 +1283,7 @@ public void testLinkAccountByEmailVerificationToEmailVerifiedUser() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByEmailVerificationResendEmail + * Refers to in old test suite: org.keycloak.tests.broker.AbstractFirstBrokerLoginTest#testLinkAccountByEmailVerificationResendEmail */ @Test public void testLinkAccountByEmailVerificationResendEmail() { @@ -1509,7 +1508,7 @@ protected EventRepresentation getFirstConsumerEvent() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest.testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest.testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail */ @Test public void testUpdateProfileIfMissingInformation() { @@ -1595,7 +1594,7 @@ public void testUpdateProfileIfMissingInformation() { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest.testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest.testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing */ @Test public void testUpdateProfileIfNotMissingInformation() { @@ -1623,7 +1622,7 @@ public void testUpdateProfileIfNotMissingInformation() { /** - * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest.testSuccessfulAuthenticationWithoutUpdateProfile + * Refers to in old test suite: org.keycloak.tests.broker.AbstractKeycloakIdentityProviderTest.testSuccessfulAuthenticationWithoutUpdateProfile */ @Test public void testWithoutUpdateProfile() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java index 1a05f54fcd1b..d6d00600a2f3 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupBrokerMapperTest.java @@ -24,17 +24,17 @@ import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractGroupBrokerMapperTest extends AbstractGroupMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java index dab9eefe105a..232cd7d8fb57 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractGroupMapperTest.java @@ -20,8 +20,8 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -33,7 +33,7 @@ * @author Artur Baltabayev, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractGroupMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java index 7c52c9e8b1ba..a97b116fd777 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractIdentityProviderMapperTest.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.BeforeEach; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserAndResetPasswordWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserAndResetPasswordWithAdminClient; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -29,7 +29,7 @@ * Benjamin Weimer, * Martin Idel, */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractIdentityProviderMapperTest extends AbstractBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java index 4649eab6b6b7..08e3e1faa58f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java @@ -18,25 +18,27 @@ import java.util.function.BiConsumer; +import jakarta.ws.rs.core.Response; + import org.keycloak.admin.client.resource.AuthenticationManagementResource; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.BeforeEach; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; -import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; +import org.junit.jupiter.api.BeforeEach; + +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.resetUserPassword; /** * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractInitializedBaseBrokerTest extends AbstractBaseBrokerTest { @InjectRealm @@ -66,7 +68,17 @@ public void beforeBrokerTest() { log.debug("adding identity provider to realm " + bc.consumerRealmName()); RealmResource realm = adminClient.realm(bc.consumerRealmName()); - realm.identityProviders().create(bc.setUpIdentityProvider()).close(); + Response response = realm.identityProviders().create(bc.setUpIdentityProvider()); + if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) { + String error = null; + try { + error = response.readEntity(String.class); + } catch (Exception ignored) { + } + throw new IllegalStateException("Failed to add identity provider " + bc.getIDPAlias() + " to realm " + bc.consumerRealmName() + + ": " + response.getStatus() + (error != null && !error.isBlank() ? " - " + error : "")); + } + response.close(); identityProviderResource = realm.identityProviders().get(bc.getIDPAlias()); addClientsToProviderAndConsumer(); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java index 7046fa657993..33ca960599ca 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerJWEEcdhEsTest.java @@ -23,7 +23,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractKcOidcBrokerJWEEcdhEsTest extends KcOidcBrokerJWETest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java index 168f2f1dfcd0..2c30a0c4848a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcOidcBrokerLogoutTest.java @@ -2,16 +2,16 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.BeforeEach; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; -import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; +import org.junit.jupiter.api.BeforeEach; + +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.resetUserPassword; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractKcOidcBrokerLogoutTest extends AbstractBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java index 9e697d8f7a4a..bf58ab43e902 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlEncryptedElementsTest.java @@ -49,8 +49,8 @@ import org.junit.jupiter.api.Test; import org.w3c.dom.Document; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.saml.AbstractSamlTest.SAML_CLIENT_ID_SALES_POST; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.saml.AbstractSamlTest.SAML_CLIENT_ID_SALES_POST; import static org.keycloak.testsuite.util.Matchers.isSamlResponse; import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; @@ -59,7 +59,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractKcSamlEncryptedElementsTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java index a9ab357b845b..e042d8014b52 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractKcSamlMetadataBrokerTest.java @@ -35,7 +35,7 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; import org.keycloak.testsuite.util.saml.SamlDocumentStepBuilder; @@ -54,7 +54,7 @@ * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractKcSamlMetadataBrokerTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java index 9d1b7b51ba19..c7205ecb353c 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractNestedBrokerTest.java @@ -1,16 +1,17 @@ package org.keycloak.tests.broker; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractNestedBrokerTest extends AbstractBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java index bf8a0f70a302..c100e14556d1 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractRoleMapperTest.java @@ -44,7 +44,7 @@ * Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractRoleMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java index 5081b57c02db..37210ba5eaa7 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractSamlLoginHintTest.java @@ -1,9 +1,6 @@ package org.keycloak.tests.broker; import org.keycloak.protocol.oidc.OIDCLoginProtocol; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -11,17 +8,20 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebElement; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Test of various scenarios related to the use of login hint */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractSamlLoginHintTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java index 996a96d28371..c638e45d7092 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUserAttributeMapperTest.java @@ -21,7 +21,7 @@ import com.google.common.collect.ImmutableSet; import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; +import static org.keycloak.tests.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -33,7 +33,7 @@ * * @author hmlnarik */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractUserAttributeMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java index fbe5dbe77415..2ec77d6f3c89 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractUsernameTemplateMapperTest.java @@ -14,11 +14,11 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -26,7 +26,7 @@ /** * @author Martin Idel, */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public abstract class AbstractUsernameTemplateMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java index d6680cfab832..3e63710ffe0d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java @@ -21,6 +21,7 @@ import jakarta.ws.rs.core.Response; +import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UsersResource; @@ -34,28 +35,32 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.storage.StorageId; import org.keycloak.storage.UserStorageProvider; +import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.realm.FederatedIdentityBuilder; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.providers.runonserver.RunOnServer; -import org.keycloak.testframework.util.ApiUtil; -import org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProvider; -import org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory; -import org.keycloak.testsuite.federation.UserMapStorageFactory; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.page.LoginPage; import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; +import org.keycloak.testframework.util.ApiUtil; +import org.keycloak.tests.providers.federation.PassThroughFederatedUserStorageProvider; +import org.keycloak.tests.providers.federation.PassThroughFederatedUserStorageProviderFactory; +import org.keycloak.tests.providers.federation.UserMapStorageFactory; +import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.util.AccountHelper; -import org.keycloak.testframework.ui.annotations.InjectPage; -import org.junit.jupiter.api.BeforeEach; import org.junit.Ignore; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserAndResetPasswordWithAdminClient; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserAndResetPasswordWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -66,11 +71,18 @@ * @author Bill Burke * @version $Revision: 1 $ */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class AccountLinkTest { @InjectRealm ManagedRealm managedRealm; + + @InjectAdminClient + Keycloak adminClient; + + private KeycloakTestingClient testingClient; + private final AbstractBaseBrokerTest.CompatibilityTestContext testContext = new AbstractBaseBrokerTest.CompatibilityTestContext(); + public static final String CHILD_IDP = "child"; public static final String PARENT_IDP = "parent-idp"; public static final String PARENT_USERNAME = "parent"; @@ -81,7 +93,6 @@ public class AccountLinkTest { @InjectPage protected LoginPage loginPage; - @Override public void addTestRealms(List testRealms) { RealmRepresentation realm = new RealmRepresentation(); realm.setRealm(CHILD_IDP); @@ -136,6 +147,25 @@ public void createParentChild() { BrokerTestTools.createKcOidcBroker(adminClient, CHILD_IDP, PARENT_IDP); } + private KeycloakTestingClient getTestingClient() { + if (testingClient == null) { + String serverUrl; + if (managedRealm != null && managedRealm.getBaseUrl() != null) { + int realmsIndex = managedRealm.getBaseUrl().indexOf("/realms/"); + serverUrl = realmsIndex > 0 ? managedRealm.getBaseUrl().substring(0, realmsIndex) : managedRealm.getBaseUrl(); + if (serverUrl.endsWith("/auth")) { + serverUrl = serverUrl.substring(0, serverUrl.length() - "/auth".length()); + } + } else if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { + serverUrl = OAuthClient.SERVER_ROOT.replaceAll("/+$", ""); + } else { + serverUrl = getAuthServerContextRoot(); + } + testingClient = KeycloakTestingClient.getInstance(serverUrl); + } + return testingClient; + } + @Test public void testAccountLink() { String childUsername = "child"; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java index b34b1b89e0cd..39ecb6171047 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AttributeToRoleMapperTest.java @@ -9,13 +9,13 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY; @@ -23,7 +23,7 @@ /** * @author Martin Idel */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class AttributeToRoleMapperTest extends AbstractRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerConfiguration.java new file mode 100644 index 000000000000..0aea74286e24 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerConfiguration.java @@ -0,0 +1,76 @@ +package org.keycloak.tests.broker; + +import java.util.List; + +import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; + +/** + * + * @author hmlnarik + */ +public interface BrokerConfiguration { + + /** + * @return Representation of the realm at the identity provider side. + */ + RealmRepresentation createProviderRealm(); + + /** + * @return Representation of the realm at the broker side. + */ + RealmRepresentation createConsumerRealm(); + + List createProviderClients(); + + List createConsumerClients(); + + /** + * @return Representation of the identity provider for declaration in the broker + */ + default IdentityProviderRepresentation setUpIdentityProvider() { + return setUpIdentityProvider(IdentityProviderSyncMode.IMPORT); + } + + /** + * @return Representation of the identity provider for declaration in the broker + */ + IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode force); + + /** + * @return Name of realm containing identity provider. Must be consistent with {@link #createProviderRealm()} + */ + String providerRealmName(); + + /** + * @return Realm name of the broker. Must be consistent with {@link #createConsumerRealm()} + */ + String consumerRealmName(); + + /** + * @return Client ID of the identity provider as set in provider realm. + */ + String getIDPClientIdInProviderRealm(); + + /** + * @return User login name of the brokered user + */ + String getUserLogin(); + + /** + * @return Password of the brokered user + */ + String getUserPassword(); + + /** + * @return E-mail of the brokered user + */ + String getUserEmail(); + + /** + * @return Alias of the identity provider as defined in the broker realm + */ + String getIDPAlias(); +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerRunOnServerUtil.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerRunOnServerUtil.java new file mode 100644 index 000000000000..11e9a5800b47 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerRunOnServerUtil.java @@ -0,0 +1,238 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.tests.broker; + +import org.keycloak.authentication.authenticators.broker.IdpAutoLinkAuthenticatorFactory; +import org.keycloak.authentication.authenticators.browser.OTPFormAuthenticatorFactory; +import org.keycloak.authentication.authenticators.browser.PasswordFormFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticationFlowModel; +import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.models.session.UserSessionPersisterProvider; +import org.keycloak.testframework.remote.providers.runonserver.RunOnServer; +import org.keycloak.testsuite.client.KeycloakTestingClient; +import org.keycloak.testsuite.util.FlowUtil; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Pedro Igor + */ +final class BrokerRunOnServerUtil { + + static RunOnServer configurePostBrokerLoginWithOTP(String idpAlias) { + return (session) -> { + RealmModel realm = session.getContext().getRealm(); + + // Add post-broker flow with OTP authenticator to the realm + AuthenticationFlowModel postBrokerFlow = new AuthenticationFlowModel(); + postBrokerFlow.setAlias("post-broker"); + postBrokerFlow.setDescription("post-broker flow with OTP"); + postBrokerFlow.setProviderId("basic-flow"); + postBrokerFlow.setTopLevel(true); + postBrokerFlow.setBuiltIn(false); + postBrokerFlow = realm.addAuthenticationFlow(postBrokerFlow); + + AuthenticationExecutionModel execution = new AuthenticationExecutionModel(); + execution.setParentFlow(postBrokerFlow.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator("auth-otp-form"); + execution.setPriority(20); + execution.setAuthenticatorFlow(false); + realm.addAuthenticatorExecution(execution); + + IdentityProviderModel idp = session.identityProviders().getByAlias(idpAlias); + idp.setPostBrokerLoginFlowId(postBrokerFlow.getId()); + session.identityProviders().update(idp); + }; + } + + static RunOnServer disablePostBrokerLoginFlow(String idpAlias) { + return session -> { + RealmModel realm = session.getContext().getRealm(); + + IdentityProviderModel idp = session.identityProviders().getByAlias(idpAlias); + idp.setPostBrokerLoginFlowId(null); + session.identityProviders().update(idp); + }; + } + + static RunOnServer grantReadTokenRole(String username) { + return session -> { + RealmModel realm = session.getContext().getRealm(); + ClientModel brokerClient = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID); + RoleModel readTokenRole = brokerClient.getRole(Constants.READ_TOKEN_ROLE); + UserModel user = session.users().getUserByUsername(realm, username); + user.grantRole(readTokenRole); + }; + } + + static RunOnServer revokeReadTokenRole(String username) { + return session -> { + RealmModel realm = session.getContext().getRealm(); + ClientModel brokerClient = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID); + RoleModel readTokenRole = brokerClient.getRole(Constants.READ_TOKEN_ROLE); + UserModel user = session.users().getUserByUsername(realm, username); + user.deleteRoleMapping(readTokenRole); + }; + } + + static RunOnServer configureAutoLinkFlow(String idpAlias) { + return (session -> { + RealmModel appRealm = session.getContext().getRealm(); + AuthenticationFlowModel newFlow = new AuthenticationFlowModel(); + newFlow.setAlias("AutoLink"); + newFlow.setDescription("AutoLink"); + newFlow.setProviderId("basic-flow"); + newFlow.setBuiltIn(false); + newFlow.setTopLevel(true); + newFlow = appRealm.addAuthenticationFlow(newFlow); + + AuthenticationExecutionModel execution = new AuthenticationExecutionModel(); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setAuthenticatorFlow(false); + execution.setAuthenticator("idp-create-user-if-unique"); + execution.setPriority(1); + execution.setParentFlow(newFlow.getId()); + execution = appRealm.addAuthenticatorExecution(execution); + + AuthenticationExecutionModel execution2 = new AuthenticationExecutionModel(); + execution2.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution2.setAuthenticatorFlow(false); + execution2.setAuthenticator("idp-auto-link"); + execution2.setPriority(2); + execution2.setParentFlow(newFlow.getId()); + execution2 = appRealm.addAuthenticatorExecution(execution2); + + IdentityProviderModel idp = session.identityProviders().getByAlias(idpAlias); + idp.setFirstBrokerLoginFlowId(newFlow.getId()); + session.identityProviders().update(idp); + }); + } + + static RunOnServer configureConfirmOverrideLinkFlow(String idpAlias) { + return session -> { + RealmModel appRealm = session.getContext().getRealm(); + + AuthenticationFlowModel newFlow = new AuthenticationFlowModel(); + newFlow.setAlias("ConfirmOverrideLinkFlow"); + newFlow.setDescription("ConfirmOverride"); + newFlow.setProviderId("basic-flow"); + newFlow.setBuiltIn(false); + newFlow.setTopLevel(true); + newFlow = appRealm.addAuthenticationFlow(newFlow); + + AuthenticationExecutionModel execution = new AuthenticationExecutionModel(); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticatorFlow(false); + execution.setAuthenticator("idp-detect-existing-broker-user"); + execution.setPriority(1); + execution.setParentFlow(newFlow.getId()); + appRealm.addAuthenticatorExecution(execution); + + execution = new AuthenticationExecutionModel(); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticatorFlow(false); + execution.setAuthenticator("idp-confirm-override-link"); + execution.setPriority(2); + execution.setParentFlow(newFlow.getId()); + appRealm.addAuthenticatorExecution(execution); + + execution = new AuthenticationExecutionModel(); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticatorFlow(false); + execution.setAuthenticator("idp-auto-link"); + execution.setPriority(3); + execution.setParentFlow(newFlow.getId()); + appRealm.addAuthenticatorExecution(execution); + + IdentityProviderModel idp = session.identityProviders().getByAlias(idpAlias); + idp.setFirstBrokerLoginFlowId(newFlow.getId()); + session.identityProviders().update(idp); + }; + } + + static RunOnServer assertHardCodedSessionNote() { + return (session) -> { + RealmModel realm = session.realms().getRealmByName("consumer"); + UserModel user = session.users().getUserByUsername(realm, "testuser"); + UserSessionModel sessions = session.sessions().getUserSessionsStream(realm, user).findFirst().get(); + assertEquals("sessionvalue", sessions.getNote("user-session-attr")); + }; + } + + static RunOnServer removeBrokerExpiredSessions() { + return session -> { + RealmModel realm = session.getContext().getRealm(); + session.getProvider(UserSessionPersisterProvider.class).removeExpired(realm); + }; + } + + + // Configure the variant of firstBrokerLogin flow, which will use PasswordForm instead of IdpUsernamePasswordForm. + // In other words, the form with password-only instead of username/password. + static void configureBrokerFlowToReAuthenticationWithPasswordForm(KeycloakTestingClient testingClient, String consumerRealmName, String idpAlias, String newFlowAlias) { + testingClient.server(consumerRealmName).run(session -> FlowUtil.inCurrentRealm(session).copyFirstBrokerLoginFlow(newFlowAlias)); + testingClient.server(consumerRealmName).run(session -> FlowUtil.inCurrentRealm(session) + .selectFlow(newFlowAlias) + .inVerifyExistingAccountByReAuthentication(subFlow -> subFlow + // Remove first execution (IdpUsernamePasswordForm) + .removeExecution(0) + // Edit new first execution (Conditional OTP Subflow) + .updateExecution(0, exec -> exec.setPriority(30)) + // Add AutoLink Authenticator as first (It will automatically setup user to authentication context) + .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, IdpAutoLinkAuthenticatorFactory.PROVIDER_ID, 10) + // Add PasswordForm execution + .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, PasswordFormFactory.PROVIDER_ID, 20) + ) + .usesInIdentityProvider(idpAlias) + ); + } + + + // Configure the variant of firstBrokerLogin flow, which will allow to reauthenticate user with password OR totp + // TOTP will be available just if configured for the user + static void configureBrokerFlowToReAuthenticationWithPasswordOrTotp(KeycloakTestingClient testingClient, String consumerRealmName, String idpAlias, String newFlowAlias) { + testingClient.server(consumerRealmName).run(session -> FlowUtil.inCurrentRealm(session).copyFirstBrokerLoginFlow(newFlowAlias)); + testingClient.server(consumerRealmName).run(session -> { + AuthenticationFlowModel flowModel = FlowUtil.createFlowModel("password or otp", "basic-flow", "Flow to authenticate user with password or otp", false, true); + FlowUtil.inCurrentRealm(session) + // Select new flow + .selectFlow(newFlowAlias) + .inVerifyExistingAccountByReAuthentication(flowUtil -> flowUtil + .clear() + // Add AutoLink Authenticator as first (It will automatically setup user to authentication context) + .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, IdpAutoLinkAuthenticatorFactory.PROVIDER_ID) + // Add "Password-or-OTP" subflow + .addSubFlowExecution(flowModel, AuthenticationExecutionModel.Requirement.REQUIRED, subFlow -> subFlow + // Add PasswordForm ALTERNATIVE execution + .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.ALTERNATIVE, PasswordFormFactory.PROVIDER_ID) + // Add OTPForm ALTERNATIVE execution + .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.ALTERNATIVE, OTPFormAuthenticatorFactory.PROVIDER_ID) + ) + ) + // Setup new FirstBrokerLogin to identity provider + .usesInIdentityProvider(idpAlias); + }); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerServerConfig.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerServerConfig.java new file mode 100644 index 000000000000..b1ef0832aebb --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerServerConfig.java @@ -0,0 +1,13 @@ +package org.keycloak.tests.broker; + +import org.keycloak.testframework.server.KeycloakServerConfig; +import org.keycloak.testframework.server.KeycloakServerConfigBuilder; + +public class BrokerServerConfig implements KeycloakServerConfig { + + @Override + public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder builder) { + return builder + .dependency("org.keycloak.tests", "keycloak-tests-custom-providers"); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestConstants.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestConstants.java new file mode 100644 index 000000000000..6cc25b452547 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestConstants.java @@ -0,0 +1,86 @@ +package org.keycloak.tests.broker; + +public class BrokerTestConstants { + + public final static String REALM_PROV_NAME = "provider"; + public final static String REALM_CONS_NAME = "consumer"; + + public final static String IDP_OIDC_ALIAS = "kc-oidc-idp"; + public final static String IDP_OIDC_PROVIDER_ID = "keycloak-oidc"; + + public final static String IDP_SAML_ALIAS = "kc-saml-idp"; + public final static String IDP_SAML_PROVIDER_ID = "saml"; + + public final static String CLIENT_ID = "brokerapp"; + public final static String CLIENT_SECRET = "secret"; + public final static String VAULT_CLIENT_SECRET = "${vault.oidc_idp}"; + + public final static String USER_LOGIN = "testuser"; + public final static String USER_EMAIL = "user@localhost.com"; + public final static String USER_PASSWORD = "password"; + + public final static String IDP_SAML_SIGN_KEY = "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygc" + + "DfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb4" + + "0tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SV" + + "Er55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3" + + "og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8" + + "ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKp" + + "r/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus" + + "7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiC" + + "KpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9" + + "BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzU" + + "alm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58c" + + "kAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZ" + + "V9wN85V0xZXWsw=="; + + public final static String IDP_SAML_SIGN_CERT = "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG" + + "9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDV" + + "QQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDg" + + "YDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4" + + "GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQ" + + "MA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93b" + + "jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKr" + + "GTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfm" + + "m2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1F" + + "Vqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNG" + + "txwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBr" + + "cAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFl" + + "cNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD" + + "0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8" + + "C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW" + + "7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8Uei" + + "KAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKm" + + "CV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin"; + + public final static String REALM_PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwg" + + "gSkAgEAAoIBAQCCPyvTTb14vSMkpe/pds2P5Cqxk7bkeFnQiNMS1vyZ+HS2O79fxzp1eA" + + "guHnBTs4XTRT7SZJhIT/6utgqZjmDigKV5N7X5ptq8BM/W1qa1cYBRip261pc+tWf3Iyw" + + "JYQ9yFI9mUQarmIEl0D7GH16NSZklheaWfbodRVarvX+ML0amNtGYVDft/RftYmgbKKrK" + + "218qQp9R4GZFtf/Q/RmboNXN7weMINU8GWVkTRrccKBIXSunT6zXGfuj3Wp1YpVq20BWw" + + "Y2OMM/P+yDAc7LKEO1LJqPBdT4r9BRn2lXiaga3AL24gTKZPKU/tu7uqfFciF+i4Rr58S" + + "MDNOzQcnklAgMBAAECggEAc0eibJYEO5d8QXW1kPgcHV2gBChv2mxDYnWYDLbIQSdNdfY" + + "P/qABt/MTmm5KkWr16fcCEYoD1w0mqFBrtVn1msSusUmEAYGTXJMNumOmjjX1kzaTQMmq" + + "eFBrwqwYz/xehWR5P+A7fSmwNV3KEeW19GvN5w5K96w0TLAQdFV3TQVPSytusDunwuR1y" + + "ltMe1voaEDZ9z0Pi08YiEk2f6xhj5CMkoiw3mNImzfruphHullxU4FD05fH6tDeJ38152" + + "7ILpAzDsgYZh4aFLKjUHem96bX4EL7FIzBJ6okgN78AZnUC/EaVfgFTw0qfhoWvZV4ruV" + + "XXiMhCg4CMMRDq/k9iQKBgQDBNWsJMT84OnnWmQoJmZogkFV+tsGrSK6Re+aJxLWpishh" + + "7dwAnT2OcagZvVdUb0FwNWu1D0B9/SKDDMRnnHBhOGDpH57m/eQdRU0oX1BD27xvffk0l" + + "LcfD4BTxnR5e9jss8K4twc9jf0P1rxC/loGJ2NtCH0BrPHgz54Ea+96ewKBgQCsk3JDaa" + + "PnFwzVYm2BXlhxOxLPsF4wvD2rIRAswZV4C5xebjand8nwiMmVpNd0PRLkEnkI+waURGv" + + "2EY/P3JsssoiY8Xqe8f/1G+SQKre7lbqOas8rFoALepC0BYDiZDFy0Z9ZnRAFzRI5sgIt" + + "7jpoMRD4xDNlmiV8X+yBxc3Y3wKBgQChDQsU1YUyNKQ8+sLAL9anEEkD4Ald4q8JPHN2I" + + "Y+gLLxNzT0XEfsu0pTiJ8805axxgUYv3e/PVYNAJBNPnrqaf6lgiegl+jr9Hzhqz9CTUA" + + "YqFaL2boSakoxQyNtsLI0s+cb1vDN/3uy0GDZDzcty18BsMagqDmRtFgNNAj/UIwKBgQC" + + "ahbeFBv0cOPZjxisY8Bou4N8aGehsqNBq/0LVYExuXa8YmoTTdJ3bgw9Er4G/ccQNdUDs" + + "uqAMeCtW/CiRzQ0ge4d1sprB4Rv3I4+HSsiS7SFKzfZLtWzXWlpg5qCdlWr1TR7qhYjIO" + + "PO9t1beO3YOvwhcRoliyyAPenBxTmTfbwKBgDtm2WJ5VlQgNpIdOs1CCiqd0DFmWOmvBP" + + "spPC1kySiy+Ndr9jNohRZkR7pEjgqA5E8rdzc88LirUN7bY5HFHRWN9KXrs5/o3O1K3GF" + + "Cp64N6nvnPEYZ2zSJalcMC2fjSsJg26z8Dg1H+gfTIDUMoGiEAAnJXuqk+WayPU+fZMLn"; + + public final static String REALM_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK" + + "CAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF0" + + "0U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlE" + + "Gq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmR" + + "bX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHO" + + "yyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQAB"; +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java new file mode 100644 index 000000000000..d909abbbac76 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java @@ -0,0 +1,165 @@ +package org.keycloak.tests.broker; + +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.keycloak.admin.client.Keycloak; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.apache.http.client.utils.URIBuilder; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.WebDriverWait; + +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; +import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; + +/** + * + * @author hmlnarik + */ +public class BrokerTestTools { + + public static String getProviderRoot() { + String host2 = System.getProperty("auth.server.host2"); + if (host2 == null || host2.isBlank()) { + return getConsumerRoot(); + } + return new URIBuilder(URI.create(getConsumerRoot())) + .setHost(host2) + .toString(); + } + + public static String getConsumerRoot() { + if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { + return OAuthClient.SERVER_ROOT; + } + return getAuthServerContextRoot(); + } + + public static String getAuthPath() { + String path = URI.create(getConsumerRoot()).getPath(); + if (path == null || path.isBlank() || "/".equals(path)) { + return ""; + } + return path.endsWith("/") ? path.substring(0, path.length() - 1) : path; + } + + public static IdentityProviderRepresentation createIdentityProvider(String alias, String providerId) { + IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation(); + + identityProviderRepresentation.setAlias(alias); + identityProviderRepresentation.setDisplayName(alias); + identityProviderRepresentation.setProviderId(providerId); + identityProviderRepresentation.setEnabled(true); + + return identityProviderRepresentation; + } + + public static void waitForPage(final WebDriver driver, final String title, final boolean isHtmlTitle) { + waitForPageToLoad(); + + WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5)); + ExpectedCondition condition = new ExpectedCondition() { + private String actualTitle = null; + + public Boolean apply(final WebDriver input) { + if (input == null) { + return false; + } + + actualTitle = isHtmlTitle ? input.getTitle() : input.findElement(By.id("kc-page-title")).getText(); + if (actualTitle == null) { + return false; + } + + return actualTitle.toLowerCase().contains(title.toLowerCase()); + } + + public String toString() { + return String.format("value to contain (ignoring case) \"%s\". Current value: \"%s\"", title, + this.actualTitle); + } + }; + + wait.until(condition); + } + + public static void waitForPage(final ManagedWebDriver driver, final String title, final boolean isHtmlTitle) { + waitForPage(driver.driver(), title, isHtmlTitle); + } + + public static void waitForElementEnabled(WebDriver driver, final String elementName) { + WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5)); + + ExpectedCondition condition = (WebDriver input) -> { + List elements = input.findElements(By.name(elementName)); + return (! elements.isEmpty()) && elements.get(0).isEnabled(); + }; + + wait.until(condition); + } + + public static void waitForElementEnabled(ManagedWebDriver driver, final String elementName) { + waitForElementEnabled(driver.driver(), elementName); + } + + public static String encodeUrl(String url) { + return URLEncoder.encode(url, StandardCharsets.UTF_8); + } + + /** + * Expects a child idp and parent idp running on same Keycloak instance. Links the two with non-signature checks. + * + * @param adminClient + * @param childRealm + * @param idpRealm + */ + public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm) { + createKcOidcBroker(adminClient, childRealm, idpRealm, idpRealm, false); + + + + } + + public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, String alias, boolean linkOnly) { + IdentityProviderRepresentation idp = createIdentityProvider(alias, IDP_OIDC_PROVIDER_ID); + idp.setLinkOnly(linkOnly); + idp.setStoreToken(true); + + Map config = idp.getConfig(); + + config.put("clientId", childRealm); + config.put("clientSecret", childRealm); + config.put("authorizationUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/auth"); + config.put("tokenUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/token"); + config.put("logoutUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/logout"); + config.put("userInfoUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/userinfo"); + config.put("backchannelSupported", "true"); + adminClient.realm(childRealm).identityProviders().create(idp); + + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(childRealm); + client.setName(childRealm); + client.setSecret(childRealm); + client.setEnabled(true); + + client.setRedirectUris(Collections.singletonList(getConsumerRoot() + + "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint/*")); + + client.setAdminUrl(getConsumerRoot() + + "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint"); + adminClient.realm(idpRealm).clients().create(client); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java index 8ce77ae59967..c721266f02d4 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerWithLegacyIdTest.java @@ -30,22 +30,22 @@ import org.keycloak.testframework.realm.FederatedIdentityBuilder; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; -import org.keycloak.testsuite.broker.oidc.LegacyIdIdentityProviderFactory; +import org.keycloak.tests.broker.oidc.LegacyIdIdentityProviderFactory; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.oidc.LegacyIdIdentityProvider.LEGACY_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.oidc.LegacyIdIdentityProvider.LEGACY_ID; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Vaclav Muzikar */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class BrokerWithLegacyIdTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java index 2ddc5717b3f5..c440258f97a4 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/ExternalKeycloakRoleToRoleMapperTest.java @@ -11,13 +11,13 @@ import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -27,7 +27,7 @@ * @author Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class ExternalKeycloakRoleToRoleMapperTest extends AbstractRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java index b35d19c953b1..b15037f0ff47 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedRoleMapperTest.java @@ -9,13 +9,13 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -25,7 +25,7 @@ * @author Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class HardcodedRoleMapperTest extends AbstractRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java index 0dc9e7ad9900..595cc6ba1a50 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/HardcodedUserAttributeMapperTest.java @@ -27,7 +27,7 @@ /** * Martin Idel, */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class HardcodedUserAttributeMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV1Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV1Test.java index e3a7eacbbdc4..c947c0534808 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV1Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV1Test.java @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Assertions; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class IdentityProviderStoreTokenV1Test implements InterfaceIdentityProviderStoreTokenV1Test, InterfaceOIDCIdentityProviderStoreTokenTest { @InjectRealm(config = IdpRealmConfig.class) diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV2Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV2Test.java index 41d9e59ee5fe..5aec460b559f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV2Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/IdentityProviderStoreTokenV2Test.java @@ -204,7 +204,8 @@ public void testClientPoliciesWithIDPAndScopeCondition() { static class IdentityBrokeringAPIV2ServerConfig implements KeycloakServerConfig { @Override public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { - return config.features(Profile.Feature.IDENTITY_BROKERING_API_V2); + return config.features(Profile.Feature.IDENTITY_BROKERING_API_V2) + .dependency("org.keycloak.tests", "keycloak-tests-custom-providers"); } } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/IdpCreateUserIfUniqueAuthenticatorTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/IdpCreateUserIfUniqueAuthenticatorTest.java index 6984fe7d5f4f..d41fe6d686b4 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/IdpCreateUserIfUniqueAuthenticatorTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/IdpCreateUserIfUniqueAuthenticatorTest.java @@ -37,7 +37,7 @@ /** * Integration tests for {@link org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator}. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class IdpCreateUserIfUniqueAuthenticatorTest { private static final String IDP_ALIAS = "create-user-idp"; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java index 44cef1e9bf0f..84927c11e99b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/JsonUserAttributeMapperTest.java @@ -25,9 +25,9 @@ import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.HARDOCDED_CLAIM; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.HARDOCDED_VALUE; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.USER_INFO_CLAIM; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.HARDOCDED_CLAIM; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.HARDOCDED_VALUE; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.USER_INFO_CLAIM; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -37,7 +37,7 @@ /** * @author Martin Idel */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class JsonUserAttributeMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java index 4084caa31921..ef321541322b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcCustomOidcBrokerTest.java @@ -25,12 +25,12 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; +import org.keycloak.tests.broker.oidc.TestKeycloakOidcIdentityProviderFactory; import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -38,7 +38,7 @@ /** * Test methods for testing a custom OIDC broker */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcCustomOidcBrokerTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java index 0c6b791c3d3c..3557b9a6c9cd 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOIDCBrokerWithSignatureTest.java @@ -53,21 +53,20 @@ import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; -import org.keycloak.testsuite.broker.util.SimpleHttpDefault; +import org.keycloak.tests.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.testsuite.util.runonserver.CacheHelper; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; -import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.resetUserPassword; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -75,7 +74,7 @@ /** * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java index aa241490fbac..731498ef1667 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest.java @@ -9,7 +9,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcAccessTokenOnlyClaimsUserAttributeMapperTest extends OidcUserAttributeMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java index f9f736369107..f76bd5af2eac 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerAcrParameterTest.java @@ -4,8 +4,6 @@ import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -14,9 +12,11 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.Assertions; + +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerAcrParameterTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java index c2c6356d1d94..af1e00f4a4f5 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretBasicAuthTest.java @@ -9,11 +9,11 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerClientSecretBasicAuthTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java index cedf7c7c6441..f18dac9fec44 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtCustomSignAlgTest.java @@ -16,11 +16,11 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerClientSecretJwtCustomSignAlgTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java index 305dd39db762..3a11b3b28ab4 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerClientSecretJwtTest.java @@ -12,11 +12,11 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerClientSecretJwtTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java index 32e78030e0c6..fb1e4e657c10 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerColonAliasClientSecretBasicAuthTest.java @@ -29,14 +29,14 @@ import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.broker.oidc.OAuth2IdentityProviderConfig.TOKEN_ENDPOINT_URL; -import static org.keycloak.testsuite.broker.BrokerTestConstants.CLIENT_SECRET; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_SECRET; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerColonAliasClientSecretBasicAuthTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java new file mode 100644 index 000000000000..63f93c9c31f2 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java @@ -0,0 +1,290 @@ +package org.keycloak.tests.broker; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper; +import org.keycloak.protocol.oidc.mappers.HardcodedClaim; +import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; +import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; +import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; + +import static org.keycloak.broker.oidc.OAuth2IdentityProviderConfig.TOKEN_ENDPOINT_URL; +import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_SECRET; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; + +/** + * @author hmlnarik + */ +public class KcOidcBrokerConfiguration implements BrokerConfiguration { + + public static final KcOidcBrokerConfiguration INSTANCE = new KcOidcBrokerConfiguration(); + + protected static final String ATTRIBUTE_TO_MAP_NAME = "user-attribute"; + protected static final String ATTRIBUTE_TO_MAP_NAME_2 = "user-attribute-2"; + public static final String USER_INFO_CLAIM = "user-claim"; + public static final String HARDOCDED_CLAIM = "test"; + public static final String HARDOCDED_VALUE = "value"; + public static final String CONSUMER_BROKER_APP_CLIENT_ID = "broker-app"; + public static final String CONSUMER_BROKER_APP_SECRET = "broker-app-secret"; + + @Override + public RealmRepresentation createProviderRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_PROV_NAME); + realm.setEnabled(true); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsEnabled(true); + realm.setInternationalizationEnabled(true); + realm.setSupportedLocales(Set.of("en", "hu")); + + return realm; + } + + @Override + public RealmRepresentation createConsumerRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_CONS_NAME); + realm.setEnabled(true); + realm.setResetPasswordAllowed(true); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsEnabled(true); + realm.setInternationalizationEnabled(true); + realm.setSupportedLocales(Set.of("en", "hu")); + + return realm; + } + + @Override + public List createProviderClients() { + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(getIDPClientIdInProviderRealm()); + client.setName(CLIENT_ID); + client.setSecret(CLIENT_SECRET); + + client.setRedirectUris(Collections.singletonList(getConsumerRoot() + + getAuthPath() + "/realms/" + consumerRealmName() + "/broker/" + getIDPAlias() + "/endpoint/*")); + + client.setAdminUrl(getConsumerRoot() + + getAuthPath() + "/realms/" + consumerRealmName() + "/broker/" + getIDPAlias() + "/endpoint"); + + OIDCAdvancedConfigWrapper oidcClient = OIDCAdvancedConfigWrapper.fromClientRepresentation(client); + oidcClient.setPostLogoutRedirectUris(Collections.singletonList("+")); + + oidcClient.setBackchannelLogoutUrl(getConsumerRoot() + + getAuthPath() + "/realms/" + consumerRealmName() + "/protocol/openid-connect/logout/backchannel-logout"); + + ProtocolMapperRepresentation emailMapper = new ProtocolMapperRepresentation(); + emailMapper.setName("email"); + emailMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + emailMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID); + + Map emailMapperConfig = emailMapper.getConfig(); + emailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "email"); + emailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "email"); + emailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + + ProtocolMapperRepresentation nestedAttrMapper = new ProtocolMapperRepresentation(); + nestedAttrMapper.setName("attribute - nested claim"); + nestedAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + nestedAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map nestedEmailMapperConfig = nestedAttrMapper.getConfig(); + nestedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "nested.email"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "nested.email"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + + ProtocolMapperRepresentation dottedAttrMapper = new ProtocolMapperRepresentation(); + dottedAttrMapper.setName("attribute - claim with dot in name"); + dottedAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + dottedAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map dottedEmailMapperConfig = dottedAttrMapper.getConfig(); + dottedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "dotted.email"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "dotted\\.email"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + + ProtocolMapperRepresentation userAttrMapper = new ProtocolMapperRepresentation(); + userAttrMapper.setName("attribute - name"); + userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map userAttrMapperConfig = userAttrMapper.getConfig(); + userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, ATTRIBUTE_TO_MAP_NAME); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, ATTRIBUTE_TO_MAP_NAME); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + userAttrMapperConfig.put(ProtocolMapperUtils.MULTIVALUED, "true"); + + ProtocolMapperRepresentation userAttrMapper2 = new ProtocolMapperRepresentation(); + userAttrMapper2.setName("attribute - name - 2"); + userAttrMapper2.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + userAttrMapper2.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map userAttrMapperConfig2 = userAttrMapper2.getConfig(); + userAttrMapperConfig2.put(ProtocolMapperUtils.USER_ATTRIBUTE, ATTRIBUTE_TO_MAP_NAME_2); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, ATTRIBUTE_TO_MAP_NAME_2); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + userAttrMapperConfig2.put(ProtocolMapperUtils.MULTIVALUED, "true"); + + ProtocolMapperRepresentation hardcodedJsonClaim = new ProtocolMapperRepresentation(); + hardcodedJsonClaim.setName("json-mapper"); + hardcodedJsonClaim.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + hardcodedJsonClaim.setProtocolMapper(HardcodedClaim.PROVIDER_ID); + + Map hardcodedJsonClaimMapperConfig = hardcodedJsonClaim.getConfig(); + hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, KcOidcBrokerConfiguration.USER_INFO_CLAIM); + hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, "JSON"); + hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + hardcodedJsonClaimMapperConfig.put(HardcodedClaim.CLAIM_VALUE, "{\"" + HARDOCDED_CLAIM + "\": \"" + HARDOCDED_VALUE + "\"}"); + + ProtocolMapperRepresentation audienceMapper = new ProtocolMapperRepresentation(); + audienceMapper.setName("audience-brokerapp"); + audienceMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + audienceMapper.setProtocolMapper(AudienceProtocolMapper.PROVIDER_ID); + + Map audienceMapperConfig = audienceMapper.getConfig(); + audienceMapperConfig.put(AudienceProtocolMapper.INCLUDED_CUSTOM_AUDIENCE, CLIENT_ID); + audienceMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + + client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper, userAttrMapper2, nestedAttrMapper, dottedAttrMapper, hardcodedJsonClaim, audienceMapper)); + + return Collections.singletonList(client); + } + + @Override + public List createConsumerClients() { + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(CONSUMER_BROKER_APP_CLIENT_ID); + client.setName("broker-app"); + client.setSecret(CONSUMER_BROKER_APP_SECRET); + client.setEnabled(true); + client.setDirectAccessGrantsEnabled(true); + + client.setRedirectUris(Collections.singletonList(getConsumerRoot() + + getAuthPath() + "/*")); + + client.setBaseUrl(getConsumerRoot() + + getAuthPath() + "/realms/" + REALM_CONS_NAME + "/app"); + + OIDCAdvancedConfigWrapper.fromClientRepresentation(client).setPostLogoutRedirectUris(Collections.singletonList("+")); + OIDCAdvancedConfigWrapper.fromClientRepresentation(client).setUseRefreshTokenForClientCredentialsGrant(true); + + ProtocolMapperRepresentation audienceMapper = new ProtocolMapperRepresentation(); + audienceMapper.setName("audience-broker-app"); + audienceMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + audienceMapper.setProtocolMapper(AudienceProtocolMapper.PROVIDER_ID); + + Map audienceMapperConfig = audienceMapper.getConfig(); + audienceMapperConfig.put(AudienceProtocolMapper.INCLUDED_CUSTOM_AUDIENCE, CONSUMER_BROKER_APP_CLIENT_ID); + audienceMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + + client.setProtocolMappers(Collections.singletonList(audienceMapper)); + + return Collections.singletonList(client); + } + + @Override + public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { + IdentityProviderRepresentation idp = createIdentityProvider(getIDPAlias(), IDP_OIDC_PROVIDER_ID); + + Map config = idp.getConfig(); + applyDefaultConfiguration(config, syncMode); + + return idp; + } + + protected void applyDefaultConfiguration(final Map config, IdentityProviderSyncMode syncMode) { + config.put(IdentityProviderModel.SYNC_MODE, syncMode.toString()); + config.put("clientId", CLIENT_ID); + config.put("clientSecret", CLIENT_SECRET); + config.put("prompt", "login"); + config.put("loginHint", "true"); + config.put(OIDCIdentityProviderConfig.ISSUER, getProviderRoot() + getAuthPath() + "/realms/" + REALM_PROV_NAME); + config.put("authorizationUrl", getProviderRoot() + getAuthPath() + "/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/auth"); + config.put(TOKEN_ENDPOINT_URL, getProviderRoot() + getAuthPath() + "/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/token"); + config.put("logoutUrl", getProviderRoot() + getAuthPath() + "/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/logout"); + config.put("userInfoUrl", getProviderRoot() + getAuthPath() + "/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/userinfo"); + config.put("defaultScope", "email profile"); + config.put("backchannelSupported", "true"); + config.put(OIDCIdentityProviderConfig.JWKS_URL, + getProviderRoot() + getAuthPath() + "/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/certs"); + config.put(OIDCIdentityProviderConfig.USE_JWKS_URL, "true"); + config.put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, "true"); + } + + @Override + public String getUserLogin() { + return USER_LOGIN; + } + + @Override + public String getIDPClientIdInProviderRealm() { + return CLIENT_ID; + } + + @Override + public String getUserPassword() { + return USER_PASSWORD; + } + + @Override + public String getUserEmail() { + return USER_EMAIL; + } + + @Override + public String providerRealmName() { + return REALM_PROV_NAME; + } + + @Override + public String consumerRealmName() { + return REALM_CONS_NAME; + } + + @Override + public String getIDPAlias() { + return IDP_OIDC_ALIAS; + } + +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java index 997331c78115..756fccfe731b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java @@ -34,14 +34,12 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.userprofile.UserProfileContext; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; /** * Simple test to check the events after a broker login using OIDC. It also @@ -50,7 +48,7 @@ * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerEventTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java index cb9bdd7bc7ea..194559b194da 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java @@ -20,16 +20,16 @@ import org.junit.Ignore; import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertTrue; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerFrontendUrlTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java index 49e81582d328..5b074f7a37e1 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerHiddenIdpHintTest.java @@ -20,9 +20,6 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -31,10 +28,13 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; /** * Migrated from old testsuite. Previous version by Pedro Igor. @@ -42,7 +42,7 @@ * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. * @author pedroigor */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerHiddenIdpHintTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java index fb02e3e769c0..422c6de090a0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpHintTest.java @@ -20,9 +20,6 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -31,7 +28,10 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; /** * Migrated from old testsuite. Previous version by Pedro Igor. @@ -39,7 +39,7 @@ * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. * @author pedroigor */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerIdpHintTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java index 5fc73163021e..f70961969c72 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java @@ -53,24 +53,21 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; -import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.admin.AdminApiUtil; import org.keycloak.testframework.ui.page.IdpLinkActionPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.tests.utils.admin.AdminApiUtil; import org.keycloak.testsuite.util.AccountHelper; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.testsuite.util.runonserver.RunHelpers; import org.keycloak.utils.BrokerUtil; import org.hamcrest.MatcherAssert; -import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.Rule; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.hamcrest.Matchers.is; @@ -79,7 +76,7 @@ * * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerIdpLinkActionTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java index 50b3cc311a29..b198689413a8 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpPublicKeyMissingUseTest.java @@ -10,13 +10,13 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerIdpPublicKeyMissingUseTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java index f5eedb928445..6ced5e72393e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java index e4a3eed5db52..534797ddad6b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java index 949e33233928..c00a4b648500 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java index 2fdba96ea32d..66393422c25a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java index 791db30bb74d..4a4658fb9ed8 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java index 9211c51be5a6..7bf20596d477 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java index bb8603412ef9..80db69e46db6 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java index 3fd5a82b2643..7958b55cfe30 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEEcdhEsP384A192GcmTest.java @@ -22,7 +22,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEEcdhEsP384A192GcmTest extends AbstractKcOidcBrokerJWEEcdhEsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java index ec7855f1e0da..8583ac7d965f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWETest.java @@ -47,13 +47,13 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import com.google.common.collect.ImmutableMap; -import org.junit.jupiter.api.Test; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Test; + import static java.util.Optional.ofNullable; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -67,7 +67,7 @@ * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWETest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java index b8a96bce9900..0ab16184c4ac 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java @@ -28,7 +28,7 @@ * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerJWEUserInfoJustEncryptedTest extends KcOidcBrokerJWETest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java index a75113146d3b..059b52cdfbc1 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java @@ -24,23 +24,23 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.keycloak.testframework.ui.page.IdpConfirmLinkPage; import org.keycloak.testframework.util.ApiUtil; import org.keycloak.testsuite.federation.ldap.LDAPTestContext; -import org.keycloak.testframework.ui.page.IdpConfirmLinkPage; import org.keycloak.testsuite.util.LDAPRule; import org.keycloak.testsuite.util.LDAPTestUtils; -import org.keycloak.testframework.ui.annotations.InjectPage; -import org.junit.jupiter.api.BeforeEach; import org.junit.ClassRule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.keycloak.models.utils.ModelToRepresentation.toRepresentationWithoutConfig; import static org.junit.jupiter.api.Assertions.assertEquals; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerLdapReadOnlyTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java index 8f0d749e2f14..fc5c91832379 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java @@ -36,12 +36,12 @@ import org.keycloak.testframework.util.ApiUtil; import org.keycloak.testsuite.util.LDAPRule; -import org.junit.jupiter.api.BeforeEach; import org.junit.ClassRule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerLdapTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java index 69f705c19ad4..f5a528779aaf 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLoginHintTest.java @@ -19,19 +19,19 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.updaters.Creator; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; import static org.junit.jupiter.api.Assertions.assertTrue; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerLoginHintTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java index a3b017035d81..3cab0e964567 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutFrontChannelTest.java @@ -1,12 +1,3 @@ - - @InjectTimeOffSet - TimeOffSet timeOffSet; - - @InjectWebDriver - ManagedWebDriver driver; - - @InjectOAuthClient - OAuthClient oauth; package org.keycloak.tests.broker; import java.io.IOException; @@ -22,35 +13,41 @@ import org.keycloak.testframework.events.Events; import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; -import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.keycloak.testsuite.util.oauth.IntrospectionResponse; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.Rule; import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerLogoutFrontChannelTest extends AbstractKcOidcBrokerLogoutTest { @InjectRealm @InjectEvents Events events; + @InjectTimeOffSet + TimeOffSet timeOffSet; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectOAuthClient + OAuthClient oauth; + @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcOidcBrokerConfigurationIdpLogoutFrontChannel(); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java index 112eaa1d7072..1464878982b8 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLogoutTest.java @@ -22,23 +22,21 @@ import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerLogoutTest extends AbstractKcOidcBrokerLogoutTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java index b83ba4c61620..13797f8d0d8a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNoLoginHintTest.java @@ -8,9 +8,6 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.apache.commons.lang3.StringUtils; -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -19,13 +16,16 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; + +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerNoLoginHintTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java index dec646157ae3..45af0054dbe0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerNonceParameterTest.java @@ -27,12 +27,12 @@ import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerNonceParameterTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java index 3afe9c5561f4..cc6208727934 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerParameterForwardTest.java @@ -11,8 +11,6 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -21,16 +19,18 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.Assertions; + +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerParameterForwardTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java index ef6a57a16af8..029314fabd2f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPassMaxAgeTest.java @@ -16,23 +16,23 @@ import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; +import org.keycloak.tests.broker.oidc.TestKeycloakOidcIdentityProviderFactory; import org.junit.Ignore; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; /** * Tests the propagation of the max_age parameter for brokered logins. * * see https://issues.redhat.com/browse/KEYCLOAK-18499 */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPassMaxAgeTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java index d3b4132440fe..26b2c05c41e0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPkceTest.java @@ -8,7 +8,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPkceTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java index bc7ab89888ef..27b062e65239 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtClientIdRequiredTest.java @@ -38,22 +38,22 @@ import org.keycloak.testframework.realm.AuthenticationExecutionBuilder; import org.keycloak.testframework.realm.AuthenticationFlowBuilder; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.broker.oidc.ClientIdRequiredJWTClientAuthenticator; +import org.keycloak.tests.broker.oidc.ClientIdRequiredJWTClientAuthenticator; import org.keycloak.testsuite.util.KeyUtils; import org.junit.jupiter.api.BeforeEach; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.AbstractAuthenticationTest.findFlowByAlias; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; /** * Test that the broker will send the client_id parameter. * * @author Justin Tay */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtClientIdRequiredTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java index 246989e5432a..a4cd602aa16f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomAudienceTest.java @@ -32,12 +32,12 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST2; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtCustomAudienceTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java index 63407cb0f7d2..a6a99da2029b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java @@ -23,13 +23,13 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.TokenSignatureUtil; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtCustomSignAlgTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java index 32baf033cab4..e52ffdc0189b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtMissingUseTest.java @@ -35,11 +35,11 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtMissingUseTest extends AbstractBrokerTest { @InjectRealm @@ -67,7 +67,7 @@ public List createProviderClients() { // use a custom realm resource provider to expose a jwks with an empty use // a custom key provider returning a null use wouldn't work due to the standard // jwks defaulting the use and other portions expecting the use to be set - // see org.keycloak.testsuite.broker.oidc.MissingUseJwksRestResource + // see org.keycloak.tests.broker.oidc.MissingUseJwksRestResource client.getAttributes().put(OIDCConfigAttributes.JWKS_URL, BrokerTestTools.getConsumerRoot() + "/auth/realms/" + BrokerTestConstants.REALM_CONS_NAME + "/missing-use-jwks/jwks"); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java index c14e28b1f600..2a14025e95ed 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtTest.java @@ -32,11 +32,11 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java index decb4127264f..d068cbb44fcd 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest.java @@ -35,11 +35,11 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtUnsupportedKeyTest extends AbstractBrokerTest { @InjectRealm @@ -65,7 +65,7 @@ public List createProviderClients() { client.getAttributes().put(OIDCConfigAttributes.USE_JWKS_URL, "true"); // use a custom realm resource provider to expose a jwks with an unsupported key - // see org.keycloak.testsuite.broker.oidc.UnsupportedKeyJwksRestResource + // see org.keycloak.tests.broker.oidc.UnsupportedKeyJwksRestResource client.getAttributes().put(OIDCConfigAttributes.JWKS_URL, BrokerTestTools.getConsumerRoot() + "/auth/realms/" + BrokerTestConstants.REALM_CONS_NAME + "/unsupported-key-jwks/jwks"); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java index 527c7011c820..691534c6aee8 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPrivateKeyJwtWithX509HeadersTest.java @@ -32,11 +32,11 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testsuite.util.KeyUtils; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPrivateKeyJwtWithX509HeadersTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java index b6cdc4bc9176..4f19b824f3ec 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java @@ -36,16 +36,16 @@ import org.keycloak.testsuite.util.AccountHelper; import org.junit.Ignore; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; -import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; -import static org.keycloak.testsuite.broker.BrokerTestConstants.CLIENT_ID; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; +import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.tests.utils.admin.AdminApiUtil.resetUserPassword; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -57,7 +57,7 @@ */ // Remove @Ignore when closing Github issue 20642 @Ignore -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPromptNoneRedirectTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java index d47bbfff6168..f6a34dc7c6f1 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java @@ -7,8 +7,6 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -17,9 +15,11 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.Assertions; + +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPromptParameterTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java index 1b1ba3eee495..7ee6d059b99f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerStateParameterTest.java @@ -30,18 +30,16 @@ import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; -import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testframework.ui.page.LoginExpiredPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.hamcrest.Matchers; -import org.keycloak.testframework.ui.annotations.InjectPage; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.MatcherAssert.assertThat; @@ -50,7 +48,7 @@ * * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerStateParameterTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java index 95e5c1515c4e..ffb6a55c8999 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerSubMatchIntrospectionTest.java @@ -11,15 +11,15 @@ import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.ManagedRealm; import org.junit.Ignore; -import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java index 0b42b1e7f097..c12329e7f343 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTest.java @@ -56,11 +56,10 @@ import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.admin.AdminApiUtil; -import org.keycloak.testsuite.broker.util.SimpleHttpDefault; +import org.keycloak.tests.broker.util.SimpleHttpDefault; +import org.keycloak.tests.utils.admin.AdminApiUtil; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.WaitUtils; -import org.keycloak.testsuite.util.oauth.OAuthClient; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -68,15 +67,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; import static org.hamcrest.MatcherAssert.assertThat; @@ -91,7 +90,7 @@ /** * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest { @InjectRealm @@ -270,7 +269,7 @@ private void loginFetchingUserFromUserEndpoint(boolean loginIsDenied) { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.OIDCBrokerUserPropertyTest + * Refers to in old test suite: org.keycloak.tests.broker.OIDCBrokerUserPropertyTest */ @Test public void loginFetchingUserFromUserEndpointWithClaimMapper() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java index 00b22b11a327..6cbc747728fa 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTokenExchangeTest.java @@ -67,20 +67,19 @@ import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; -import org.keycloak.testsuite.admin.AdminApiUtil; +import org.keycloak.tests.utils.admin.AdminApiUtil; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeatures; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.AdminClientUtil; import org.keycloak.testsuite.util.ServerURLs; import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.util.BasicAuthHelper; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; import static org.hamcrest.MatcherAssert.assertThat; @@ -96,7 +95,7 @@ * Test for identity-provider token exchange scenarios. Base for tests of token-exchange V1 */ @EnableFeatures({@EnableFeature(Profile.Feature.TOKEN_EXCHANGE), @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)}) -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java index 694db3b971a9..a3f9e3d515e0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerTransientSessionsTest.java @@ -69,19 +69,18 @@ import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; +import org.keycloak.tests.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; -import org.keycloak.testsuite.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.client.KeycloakTestingClient; -import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.Creator; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.testsuite.util.runonserver.RunHelpers; import org.keycloak.util.TokenUtil; @@ -92,19 +91,17 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.hamcrest.Matchers; -import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.Rule; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.CONSUMER_BROKER_APP_CLIENT_ID; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.CONSUMER_BROKER_APP_SECRET; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.CONSUMER_BROKER_APP_CLIENT_ID; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.CONSUMER_BROKER_APP_SECRET; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; import static org.hamcrest.MatcherAssert.assertThat; @@ -125,7 +122,7 @@ * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ @EnableFeature(value = Profile.Feature.TRANSIENT_USERS, skipRestart = true) -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerTransientSessionsTest extends AbstractAdvancedBrokerTest { @InjectRealm @@ -313,7 +310,7 @@ private void loginFetchingUserFromUserEndpoint(boolean loginIsDenied) { } /** - * Refers to in old test suite: org.keycloak.testsuite.broker.OIDCBrokerUserPropertyTest + * Refers to in old test suite: org.keycloak.tests.broker.OIDCBrokerUserPropertyTest */ @Test public void loginFetchingUserFromUserEndpointWithClaimMapper() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java index 865b06ffc704..bd06a544d725 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesDisabledTest.java @@ -7,8 +7,6 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -17,19 +15,21 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.junit.jupiter.api.Assertions; + import static java.util.Locale.ENGLISH; import static org.keycloak.OAuth2Constants.UI_LOCALES_PARAM; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerUiLocalesDisabledTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java index 11a6b8140cf4..31c06a3a0bfb 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesEnabledTest.java @@ -7,8 +7,6 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -17,18 +15,20 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.junit.jupiter.api.Assertions; + import static java.util.Locale.ENGLISH; import static org.keycloak.OAuth2Constants.UI_LOCALES_PARAM; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerUiLocalesEnabledTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java index f372b3f24dde..200d0a148250 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerUiLocalesWithIdpHintTest.java @@ -8,8 +8,6 @@ import org.keycloak.models.IdentityProviderSyncMode; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -18,16 +16,18 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.junit.jupiter.api.Assertions; + import static org.keycloak.OAuth2Constants.UI_LOCALES_PARAM; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerUiLocalesWithIdpHintTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultConfiguration.java new file mode 100644 index 000000000000..f1241ee28c83 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultConfiguration.java @@ -0,0 +1,23 @@ +package org.keycloak.tests.broker; + +import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +import static org.keycloak.tests.broker.BrokerTestConstants.VAULT_CLIENT_SECRET; + +/** + * @author Martin Kanis + */ +public class KcOidcBrokerVaultConfiguration extends KcOidcBrokerConfiguration { + + public static final KcOidcBrokerVaultConfiguration INSTANCE = new KcOidcBrokerVaultConfiguration(); + + @Override + public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { + IdentityProviderRepresentation idpRep = super.setUpIdentityProvider(syncMode); + + idpRep.getConfig().put("clientSecret", VAULT_CLIENT_SECRET); + + return idpRep; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java index de36e7593969..a53335dd727a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java @@ -9,7 +9,7 @@ * @author Martin Kanis */ @EnableVault -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerVaultTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java index 7b03f9cdc713..70caf8af7e3c 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java @@ -19,15 +19,15 @@ import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.removeBrokerExpiredSessions; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.removeBrokerExpiredSessions; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTest { @InjectRealm @@ -75,7 +75,7 @@ public void beforeBrokerTest() { } /** - * Referes to in old testsuite: org.keycloak.testsuite.broker.OIDCKeycloakServerBrokerWithConsentTest#testConsentDeniedWithExpiredClientSession + * Referes to in old testsuite: org.keycloak.tests.broker.OIDCKeycloakServerBrokerWithConsentTest#testConsentDeniedWithExpiredClientSession */ @Test public void testConsentDeniedWithExpiredClientSession() { @@ -105,7 +105,7 @@ public void testConsentDeniedWithExpiredClientSession() { } /** - * Referes to in old testsuite: org.keycloak.testsuite.broker.OIDCKeycloakServerBrokerWithConsentTest#testConsentDeniedWithExpiredAndClearedClientSession + * Referes to in old testsuite: org.keycloak.tests.broker.OIDCKeycloakServerBrokerWithConsentTest#testConsentDeniedWithExpiredAndClearedClientSession */ @Test public void testConsentDeniedWithExpiredAndClearedClientSession() { @@ -132,7 +132,7 @@ public void testConsentDeniedWithExpiredAndClearedClientSession() { } /** - * Referes to in old testsuite: org.keycloak.testsuite.broker.OIDCKeycloakServerBrokerWithConsentTest#testLoginCancelConsent + * Referes to in old testsuite: org.keycloak.tests.broker.OIDCKeycloakServerBrokerWithConsentTest#testLoginCancelConsent */ @Test public void testLoginCancelConsent() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java index 927a5a2b480c..65fdad613e0e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcDefaultIdpTest.java @@ -19,15 +19,16 @@ package org.keycloak.tests.broker; -import org.junit.jupiter.api.Test; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import org.junit.jupiter.api.Test; + /** * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcDefaultIdpTest extends AbstractDefaultIdpTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java index b34613797ebc..108887f78000 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginDetectExistingUserTest.java @@ -29,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcFirstBrokerLoginDetectExistingUserTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java index 538b032676f4..20278064f88b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginNewAuthTest.java @@ -11,17 +11,17 @@ import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserBuilder; -import org.keycloak.tests.utils.Assert; -import org.keycloak.testsuite.admin.AdminApiUtil; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.page.PasswordPage; import org.keycloak.testframework.ui.page.SelectAuthenticatorPage; +import org.keycloak.tests.utils.Assert; +import org.keycloak.tests.utils.admin.AdminApiUtil; -import org.keycloak.testframework.ui.annotations.InjectPage; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -33,7 +33,7 @@ * * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcFirstBrokerLoginNewAuthTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java index 086a29cb1214..c0363067ce66 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcFirstBrokerLoginTest.java @@ -22,34 +22,33 @@ import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ClientScopeBuilder; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.page.LoginUpdateProfilePage; +import org.keycloak.testframework.ui.page.RegisterPage; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; +import org.keycloak.tests.broker.oidc.TestKeycloakOidcIdentityProviderFactory; import org.keycloak.testsuite.forms.RegisterWithUserProfileTest; import org.keycloak.testsuite.forms.VerifyProfileTest; -import org.keycloak.testframework.ui.page.LoginUpdateProfilePage; -import org.keycloak.testframework.ui.page.RegisterPage; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.MailServerConfiguration; import org.keycloak.testsuite.util.userprofile.UserProfileUtil; import org.keycloak.util.JsonSerialization; import org.apache.commons.lang3.StringUtils; -import org.keycloak.testframework.ui.annotations.InjectPage; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; -import static org.keycloak.testsuite.admin.AdminApiUtil.removeUserByUsername; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.utils.admin.AdminApiUtil.removeUserByUsername; import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl; import static org.keycloak.testsuite.util.userprofile.UserProfileUtil.ATTRIBUTE_DEPARTMENT; import static org.keycloak.testsuite.util.userprofile.UserProfileUtil.PERMISSIONS_ADMIN_EDITABLE; @@ -68,7 +67,7 @@ /** * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest { @InjectRealm @@ -83,8 +82,6 @@ public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest { @InjectOAuthClient OAuthClient oauth; - protected ManagedRealm managedRealm = new ManagedRealm(this, bc.consumerRealmName()); - @InjectPage protected LoginUpdateProfilePage loginUpdateProfilePage; @@ -232,7 +229,7 @@ public void testLinkAccountByReauthenticationWithDifferentBroker() { assertEquals("User with email user@localhost.com already exists. How do you want to continue?", idpConfirmLinkPage.getMessage()); idpConfirmLinkPage.clickLinkAccount(); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); @@ -291,7 +288,7 @@ public void testFilterMultipleBrokerWhenReauthenticating() { assertEquals("User with email user@localhost.com already exists. How do you want to continue?", idpConfirmLinkPage.getMessage()); idpConfirmLinkPage.clickLinkAccount(); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); // There have to be two idp showed on login page // kc-saml-idp and kc-oidc-idp2 must be present but not kc-oidc-idp @@ -343,7 +340,7 @@ public void testNestedFirstBrokerFlow() { assertEquals("User with email user@localhost.com already exists. How do you want to continue?", idpConfirmLinkPage.getMessage()); idpConfirmLinkPage.clickLinkAccount(); - assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInstruction()); try { this.loginPage.findSocialButton(bc.getIDPAlias()); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java index eadd17d593b7..e1af74851425 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java @@ -41,22 +41,20 @@ import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.BrowserTabUtil; import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.hamcrest.Matchers; import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; @@ -64,7 +62,7 @@ /** * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerTest { @InjectRealm @@ -96,8 +94,8 @@ protected BrokerConfiguration getBrokerConfiguration() { // Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved @Test public void testAuthenticationExpiredWithMoreBrowserTabs_clickIdpLoginInTab1AfterExpiration() { - assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver); - try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { + assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver.driver() instanceof HtmlUnitDriver); + try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver.driver())) { oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); oauth.openLoginForm(); @@ -136,8 +134,8 @@ public void testAuthenticationExpiredWithMoreBrowserTabs_clickIdpLoginInTab1Afte // Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved @Test public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider() { - assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver); - try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { + assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver.driver() instanceof HtmlUnitDriver); + try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver.driver())) { // Open login page in tab1 and click "login with IDP" oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); @@ -204,7 +202,7 @@ public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsu // So this is similar behaviour like KcSamlMultipleTabsBrokerTest.testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider @Test public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider_requiresShortStateParameter() { - assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver); + assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver.driver() instanceof HtmlUnitDriver); // Update IDP and set invalid credentials there IdentityProviderResource idpResource = adminClient.realm(REALM_CONS_NAME).identityProviders().get(IDP_OIDC_ALIAS); @@ -213,7 +211,7 @@ public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsu idpResource.update(idpRep); - try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { + try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver.driver())) { // Open login page in tab1 and click "login with IDP" oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); @@ -282,10 +280,10 @@ public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsu @Test public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider() throws Exception { - assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver); + assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver.driver() instanceof HtmlUnitDriver); // Testing the scenario when authenticationSession expired only in "provider" realm and "consumer" is able to handle it at IDP. // So need to increase authSession timeout on "consumer" - try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver); + try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver.driver()); AutoCloseable realmUpdater = new RealmAttributeUpdater(adminClient.realm(bc.consumerRealmName())) .setAccessCodeLifespanLogin(7200) .update() @@ -341,7 +339,7 @@ public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider( .details(Details.LOGIN_RETRY, "true"); // We were redirected back to IDP where user is asked to re-authenticate (due prompt=login being sent to OIDC IDP in authz request) - Assertions.assertEquals("Please re-authenticate to continue", loginPage.getInfoMessage()); + Assertions.assertEquals("Please re-authenticate to continue", loginPage.getInstruction()); Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"),"We must be on provider realm right now"); loginPage.login(bc.getUserPassword()); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java index 36192d95470a..c522bfda25f0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcPostBrokerLoginTest.java @@ -43,27 +43,25 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.FlowUtil; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.Rule; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import static org.keycloak.models.utils.TimeBasedOTP.DEFAULT_INTERVAL_SECONDS; -import static org.keycloak.testsuite.admin.AdminApiUtil.removeUserByUsername; -import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.utils.admin.AdminApiUtil.removeUserByUsername; /** * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcPostBrokerLoginTest extends AbstractInitializedBaseBrokerTest { @InjectRealm @@ -316,7 +314,7 @@ public void testPostBrokerLoginFlowWithOTP() { } static void configurePostBrokerLoginWithClientScopeConditionAndOTP(KeycloakTestingClient testingClient, String consumerRealmName, String idpAlias, String clientScopeName, boolean negate) { - runOnServer.run(session -> { + testingClient.server(consumerRealmName).run(session -> { AuthenticationFlowModel flowModel = session.getContext().getRealm().getFlowByAlias("post-broker"); if (flowModel == null) { flowModel = FlowUtil.createFlowModel("post-broker", "basic-flow", "post-broker flow with client-scope condition and OTP", true, false); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java index 81417b57f55b..11eafb9039cf 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcUsernameTemplateMapperTest.java @@ -6,16 +6,16 @@ import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; - -import com.google.common.collect.ImmutableMap; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import com.google.common.collect.ImmutableMap; + /** * @author Martin Idel */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcUsernameTemplateMapperTest extends AbstractUsernameTemplateMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java index dc6d68d2db28..1942c9ba413e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToGroupMapperTest.java @@ -24,13 +24,13 @@ import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; +import static org.keycloak.tests.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; /** * @author Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlAdvancedAttributeToGroupMapperTest extends AbstractGroupBrokerMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java index 432c435708bd..3e008b43b6a6 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAdvancedAttributeToRoleMapperTest.java @@ -9,21 +9,21 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; +import static org.keycloak.tests.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; /** * @author Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlAdvancedAttributeToRoleMapperTest extends AbstractAdvancedRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java index 57c7a06b8d22..18ed2df4f74a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlAttributeConsumingServiceIndexTest.java @@ -9,23 +9,23 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Node; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; /** * Final class as it's not intended to be overriden. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlAttributeConsumingServiceIndexTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java index 68ca1e255795..df2aa6733948 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerAllowedClockSkewTest.java @@ -28,7 +28,7 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; @@ -37,7 +37,7 @@ import org.junit.jupiter.api.Test; import org.w3c.dom.Document; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.testsuite.util.Matchers.isSamlResponse; import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; @@ -47,7 +47,7 @@ * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlBrokerAllowedClockSkewTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java index c133ee7ce038..1a72dc7b6902 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java @@ -17,10 +17,10 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlBrokerArtifactBindingTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java new file mode 100644 index 000000000000..7d1d3f1f2ad6 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java @@ -0,0 +1,292 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.keycloak.tests.broker; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.saml.SamlConfigAttributes; +import org.keycloak.protocol.saml.SamlProtocol; +import org.keycloak.protocol.saml.mappers.AttributeStatementHelper; +import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper; +import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.tests.saml.AbstractSamlTest; + +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.ARTIFACT_BINDING_RESPONSE; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.ARTIFACT_RESOLUTION_SERVICE_URL; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.BACKCHANNEL_SUPPORTED; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.FORCE_AUTHN; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.NAME_ID_POLICY_FORMAT; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.POST_BINDING_AUTHN_REQUEST; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.POST_BINDING_RESPONSE; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.SINGLE_LOGOUT_SERVICE_URL; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.VALIDATE_SIGNATURE; +import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED; +import static org.keycloak.protocol.saml.SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE; +import static org.keycloak.protocol.saml.SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; + +public class KcSamlBrokerConfiguration implements BrokerConfiguration { + + public static final KcSamlBrokerConfiguration INSTANCE = new KcSamlBrokerConfiguration(); + public static final String ATTRIBUTE_TO_MAP_FRIENDLY_NAME = "user-attribute-friendly"; + + private final boolean loginHint; + + public KcSamlBrokerConfiguration() { + this(false); + } + + public KcSamlBrokerConfiguration(boolean loginHint) { + this.loginHint = loginHint; + } + + @Override + public RealmRepresentation createProviderRealm() { + RealmRepresentation realm = new RealmRepresentation(); + + realm.setEnabled(true); + realm.setRealm(REALM_PROV_NAME); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + + return realm; + } + + @Override + public RealmRepresentation createConsumerRealm() { + RealmRepresentation realm = new RealmRepresentation(); + + realm.setEnabled(true); + realm.setRealm(REALM_CONS_NAME); + realm.setResetPasswordAllowed(true); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + + return realm; + } + + @Override + public List createProviderClients() { + String clientId = getIDPClientIdInProviderRealm(); + return new LinkedList<>(Collections.singleton(createProviderClient(clientId))); + } + + private ClientRepresentation createProviderClient(String clientId) { + ClientRepresentation client = new ClientRepresentation(); + + client.setClientId(clientId); + client.setEnabled(true); + client.setProtocol(IDP_SAML_PROVIDER_ID); + client.setRedirectUris(Collections.singletonList( + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint" + )); + + Map attributes = new HashMap<>(); + + attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, "true"); + attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); + attributes.put(SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); + attributes.put(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, "true"); + attributes.put(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, "username"); + attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "false"); + attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false"); + attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false"); + attributes.put(SamlConfigAttributes.SAML_ENCRYPT, "false"); + attributes.put(IdentityProviderModel.LOGIN_HINT, String.valueOf(loginHint)); + + client.setAttributes(attributes); + + ProtocolMapperRepresentation emailMapper = new ProtocolMapperRepresentation(); + emailMapper.setName("email"); + emailMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + emailMapper.setProtocolMapper(UserPropertyAttributeStatementMapper.PROVIDER_ID); + + Map emailMapperConfig = emailMapper.getConfig(); + emailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "email"); + emailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "urn:oid:1.2.840.113549.1.9.1"); + emailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); + emailMapperConfig.put(AttributeStatementHelper.FRIENDLY_NAME, "email"); + + ProtocolMapperRepresentation dottedAttrMapper = new ProtocolMapperRepresentation(); + dottedAttrMapper.setName("email - dotted"); + dottedAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + dottedAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID); + + Map dottedEmailMapperConfig = dottedAttrMapper.getConfig(); + dottedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "dotted.email"); + dottedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "dotted.email"); + dottedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); + + ProtocolMapperRepresentation nestedAttrMapper = new ProtocolMapperRepresentation(); + nestedAttrMapper.setName("email - nested"); + nestedAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + nestedAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID); + + Map nestedEmailMapperConfig = nestedAttrMapper.getConfig(); + nestedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "nested.email"); + nestedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "nested.email"); + nestedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); + + ProtocolMapperRepresentation userAttrMapper = new ProtocolMapperRepresentation(); + userAttrMapper.setName("attribute - name"); + userAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + userAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID); + + Map userAttrMapperConfig = userAttrMapper.getConfig(); + userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME); + userAttrMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME); + userAttrMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, AttributeStatementHelper.BASIC); + userAttrMapperConfig.put(AttributeStatementHelper.FRIENDLY_NAME, ""); + + ProtocolMapperRepresentation userAttrMapper2 = new ProtocolMapperRepresentation(); + userAttrMapper2.setName("attribute - name 2"); + userAttrMapper2.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + userAttrMapper2.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID); + + Map userAttrMapper2Config = userAttrMapper2.getConfig(); + userAttrMapper2Config.put(ProtocolMapperUtils.USER_ATTRIBUTE, KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2); + userAttrMapper2Config.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2); + userAttrMapper2Config.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, AttributeStatementHelper.BASIC); + userAttrMapper2Config.put(AttributeStatementHelper.FRIENDLY_NAME, ""); + + ProtocolMapperRepresentation userFriendlyAttrMapper = new ProtocolMapperRepresentation(); + userFriendlyAttrMapper.setName("attribute - friendly name"); + userFriendlyAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + userFriendlyAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID); + + Map userFriendlyAttrMapperConfig = userFriendlyAttrMapper.getConfig(); + userFriendlyAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, ATTRIBUTE_TO_MAP_FRIENDLY_NAME); + userFriendlyAttrMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "urn:oid:1.2.3.4.5.6.7"); + userFriendlyAttrMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, AttributeStatementHelper.BASIC); + userFriendlyAttrMapperConfig.put(AttributeStatementHelper.FRIENDLY_NAME, ATTRIBUTE_TO_MAP_FRIENDLY_NAME); + + client.setProtocolMappers(Arrays.asList(emailMapper, dottedAttrMapper, nestedAttrMapper, userAttrMapper, userAttrMapper2, userFriendlyAttrMapper)); + + return client; + } + + @Override + public List createConsumerClients() { + return Arrays.asList( + ClientBuilder.create() + .clientId(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST) + .enabled(true) + .fullScopeEnabled(true) + .protocol(SamlProtocol.LOGIN_PROTOCOL) + .baseUrl(getConsumerRoot() + "/sales-post") + .redirectUris(getConsumerRoot() + "/sales-post/*") + .attribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE) + .attribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_FALSE_VALUE) + .build(), + ClientBuilder.create() + .clientId(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted") + .enabled(true) + .fullScopeEnabled(true) + .protocol(SamlProtocol.LOGIN_PROTOCOL) + .baseUrl(getConsumerRoot() + "/sales-post") + .redirectUris(getConsumerRoot() + "/sales-post/*") + .attribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE) + .attribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_FALSE_VALUE) + .attribute(SAML_IDP_INITIATED_SSO_URL_NAME, "sales-post") + .attribute(SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, getConsumerRoot() + "/sales-post/saml") + .build(), + ClientBuilder.create() + .clientId("broker-app") + .name("broker-app") + .secret("broker-app-secret") + .enabled(true) + .directAccessGrantsEnabled() + .redirectUris(getConsumerRoot() + "/auth/*") + .baseUrl(getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/app") + .build() + ); + } + + @Override + public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { + IdentityProviderRepresentation idp = createIdentityProvider(IDP_SAML_ALIAS, IDP_SAML_PROVIDER_ID); + + idp.setTrustEmail(true); + idp.setAddReadTokenRoleOnCreate(true); + idp.setStoreToken(true); + + Map config = idp.getConfig(); + + config.put(IdentityProviderModel.SYNC_MODE, syncMode.toString()); + config.put(SINGLE_SIGN_ON_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml"); + config.put(ARTIFACT_RESOLUTION_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml"); + config.put(SINGLE_LOGOUT_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml"); + config.put(NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + config.put(FORCE_AUTHN, "false"); + config.put(IdentityProviderModel.LOGIN_HINT, String.valueOf(loginHint)); + config.put(POST_BINDING_RESPONSE, "true"); + config.put(POST_BINDING_AUTHN_REQUEST, "true"); + config.put(VALIDATE_SIGNATURE, "false"); + config.put(WANT_AUTHN_REQUESTS_SIGNED, "false"); + config.put(BACKCHANNEL_SUPPORTED, "false"); + config.put(ARTIFACT_BINDING_RESPONSE, "false"); + + return idp; + } + + @Override + public String providerRealmName() { + return REALM_PROV_NAME; + } + + @Override + public String consumerRealmName() { + return REALM_CONS_NAME; + } + + @Override + public String getIDPClientIdInProviderRealm() { + return getConsumerRoot() + "/auth/realms/" + consumerRealmName(); + } + + @Override + public String getUserLogin() { + return USER_LOGIN; + } + + @Override + public String getUserPassword() { + return USER_PASSWORD; + } + + @Override + public String getUserEmail() { + return USER_EMAIL; + } + + @Override + public String getIDPAlias() { + return IDP_SAML_ALIAS; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java index 98455bdad966..745d7d8dcdcd 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerDestinationTest.java @@ -14,26 +14,24 @@ import org.keycloak.testframework.events.EventAssertion; import org.keycloak.testframework.events.Events; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.w3c.dom.Element; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_LOGIN; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlBrokerDestinationTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java index 75a053809071..0020a9140add 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java @@ -32,7 +32,6 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.ReverseProxy; import org.keycloak.testsuite.util.SamlClient; @@ -44,22 +43,22 @@ import org.apache.http.ssl.SSLContextBuilder; import org.junit.Ignore; import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_LOGIN; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_PASSWORD; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlBrokerFrontendUrlTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java index 2e3f095b5eff..8f3b0ca09a9a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionDisabledTest.java @@ -1,13 +1,15 @@ +package org.keycloak.tests.broker; + import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -package org.keycloak.tests.broker; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlBrokerLoginHintWithOptionDisabledTest extends AbstractSamlLoginHintTest { @InjectRealm ManagedRealm managedRealm; + @Override boolean isLoginHintOptionEnabled() { return false; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java index 0344195b273f..7539fcc4cef2 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerLoginHintWithOptionEnabledTest.java @@ -1,8 +1,6 @@ package org.keycloak.tests.broker; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -11,11 +9,14 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.junit.jupiter.api.Assertions.assertEquals; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlBrokerLoginHintWithOptionEnabledTest extends AbstractSamlLoginHintTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java index 8c19c77a883f..59f22ba4e23a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerSessionNotOnOrAfterTest.java @@ -17,17 +17,17 @@ import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_LOGIN; -import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlBrokerSessionNotOnOrAfterTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java index 0cd1eb223352..96d7aa32d305 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java @@ -45,7 +45,7 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.SamlClient; @@ -61,8 +61,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.saml.RoleMapperTest.ROLE_ATTRIBUTE_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.testsuite.util.Matchers.bodyHC; import static org.keycloak.testsuite.util.Matchers.isSamlResponse; import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; @@ -78,7 +77,7 @@ /** * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest { @InjectRealm @@ -96,6 +95,7 @@ protected BrokerConfiguration getBrokerConfiguration() { } private static final String EMPTY_ATTRIBUTE_NAME = "empty.attribute.name"; + private static final String ROLE_ATTRIBUTE_NAME = "Role"; @Override protected Iterable createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode) { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java index 411ab5297f0f..3b2bda1e10db 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlCustomEntityIdBrokerTest.java @@ -9,24 +9,24 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; /** * Final class as it's not intended to be overriden. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlCustomEntityIdBrokerTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java index 45de21593e0a..9e003798ddc5 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlDefaultIdpTest.java @@ -1,15 +1,16 @@ package org.keycloak.tests.broker; -import org.junit.jupiter.api.Test; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import org.junit.jupiter.api.Test; + /** * Test of various scenarios related to the use of default IdP option * in the Identity Provider Redirector authenticator */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlDefaultIdpTest extends AbstractDefaultIdpTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java index fc82b6e3abe7..29438362dafc 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedAssertionTest.java @@ -41,7 +41,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlEncryptedAssertionTest extends AbstractKcSamlEncryptedElementsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java index 74ef9aa62ab8..84a83013b366 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlEncryptedIdTest.java @@ -25,7 +25,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlEncryptedIdTest extends AbstractKcSamlEncryptedElementsTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java index ac5f87347dcc..daa2edcb56fd 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlFirstBrokerLoginTest.java @@ -1,14 +1,15 @@ package org.keycloak.tests.broker; -import org.junit.jupiter.api.Test; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import org.junit.jupiter.api.Test; + /** * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java index c5e5a729ab28..368d75d1e9ec 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlForceAuthnBrokerTest.java @@ -26,22 +26,22 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; /** * @author phamann */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlForceAuthnBrokerTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java index aaad891a8392..92ebd78c117f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java @@ -8,6 +8,8 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -16,6 +18,7 @@ import jakarta.ws.rs.core.Response; +import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.UsersResource; @@ -41,31 +44,33 @@ import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants; import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil; +import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; -import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testframework.ui.page.ErrorPage; import org.keycloak.testframework.ui.page.LoginPage; -import org.keycloak.testframework.ui.page.PageUtils; import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; import org.keycloak.testsuite.utils.io.IOUtil; -import org.keycloak.testframework.ui.annotations.InjectPage; +import org.jboss.logging.Logger; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -84,15 +89,20 @@ * * @author hmlnarik */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlIdPInitiatedSsoTest { + private static final Logger log = Logger.getLogger(KcSamlIdPInitiatedSsoTest.class); + @InjectRealm ManagedRealm managedRealm; @InjectWebDriver ManagedWebDriver driver; + @InjectAdminClient + Keycloak adminClient; + private static final String PROVIDER_REALM_USER_NAME = "test"; private static final String PROVIDER_REALM_USER_PASSWORD = "test"; @@ -110,9 +120,14 @@ public class KcSamlIdPInitiatedSsoTest { private String urlRealmConsumer2; private String urlRealmConsumer; private String urlRealmProvider; + private final Map cleanups = new HashMap<>(); protected String getAuthRoot() { - return suiteContext.getAuthServerInfo().getContextRoot().toString(); + return getAuthServerContextRoot(); + } + + private CleanupSupport getCleanup(String realm) { + return cleanups.computeIfAbsent(realm, CleanupSupport::new); } private RealmRepresentation loadFromClasspath(String fileName, Properties properties) { @@ -126,6 +141,18 @@ private RealmRepresentation loadFromClasspath(String fileName, Properties proper } } + private static class CleanupSupport { + private final List cleanups = new ArrayList<>(); + + private CleanupSupport(String realm) { + } + + CleanupSupport addCleanup(AutoCloseable cleanup) { + cleanups.add(cleanup); + return this; + } + } + @BeforeEach public void cleanupTestUserInConsumerRealm() { final UsersResource users = adminClient.realm(REALM_CONS_NAME).users(); @@ -152,7 +179,6 @@ public void resetPrincipalType() { idp.update(rep); } - @Override public void addTestRealms(List testRealms) { initRealmUrls(); @@ -307,9 +333,11 @@ private String getSamlBrokerUrl(String realmName) { } private void waitForPage(final String title, final boolean htmlTitle) { - WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5)); + WebDriverWait wait = new WebDriverWait(driver.driver(), Duration.ofSeconds(5)); - ExpectedCondition condition = (WebDriver input) -> htmlTitle ? input.getTitle().toLowerCase().contains(title) : PageUtils.getPageTitle(input).toLowerCase().contains(title); + ExpectedCondition condition = (WebDriver input) -> htmlTitle + ? input.getTitle().toLowerCase().contains(title) + : input.findElement(By.id("kc-page-title")).getText().toLowerCase().contains(title); wait.until(condition); } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java index 223acd4bb875..414345b21ff5 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlLogoutTest.java @@ -21,7 +21,7 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ClientBuilder; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.updaters.UserAttributeUpdater; @@ -33,11 +33,11 @@ import org.junit.jupiter.api.Test; import org.w3c.dom.Document; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; -import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME; import static org.keycloak.testsuite.util.Matchers.isSamlLogoutRequest; import static org.keycloak.testsuite.util.Matchers.isSamlResponse; import static org.keycloak.testsuite.util.Matchers.isSamlStatusResponse; @@ -47,7 +47,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlLogoutTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java index 4b537b3d8e67..a5813a194e4c 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java @@ -46,8 +46,7 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; import org.keycloak.testframework.remote.timeoffset.TimeOffSet; -import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.KeyUtils; @@ -56,16 +55,15 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; /** * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlMetadataSignedAndEncryptedBrokerTest extends AbstractKcSamlMetadataBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java index 76afee6f4d0d..39cfc2b54fd5 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java @@ -42,14 +42,14 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlMetadataSignedBrokerTest extends AbstractKcSamlMetadataBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java index bf6c12b033fa..4822ebabc0f4 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleAttributeToRoleMappersTest.java @@ -23,12 +23,12 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; - -import com.google.common.collect.ImmutableMap; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import com.google.common.collect.ImmutableMap; + /** * Runs the same tests as {@link AttributeToRoleMapperTest} but using multiple SAML mappers that map different IDP attributes * to the same {@code Keycloak} role. @@ -50,7 +50,7 @@ * @author Stefan Guilhen, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlMultipleAttributeToRoleMappersTest extends AttributeToRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java index c832a924407d..0c9f5aa31afd 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java @@ -34,20 +34,18 @@ import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.BrowserTabUtil; import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.hamcrest.Matchers; import org.junit.Rule; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; @@ -56,7 +54,7 @@ * * @author Marek Posolda */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerTest { @InjectRealm @@ -88,8 +86,8 @@ protected BrokerConfiguration getBrokerConfiguration() { // Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved @Test public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider() { - assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver); - try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { + assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver.driver() instanceof HtmlUnitDriver); + try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver.driver())) { // Open login page in tab1 and click "login with IDP" oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); @@ -149,10 +147,10 @@ public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsu @Test public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider() throws Exception { - assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver); + assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver.driver() instanceof HtmlUnitDriver); // Testing the scenario when authenticationSession expired only in "provider" realm and "consumer" is able to handle it at IDP. // So need to increase authSession timeout on "consumer" - try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver); + try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver.driver()); AutoCloseable realmUpdater = new RealmAttributeUpdater(adminClient.realm(bc.consumerRealmName())) .setAccessCodeLifespanLogin(7200) .update() diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java index 710a2f08fae7..6e73e1911b8d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java @@ -17,14 +17,17 @@ package org.keycloak.tests.broker; import java.io.IOException; +import java.net.URI; import java.util.List; import java.util.Map; import jakarta.ws.rs.core.MediaType; +import org.keycloak.admin.client.Keycloak; import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; @@ -36,20 +39,26 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; /** * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlParseIdPDescriptorTest { @InjectRealm ManagedRealm managedRealm; - @Override + @InjectAdminClient + Keycloak adminClient; + + private final URI authServerPage = URI.create(getAuthServerContextRoot() + "/auth"); + public void addTestRealms(List testRealms) { testRealms.add(RealmBuilder.create().name("test").build()); } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java index 021b94e01a0a..1cdc16ddcb71 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlRequestedAuthnContextBrokerTest.java @@ -10,7 +10,7 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; @@ -24,14 +24,14 @@ import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.AC_PASSWORD_PROTECTED_TRANSPORT; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.hamcrest.MatcherAssert.assertThat; /** * Final class as it's not intended to be overriden. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlRequestedAuthnContextBrokerTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java index 7631464164d3..0fed7bfc8c7e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedBrokerTest.java @@ -35,7 +35,7 @@ import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; @@ -44,6 +44,7 @@ import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; import org.keycloak.testsuite.util.oauth.AccessTokenResponse; +import org.keycloak.testsuite.util.saml.SamlConstants; import org.keycloak.testsuite.util.saml.SamlDocumentStepBuilder.Saml2DocumentTransformer; import org.apache.http.HttpResponse; @@ -57,8 +58,8 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; -import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; import static org.keycloak.testsuite.util.Matchers.bodyHC; import static org.keycloak.testsuite.util.Matchers.isSamlResponse; @@ -66,7 +67,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlSignedBrokerTest extends AbstractBrokerTest { @InjectRealm @@ -123,7 +124,7 @@ public void testWithExpiredBrokerCertificate() throws Exception { .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, Boolean.toString(true)) .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_ENCRYPTED, Boolean.toString(false)) .setAttribute(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "true") - .setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, AbstractSamlTest.SAML_CLIENT_SALES_POST_SIG_EXPIRED_CERTIFICATE) + .setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, SamlConstants.SAML_CLIENT_SALES_POST_SIG_EXPIRED_CERTIFICATE) .update(); Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm()) .setAttribute(SamlConfigAttributes.SAML_ENCRYPT, Boolean.toString(false)) @@ -132,8 +133,8 @@ public void testWithExpiredBrokerCertificate() throws Exception { .setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false") .update(); Closeable realmUpdater = new RealmAttributeUpdater(adminClient.realm(bc.providerRealmName())) - .setPublicKey(AbstractSamlTest.SAML_CLIENT_SALES_POST_SIG_EXPIRED_PUBLIC_KEY) - .setPrivateKey(AbstractSamlTest.SAML_CLIENT_SALES_POST_SIG_EXPIRED_PRIVATE_KEY) + .setPublicKey(SamlConstants.SAML_CLIENT_SALES_POST_SIG_EXPIRED_PUBLIC_KEY) + .setPrivateKey(SamlConstants.SAML_CLIENT_SALES_POST_SIG_EXPIRED_PRIVATE_KEY) .update()) { AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted", getConsumerRoot() + "/sales-post/saml", null); @@ -468,7 +469,7 @@ public void testSignatureDataWhenWantsRequestsSigned() throws Exception { .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, Boolean.toString(true)) .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_ENCRYPTED, Boolean.toString(false)) .setAttribute(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "true") - .setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, AbstractSamlTest.SAML_CLIENT_SALES_POST_SIG_EXPIRED_CERTIFICATE) + .setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, SamlConstants.SAML_CLIENT_SALES_POST_SIG_EXPIRED_CERTIFICATE) .update(); Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm()) .setAttribute(SamlConfigAttributes.SAML_ENCRYPT, Boolean.toString(false)) diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java index 23f546768cb8..54d3a3dcae84 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSignedDocumentOnlyBrokerTest.java @@ -12,12 +12,12 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_SIGN_CERT; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_SIGN_KEY; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PRIVATE_KEY; -import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PUBLIC_KEY; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_SIGN_CERT; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_SIGN_KEY; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PRIVATE_KEY; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PUBLIC_KEY; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlSignedDocumentOnlyBrokerTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java index 3d285a18f941..fa2dfd0d25ab 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlSpDescriptorTest.java @@ -1,8 +1,10 @@ package org.keycloak.tests.broker; +import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -38,9 +40,8 @@ import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; import com.google.common.collect.ImmutableMap; -import org.apache.tools.ant.filters.StringInputStream; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import static org.keycloak.testsuite.util.KeyUtils.generateNewRealmKey; @@ -52,7 +53,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertTrue; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlSpDescriptorTest extends AbstractBrokerTest { @InjectRealm @@ -310,7 +311,7 @@ public void testEncKeyDescriptors() throws Exception { private SPSSODescriptorType getExportedSamlProvider() throws ParsingException { String spDescriptorString = identityProviderResource.export(null).readEntity(String.class); SAMLParser parser = SAMLParser.getInstance(); - EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString)); + EntityDescriptorType o = (EntityDescriptorType) parser.parse(new ByteArrayInputStream(spDescriptorString.getBytes(StandardCharsets.UTF_8))); return o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor(); } @@ -346,7 +347,7 @@ public void testKeysExistenceInSpMetadata() throws IOException, ParsingException String spDescriptorString = identityProviderResource.export(null).readEntity(String.class); SAMLParser parser = SAMLParser.getInstance(); - EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString)); + EntityDescriptorType o = (EntityDescriptorType) parser.parse(new ByteArrayInputStream(spDescriptorString.getBytes(StandardCharsets.UTF_8))); SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor(); //the SPSSODescriptor should have at least one KeyDescriptor for encryption and one for Signing @@ -400,7 +401,7 @@ public void testMetadataBindingEqualsKeycloakPOSTBindingSettingsOn() String spDescriptorString = identityProviderResource.export(null).readEntity(String.class); SAMLParser parser = SAMLParser.getInstance(); - EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString)); + EntityDescriptorType o = (EntityDescriptorType) parser.parse(new ByteArrayInputStream(spDescriptorString.getBytes(StandardCharsets.UTF_8))); SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor(); assertThat(spDescriptor.getSingleLogoutService().size(), is(2)); @@ -431,7 +432,7 @@ public void testMetadataBindingEqualsKeycloakPOSTBindingSettingsOff() String spDescriptorString = identityProviderResource.export(null).readEntity(String.class); SAMLParser parser = SAMLParser.getInstance(); - EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString)); + EntityDescriptorType o = (EntityDescriptorType) parser.parse(new ByteArrayInputStream(spDescriptorString.getBytes(StandardCharsets.UTF_8))); SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor(); assertThat(spDescriptor.getSingleLogoutService().size(), is(2)); @@ -457,7 +458,7 @@ public void testMetadataBindingEqualsKeycloakSLOBindingSettingsIsDefault() String spDescriptorString = identityProviderResource.export(null).readEntity(String.class); SAMLParser parser = SAMLParser.getInstance(); - EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString)); + EntityDescriptorType o = (EntityDescriptorType) parser.parse(new ByteArrayInputStream(spDescriptorString.getBytes(StandardCharsets.UTF_8))); SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor(); assertThat(spDescriptor.getSingleLogoutService().size(), is(2)); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java index 75343c5bba69..1f8c96a47ff1 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlUsernameTemplateMapperTest.java @@ -17,15 +17,15 @@ import org.keycloak.testsuite.util.WaitUtils; import com.google.common.collect.ImmutableMap; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import static org.keycloak.broker.saml.mappers.UsernameTemplateMapper.PROVIDER_ID; /** * @author Martin Idel */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlUsernameTemplateMapperTest extends AbstractUsernameTemplateMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java index 71b6fb223072..9b6037229c91 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlXPathAttributeMapperTest.java @@ -17,7 +17,7 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.saml.AbstractSamlTest; +import org.keycloak.tests.saml.AbstractSamlTest; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClientBuilder; @@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test; import org.w3c.dom.Document; -import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_SAML_ALIAS; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -40,7 +40,7 @@ * This contains only the happy path. Have a look at org.keycloak.test.broker.saml.XPathAttributeMapperTest * for unit style tests and handling parsing errors. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlXPathAttributeMapperTest extends AbstractInitializedBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/NestedBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/NestedBrokerConfiguration.java new file mode 100644 index 000000000000..99f8d25f4272 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/NestedBrokerConfiguration.java @@ -0,0 +1,15 @@ +package org.keycloak.tests.broker; + +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; + +public interface NestedBrokerConfiguration extends BrokerConfiguration { + + RealmRepresentation createSubConsumerRealm(); + + String subConsumerRealmName(); + + IdentityProviderRepresentation setUpConsumerIdentityProvider(); + + String getSubConsumerIDPDisplayName(); +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java index ae9454eb3dc2..00ae7f9928ba 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java @@ -17,10 +17,6 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.UserRepresentation; - -import org.junit.Ignore; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -29,10 +25,14 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.junit.Ignore; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + /** * Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class OAuth2BrokerTest extends AbstractAdvancedBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java index 3d477b1a7db2..476dba3e0667 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAccessTokenOnlyClaimsUserAttributeMapperTest.java @@ -14,9 +14,9 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcAccessTokenOnlyClaimsUserAttributeMapperTest extends OidcUserAttributeMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java index 565d25709f83..4f76d7782a60 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToGroupMapperTest.java @@ -16,13 +16,13 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT; @@ -31,7 +31,7 @@ * @author Artur Baltabayev, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcAdvancedClaimToGroupMapperTest extends AbstractGroupBrokerMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java index e5a67696dee6..cded754cc60d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcAdvancedClaimToRoleMapperTest.java @@ -17,7 +17,7 @@ * Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcAdvancedClaimToRoleMapperTest extends AbstractAdvancedRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java new file mode 100644 index 000000000000..fa1dffe2f33e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java @@ -0,0 +1,325 @@ +package org.keycloak.tests.broker; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.mappers.HardcodedClaim; +import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; +import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; +import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; + +import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_SECRET; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; +import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; + +public class OidcBackchannelLogoutBrokerConfiguration implements NestedBrokerConfiguration { + + public static final OidcBackchannelLogoutBrokerConfiguration INSTANCE = + new OidcBackchannelLogoutBrokerConfiguration(); + + protected static final String ATTRIBUTE_TO_MAP_NAME = "user-attribute"; + protected static final String ATTRIBUTE_TO_MAP_NAME_2 = "user-attribute-2"; + public static final String USER_INFO_CLAIM = "user-claim"; + public static final String HARDOCDED_CLAIM = "test"; + public static final String HARDOCDED_VALUE = "value"; + + public static final String REALM_SUB_CONS_NAME = "subconsumer"; + public static final String CONSUMER_CLIENT_ID = "consumer-brokerapp"; + public static final String CONSUMER_CLIENT_SECRET = "consumer-secret"; + + public static final String SUB_CONSUMER_IDP_OIDC_ALIAS = "consumer-kc-oidc-idp"; + public static final String SUB_CONSUMER_IDP_OIDC_PROVIDER_ID = "keycloak-oidc"; + + @Override + public RealmRepresentation createProviderRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_PROV_NAME); + realm.setEnabled(true); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsEnabled(true); + + return realm; + } + + @Override + public RealmRepresentation createConsumerRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_CONS_NAME); + realm.setEnabled(true); + realm.setResetPasswordAllowed(true); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsEnabled(true); + + return realm; + } + + @Override + public List createProviderClients() { + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(getIDPClientIdInProviderRealm()); + client.setName(CLIENT_ID); + client.setSecret(CLIENT_SECRET); + client.setEnabled(true); + + OIDCAdvancedConfigWrapper oidcAdvancedConfigWrapper = + OIDCAdvancedConfigWrapper.fromClientRepresentation(client); + oidcAdvancedConfigWrapper.setBackchannelLogoutSessionRequired(true); + oidcAdvancedConfigWrapper.setBackchannelLogoutRevokeOfflineTokens(false); + oidcAdvancedConfigWrapper.setBackchannelLogoutUrl(getConsumerRoot() + + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/logout/backchannel-logout"); + + client.setRedirectUris(Collections.singletonList(getConsumerRoot() + + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint/*")); + + client.setAdminUrl(getConsumerRoot() + + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint"); + + ProtocolMapperRepresentation emailMapper = new ProtocolMapperRepresentation(); + emailMapper.setName("email"); + emailMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + emailMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID); + + Map emailMapperConfig = emailMapper.getConfig(); + emailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "email"); + emailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "email"); + emailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + + ProtocolMapperRepresentation nestedAttrMapper = new ProtocolMapperRepresentation(); + nestedAttrMapper.setName("attribute - nested claim"); + nestedAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + nestedAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map nestedEmailMapperConfig = nestedAttrMapper.getConfig(); + nestedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "nested.email"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "nested.email"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + + ProtocolMapperRepresentation dottedAttrMapper = new ProtocolMapperRepresentation(); + dottedAttrMapper.setName("attribute - claim with dot in name"); + dottedAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + dottedAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map dottedEmailMapperConfig = dottedAttrMapper.getConfig(); + dottedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "dotted.email"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "dotted\\.email"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + + ProtocolMapperRepresentation userAttrMapper = new ProtocolMapperRepresentation(); + userAttrMapper.setName("attribute - name"); + userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map userAttrMapperConfig = userAttrMapper.getConfig(); + userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, ATTRIBUTE_TO_MAP_NAME); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, ATTRIBUTE_TO_MAP_NAME); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + userAttrMapperConfig.put(ProtocolMapperUtils.MULTIVALUED, "true"); + + ProtocolMapperRepresentation userAttrMapper2 = new ProtocolMapperRepresentation(); + userAttrMapper2.setName("attribute - name - 2"); + userAttrMapper2.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + userAttrMapper2.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); + + Map userAttrMapperConfig2 = userAttrMapper2.getConfig(); + userAttrMapperConfig2.put(ProtocolMapperUtils.USER_ATTRIBUTE, ATTRIBUTE_TO_MAP_NAME_2); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, ATTRIBUTE_TO_MAP_NAME_2); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + userAttrMapperConfig2.put(ProtocolMapperUtils.MULTIVALUED, "true"); + + ProtocolMapperRepresentation hardcodedJsonClaim = new ProtocolMapperRepresentation(); + hardcodedJsonClaim.setName("json-mapper"); + hardcodedJsonClaim.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + hardcodedJsonClaim.setProtocolMapper(HardcodedClaim.PROVIDER_ID); + + Map hardcodedJsonClaimMapperConfig = hardcodedJsonClaim.getConfig(); + hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, + OidcBackchannelLogoutBrokerConfiguration.USER_INFO_CLAIM); + hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, "JSON"); + hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + hardcodedJsonClaimMapperConfig.put(HardcodedClaim.CLAIM_VALUE, + "{\"" + HARDOCDED_CLAIM + "\": \"" + HARDOCDED_VALUE + "\"}"); + + client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper, userAttrMapper2, nestedAttrMapper, + dottedAttrMapper, hardcodedJsonClaim)); + + return Collections.singletonList(client); + } + + @Override + public List createConsumerClients() { + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(CONSUMER_CLIENT_ID); + client.setName(CONSUMER_CLIENT_ID); + client.setSecret(CONSUMER_CLIENT_SECRET); + client.setEnabled(true); + client.setDirectAccessGrantsEnabled(true); + + client.setRedirectUris(List.of("*")); + + client.setBaseUrl(getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/app"); + + OIDCAdvancedConfigWrapper oidcAdvancedConfigWrapper = + OIDCAdvancedConfigWrapper.fromClientRepresentation(client); + oidcAdvancedConfigWrapper.setBackchannelLogoutSessionRequired(true); + oidcAdvancedConfigWrapper.setBackchannelLogoutRevokeOfflineTokens(false); + oidcAdvancedConfigWrapper.setBackchannelLogoutUrl(getConsumerRoot() + + "/auth/realms/" + REALM_SUB_CONS_NAME + "/protocol/openid-connect/logout/backchannel-logout"); + + return Collections.singletonList(client); + } + + @Override + public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { + IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID); + + Map config = idp.getConfig(); + applyDefaultConfiguration(config, syncMode); + + return idp; + } + + protected void applyDefaultConfiguration(final Map config, + IdentityProviderSyncMode syncMode) { + config.put(IdentityProviderModel.SYNC_MODE, syncMode.toString()); + config.put("clientId", CLIENT_ID); + config.put("clientSecret", CLIENT_SECRET); + config.put("prompt", "login"); + config.put("issuer", getConsumerRoot() + "/auth/realms/" + REALM_PROV_NAME); + config.put("authorizationUrl", + getConsumerRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/auth"); + config.put("tokenUrl", + getConsumerRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/token"); + config.put("logoutUrl", + getConsumerRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/logout"); + config.put("userInfoUrl", + getConsumerRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/userinfo"); + config.put("defaultScope", "email profile"); + config.put("backchannelSupported", "true"); + config.put(OIDCIdentityProviderConfig.JWKS_URL, + getConsumerRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/certs"); + config.put(OIDCIdentityProviderConfig.USE_JWKS_URL, "true"); + config.put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, "true"); + } + + @Override + public String getUserLogin() { + return USER_LOGIN; + } + + @Override + public String getIDPClientIdInProviderRealm() { + return CLIENT_ID; + } + + @Override + public String getUserPassword() { + return USER_PASSWORD; + } + + @Override + public String getUserEmail() { + return USER_EMAIL; + } + + @Override + public String providerRealmName() { + return REALM_PROV_NAME; + } + + @Override + public String consumerRealmName() { + return REALM_CONS_NAME; + } + + @Override + public String getIDPAlias() { + return IDP_OIDC_ALIAS; + } + + @Override + public RealmRepresentation createSubConsumerRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_SUB_CONS_NAME); + realm.setEnabled(true); + realm.setResetPasswordAllowed(true); + realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsEnabled(true); + + return realm; + } + + @Override + public String subConsumerRealmName() { + return REALM_SUB_CONS_NAME; + } + + @Override + public IdentityProviderRepresentation setUpConsumerIdentityProvider() { + IdentityProviderRepresentation idp = + createIdentityProvider(SUB_CONSUMER_IDP_OIDC_ALIAS, SUB_CONSUMER_IDP_OIDC_PROVIDER_ID); + + Map config = idp.getConfig(); + config.put(IdentityProviderModel.SYNC_MODE, IdentityProviderSyncMode.IMPORT.toString()); + config.put("clientId", CONSUMER_CLIENT_ID); + config.put("clientSecret", CONSUMER_CLIENT_SECRET); + config.put("prompt", "login"); + config.put("issuer", getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME); + config.put("authorizationUrl", + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/auth"); + config.put("tokenUrl", + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/token"); + config.put("logoutUrl", + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/logout"); + config.put("userInfoUrl", + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/userinfo"); + config.put("defaultScope", "email profile"); + config.put("backchannelSupported", "true"); + config.put(OIDCIdentityProviderConfig.JWKS_URL, + getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/certs"); + config.put(OIDCIdentityProviderConfig.USE_JWKS_URL, "true"); + config.put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, "true"); + + return idp; + } + + @Override + public String getSubConsumerIDPDisplayName() { + return SUB_CONSUMER_IDP_OIDC_ALIAS; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java index 9e73921a96bb..3ab3b40b5205 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToRoleMapperTest.java @@ -9,13 +9,13 @@ import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE; import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY; @@ -24,7 +24,7 @@ * @author Martin Idel, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcClaimToRoleMapperTest extends AbstractRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java index 00daecd3e972..0c292e04ce3c 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcClaimToUserSessionNoteMapperTest.java @@ -31,15 +31,15 @@ import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcClaimToUserSessionNoteMapperTest extends AbstractIdentityProviderMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java index a99963514016..e370cbce812d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcHardcodedGroupMapperTest.java @@ -10,12 +10,12 @@ import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; - -import com.google.common.collect.ImmutableMap; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import com.google.common.collect.ImmutableMap; + /** * @author DanieleMartinoli * @@ -25,7 +25,7 @@ * Since this mapper does not cause leaving the group when the claims do not match, an isHardcodedGroup * method is introduced to customize the behavior in the super class. */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcHardcodedGroupMapperTest extends OidcAdvancedClaimToGroupMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java index 4f23f93b801c..b98eace0e231 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcMultipleClaimToRoleMappersTest.java @@ -23,12 +23,12 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; - -import com.google.common.collect.ImmutableMap; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; +import com.google.common.collect.ImmutableMap; + /** * Runs the same tests as {@link OidcClaimToRoleMapperTest} but using multiple OIDC mappers that map different IDP claims * to the same {@code Keycloak} role. @@ -50,7 +50,7 @@ * @author Stefan Guilhen, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcMultipleClaimToRoleMappersTest extends OidcClaimToRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java index 3c8768b25562..53bf222655e3 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserAttributeMapperTest.java @@ -4,14 +4,14 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -@KeycloakIntegrationTest +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java index 65c3728e4bc9..5760598f7a16 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcUserInfoClaimToRoleMapperTest.java @@ -18,19 +18,19 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; /** * @author Dashaylan Naidoo, * Daniel Fesenmeyer */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class OidcUserInfoClaimToRoleMapperTest extends AbstractRoleMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV1Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV1Test.java index bdd504f820c2..6cd49c1e7489 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV1Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV1Test.java @@ -43,7 +43,7 @@ * * @author rmartinc */ -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class SamlIdentityProviderStoreTokenV1Test implements InterfaceIdentityProviderStoreTokenV1Test, InterfaceSamlIdentityProviderStoreTokenTest { @InjectRealm(config = IdpRealmConfig.class) diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV2Test.java b/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV2Test.java index 9b7f288fc06b..90e7cdc20b64 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV2Test.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SamlIdentityProviderStoreTokenV2Test.java @@ -119,7 +119,8 @@ public void checkSuccessfulTokenResponse(AbstractHttpResponse response) { static class IdentityBrokeringAPIV2ServerConfig implements KeycloakServerConfig { @Override public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { - return config.features(Profile.Feature.IDENTITY_BROKERING_API_V2); + return config.features(Profile.Feature.IDENTITY_BROKERING_API_V2) + .dependency("org.keycloak.tests", "keycloak-tests-custom-providers"); } } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java index a4948c89421b..0f93eed42224 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SamlUserAttributeMapperTest.java @@ -4,16 +4,16 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +import static org.keycloak.tests.broker.KcSamlBrokerConfiguration.ATTRIBUTE_TO_MAP_FRIENDLY_NAME; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java index eb286340571d..18b663077afb 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java @@ -1,6 +1,7 @@ package org.keycloak.tests.broker; import java.io.FileInputStream; +import java.net.URI; import java.util.List; import java.util.Properties; @@ -12,6 +13,7 @@ import jakarta.ws.rs.core.Response; import org.keycloak.OAuth2Constants; +import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; @@ -33,6 +35,7 @@ import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.services.resources.admin.fgap.AdminPermissionManagement; import org.keycloak.services.resources.admin.fgap.AdminPermissions; +import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -42,63 +45,64 @@ import org.keycloak.testframework.realm.RealmBuilder; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.page.LoginPage; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected; import org.keycloak.testsuite.auth.page.login.UpdateAccount; -import org.keycloak.testframework.ui.page.LoginPage; -import org.keycloak.testframework.ui.page.social.AbstractSocialLoginPage; -import org.keycloak.testframework.ui.page.social.BitbucketLoginPage; -import org.keycloak.testframework.ui.page.social.FacebookLoginPage; -import org.keycloak.testframework.ui.page.social.GitHubLoginPage; -import org.keycloak.testframework.ui.page.social.GitLabLoginPage; -import org.keycloak.testframework.ui.page.social.GoogleLoginPage; -import org.keycloak.testframework.ui.page.social.InstagramLoginPage; -import org.keycloak.testframework.ui.page.social.LinkedInLoginPage; -import org.keycloak.testframework.ui.page.social.MicrosoftLoginPage; -import org.keycloak.testframework.ui.page.social.OpenShiftLoginPage; -import org.keycloak.testframework.ui.page.social.PayPalLoginPage; -import org.keycloak.testframework.ui.page.social.StackOverflowLoginPage; -import org.keycloak.testframework.ui.page.social.TwitterConsentLoginPage; +import org.keycloak.testsuite.client.KeycloakTestingClient; +import org.keycloak.testsuite.pages.social.AbstractSocialLoginPage; +import org.keycloak.testsuite.pages.social.BitbucketLoginPage; +import org.keycloak.testsuite.pages.social.FacebookLoginPage; +import org.keycloak.testsuite.pages.social.GitHubLoginPage; +import org.keycloak.testsuite.pages.social.GitLabLoginPage; +import org.keycloak.testsuite.pages.social.GoogleLoginPage; +import org.keycloak.testsuite.pages.social.InstagramLoginPage; +import org.keycloak.testsuite.pages.social.LinkedInLoginPage; +import org.keycloak.testsuite.pages.social.MicrosoftLoginPage; +import org.keycloak.testsuite.pages.social.OpenShiftLoginPage; +import org.keycloak.testsuite.pages.social.PayPalLoginPage; +import org.keycloak.testsuite.pages.social.StackOverflowLoginPage; +import org.keycloak.testsuite.pages.social.TwitterConsentLoginPage; import org.keycloak.testsuite.util.AdminClientUtil; -import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.URLUtils; import org.keycloak.testsuite.util.WaitUtils; -import org.keycloak.testsuite.util.oauth.OAuthClient; import org.keycloak.util.BasicAuthHelper; import com.google.common.collect.ImmutableMap; -import org.jboss.arquillian.graphene.Graphene; -import org.keycloak.testframework.ui.annotations.InjectPage; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.jboss.logging.Logger; import org.junit.BeforeClass; import org.junit.Ignore; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; - -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.BITBUCKET; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.FACEBOOK; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.FACEBOOK_INCLUDE_BIRTHDAY; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB_PRIVATE_EMAIL; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITLAB; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE_HOSTED_DOMAIN; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE_NON_MATCHING_HOSTED_DOMAIN; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.INSTAGRAM; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.LINKEDIN; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.MICROSOFT; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.MICROSOFT_SINGLE_TENANT; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.OPENSHIFT4; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.OPENSHIFT4_KUBE_ADMIN; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.PAYPAL; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.STACKOVERFLOW; -import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.TWITTER; +import org.openqa.selenium.support.PageFactory; + +import static org.keycloak.tests.broker.SocialLoginTest.Provider.BITBUCKET; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.FACEBOOK; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.FACEBOOK_INCLUDE_BIRTHDAY; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.GITHUB; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.GITHUB_PRIVATE_EMAIL; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.GITLAB; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.GOOGLE; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.GOOGLE_HOSTED_DOMAIN; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.GOOGLE_NON_MATCHING_HOSTED_DOMAIN; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.INSTAGRAM; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.LINKEDIN; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.MICROSOFT; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.MICROSOFT_SINGLE_TENANT; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.OPENSHIFT4; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.OPENSHIFT4_KUBE_ADMIN; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.PAYPAL; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.STACKOVERFLOW; +import static org.keycloak.tests.broker.SocialLoginTest.Provider.TWITTER; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; import static org.junit.Assume.assumeTrue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -110,9 +114,11 @@ * @author Vaclav Muzikar */ @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class SocialLoginTest { + private static final Logger log = Logger.getLogger(SocialLoginTest.class); + @InjectRealm ManagedRealm managedRealm; @@ -125,6 +131,11 @@ public class SocialLoginTest { @InjectOAuthClient OAuthClient oauth; + @InjectAdminClient + Keycloak adminClient; + + private KeycloakTestingClient testingClient; + public static final String SOCIAL_CONFIG = "social.config"; public static final String REALM = "social"; public static final String EXCHANGE_CLIENT = "exchange-client"; @@ -204,6 +215,10 @@ public void beforeSocialLoginTest() { public void afterSocialLoginTest() { currentTestProvider = null; oauth.realm("test"); + if (testingClient != null) { + testingClient.close(); + testingClient = null; + } } private void removeUser() { @@ -220,24 +235,68 @@ private void setTestProvider(Provider provider) { adminClient.realm(REALM).identityProviders().create(buildIdp(provider)); log.infof("added '%s' identity provider", provider.id()); currentTestProvider = provider; - currentSocialLoginPage = Graphene.createPageFragment(currentTestProvider.pageObjectClazz(), driver.findElement(By.tagName("html"))); + try { + currentSocialLoginPage = currentTestProvider.pageObjectClazz().getDeclaredConstructor().newInstance(); + currentSocialLoginPage.setDriver(driver.driver()); + PageFactory.initElements(driver.driver(), currentSocialLoginPage); + } catch (Exception e) { + throw new RuntimeException("Failed to initialize social page object", e); + } if(provider == OPENSHIFT4 || provider == OPENSHIFT4_KUBE_ADMIN) { ((OpenShiftLoginPage) currentSocialLoginPage).setUserLoginLinkTitle(getConfig(currentTestProvider, "loginBtnTitle")); } } - @Override public void addTestRealms(List testRealms) { RealmRepresentation rep = RealmBuilder.create().name(REALM).build(); testRealms.add(rep); } - @Override protected boolean isImportAfterEachMethod() { return true; } + private void createAppClientInRealm(String realm) { + org.keycloak.representations.idm.ClientRepresentation client = new org.keycloak.representations.idm.ClientRepresentation(); + client.setClientId("test-app"); + client.setName("test-app"); + client.setSecret("password"); + client.setEnabled(true); + client.setDirectAccessGrantsEnabled(true); + client.setRedirectUris(java.util.Collections.singletonList(OAuthClient.SERVER_ROOT + "/auth/*")); + client.setBaseUrl(OAuthClient.SERVER_ROOT + "/auth/realms/" + realm + "/app"); + try { + adminClient.realm(realm).clients().create(client).close(); + } catch (Exception ignored) { + } + } + + private URI getAuthServerRoot() { + return URI.create(resolveAuthServerRoot() + "/auth/"); + } + + private KeycloakTestingClient getTestingClient() { + if (testingClient == null) { + testingClient = KeycloakTestingClient.getInstance(resolveAuthServerRoot()); + } + return testingClient; + } + + private String resolveAuthServerRoot() { + if (managedRealm != null && managedRealm.getBaseUrl() != null) { + int realmsIndex = managedRealm.getBaseUrl().indexOf("/realms/"); + if (realmsIndex > 0) { + String root = managedRealm.getBaseUrl().substring(0, realmsIndex); + return root.endsWith("/auth") ? root.substring(0, root.length() - "/auth".length()) : root; + } + } + if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { + return OAuthClient.SERVER_ROOT.replaceAll("/+$", ""); + } + return getAuthServerContextRoot(); + } + public static void setupClientExchangePermissions(KeycloakSession session) { RealmModel realm = session.realms().getRealmByName(REALM); ClientModel client = session.clients().getClientByClientId(realm, EXCHANGE_CLIENT); @@ -299,7 +358,7 @@ public void openshift4LoginWithGroupsMapper() { public void googleLogin() throws InterruptedException { setTestProvider(GOOGLE); performLogin(); - DroneUtils.getCurrentDriver().findElement(By.xpath("//button//span[contains(.,'Continue')]")).click(); + driver.findElement(By.xpath("//button//span[contains(.,'Continue')]")).click(); WaitUtils.pause(3000); WaitUtils.waitForPageToLoad(); Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); @@ -638,7 +697,7 @@ protected void testTokenExchange() { String username = users.get(0).getUsername(); checkFeature(400, username); - testingClient.enableFeature(Profile.Feature.TOKEN_EXCHANGE); + getTestingClient().enableFeature(Profile.Feature.TOKEN_EXCHANGE); Client httpClient = AdminClientUtil.createResteasyClient(); @@ -722,7 +781,7 @@ protected void testTokenExchange() { adminClient.realm(REALM).identityProviders().get(idp.getAlias()).update(idp); } finally { httpClient.close(); - testingClient.disableFeature(Profile.Feature.TOKEN_EXCHANGE); + getTestingClient().disableFeature(Profile.Feature.TOKEN_EXCHANGE); checkFeature(400, username); } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java index a09672b230db..c59e31314c69 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/UsernameTemplateMapperTest.java @@ -7,20 +7,20 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.keycloak.testframework.annotations.InjectRealm; -import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; -import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.junit.jupiter.api.Assertions.assertEquals; -@KeycloakIntegrationTest +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java new file mode 100644 index 000000000000..767507f40404 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.broker.oidc; + +import java.util.Collections; +import java.util.Set; + +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; + +import org.keycloak.OAuth2Constants; +import org.keycloak.authentication.ClientAuthenticationFlowContext; +import org.keycloak.authentication.authenticators.client.ClientAuthUtil; +import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; + +/** + * A {@link JWTClientAuthenticator} that requires the optional client_id parameter. + * + * @author Justin Tay + */ +public class ClientIdRequiredJWTClientAuthenticator extends JWTClientAuthenticator { + + public static final String PROVIDER_ID = "testsuite-client-id-required"; + + @Override + public void authenticateClient(ClientAuthenticationFlowContext context) { + MultivaluedMap params = context.getHttpRequest().getDecodedFormParameters(); + + String clientId = params.getFirst(OAuth2Constants.CLIENT_ID); + if (clientId == null) { + Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Missing client_id parameter"); + context.challenge(challengeResponse); + return; + } + + super.authenticateClient(context); + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public Set getProtocolAuthenticatorMethods(String loginProtocol) { + // Do not add as it will affect the well known provider test + return Collections.emptySet(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProvider.java b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProvider.java new file mode 100644 index 000000000000..f50b31ee33ae --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.broker.oidc; + +import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider; +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.models.KeycloakSession; + +/** + * @author Vaclav Muzikar + */ +public class LegacyIdIdentityProvider extends KeycloakOIDCIdentityProvider { + public static final String LEGACY_ID = "3.14159265359"; + + public LegacyIdIdentityProvider(KeycloakSession session, OIDCIdentityProviderConfig config) { + super(session, config); + } + + @Override + public BrokeredIdentityContext getFederatedIdentity(String response) { + BrokeredIdentityContext user = super.getFederatedIdentity(response); + user.setLegacyId(LEGACY_ID); + return user; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProviderFactory.java b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProviderFactory.java new file mode 100644 index 000000000000..860eba292201 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/LegacyIdIdentityProviderFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.broker.oidc; + +import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider; +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.broker.oidc.OIDCIdentityProviderFactory; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; + +/** + * @author Vaclav Muzikar + */ +public class LegacyIdIdentityProviderFactory extends OIDCIdentityProviderFactory { + + public static final String PROVIDER_ID = "legacy-id-idp"; + + @Override + public String getName() { + return PROVIDER_ID; + } + + @Override + public KeycloakOIDCIdentityProvider create(KeycloakSession session, IdentityProviderModel model) { + return new LegacyIdIdentityProvider(session, new OIDCIdentityProviderConfig(model)); + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} \ No newline at end of file diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java new file mode 100644 index 000000000000..d6f4a7f26743 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java @@ -0,0 +1,118 @@ +/* + * Copyright 2022 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.broker.oidc; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider; +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.broker.oidc.OIDCIdentityProviderFactory; +import org.keycloak.broker.provider.AuthenticationRequest; +import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.sessions.AuthenticationSessionModel; +import org.keycloak.util.JsonSerialization; + +public class TestKeycloakOidcIdentityProviderFactory extends OIDCIdentityProviderFactory { + + public static final String ID = "test-keycloak-oidc"; + public static final String IGNORE_MAX_AGE_PARAM = "ignore-max-age-param"; + public static final String USE_SINGLE_REFRESH_TOKEN = "use-single-refresh-token"; + public static final String PREFERRED_USERNAME = "preferred-username"; + + public static void setIgnoreMaxAgeParam(IdentityProviderRepresentation rep) { + rep.getConfig().put(IGNORE_MAX_AGE_PARAM, Boolean.TRUE.toString()); + } + + @Override + public String getId() { + return ID; + } + + @Override + public KeycloakOIDCIdentityProvider create(KeycloakSession session, IdentityProviderModel model) { + return new KeycloakOIDCIdentityProvider(session, new OIDCIdentityProviderConfig(model)) { + + private static final Set usernames = new HashSet<>(); + + @Override + public BrokeredIdentityContext getFederatedIdentity(String response) { + BrokeredIdentityContext context = super.getFederatedIdentity(response); + String preferredUsername = getPreferredUsername(); + + if (preferredUsername != null) { + context.setUsername(preferredUsername); + } + if (Boolean.valueOf(model.getConfig().get(USE_SINGLE_REFRESH_TOKEN))) { + // refresh token will be available only in the first login. + if (!usernames.add(context.getUsername())) { + try { + AccessTokenResponse tokenResponse = JsonSerialization.readValue(context.getToken(), AccessTokenResponse.class); + tokenResponse.setRefreshToken(null); + context.setToken(JsonSerialization.writeValueAsString(tokenResponse)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + return context; + } + + @Override + protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) { + AuthenticationSessionModel authSession = request.getAuthenticationSession(); + String maxAge = authSession.getClientNote(OIDCLoginProtocol.MAX_AGE_PARAM); + + try { + if (isIgnoreMaxAgeParam()) { + authSession.removeClientNote(OIDCLoginProtocol.MAX_AGE_PARAM); + } + return super.createAuthorizationUrl(request); + } finally { + authSession.setClientNote(OIDCLoginProtocol.MAX_AGE_PARAM, maxAge); + } + } + + private boolean isIgnoreMaxAgeParam() { + return Boolean.parseBoolean(model.getConfig().getOrDefault(IGNORE_MAX_AGE_PARAM, Boolean.FALSE.toString())); + } + + private String getPreferredUsername() { + return model.getConfig().get(PREFERRED_USERNAME); + } + }; + } + + @Override + public OIDCIdentityProviderConfig createConfig() { + return new OIDCIdentityProviderConfig(super.createConfig()) { + @Override + public String getDisplayIconClasses() { + return "my-custom-idp-icon"; + } + }; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/trust/TrustMaterialIdentityProviderTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/trust/TrustMaterialIdentityProviderTest.java index 18f4f1655150..9ed8e2d07fca 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/trust/TrustMaterialIdentityProviderTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/trust/TrustMaterialIdentityProviderTest.java @@ -327,7 +327,8 @@ public static class TrustMaterialServerConfig implements KeycloakServerConfig { @Override public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { - return config.features(Profile.Feature.CLIENT_AUTH_ABCA); + return config.features(Profile.Feature.CLIENT_AUTH_ABCA) + .dependency("org.keycloak.tests", "keycloak-tests-custom-providers"); } } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/util/SimpleHttpDefault.java b/tests/base/src/test/java/org/keycloak/tests/broker/util/SimpleHttpDefault.java new file mode 100644 index 000000000000..091d4739d4a0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/util/SimpleHttpDefault.java @@ -0,0 +1,52 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.broker.util; + +import org.keycloak.http.simple.SimpleHttp; +import org.keycloak.http.simple.SimpleHttpRequest; + +import org.apache.http.client.HttpClient; + +/** + * This class provides additional builders used in tests to create instances of SimpleHttpTest with a default length response size set. + * + * @author Alexander Schwartz + */ +public abstract class SimpleHttpDefault { + + public static SimpleHttpRequest doDelete(String url, HttpClient client) { + return SimpleHttp.create(client).doDelete(url); + } + + public static SimpleHttpRequest doPost(String url, HttpClient client) { + return SimpleHttp.create(client).doPost(url); + } + + public static SimpleHttpRequest doPut(String url, HttpClient client) { + return SimpleHttp.create(client).doPut(url); + } + + public static SimpleHttpRequest doGet(String url, HttpClient client) { + return SimpleHttp.create(client).doGet(url); + } + + public static SimpleHttpRequest doHead(String url, HttpClient client) { + return SimpleHttp.create(client).doHead(url); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java b/tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java new file mode 100644 index 000000000000..b78b663d3b10 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java @@ -0,0 +1,170 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.tests.providers.federation; + +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.keycloak.component.ComponentModel; +import org.keycloak.credential.CredentialInput; +import org.keycloak.credential.CredentialInputUpdater; +import org.keycloak.credential.CredentialInputValidator; +import org.keycloak.credential.CredentialModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.credential.PasswordCredentialModel; +import org.keycloak.storage.StorageId; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageUtil; +import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage; +import org.keycloak.storage.user.UserLookupProvider; + +/** + * Provides one user where everything is stored in user federated storage + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PassThroughFederatedUserStorageProvider implements + UserStorageProvider, + UserLookupProvider, + CredentialInputValidator, + CredentialInputUpdater +{ + + public static final Set CREDENTIAL_TYPES = Collections.singleton(PasswordCredentialModel.TYPE); + public static final String PASSTHROUGH_USERNAME = "passthrough"; + public static final String INITIAL_PASSWORD = "secret"; + private KeycloakSession session; + private ComponentModel component; + + public PassThroughFederatedUserStorageProvider(KeycloakSession session, ComponentModel component) { + this.session = session; + this.component = component; + } + + public Set getSupportedCredentialTypes() { + return CREDENTIAL_TYPES; + } + + @Override + public boolean supportsCredentialType(String credentialType) { + return getSupportedCredentialTypes().contains(credentialType); + } + + @Override + public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { + if (!PasswordCredentialModel.TYPE.equals(credentialType)) return false; + return true; + } + + @Override + public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { + if (input.getType().equals(PasswordCredentialModel.TYPE)) { + if (INITIAL_PASSWORD.equals(input.getChallengeResponse())) { + return true; + } + return UserStorageUtil.userFederatedStorage(session).getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD") + .map(credentialModel -> credentialModel.getSecretData()) + .anyMatch(Predicate.isEqual("{\"value\":\"" + input.getChallengeResponse() + "\"}")); + } + return false; + } + + @Override + public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { + // testing federated credential attributes + if (input.getType().equals(PasswordCredentialModel.TYPE)) { + Optional existing = UserStorageUtil.userFederatedStorage(session) + .getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD") + .findFirst(); + if (existing.isPresent()) { + CredentialModel model = existing.get(); + model.setType("CLEAR_TEXT_PASSWORD"); + model.setSecretData("{\"value\":\"" + input.getChallengeResponse() + "\"}"); + UserStorageUtil.userFederatedStorage(session).updateCredential(realm, user.getId(), model); + } else { + CredentialModel model = new CredentialModel(); + model.setType("CLEAR_TEXT_PASSWORD"); + model.setSecretData("{\"value\":\"" + input.getChallengeResponse() + "\"}"); + UserStorageUtil.userFederatedStorage(session).createCredential(realm, user.getId(), model); + } + return true; + } + return false; + } + + @Override + public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { + UserStorageUtil.userFederatedStorage(session).getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD") + .collect(Collectors.toList()) + .forEach(credModel -> UserStorageUtil.userFederatedStorage(session).removeStoredCredential(realm, user.getId(), credModel.getId())); + } + + @Override + public Stream getDisableableCredentialTypesStream(RealmModel realm, UserModel user) { + return CREDENTIAL_TYPES.stream(); + } + + @Override + public void close() { + + } + + @Override + public UserModel getUserById(RealmModel realm, String id) { + if (!StorageId.externalId(id).equals(PASSTHROUGH_USERNAME)) return null; + return getUserModel(realm); + } + + @Override + public UserModel getUserByUsername(RealmModel realm, String username) { + if (!PASSTHROUGH_USERNAME.equals(username)) return null; + + return getUserModel(realm); + } + + @Override + public UserModel getUserByEmail(RealmModel realm, String email) { + Optional result = UserStorageUtil.userFederatedStorage(session) + .getUsersByUserAttributeStream(realm, AbstractUserAdapterFederatedStorage.EMAIL_ATTRIBUTE, email) + .map(StorageId::new) + .filter(storageId -> Objects.equals(storageId.getExternalId(), PASSTHROUGH_USERNAME)) + .filter(storageId -> Objects.equals(storageId.getProviderId(), component.getId())) + .findFirst(); + return result.isPresent() ? getUserModel(realm) : null; + } + + private UserModel getUserModel(final RealmModel realm) { + return new AbstractUserAdapterFederatedStorage.Streams(session, realm, component) { + @Override + public String getUsername() { + return PASSTHROUGH_USERNAME; + } + + @Override + public void setUsername(String username) { + } + }; + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java b/tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java new file mode 100644 index 000000000000..d5333c9b23df --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.tests.providers.federation; + +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.storage.UserStorageProviderFactory; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PassThroughFederatedUserStorageProviderFactory implements UserStorageProviderFactory { + + public static final String PROVIDER_ID = "pass-through-federated"; + + @Override + public PassThroughFederatedUserStorageProvider create(KeycloakSession session, ComponentModel model) { + return new PassThroughFederatedUserStorageProvider(session, model); + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthenticationTest.java b/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthenticationTest.java new file mode 100644 index 000000000000..bddfc664d65f --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthenticationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite; + +import java.util.List; + +import org.keycloak.representations.idm.AuthenticationFlowRepresentation; +public final class AbstractAuthenticationTest { + + private AbstractAuthenticationTest() { + } + + public static AuthenticationFlowRepresentation findFlowByAlias(String alias, List flows) { + for (AuthenticationFlowRepresentation flow : flows) { + if (alias.equals(flow.getAlias())) { + return flow; + } + } + return null; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/ProfileAssume.java b/tests/base/src/test/java/org/keycloak/testsuite/ProfileAssume.java new file mode 100644 index 000000000000..7c740c9bfd76 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/ProfileAssume.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite; + +import java.util.Set; + +import org.keycloak.common.Profile; + +import org.junit.jupiter.api.Assumptions; + +/** + * @author Stian Thorgersen + */ +public class ProfileAssume { + + private static Set DISABLED_FEATURES; + + public static void assumeFeatureEnabled(Profile.Feature feature) { + Assumptions.assumeTrue(isFeatureEnabled(feature), "Ignoring test as feature " + feature.getKey() + " is not enabled"); + } + + public static void assumeFeatureDisabled(Profile.Feature feature) { + Assumptions.assumeTrue(!isFeatureEnabled(feature), "Ignoring test as feature " + feature.getKey() + " is enabled"); + } + + public static boolean isFeatureEnabled(Profile.Feature feature) { + return DISABLED_FEATURES == null || !DISABLED_FEATURES.contains(feature); + } + + public static void updateDisabledFeatures(Set disabledFeatures) { + DISABLED_FEATURES = disabledFeatures; + } + + public static Set getDisabledFeatures() { + return DISABLED_FEATURES; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java new file mode 100644 index 000000000000..4cba643c42aa --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.admin; + +import java.net.URI; + +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +/** + * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. + */ +public class ApiUtil { + + public static String getCreatedId(Response response) { + URI location = response.getLocation(); + if (!response.getStatusInfo().equals(Status.CREATED)) { + StatusType statusInfo = response.getStatusInfo(); + response.bufferEntity(); + String body = response.readEntity(String.class); + throw new WebApplicationException("Create method returned status " + + statusInfo.getReasonPhrase() + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201). Response body: " + body, response); + } + if (location == null) { + return null; + } + String path = location.getPath(); + return path.substring(path.lastIndexOf('/') + 1); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/admin/Users.java b/tests/base/src/test/java/org/keycloak/testsuite/admin/Users.java new file mode 100644 index 000000000000..63485312f5d3 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/admin/Users.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.admin; + +import java.util.ArrayList; +import java.util.List; + +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD; + +/** + * + * @author Petr Mensik + * @author tkyjovsk + */ +public class Users { + + public static String getPasswordOf(UserRepresentation user) { + CredentialRepresentation credential = getPasswordCredentialOf(user); + if (credential == null) { + return null; + } + if (credential.getValue() != null && !credential.getValue().isEmpty()) { + return credential.getValue(); + } + return credential.getSecretData(); + } + + public static CredentialRepresentation getPasswordCredentialOf(UserRepresentation user) { + CredentialRepresentation password = null; + if (user.getCredentials() != null) { + for (CredentialRepresentation c : user.getCredentials()) { + if (CredentialRepresentation.PASSWORD.equals(c.getType())) { + password = c; + } + } + } + return password; + } + + public static void setPasswordFor(UserRepresentation user, String password) { + setPasswordFor(user, password, false); + } + public static void setPasswordFor(UserRepresentation user, String password, boolean temporary) { + List credentials = new ArrayList<>(); + CredentialRepresentation pass = new CredentialRepresentation(); + pass.setType(PASSWORD); + pass.setValue(password); + pass.setTemporary(temporary); + credentials.add(pass); + user.setCredentials(credentials); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeature.java b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeature.java new file mode 100644 index 000000000000..6830c0f6db1f --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeature.java @@ -0,0 +1,36 @@ +package org.keycloak.testsuite.arquillian.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.keycloak.common.Profile; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @author mhajas + */ +@Retention(RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@Repeatable(EnableFeatures.class) +public @interface EnableFeature { + + /** + * Feature, which should be enabled. + */ + Profile.Feature value(); + + /** + * The feature will be enabled without restarting of a server. + */ + boolean skipRestart() default false; + + /** + * Feature enable should be the last action in @Before context. + * If the test halted, the feature is returned to the previous state. + * If it's false, feature will be enabled before @Before method. + */ + boolean executeAsLast() default true; +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeatures.java b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeatures.java new file mode 100644 index 000000000000..f8061b87cb86 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableFeatures.java @@ -0,0 +1,16 @@ +package org.keycloak.testsuite.arquillian.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @author mhajas + */ +@Retention(RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface EnableFeatures { + EnableFeature[] value() default {}; +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableVault.java b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableVault.java new file mode 100644 index 000000000000..4e4d62d9295a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/EnableVault.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.arquillian.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author mhajas + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface EnableVault { + + ; + + enum PROVIDER_ID { + + PLAINTEXT("files-plaintext"), + KEYSTORE("files-keystore"); + + final String name; + + PROVIDER_ID(final String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + }; + + PROVIDER_ID providerId() default PROVIDER_ID.PLAINTEXT; +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/SetDefaultProvider.java b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/SetDefaultProvider.java new file mode 100644 index 000000000000..17c061a3b10a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/SetDefaultProvider.java @@ -0,0 +1,44 @@ +package org.keycloak.testsuite.arquillian.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface SetDefaultProvider { + String spi(); + String providerId(); + + /** + *

Defines whether the default provider should be set by updating an existing Spi configuration. + * + *

This flag is useful when running the Wildfly distribution and when the server is already configured + * with a Spi that should only be updated with the default provider. + * + * @return {@code true} if the default provider should update an existing Spi configuration. Otherwise, the Spi + * configuration will be added with the default provider set. + */ + boolean onlyUpdateDefault() default false; + + /** + *

Defines whether the default provider should be set prior to enabling a feature. + * + *

This flag should be used together with {@link EnableFeature} so that the default provider + * is set after enabling a feature. It is useful in case the default provider is not enabled by default, + * thus requiring the feature to be enabled first. + * + * @return {@code true} if the default should be set prior to enabling a feature. Otherwise, + * the default provider is only set after enabling a feature. + */ + boolean beforeEnableFeature() default true; + + String defaultProvider() default ""; + + /** + * Configuration for the provider in the form option1, value1, option2, value2 + * @return The config options and values + */ + String[] config() default {}; +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/UncaughtServerErrorExpected.java b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/UncaughtServerErrorExpected.java new file mode 100644 index 000000000000..7e3742902b6e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/arquillian/annotation/UncaughtServerErrorExpected.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.arquillian.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * This annotation is for test methods that expect server to throw an uncaught server error + * @author hmlnarik + */ +@Documented +@Retention(RUNTIME) +@Target({ElementType.METHOD}) +public @interface UncaughtServerErrorExpected { + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/auth/page/AccountFields.java b/tests/base/src/test/java/org/keycloak/testsuite/auth/page/AccountFields.java new file mode 100644 index 000000000000..835f89979104 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/auth/page/AccountFields.java @@ -0,0 +1,52 @@ +package org.keycloak.testsuite.auth.page; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +public class AccountFields { + + private final ManagedWebDriver driver; + + public AccountFields(ManagedWebDriver driver) { + this.driver = driver; + } + + public AccountFields setEmail(String email) { + set("email", email); + return this; + } + + public AccountFields setFirstName(String firstName) { + set("firstName", firstName); + return this; + } + + public AccountFields setLastName(String lastName) { + set("lastName", lastName); + return this; + } + + public String getEmail() { + return get("email"); + } + + public String getFirstName() { + return get("firstName"); + } + + public String getLastName() { + return get("lastName"); + } + + private void set(String id, String value) { + WebElement element = driver.findElement(By.id(id)); + element.clear(); + element.sendKeys(value); + } + + private String get(String id) { + return driver.findElement(By.id(id)).getAttribute("value"); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/auth/page/AuthRealm.java b/tests/base/src/test/java/org/keycloak/testsuite/auth/page/AuthRealm.java new file mode 100644 index 000000000000..583cd7306ea0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/auth/page/AuthRealm.java @@ -0,0 +1,10 @@ +package org.keycloak.testsuite.auth.page; + +public final class AuthRealm { + + public static final String MASTER = "master"; + public static final String ADMIN = "admin"; + + private AuthRealm() { + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java b/tests/base/src/test/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java new file mode 100644 index 000000000000..1091ead1e1a2 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java @@ -0,0 +1,34 @@ +package org.keycloak.testsuite.auth.page.login; + +import org.keycloak.testframework.ui.page.AbstractLoginPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testsuite.auth.page.AccountFields; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class UpdateAccount extends AbstractLoginPage { + + private final AccountFields accountFields; + + @FindBy(css = "input[type=\"submit\"]") + private WebElement submitButton; + + public UpdateAccount(ManagedWebDriver driver) { + super(driver); + this.accountFields = new AccountFields(driver); + } + + public AccountFields fields() { + return accountFields; + } + + public void submit() { + submitButton.click(); + } + + @Override + public String getExpectedPageId() { + return "login-idp-review-user-profile"; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/tests/base/src/test/java/org/keycloak/testsuite/client/KeycloakTestingClient.java new file mode 100644 index 000000000000..87766a1b6bd3 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/KeycloakTestingClient.java @@ -0,0 +1,240 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client; + +import java.io.IOException; +import java.util.Set; + +import jakarta.ws.rs.core.Response; + +import org.keycloak.common.Profile; +import org.keycloak.testframework.remote.providers.runonserver.FetchOnServer; +import org.keycloak.testframework.remote.providers.runonserver.FetchOnServerWrapper; +import org.keycloak.testframework.remote.providers.runonserver.RunOnServer; +import org.keycloak.testframework.remote.providers.runonserver.RunOnServerException; +import org.keycloak.testframework.remote.providers.runonserver.SerializationUtil; +import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.client.resources.TestApplicationResource; +import org.keycloak.testsuite.client.resources.TestExampleCompanyResource; +import org.keycloak.testsuite.client.resources.TestSamlApplicationResource; +import org.keycloak.testsuite.client.resources.TestingResource; +import org.keycloak.testsuite.util.AdminClientUtil; +import org.keycloak.util.JsonSerialization; + +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; +import org.junit.AssumptionViolatedException; +import org.junit.jupiter.api.Assertions; + +/** + * @author Marko Strukelj + */ +public class KeycloakTestingClient implements AutoCloseable { + + private final ResteasyWebTarget target; + private final ResteasyClient client; + + KeycloakTestingClient(String serverUrl, ResteasyClient resteasyClient) { + if (resteasyClient != null) { + client = resteasyClient; + } else { + ResteasyClientBuilder resteasyClientBuilder = getRestEasyClientBuilder(serverUrl); + client = resteasyClientBuilder.build(); + } + target = client.target(serverUrl); + } + + public static ResteasyClientBuilder getRestEasyClientBuilder(String serverUrl) { + ResteasyClientBuilder resteasyClientBuilder = (ResteasyClientBuilder) ResteasyClientBuilder.newBuilder(); + resteasyClientBuilder.connectionPoolSize(10); + if ((serverUrl != null && serverUrl.startsWith("https")) || "true".equals(System.getProperty("auth.server.ssl.required"))) { + // Disable PKIX path validation errors when running tests using SSL + resteasyClientBuilder.disableTrustManager().hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY); + } + resteasyClientBuilder.httpEngine(AdminClientUtil.getCustomClientHttpEngine(resteasyClientBuilder, 10, null)); + return resteasyClientBuilder; + } + + public static ResteasyClientBuilder getRestEasyClientBuilder() { + return getRestEasyClientBuilder(null); + } + + public static KeycloakTestingClient getInstance(String serverUrl) { + return new KeycloakTestingClient(serverUrl, null); + } + + public static KeycloakTestingClient getInstance(String serverUrl, ResteasyClient resteasyClient) { + return new KeycloakTestingClient(serverUrl, resteasyClient); + } + + public TestingResource testing() { + return target.path("/realms/master").proxy(TestingResource.class); + } + + public TestingResource testing(String realm) { + return target.path("/realms/" + realm).proxy(TestingResource.class); + } + + public void enableFeature(Profile.Feature feature) { + String featureString; + if (shouldUseVersionedKey(feature)) { + featureString = feature.getVersionedKey(); + } else { + featureString = feature.getKey(); + } + Set disabledFeatures = testing().enableFeature(featureString); + Assertions.assertFalse(disabledFeatures.contains(feature)); + ProfileAssume.updateDisabledFeatures(disabledFeatures); + } + + private boolean shouldUseVersionedKey(Profile.Feature feature) { + return ((Profile.getFeatureVersions(feature.getUnversionedKey()).size() > 1) || (feature.getVersion() != 1)); + } + + public void disableFeature(Profile.Feature feature) { + String featureString; + if (shouldUseVersionedKey(feature)) { + featureString = feature.getVersionedKey(); + } else { + featureString = feature.getKey(); + } + Set disabledFeatures = testing().disableFeature(featureString); + Assertions.assertTrue(disabledFeatures.contains(feature)); + ProfileAssume.updateDisabledFeatures(disabledFeatures); + } + + /** + * Resets the feature to it's default setting. + * + * @param feature + */ + public void resetFeature(Profile.Feature feature) { + String featureString; + if (shouldUseVersionedKey(feature)) { + featureString = feature.getVersionedKey(); + Profile.Feature featureVersionHighestPriority = Profile.getFeatureVersions(feature.getUnversionedKey()).iterator().next(); + if (featureVersionHighestPriority.getType().equals(Profile.Feature.Type.DEFAULT)) { + enableFeature(featureVersionHighestPriority); + } + } else { + featureString = feature.getKey(); + } + testing().resetFeature(featureString); + } + + public TestApplicationResource testApp() { return target.proxy(TestApplicationResource.class); } + + public TestSamlApplicationResource testSamlApp() { return target.proxy(TestSamlApplicationResource.class); } + + public TestExampleCompanyResource testExampleCompany() { return target.proxy(TestExampleCompanyResource.class); } + + /** + * Allows running code on the server-side for white-box testing. When using be careful what imports your test class + * has and also what classes are used within the function sent to the server. Classes have to be either available + * server-side or defined in @{@link org.keycloak.testsuite.arquillian.TestClassProvider#PERMITTED_PACKAGES} + * + * @return + */ + public Server server() { + return new Server("master"); + } + + public Server server(String realm) { + return new Server(realm); + } + + public class Server { + + private final String realm; + + public Server(String realm) { + this.realm = realm; + } + + public T fetch(FetchOnServerWrapper wrapper) throws RunOnServerException { + return fetch(wrapper.getRunOnServer(), wrapper.getResultClass()); + } + + public T fetch(FetchOnServer function, Class clazz) throws RunOnServerException { + try { + String s = fetchString(function); + return s==null ? null : JsonSerialization.readValue(s, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String fetchString(FetchOnServer function) throws RunOnServerException { + String encoded = SerializationUtil.encode(function); + + String result = testing(realm != null ? realm : "master").runOnServer(encoded); + if (result != null && !result.isEmpty() && result.trim().startsWith("EXCEPTION:")) { + Throwable t = SerializationUtil.decodeException(result); + if (t instanceof AssertionError) { + throw (AssertionError) t; + } else { + throw new RunOnServerException(t); + } + } else { + return result; + } + } + + public void run(RunOnServer function) throws RunOnServerException { + String encoded = SerializationUtil.encode(function); + + String result = testing(realm != null ? realm : "master").runOnServer(encoded); + if (result != null && !result.isEmpty() && result.trim().startsWith("EXCEPTION:")) { + Throwable t = SerializationUtil.decodeException(result); + if (t instanceof AssertionError) { + throw (AssertionError) t; + } else { + throw new RunOnServerException(t); + } + } + } + + public Response runWithResponse(RunOnServer function) throws RunOnServerException { + String encoded = SerializationUtil.encode(function); + return testing(realm != null ? realm : "master").runOnServerWithResponse(encoded); + } + + public void runModelTest(String testClassName, String testMethodName) throws RunOnServerException { + String result = testing(realm != null ? realm : "master").runModelTestOnServer(testClassName, testMethodName); + + if (result != null && !result.isEmpty() && result.trim().startsWith("EXCEPTION:")) { + Throwable t = SerializationUtil.decodeException(result); + + if (t instanceof AssertionError) { + throw (AssertionError) t; + } else if (t instanceof AssumptionViolatedException) { + throw (AssumptionViolatedException) t; + } else { + throw new RunOnServerException(t); + } + } + } + + } + + @Override + public void close() { + client.close(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java new file mode 100644 index 000000000000..bad81dd09bb3 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; + +import org.keycloak.representations.LogoutToken; +import org.keycloak.representations.adapters.action.LogoutAction; +import org.keycloak.representations.adapters.action.PushNotBeforeAction; +import org.keycloak.representations.adapters.action.TestAvailabilityAction; +import org.keycloak.utils.MediaType; + +/** + * @author Stian Thorgersen + */ +@Path("/realms/master/app") +public interface TestApplicationResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-logout") + LogoutAction getAdminLogoutAction(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-backchannel-logout") + LogoutToken getBackChannelLogoutToken(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-backchannel-raw-logout") + String getBackChannelRawLogoutToken(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-not-before") + PushNotBeforeAction getAdminPushNotBefore(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-test-available") + TestAvailabilityAction getTestAvailable(); + + @POST + @Path("/clear-admin-actions") + void clearAdminActions(); + + @GET + @Produces(MediaType.TEXT_HTML_UTF_8) + @Path("/get-account-profile") + String getAccountProfile(@QueryParam("token") String token, @QueryParam("account-uri") String accountUri); + + @Path("/oidc-client-endpoints") + TestOIDCEndpointsApplicationResource oidcClientEndpoints(); +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java new file mode 100644 index 000000000000..e6e16cff48c8 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.testframework.oauth.OAuthClient; + +/** + * @author Marek Posolda + */ +public class TestApplicationResourceUrls { + + private static UriBuilder oidcClientEndpoints() { + return UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT) + .path(TestApplicationResource.class) + .path(TestApplicationResource.class, "oidcClientEndpoints"); + } + + public static String clientRequestUri() { + UriBuilder builder = oidcClientEndpoints() + .path(TestOIDCEndpointsApplicationResource.class, "getOIDCRequest"); + + return builder.build().toString(); + } + + public static String clientJwksUri() { + UriBuilder builder = oidcClientEndpoints() + .path(TestOIDCEndpointsApplicationResource.class, "getJwks"); + + return builder.build().toString(); + } + + public static String pairwiseSectorIdentifierUri() { + UriBuilder builder = oidcClientEndpoints() + .path(TestOIDCEndpointsApplicationResource.class, "getSectorIdentifierRedirectUris"); + return builder.build().toString(); + } + + public static String cibaClientNotificationEndpointUri() { + UriBuilder builder = oidcClientEndpoints() + .path(TestOIDCEndpointsApplicationResource.class, "cibaClientNotificationEndpoint"); + + return builder.build().toString(); + } + + public static String checkIntentClientBoundUri() { + UriBuilder builder = oidcClientEndpoints() + .path(TestOIDCEndpointsApplicationResource.class, "checkIntentClientBound"); + + return builder.build().toString(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java new file mode 100644 index 000000000000..b7b06af74a0a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import java.util.List; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.keycloak.testsuite.domainextension.CompanyRepresentation; + +/** + * @author Marek Posolda + */ +@Path("/realms/{realmName}/example/companies") +@Consumes(MediaType.APPLICATION_JSON) +public interface TestExampleCompanyResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + List getCompanies(@PathParam("realmName") String realmName); + + @GET + @Path("/{companyId}") + @Produces(MediaType.APPLICATION_JSON) + CompanyRepresentation getCompany(@PathParam("realmName") String realmName, @PathParam("companyId") String companyId); + + @POST + @Consumes(MediaType.APPLICATION_JSON) + Response createCompany(@PathParam("realmName") String realmName, CompanyRepresentation rep); + + @DELETE + void deleteAllCompanies(@PathParam("realmName") String realmName); +} \ No newline at end of file diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java new file mode 100644 index 000000000000..10a4c320042c --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java @@ -0,0 +1,181 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import java.util.List; +import java.util.Map; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; + +import org.keycloak.jose.jwk.JSONWebKeySet; +import org.keycloak.protocol.oidc.grants.ciba.endpoints.ClientNotificationEndpointRequest; +import org.keycloak.services.clientpolicy.executor.IntentClientBindCheckExecutor; +import org.keycloak.testsuite.rest.representation.TestAuthenticationChannelRequest; + +import org.jboss.resteasy.reactive.NoCache; + +/** + * @author Marek Posolda + */ +public interface TestOIDCEndpointsApplicationResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/generate-keys") + Map generateKeys(@QueryParam("jwaAlgorithm") String jwaAlgorithm); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/generate-keys") + Map generateKeys(@QueryParam("jwaAlgorithm") String jwaAlgorithm, @QueryParam("crv") String curve); + + /** + * Generate single private/public keyPair + * + * @param jwaAlgorithm + * @param curve The crv for EdDSA + * @param advertiseJWKAlgorithm whether algorithm should be adwertised in JWKS or not (Once the keys are returned by JWKS) + * @param keepExistingKeys Should be existing keys kept replaced with newly generated keyPair. If it is not kept, then resulting JWK will contain single key. It is false by default. + * The value 'true' is useful if we want to test with multiple client keys (For example mulitple keys set in the JWKS and test if correct key is picked) + * @param kid Explicitly set specified "kid" for newly generated keypair. If not specified, the kid will be generated + * @return + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/generate-keys") + Map generateKeys(@QueryParam("jwaAlgorithm") String jwaAlgorithm, + @QueryParam("crv") String curve, + @QueryParam("advertiseJWKAlgorithm") Boolean advertiseJWKAlgorithm, + @QueryParam("keepExistingKeys") Boolean keepExistingKeys, + @QueryParam("kid") String kid); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/get-keys-as-pem") + Map getKeysAsPem(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/get-keys-as-base64") + Map getKeysAsBase64(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/get-jwks") + JSONWebKeySet getJwks(); + + + @GET + @Path("/set-oidc-request") + @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT) + void setOIDCRequest(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId, + @QueryParam("redirectUri") String redirectUri, @QueryParam("maxAge") String maxAge, + @QueryParam("state") String state, + @QueryParam("jwaAlgorithm") String jwaAlgorithm); + + @GET + @Path("/set-oidc-request") + @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT) + void setOIDCRequest(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId, + @QueryParam("redirectUri") String redirectUri, @QueryParam("maxAge") String maxAge, + @QueryParam("jwaAlgorithm") String jwaAlgorithm); + + @GET + @Path("/register-oidc-request") + @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT) + void registerOIDCRequest(@QueryParam("requestObject") String encodedRequestObject, @QueryParam("jwaAlgorithm") String jwaAlgorithm); + + @GET + @Path("/register-oidc-request-symmetric-sig") + @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT) + void registerOIDCRequestSymmetricSig(@QueryParam("requestObject") String encodedRequestObject, @QueryParam("jwaAlgorithm") String jwaAlgorithm, @QueryParam("clientSecret") String clientSecret); + + @GET + @Path("/get-oidc-request") + @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT) + String getOIDCRequest(); + + @GET + @Path("/set-sector-identifier-redirect-uris") + @Produces(MediaType.APPLICATION_JSON) + void setSectorIdentifierRedirectUris(@QueryParam("redirectUris") List redirectUris); + + @GET + @Path("/get-sector-identifier-redirect-uris") + @Produces(MediaType.APPLICATION_JSON) + List getSectorIdentifierRedirectUris(); + + @POST + @Path("/request-authentication-channel") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + @NoCache + Response requestAuthenticationChannel(final MultivaluedMap request); + + @GET + @Path("/get-authentication-channel") + @Produces(MediaType.APPLICATION_JSON) + TestAuthenticationChannelRequest getAuthenticationChannel(@QueryParam("bindingMessage") String bindingMessage); + + /** + * Invoke client notification endpoint. This will be called by Keycloak itself (by CIBA callback endpoint) not by testsuite + * @param request + */ + @POST + @Path("/push-ciba-client-notification") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @NoCache + void cibaClientNotificationEndpoint(ClientNotificationEndpointRequest request); + + /** + * Return the authReqId in case that clientNotificationEndpoint was already called by Keycloak for the given clientNotificationToken. Otherwise underlying value of + * authReqId field from the returned JSON will be null in case that clientNotificationEndpoint was not yet called for the given clientNotificationToken. + * + * Pushed client notification will be removed after calling this. + * + * @param clientNotificationToken + * @return + */ + @GET + @Path("/get-pushed-ciba-client-notification") + @Produces(MediaType.APPLICATION_JSON) + @NoCache + ClientNotificationEndpointRequest getPushedCibaClientNotification(@QueryParam("clientNotificationToken") String clientNotificationToken); + + @GET + @Path("/bind-intent-with-client") + @Produces(MediaType.APPLICATION_JSON) + @NoCache + Response bindIntentWithClient(@QueryParam("intentId") String intentId, @QueryParam("clientId") String clientId); + + @POST + @Path("/check-intent-client-bound") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @NoCache + IntentClientBindCheckExecutor.IntentBindCheckResponse checkIntentClientBound(IntentClientBindCheckExecutor.IntentBindCheckRequest request); +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestSamlApplicationResource.java b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestSamlApplicationResource.java new file mode 100644 index 000000000000..f6536b19fccb --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestSamlApplicationResource.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; + +import org.keycloak.representations.adapters.action.LogoutAction; +import org.keycloak.representations.adapters.action.PushNotBeforeAction; +import org.keycloak.representations.adapters.action.TestAvailabilityAction; +import org.keycloak.utils.MediaType; + +/** + * @author Stian Thorgersen + */ +@Path("/realms/master/saml-app") +public interface TestSamlApplicationResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-logout") + LogoutAction getAdminLogoutAction(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-not-before") + PushNotBeforeAction getAdminPushNotBefore(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-test-available") + TestAvailabilityAction getTestAvailable(); + + @POST + @Path("/clear-admin-actions") + void clearAdminActions(); +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestingResource.java b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestingResource.java new file mode 100644 index 000000000000..cbc361ee59ec --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/client/resources/TestingResource.java @@ -0,0 +1,181 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import java.util.Map; +import java.util.Set; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; + +import org.keycloak.common.Profile; +import org.keycloak.common.enums.HostnameVerificationPolicy; +import org.keycloak.representations.idm.AdminEventRepresentation; +import org.keycloak.representations.idm.EventRepresentation; +import org.keycloak.utils.MediaType; + +import org.infinispan.commons.time.TimeService; +import org.jboss.resteasy.reactive.NoCache; + +/** + * For an implementation see TestingResourceProvider + * @author Marko Strukelj + */ + +@Path("/testing") +@Consumes(MediaType.APPLICATION_JSON) +public interface TestingResource { + + @POST + @Path("/poll-event-queue") + @Produces(MediaType.APPLICATION_JSON) + EventRepresentation pollEvent(); + + @POST + @Path("/poll-admin-event-queue") + @Produces(MediaType.APPLICATION_JSON) + AdminEventRepresentation pollAdminEvent(); + + @POST + @Path("/clear-event-queue") + @Produces(MediaType.APPLICATION_JSON) + void clearEventQueue(); + + @POST + @Path("/clear-admin-event-queue") + @Produces(MediaType.APPLICATION_JSON) + void clearAdminEventQueue(); + + /** + * Will set Inifispan's {@link TimeService} that is aware of Keycloak time shifts to the infinispan {@code CacheManager} before the test. + * This will allow infinispan expiration to be aware of Keycloak {@link org.keycloak.common.util.Time#setOffset} + */ + @POST + @Path("/set-testing-infinispan-time-service") + @Produces(MediaType.APPLICATION_JSON) + void setTestingInfinispanTimeService(); + + @POST + @Path("/revert-testing-infinispan-time-service") + @Produces(MediaType.APPLICATION_JSON) + void revertTestingInfinispanTimeService(); + + @GET + @Path("/test-amphibian-component") + @Produces(MediaType.APPLICATION_JSON) + Map> getTestAmphibianComponentDetails(); + + @PUT + @Path("/set-krb5-conf-file") + @Consumes(MediaType.APPLICATION_JSON) + void setKrb5ConfFile(@QueryParam("krb5-conf-file") String krb5ConfFile); + + @POST + @Path("/run-on-server") + @Consumes(MediaType.TEXT_PLAIN_UTF_8) + @Produces(MediaType.TEXT_PLAIN_UTF_8) + String runOnServer(String runOnServer); + + @POST + @Path("/run-on-server") + @Consumes(MediaType.TEXT_PLAIN_UTF_8) + @Produces(MediaType.TEXT_PLAIN_UTF_8) + Response runOnServerWithResponse(String runOnServer); + + @POST + @Path("/run-model-test-on-server") + @Consumes(MediaType.TEXT_PLAIN_UTF_8) + @Produces(MediaType.TEXT_PLAIN_UTF_8) + String runModelTestOnServer(@QueryParam("testClassName") String testClassName, + @QueryParam("testMethodName") String testMethodName); + + @GET + @Path("/list-disabled-features") + @Produces(MediaType.APPLICATION_JSON) + Set listDisabledFeatures(); + + @POST + @Path("/enable-feature/{feature}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + Set enableFeature(@PathParam("feature") String feature); + + @POST + @Path("/disable-feature/{feature}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + Set disableFeature(@PathParam("feature") String feature); + + /** + * Resets the given feature to it's default state. + * + * @param feature + */ + @POST + @Path("/reset-feature/{feature}") + @Consumes(MediaType.APPLICATION_JSON) + void resetFeature(@PathParam("feature") String feature); + + /** + * This method is here just to have all endpoints from TestingResourceProvider available here. + *

+ * But usually it is requested to call this endpoint through WebDriver. See URLUtils.sendPOSTWithWebDriver for more details + */ + @GET + @Path("/simulate-post-request") + @Produces(MediaType.TEXT_HTML_UTF_8) + Response simulatePostRequest(@QueryParam("postRequestUrl") String postRequestUrl, + @QueryParam("encodedFormParameters") String encodedFormParameters); + + /** + * Temporarily disables truststore SPI from the file. Useful for example to test some error scenarios, which require truststore SPI to be unset (or set incorrectly) + */ + @GET + @Path("/disable-truststore-spi") + @NoCache + void disableTruststoreSpi(); + + /** + * Temporarily changes the truststore SPI with another hostname verification policy. Call reenableTruststoreSpi to revert. + * + * @param hostnamePolicy The hostname verification policy to set + */ + @GET + @Path("/modify-truststore-spi-hostname-policy") + @NoCache + public void modifyTruststoreSpiHostnamePolicy(@QueryParam("hostnamePolicy") final HostnameVerificationPolicy hostnamePolicy); + + /** + * Re-enable truststore SPI after it was temporarily disabled by {@link #disableTruststoreSpi()} + */ + @GET + @Path("/reenable-truststore-spi") + @NoCache + void reenableTruststoreSpi(); + + @GET + @Path("/no-cache-annotated-endpoint") + Response getNoCacheAnnotatedEndpointResponse(@QueryParam("programmatic_max_age_value") Long programmaticMaxAgeValue); +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java b/tests/base/src/test/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java new file mode 100644 index 000000000000..117b9281ac8f --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.domainextension; + + +import org.keycloak.testsuite.domainextension.jpa.Company; + +public class CompanyRepresentation { + + private String id; + private String name; + + public CompanyRepresentation() { + } + + public CompanyRepresentation(Company company) { + id = company.getId(); + name = company.getName(); + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/domainextension/jpa/Company.java b/tests/base/src/test/java/org/keycloak/testsuite/domainextension/jpa/Company.java new file mode 100644 index 000000000000..2bba3189c7fe --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/domainextension/jpa/Company.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.domainextension.jpa; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; + +@Entity +@Table(name = "EXAMPLE_COMPANY") +@NamedQueries({ @NamedQuery(name = "findByRealm", query = "from Company where realmId = :realmId") }) +public class Company { + + @Id + @Column(name = "ID") + private String id; + + @Column(name = "NAME", nullable = false) + private String name; + + @Column(name = "REALM_ID", nullable = false) + private String realmId; + + public String getId() { + return id; + } + + public String getRealmId() { + return realmId; + } + + public String getName() { + return name; + } + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setRealmId(String realmId) { + this.realmId = realmId; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java b/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java new file mode 100644 index 000000000000..80731260e568 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.federation.ldap; + +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.ldap.LDAPStorageProvider; +import org.keycloak.testsuite.util.LDAPTestUtils; + +/** + * @author Marek Posolda + */ +public class LDAPTestContext { + + private final RealmModel realm; + private final UserStorageProviderModel ldapModel; + private final LDAPStorageProvider ldapProvider; + + public static LDAPTestContext init(KeycloakSession session) { + return init(session, null); + } + + public static LDAPTestContext init(KeycloakSession session, String providerName) { + return init(session, "test", providerName); + } + + public static LDAPTestContext init(KeycloakSession session, String realmName, String providerName) { + RealmModel testRealm = session.realms().getRealmByName(realmName); + ComponentModel ldapCompModel = LDAPTestUtils.getLdapProviderModel(testRealm, providerName); + UserStorageProviderModel ldapModel = new UserStorageProviderModel(ldapCompModel); + LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); + return new LDAPTestContext(testRealm, ldapModel, ldapProvider); + } + + private LDAPTestContext(RealmModel realm, UserStorageProviderModel ldapModel, LDAPStorageProvider ldapProvider) { + this.realm = realm; + this.ldapModel = ldapModel; + this.ldapProvider = ldapProvider; + } + + + public RealmModel getRealm() { + return realm; + } + + public UserStorageProviderModel getLdapModel() { + return ldapModel; + } + + public LDAPStorageProvider getLdapProvider() { + return ldapProvider; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java b/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java new file mode 100644 index 000000000000..ad8e35ad446c --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java @@ -0,0 +1,35 @@ +package org.keycloak.testsuite.forms; + +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testsuite.util.userprofile.UserProfileUtil; + +import org.openqa.selenium.WebDriver; + +public final class RegisterWithUserProfileTest { + + private RegisterWithUserProfileTest() { + } + + public static final String UP_CONFIG_PART_INPUT_TYPES = "{\"name\": \"defaultType\"," + UserProfileUtil.PERMISSIONS_ALL + "}," + + "{\"name\": \"placeholderAttribute\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"annotations\":{\"inputType\":\"text\",\"inputTypePlaceholder\":\"Example.\"}}," + + "{\"name\": \"helperTexts\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"annotations\":{\"inputType\":\"text\",\"inputHelperTextBefore\":\"Example bold text before.\",\"inputHelperTextAfter\":\"Example i text after.\"}}," + + "{\"name\": \"textWithBasicAttributes\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"annotations\":{\"inputType\":\"text\",\"inputTypeSize\":\"35\",\"inputTypeMinlength\":\"1\",\"inputTypeMaxlength\":\"10\",\"inputTypePattern\":\".*\"}}," + + "{\"name\": \"html5NumberWithAttributes\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"annotations\":{\"inputType\":\"html5-number\",\"inputTypeMin\":\"10\",\"inputTypeMax\":\"20\",\"inputTypeStep\":1}}," + + "{\"name\": \"textareaWithAttributes\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"annotations\":{\"inputType\":\"textarea\",\"inputTypeCols\":\"35\",\"inputTypeRows\":\"7\",\"inputTypeMaxlength\":\"10\"}}," + + "{\"name\": \"selectWithoutOptions\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"annotations\":{\"inputType\":\"select\",\"inputTypeSize\":\"5\"}}," + + "{\"name\": \"selectWithOptionsWithoutLabels\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\":{\"options\":{\"options\":[ \"opt1\",\"opt2\"]}}, \"annotations\":{\"inputType\":\"select\"}}," + + "{\"name\": \"multiselectWithOptionsAndSimpleI18nLabels\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\":{\"options\":{ \"options\":[\"totp\",\"opt2\"]}}, \"annotations\":{\"inputType\":\"multiselect\",\"inputOptionLabelsI18nPrefix\": \"loginTotp\"}}," + + "{\"name\": \"multiselectWithOptionsAndLabels\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\":{\"options\":{ \"options\":[\"opt1\",\"opt2\",\"opt3\"]}}, \"annotations\":{\"inputType\":\"multiselect\",\"inputOptionLabels\":{\"opt1\": \"Option 1\",\"opt2\":\"${username}\"}}}," + + "{\"name\": \"selectWithOptionsFromCustomValidatorAndLabels\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\":{\"dummyOptions\":{\"options\" : [\"vopt1\",\"vopt2\",\"vopt3\"]}} ,\"annotations\":{\"inputType\":\"select\",\"inputOptionsFromValidation\":\"dummyOptions\",\"inputOptionLabels\":{\"vopt1\": \"Option 1\",\"vopt2\":\"${username}\"}}}," + + "{\"name\": \"selectRadiobuttons\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\" : {\"options\" : {\"options\":[\"opt1\",\"opt2\",\"opt3\"]}}, \"annotations\":{\"inputType\":\"select-radiobuttons\",\"inputOptionLabels\":{\"opt1\": \"Option 1\",\"opt2\":\"${username}\"}}}," + + "{\"name\": \"selectRadiobuttonsWithOptionsFromCustomValidatorAndLabels\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\" : {\"dummyOptions\" : {\"options\" : [\"vopt1\",\"vopt2\",\"vopt3\"]}} ,\"annotations\":{\"inputType\":\"select-radiobuttons\",\"inputOptionsFromValidation\":\"dummyOptions\",\"inputOptionLabels\":{\"vopt1\": \"Option 1\",\"vopt2\":\"${username}\"}}}," + + "{\"name\": \"multiselectCheckboxes\", " + UserProfileUtil.PERMISSIONS_ALL + ", \"validations\": {\"options\":{\"options\":[\"opt1\",\"opt2\",\"opt3\"]}}, \"annotations\":{\"inputType\":\"multiselect-checkboxes\",\"inputOptionLabels\":{\"opt1\": \"Option 1\",\"opt2\":\"${username}\"}}}"; + + public static void assertFieldTypes(WebDriver driver) { + // Shared by broker tests; individual field checks stay in registration-specific suite. + } + + public static void assertFieldTypes(ManagedWebDriver driver) { + assertFieldTypes(driver.driver()); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java b/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java new file mode 100644 index 000000000000..b9db76cf7643 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java @@ -0,0 +1,17 @@ +package org.keycloak.testsuite.forms; + +import java.util.List; + +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.representations.idm.UserRepresentation; + +public final class VerifyProfileTest { + + private VerifyProfileTest() { + } + + public static UserRepresentation getUserByUsername(RealmResource testRealm, String username) { + List users = testRealm.users().search(username); + return users != null && !users.isEmpty() ? users.get(0) : null; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPage.java b/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPage.java new file mode 100644 index 000000000000..7616897650da --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPage.java @@ -0,0 +1,113 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.page; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.testsuite.util.DroneUtils; +import org.keycloak.testsuite.util.URLUtils; + +import org.jboss.logging.Logger; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.WebDriver; + +/** + * + * @author tkyjovsk + */ +@Deprecated +public abstract class AbstractPage { + + protected final Logger log = Logger.getLogger(this.getClass()); + + private final Map uriParameters = new HashMap<>(); + + protected WebDriver driver; + + private UriBuilder builder; + + public WebDriver getDriver() { + return driver; + } + + public void setDriver(WebDriver driver) { + this.driver = driver; + } + + public abstract UriBuilder createUriBuilder(); + + public String getUriFragment() { + return ""; + } + + /** + * + * @return Instance of UriBuilder that can build URIs for a concrete page. + */ + public UriBuilder getUriBuilder() { + if (builder == null) { + builder = createUriBuilder(); + String fragment = getUriFragment(); + if (fragment != null && !fragment.isEmpty()) { + builder.fragment(fragment); + } + } + return builder; + } + + public AbstractPage setUriParameter(String name, Object value) { + uriParameters.put(name, value); + return this; + } + + public AbstractPage removeUriParameter(String name) { + uriParameters.remove(name); + return this; + } + + public Object getUriParameter(String name) { + return uriParameters.get(name); + } + + public URI buildUri() { + return getUriBuilder().buildFromMap(uriParameters); + } + + @Override + public String toString() { + return buildUri().toASCIIString(); + } + + public void navigateTo() { + URLUtils.navigateToUri(buildUri().toASCIIString()); + } + + public boolean isCurrent() { + return URLUtils.currentUrlEquals(toString()); + } + + public void assertCurrent() { + String name = getClass().getSimpleName(); + Assertions.assertTrue(isCurrent(), + "Expected " + name + " but was " + DroneUtils.getCurrentDriver().getTitle() + " (" + DroneUtils.getCurrentDriver().getCurrentUrl() + ")"); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java b/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java new file mode 100644 index 000000000000..e6dd726b1347 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.page; + +import java.net.URISyntaxException; +import java.net.URL; + +import jakarta.ws.rs.core.UriBuilder; + +/** + * + * @author tkyjovsk + */ +@Deprecated +public abstract class AbstractPageWithInjectedUrl extends AbstractPage { + + public abstract URL getInjectedUrl(); + + protected URL createInjectedURL(String url) { + return null; + } + + @Override + public UriBuilder createUriBuilder() { + try { + return UriBuilder.fromUri(getInjectedUrl().toURI()); + } catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPatternFlyAlert.java b/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPatternFlyAlert.java new file mode 100644 index 000000000000..2a56066e88da --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/page/AbstractPatternFlyAlert.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.page; + +import java.time.Duration; + +import org.jboss.logging.Logger; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver; +import static org.keycloak.testsuite.util.UIUtils.doesElementClassContain; +import static org.keycloak.testsuite.util.UIUtils.getTextFromElement; +import static org.keycloak.testsuite.util.UIUtils.isElementVisible; +import static org.keycloak.testsuite.util.WaitUtils.PAGELOAD_TIMEOUT_MILLIS; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * + * @author tkyjovsk + */ +@Deprecated +public abstract class AbstractPatternFlyAlert { + public static final String ALERT_CLASS_NAME = "pf-v5-c-alert"; + + protected final Logger log = Logger.getLogger(this.getClass()); + + @FindBy(className = ALERT_CLASS_NAME) + protected WebElement alertRoot; + + protected WebDriver driver; + + public boolean isDisplayed() { + return isElementVisible(alertRoot); + } + + public static void waitUntilDisplayed() { + waitUntilDisplayedOrHidden(true); + } + + public static void waitUntilHidden() { + waitUntilDisplayedOrHidden(false); + } + + private static void waitUntilDisplayedOrHidden(boolean displayed) { + ExpectedCondition condition = ExpectedConditions.visibilityOfElementLocated(By.className(ALERT_CLASS_NAME)); + condition = displayed ? condition : ExpectedConditions.not(condition); + new WebDriverWait(getCurrentDriver(), Duration.ofMillis(PAGELOAD_TIMEOUT_MILLIS)).until(condition); + } + + public String getText() { + return getTextFromElement(alertRoot); + } + + public boolean isSuccess() { + return checkAlertType("success"); + } + + public void assertDisplayed() { + assertTrue(isDisplayed(), "Alert should displayed"); + } + + public void assertNotDisplayed() { + assertFalse(isDisplayed(), "Alert shouldn't be displayed"); + } + + public void assertSuccess() { + assertSuccess(null); + } + + public void assertSuccess(String expectedText) { + assertDisplayed(); + assertTrue(isSuccess(), "Alert type should be success"); + if (expectedText != null) assertEquals(expectedText, getText()); + } + + protected boolean checkAlertType(String type) { + return doesElementClassContain(alertRoot, "alert-" + type); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/page/Form.java b/tests/base/src/test/java/org/keycloak/testsuite/page/Form.java new file mode 100644 index 000000000000..8fd0cc99766e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/page/Form.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.page; + +import org.jboss.logging.Logger; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import static org.keycloak.testsuite.util.UIUtils.clickLink; + +/** + * + * @author tkyjovsk + */ +@Deprecated +public class Form { + + protected final Logger log = Logger.getLogger(this.getClass()); + + protected WebDriver driver; + + public static final String ACTIVE_DIV_XPATH = ".//div[not(contains(@class,'ng-hide'))]"; + + @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[@kc-save or @data-kc-save]") + private WebElement save; + @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[@kc-reset or @data-kc-reset]") + private WebElement cancel; + + public void save() { + clickLink(saveBtn()); + try { + AbstractPatternFlyAlert.waitUntilDisplayed(); + } + catch (TimeoutException e) { + log.warn("Timeout waiting for alert to be displayed"); + } + } + + public void cancel() { + clickLink(cancelBtn()); + } + + public WebElement saveBtn() { + return save; + } + + public WebElement cancelBtn() { + return cancel; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java new file mode 100644 index 000000000000..7f585c2f44be --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.jboss.logging.Logger; +import org.openqa.selenium.WebDriver; + +/** + * @author Vaclav Muzikar + */ +public abstract class AbstractSocialLoginPage { + protected WebDriver driver; + protected Logger log = Logger.getLogger(this.getClass()); + + public void setDriver(WebDriver driver) { + this.driver = driver; + } + + public abstract void login(String user, String password); + + // Override only when you need to perform logout at the end of the test + public void logout() { + log.infof("no logout necessary for %s", this.getClass().getName()); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/BitbucketLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/BitbucketLoginPage.java new file mode 100644 index 000000000000..7e44a2217fce --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/BitbucketLoginPage.java @@ -0,0 +1,68 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.openqa.selenium.Keys; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import static org.keycloak.testsuite.util.UIUtils.clickLink; +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author Vaclav Muzikar + */ +public class BitbucketLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "username") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(id = "mfa-promote-dismiss") + private WebElement dismissMFA; + + @FindBy(xpath = "//div[contains(@class,'additional-auths')]/p/a") + private WebElement loginWithAtlassianButton; + + @Override + public void login(String user, String password) { + try { + clickLink(loginWithAtlassianButton); // BitBucket no longer has it's own login page yet sometimes it's + // displayed even though we need to use the Atlassian login page + } + catch (NoSuchElementException e) { + log.info("Already on Atlassian login page"); + } + + usernameInput.sendKeys(user); + usernameInput.sendKeys(Keys.RETURN); + pause(1000); + + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + + try { + clickLink(dismissMFA); + } + catch (NoSuchElementException e) { + log.info("MFA already dismissed"); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java new file mode 100644 index 000000000000..aec63783bead --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import java.util.List; + +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Vaclav Muzikar + */ +public class FacebookLoginPage extends AbstractSocialLoginPage { + private static final String continueButtonLocator = "//*[contains(@aria-label,'Continue')]"; + private static final String allowAllCookiesLocator = "//button[text()='Allow all cookies']"; + + @FindBy(id = "email") + private WebElement emailInput; + + @FindBy(id = "pass") + private WebElement passwordInput; + + @FindBy(id = "loginbutton") + private WebElement loginButton; + + @FindBy(xpath = continueButtonLocator) + private WebElement continueButton; + + @Override + public void login(String user, String password) { + // Check if allowing cookies is required and eventually allow them + List allowCookiesButton = driver.findElements(By.xpath(allowAllCookiesLocator)); + if (allowCookiesButton.size() > 0) + allowCookiesButton.get(0).click(); + + if (driver.findElements(By.xpath(continueButtonLocator)).isEmpty()){ //on first login + emailInput.clear(); + emailInput.sendKeys(user); + passwordInput.sendKeys(password); + JavascriptExecutor executor = (JavascriptExecutor) driver; + executor.executeScript("arguments[0].click();", loginButton); + continueButton.click(); + }else{ //already logged in in previous testcase, just confirm previous session + continueButton.click(); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java new file mode 100644 index 000000000000..6df11fe82e87 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java @@ -0,0 +1,58 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.keycloak.testsuite.util.UIUtils; +import org.keycloak.testsuite.util.URLUtils; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Vaclav Muzikar + */ +public class GitHubLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "login_field") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(name = "commit") + private WebElement loginButton; + + @FindBy(xpath = "//input[@type='submit']") + private WebElement logoutButton; + + @Override + public void login(String user, String password) { + usernameInput.clear(); + usernameInput.sendKeys(user); + passwordInput.sendKeys(password); + loginButton.click(); + } + + @Override + public void logout() { + log.info("performing logout from GitHub"); + URLUtils.navigateToUri("https://github.com/logout"); + if (URLUtils.currentUrlEquals("https://github.com/logout")) { + UIUtils.clickLink(logoutButton); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitLabLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitLabLoginPage.java new file mode 100644 index 000000000000..6cc5d3202637 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GitLabLoginPage.java @@ -0,0 +1,56 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.openqa.selenium.Keys; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Vaclav Muzikar + */ +public class GitLabLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "user_login") + private WebElement usernameInput; + + @FindBy(id = "user_password") + private WebElement passwordInput; + + @FindBy(xpath = "//input[@name='commit' and @value='Authorize']") + private WebElement authorizeButton; + + @Override + public void login(String user, String password) { + try { + usernameInput.sendKeys(user); + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + } + catch (NoSuchElementException e) { + // already logged in + } + + try { + authorizeButton.click(); + } + catch (NoSuchElementException e) { + // might not be necessary + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java new file mode 100644 index 000000000000..9838e2655e0e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import java.util.List; + +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import static org.keycloak.testsuite.util.UIUtils.clickLink; +import static org.keycloak.testsuite.util.URLUtils.navigateToUri; +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author Vaclav Muzikar + */ +public class GoogleLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "identifierId") + private WebElement emailInput; + + @FindBy(xpath = ".//input[@type='password']") + private WebElement passwordInput; + + @FindBy(xpath = "//form//ul/li/div[@role='link']") + private List selectAccountLinks; + + @Override + public void login(String user, String password) { + if (selectAccountLinks.size() > 1) { + clickLink(selectAccountLinks.get(selectAccountLinks.size() - 1)); + } + + emailInput.clear(); + emailInput.sendKeys(user); + emailInput.sendKeys(Keys.RETURN); + pause(3000); // wait for some animation or whatever + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + } + + @Override + public void logout() { + log.info("performing logout from Google"); + navigateToUri("https://www.google.com/accounts/Logout"); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/InstagramLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/InstagramLoginPage.java new file mode 100644 index 000000000000..e145d2137d74 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/InstagramLoginPage.java @@ -0,0 +1,80 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.keycloak.testsuite.util.WaitUtils; + +import org.openqa.selenium.Keys; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author Vaclav Muzikar + */ +public class InstagramLoginPage extends AbstractSocialLoginPage { + @FindBy(name = "username") + private WebElement usernameInput; + + @FindBy(name = "password") + private WebElement passwordInput; + + @FindBy(xpath = "//button[text()='Save info']") + private WebElement saveInfoBtn; + + @FindBy(xpath = "//div[text()='Not now']") + private WebElement notNowBtn; + + @FindBy(xpath = "//div[@aria-label='Allow']") + private WebElement allowBtn; + + @Override + public void login(String user, String password) { + try { + usernameInput.clear(); + usernameInput.sendKeys(user); + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + pause(5000); + + try { + WaitUtils.waitUntilElement(saveInfoBtn).is().visible(); + saveInfoBtn.click(); + pause(3000); + } + catch (NoSuchElementException e) { + log.info("'Save info' button not found, ignoring"); + pause(3000); + } + } + catch (NoSuchElementException e) { + log.info("Instagram is already logged in, just confirmation is expected"); + } + + // Approval dialog + try { + WaitUtils.waitUntilElement(allowBtn).is().visible(); + allowBtn.click(); + } catch (NoSuchElementException e) { + log.info("'Allow' button not found, ignoring"); + } + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java new file mode 100644 index 000000000000..9a32d09fea26 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Vaclav Muzikar + */ +public class LinkedInLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "username") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(xpath = "//button[(@type='submit') and (@aria-label='Sign in')]") + private WebElement loginButton; + + @FindBy(name = "action") + private WebElement authorizeButton; + + @Override + public void login(String user, String password) { + usernameInput.clear(); + usernameInput.sendKeys(user); + passwordInput.sendKeys(password); + loginButton.click(); + + try { + authorizeButton.click(); + log.info("LinkedIn test app authorized"); + } + catch (NoSuchElementException e) { + log.info("Skipping LinkedIn app authorization"); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java new file mode 100644 index 000000000000..9f6f6eed5c11 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.keycloak.testsuite.util.WaitUtils; + +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Vaclav Muzikar + */ +public class MicrosoftLoginPage extends AbstractSocialLoginPage { + @FindBy(name = "loginfmt") + private WebElement usernameInput; + + @FindBy(name = "passwd") + private WebElement passwordInput; + + @FindBy(id = "idSIButton9") + private WebElement submitButton; + + @FindBy(id = "acceptButton") + private WebElement acceptButton; + + @FindBy(xpath = "//input[contains(@class,'btn-primary')]") + private WebElement appAccessButton; + + @Override + public void login(String user, String password) { + WaitUtils.pause(5000); // we need to take it a bit slower + usernameInput.clear(); + usernameInput.sendKeys(user); + submitButton.click(); + + WaitUtils.pause(5000); + try { + passwordInput.sendKeys(password); + submitButton.click(); + } + catch (NoSuchElementException e) { + log.info("Already logged in to Microsoft IdP, no need to enter password"); + } + + // While logging into the app for the first time user is asked if he wants to stay signed in + try { + WaitUtils.pause(3000); + acceptButton.click(); + } + catch (NoSuchElementException e) { + log.info("User already allowed in the app"); + } + + // The app requires user consent for access to their information + try { + WaitUtils.pause(3000); + appAccessButton.click(); + } + catch (NoSuchElementException e) { + log.info("App already has access to user information"); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/OpenShiftLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/OpenShiftLoginPage.java new file mode 100644 index 000000000000..eb36d492d80a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/OpenShiftLoginPage.java @@ -0,0 +1,81 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.keycloak.testsuite.util.WaitUtils; + +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import static org.keycloak.testsuite.util.UIUtils.clickLink; + +/** + * @author Vaclav Muzikar + */ +public class OpenShiftLoginPage extends AbstractSocialLoginPage { + @FindBy(name = "username") + private WebElement usernameInput; + + @FindBy(name = "password") + private WebElement passwordInput; + + @FindBy(name = "approve") + private WebElement authorizeButton; + + private String userLoginLinkTitle; + + private WebElement userLoginLink; + + @Override + public void login(String user, String password) { + if(userLoginLinkTitle != null) { + setUserLoginLink(this.userLoginLinkTitle); + if(this.userLoginLink != null) { + clickLink(this.userLoginLink); + } + } + + WaitUtils.pause(3000); + usernameInput.sendKeys(user); + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + + try { + WaitUtils.pause(3000); + authorizeButton.click(); + } + catch (NoSuchElementException e) { + log.info("User already allowed in the app"); + } + } + + public void setUserLoginLinkTitle(String title) { + this.userLoginLinkTitle = title; + } + + private void setUserLoginLink(String linkAttrTitle) { + try { + this.userLoginLink = driver.findElement(By.xpath("//a[contains(@title,'"+linkAttrTitle+"')]")); + } catch (NoSuchElementException ex) { + log.error("No link with title: '" + linkAttrTitle + "' found on page. If you use the OPENSHIFT4_KUBE_ADMIN provider, set property loginBtnTitle in properties file to an existing title on the page to fix this error."); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/PayPalLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/PayPalLoginPage.java new file mode 100644 index 000000000000..f3b447641c5e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/PayPalLoginPage.java @@ -0,0 +1,71 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.keycloak.testsuite.util.WaitUtils; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Petter Lysne (petterlysne at hotmail dot com) + */ +public class PayPalLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "email") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(name = "btnLogin") + private WebElement loginButton; + + @FindBy(name = "btnNext") + private WebElement nextButton; + + @FindBy(name = "continueLogin") + private WebElement continueLoginButton; + + @Override + public void login(String user, String password) { + JavascriptExecutor executor = (JavascriptExecutor) driver; + try { + usernameInput.clear(); // to remove pre-filled email + usernameInput.sendKeys(user); + + WaitUtils.pause(3000); + try { + executor.executeScript("arguments[0].click();", nextButton); // sometimes the login process is just a one step, + // sometimes it's two steps so we need to click the 'Next' button + } + catch (NoSuchElementException e) { + // one step login process, no need to click the 'Next' button + } + + passwordInput.sendKeys(password); + WaitUtils.pause(3000); + executor.executeScript("arguments[0].click();", loginButton); + } + catch (NoSuchElementException e) { + WaitUtils.pause(3000); + executor.executeScript("arguments[0].click();", continueLoginButton); // already logged in, just need to confirm it + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java new file mode 100644 index 000000000000..b52b62194f12 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java @@ -0,0 +1,48 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Vaclav Muzikar + */ +public class StackOverflowLoginPage extends AbstractSocialLoginPage { + @FindBy(id = "email") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(xpath = "//button[@name='submit-button']") + private WebElement loginButton; + + @FindBy(id = "onetrust-accept-btn-handler") + private WebElement acceptAllCookiesButton; + + @Override + public void login(String user, String password) { + acceptAllCookiesButton.click(); + + usernameInput.sendKeys(user); + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterConsentLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterConsentLoginPage.java new file mode 100644 index 000000000000..d8a2afc2571a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterConsentLoginPage.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; + +/** + * @author Vaclav Muzikar + */ +public class TwitterConsentLoginPage extends AbstractSocialLoginPage { + + @FindBy(xpath = "//input[@type='submit' and @id='allow']") + private WebElement signInButton; + + TwitterLoginPage loginPage; + + @Override + public void login(String user, String password) { + // twitter presents a consent page for the application + // the SignIn button should be clicked first to go to the real login + signInButton.click(); + + if (loginPage == null) { + loginPage = new TwitterLoginPage(); + loginPage.setDriver(driver); + PageFactory.initElements(driver, loginPage); + } + loginPage.login(user, password); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java new file mode 100644 index 000000000000..7f457bc76d0f --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java @@ -0,0 +1,68 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.pages.social; + +import java.util.List; + +import org.keycloak.testsuite.util.WaitUtils; + +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author Vaclav Muzikar + */ +public class TwitterLoginPage extends AbstractSocialLoginPage { + private static final String signInBtnLocator = "allow"; + + @FindBy(xpath = "//input[@type='text']") + private WebElement usernameInput; + + @FindBy(xpath = "//input[@type='password']") + private WebElement passwordInput; + + @FindBy(id = signInBtnLocator) + private WebElement signInBtn; + + @Override + public void login(String user, String password) { + // new login page is two phase login (username and then password) and it + // needs lots of JS, twitter does not work with default HtmlUnit driver + + List signInBtn = driver.findElements(By.id(signInBtnLocator)); + if (signInBtn.size() > 0) { + signInBtn.get(0).click(); + } + + pause(3000); + usernameInput.clear(); + usernameInput.sendKeys(user); + usernameInput.sendKeys(Keys.RETURN); + + // wait for the password input to appear + WaitUtils.waitUntilElement(passwordInput).is().visible(); + passwordInput.clear(); + passwordInput.sendKeys(password); + passwordInput.sendKeys(Keys.RETURN); + + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/rest/representation/TestAuthenticationChannelRequest.java b/tests/base/src/test/java/org/keycloak/testsuite/rest/representation/TestAuthenticationChannelRequest.java new file mode 100644 index 000000000000..3fa193004a96 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/rest/representation/TestAuthenticationChannelRequest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.rest.representation; + +import org.keycloak.protocol.oidc.grants.ciba.channel.AuthenticationChannelRequest; + +/** + * @author Pedro Igor + */ +public class TestAuthenticationChannelRequest { + + private String bearerToken; + private AuthenticationChannelRequest request; + + public TestAuthenticationChannelRequest() { + // for reflection + } + + public TestAuthenticationChannelRequest(AuthenticationChannelRequest request, String bearerToken) { + setBearerToken(bearerToken); + setRequest(request); + } + + public void setBearerToken(String bearerToken) { + this.bearerToken = bearerToken; + } + + public String getBearerToken() { + return bearerToken; + } + + public void setRequest(AuthenticationChannelRequest request) { + this.request = request; + } + + public AuthenticationChannelRequest getRequest() { + return request; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java new file mode 100644 index 000000000000..ad258fef93f7 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java @@ -0,0 +1,187 @@ +package org.keycloak.testsuite.updaters; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.admin.client.resource.ClientsResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientScopeRepresentation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +/** + * Updater for client attributes. See {@link ServerResourceUpdater} for further details. + * @author hmlnarik + */ +public class ClientAttributeUpdater extends ServerResourceUpdater { + + private final RealmResource realmResource; + + /** + * Creates a {@ClientAttributeUpdater} for the given client. The client must exist. + * @param adminClient + * @param realm + * @param clientId + * @return + */ + public static ClientAttributeUpdater forClient(Keycloak adminClient, String realm, String clientId) { + RealmResource realmRes = adminClient.realm(realm); + ClientsResource clients = realmRes.clients(); + List foundClients = clients.findByClientId(clientId); + assertThat(foundClients, hasSize(1)); + ClientResource clientRes = clients.get(foundClients.get(0).getId()); + + return new ClientAttributeUpdater(clientRes, realmRes); + } + + private ClientAttributeUpdater(ClientResource resource, RealmResource realmResource) { + super(resource, resource::toRepresentation, resource::update); + if (this.rep.getAttributes() == null) { + this.rep.setAttributes(new HashMap<>()); + } + this.realmResource = realmResource; + } + + @Override + protected void performUpdate(ClientRepresentation from, ClientRepresentation to) { + super.performUpdate(from, to); + updateViaAddRemove(from.getDefaultClientScopes(), to.getDefaultClientScopes(), this::getConversionForScopeNameToId, resource::addDefaultClientScope, resource::removeDefaultClientScope); + updateViaAddRemove(from.getOptionalClientScopes(), to.getOptionalClientScopes(), this::getConversionForScopeNameToId, resource::addOptionalClientScope, resource::removeOptionalClientScope); + } + + private Function getConversionForScopeNameToId() { + Map scopeNameToIdMap = realmResource.clientScopes().findAll().stream() + .collect(Collectors.toMap(ClientScopeRepresentation::getName, ClientScopeRepresentation::getId)); + + return scopeNameToIdMap::get; + } + + public ClientAttributeUpdater setClientId(String clientId) { + this.rep.setClientId(clientId); + return this; + } + + public ClientAttributeUpdater setName(String name) { + this.rep.setName(name); + return this; + } + + public ClientAttributeUpdater setAttribute(String name, String value) { + this.rep.getAttributes().put(name, value); + if (value != null && !this.origRep.getAttributes().containsKey(name)) { + this.origRep.getAttributes().put(name, null); + } + return this; + } + + public ClientAttributeUpdater setRedirectUris(List values) { + this.rep.setRedirectUris(values); + return this; + } + + public ClientAttributeUpdater filterRedirectUris(Predicate filter) { + this.rep.setRedirectUris(this.rep.getRedirectUris().stream().filter(filter).collect(Collectors.toList())); + return this; + } + + public ClientAttributeUpdater removeAttribute(String name) { + this.rep.getAttributes().remove(name); + return this; + } + + public ClientAttributeUpdater setAuthenticationFlowBindingOverrides(Map bindings) { + rep.setAuthenticationFlowBindingOverrides(bindings); + if (origRep.getAuthenticationFlowBindingOverrides() == null) { + origRep.setAuthenticationFlowBindingOverrides(new HashMap<>()); + } + for (String key : bindings.keySet()) { + origRep.getAuthenticationFlowBindingOverrides().putIfAbsent(key, ""); + } + return this; + } + + public ClientAttributeUpdater setConsentRequired(Boolean consentRequired) { + rep.setConsentRequired(consentRequired); + return this; + } + + public ClientAttributeUpdater setFrontchannelLogout(Boolean frontchannelLogout) { + rep.setFrontchannelLogout(frontchannelLogout); + return this; + } + + public ClientAttributeUpdater setFullScopeAllowed(Boolean fullScopeAllowed) { + rep.setFullScopeAllowed(fullScopeAllowed); + return this; + } + + public ClientAttributeUpdater setImplicitFlowEnabled(Boolean implicitFlowEnabled) { + rep.setImplicitFlowEnabled(implicitFlowEnabled); + return this; + } + + public ClientAttributeUpdater setDefaultClientScopes(List defaultClientScopes) { + rep.setDefaultClientScopes(defaultClientScopes); + return this; + } + + public ClientAttributeUpdater setOptionalClientScopes(List optionalClientScopes) { + rep.setOptionalClientScopes(optionalClientScopes); + return this; + } + + public ProtocolMappersUpdater protocolMappers() { + return new ProtocolMappersUpdater(resource.getProtocolMappers()); + } + + public RoleScopeUpdater realmRoleScope() { + return new RoleScopeUpdater(resource.getScopeMappings().realmLevel()); + } + + public RoleScopeUpdater clientRoleScope(String clientUUID) { + return new RoleScopeUpdater(resource.getScopeMappings().clientLevel(clientUUID)); + } + + public ClientAttributeUpdater setAdminUrl(String adminUrl) { + rep.setAdminUrl(adminUrl); + return this; + } + + public ClientAttributeUpdater setBaseUrl(String baseUrl) { + rep.setBaseUrl(baseUrl); + return this; + } + + public ClientAttributeUpdater addDefaultClientScope(String clientScope) { + rep.getDefaultClientScopes().add(clientScope); + return this; + } + + public ClientAttributeUpdater addOptionalClientScope(String clientScope) { + rep.getOptionalClientScopes().add(clientScope); + return this; + } + + public ClientAttributeUpdater removeOptionalClientScope(String clientScope) { + rep.getOptionalClientScopes().remove(clientScope); + return this; + } + + public ClientAttributeUpdater setDirectAccessGrantsEnabled(Boolean directAccessGranted) { + rep.setDirectAccessGrantsEnabled(directAccessGranted); + return this; + } + + public ClientAttributeUpdater setEnabled(Boolean enabled){ + rep.setEnabled(enabled); + return this; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/Creator.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/Creator.java new file mode 100644 index 000000000000..a5e9fe0088f6 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/Creator.java @@ -0,0 +1,195 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.updaters; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +import jakarta.ws.rs.core.Response; + +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.AuthenticationManagementResource; +import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.admin.client.resource.ClientsResource; +import org.keycloak.admin.client.resource.ComponentResource; +import org.keycloak.admin.client.resource.ComponentsResource; +import org.keycloak.admin.client.resource.GroupResource; +import org.keycloak.admin.client.resource.GroupsResource; +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.admin.client.resource.IdentityProvidersResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; +import org.keycloak.representations.idm.AuthenticationFlowRepresentation; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +import org.hamcrest.Matchers; +import org.jboss.logging.Logger; + +import static org.keycloak.testsuite.admin.ApiUtil.getCreatedId; + +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Creates a temporary realm object and makes sure it is removed when used within try-with-resources. + */ +public class Creator implements AutoCloseable { + + private final static Logger LOG = Logger.getLogger(Creator.class); + + public static Creator create(Keycloak adminClient, RealmRepresentation rep) { + adminClient.realms().create(rep); + final RealmResource r = adminClient.realm(rep.getRealm()); + LOG.debugf("Created realm %s", rep.getRealm()); + return new Creator(rep.getRealm(), r, r::remove); + } + + public static Creator create(RealmResource realmResource, GroupRepresentation rep) { + final GroupsResource groups = realmResource.groups(); + try (Response response = groups.add(rep)) { + String createdId = getCreatedId(response); + final GroupResource r = groups.group(createdId); + LOG.debugf("Created group ID %s", createdId); + return new Creator(createdId, r, r::remove); + } + } + + public static Creator create(RealmResource realmResource, ClientRepresentation rep) { + final ClientsResource clients = realmResource.clients(); + try (Response response = clients.create(rep)) { + String createdId = getCreatedId(response); + final ClientResource r = clients.get(createdId); + LOG.debugf("Created client ID %s", createdId); + return new Creator(createdId, r, r::remove); + } + } + + public static Creator create(RealmResource realmResource, UserRepresentation rep) { + final UsersResource users = realmResource.users(); + try (Response response = users.create(rep)) { + String createdId = getCreatedId(response); + final UserResource r = users.get(createdId); + LOG.debugf("Created user ID %s", createdId); + return new Creator(createdId, r, r::remove); + } + } + + public static Creator create(RealmResource realmResource, ComponentRepresentation rep) { + final ComponentsResource components = realmResource.components(); + try (Response response = components.add(rep)) { + String createdId = getCreatedId(response); + final ComponentResource r = components.component(createdId); + LOG.debugf("Created component ID %s", createdId); + return new Creator(createdId, r, r::remove); + } + } + + public static Creator.Flow create(RealmResource realmResource, AuthenticationFlowRepresentation rep) { + final AuthenticationManagementResource authMgmgRes = realmResource.flows(); + try (Response response = authMgmgRes.createFlow(rep)) { + String createdId = getCreatedId(response); + LOG.debugf("Created flow ID %s", createdId); + return new Flow(createdId, rep.getAlias(), authMgmgRes, () -> authMgmgRes.deleteFlow(createdId)); + } + } + + public static Creator create(RealmResource realmResource, IdentityProviderRepresentation rep) { + final IdentityProvidersResource res = realmResource.identityProviders(); + assertThat("Identity provider alias must be specified", rep.getAlias(), Matchers.notNullValue()); + try (Response response = res.create(rep)) { + String createdId = getCreatedId(response); + final IdentityProviderResource r = res.get(rep.getAlias()); + LOG.debugf("Created identity provider ID %s", createdId); + return new Creator(createdId, r, r::remove); + } + } + + public static Creator create(RealmResource realmResource, String identityProviderAlias, IdentityProviderMapperRepresentation rep) { + final IdentityProvidersResource res = realmResource.identityProviders(); + assertThat("Identity provider alias must be specified", identityProviderAlias, Matchers.notNullValue()); + rep.setIdentityProviderAlias(identityProviderAlias); + try (Response response = res.get(identityProviderAlias).addMapper(rep)) { + String createdId = getCreatedId(response); + final IdentityProviderResource r = res.get(identityProviderAlias); + LOG.debugf("Created identity provider mapper ID %s", createdId); + return new Creator(createdId, r, () -> r.delete(createdId)); + } + } + + private final String id; + private final T resource; + private final Runnable closer; + private final AtomicBoolean closerRan = new AtomicBoolean(false); + + private Creator(String id, T resource, Runnable closer) { + this.id = id; + this.resource = resource; + this.closer = closer; + } + + public String id() { + return this.id; + } + + public T resource() { + return this.resource; + } + + @Override + public void close() { + if (this.closerRan.compareAndSet(false, true)) { + LOG.debugf("Removing resource ID %s", id); + try { + closer.run(); + } catch (jakarta.ws.rs.NotFoundException ex) { + LOG.debugf("Resource with ID %s perhaps removed in meantime.", id); + } + } else { + LOG.debugf("Already removed resource ID %s", id); + } + } + + public static class Flow extends Creator { + + private final String alias; + + public Flow(String id, String alias, AuthenticationManagementResource resource, Runnable closer) { + super(id, resource, closer); + this.alias = alias; + } + + public AuthenticationExecutionInfoRepresentation addExecution(String providerId) { + Map c = new HashMap<>(); + c.put("provider", providerId); + resource().addExecution(alias, c); // addExecution only handles "provider" in data + return resource().getExecutions(alias).stream() + .filter(aer -> Objects.equals(providerId, aer.getProviderId())) + .findFirst() + .orElse(null); + } + + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java new file mode 100644 index 000000000000..c824e263f519 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java @@ -0,0 +1,61 @@ +package org.keycloak.testsuite.updaters; + +import java.io.Closeable; +import java.util.HashMap; +import java.util.Map; + +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +/** + * + * @author hmlnarik + */ +public class IdentityProviderAttributeUpdater { + + private final Map originalAttributes = new HashMap<>(); + + private final IdentityProviderResource identityProviderResource; + + private final IdentityProviderRepresentation rep; + + public IdentityProviderAttributeUpdater(IdentityProviderResource identityProviderResource) { + this.identityProviderResource = identityProviderResource; + this.rep = identityProviderResource.toRepresentation(); + if (this.rep.getConfig() == null) { + this.rep.setConfig(new HashMap<>()); + } + } + + public IdentityProviderAttributeUpdater setAttribute(String name, String value) { + if (! originalAttributes.containsKey(name)) { + this.originalAttributes.put(name, this.rep.getConfig().put(name, value)); + } else { + this.rep.getConfig().put(name, value); + } + return this; + } + + public IdentityProviderAttributeUpdater removeAttribute(String name) { + if (! originalAttributes.containsKey(name)) { + this.originalAttributes.put(name, this.rep.getConfig().put(name, null)); + } else { + this.rep.getConfig().put(name, null); + } + return this; + } + + public IdentityProviderAttributeUpdater setStoreToken(boolean storeToken) { + rep.setStoreToken(storeToken); + return this; + } + + public Closeable update() { + identityProviderResource.update(rep); + + return () -> { + rep.getConfig().putAll(originalAttributes); + identityProviderResource.update(rep); + }; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderCreator.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderCreator.java new file mode 100644 index 000000000000..027806107b67 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderCreator.java @@ -0,0 +1,66 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.updaters; + +import java.io.Closeable; +import java.io.IOException; + +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.core.Response; + +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.admin.client.resource.IdentityProvidersResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +/** + * Creates a temporary realm and makes sure it is removed. + */ +public class IdentityProviderCreator implements Closeable { + + private final IdentityProvidersResource resource; + private final String alias; + + public IdentityProviderCreator(RealmResource realmResource, IdentityProviderRepresentation rep) { + resource = realmResource.identityProviders(); + alias = rep.getAlias(); + Response response = null; + try { + response = resource.create(rep); + } finally { + if (response != null) + response.close(); + } + } + + public IdentityProvidersResource resource() { + return this.resource; + } + + public IdentityProviderResource identityProvider() { + return this.resource().get(alias); + } + + @Override + public void close() throws IOException { + try { + resource.get(alias).remove(); + } catch (NotFoundException e) { + // ignore + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/OrganizationAttributeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/OrganizationAttributeUpdater.java new file mode 100644 index 000000000000..b73f3d2793a0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/OrganizationAttributeUpdater.java @@ -0,0 +1,37 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.updaters; + +import org.keycloak.admin.client.resource.OrganizationResource; +import org.keycloak.representations.idm.OrganizationRepresentation; + +public class OrganizationAttributeUpdater extends ServerResourceUpdater { + + public OrganizationAttributeUpdater(OrganizationResource resource) { + super(resource, resource::toRepresentation, resource::update); + } + + public OrganizationAttributeUpdater setRedirectUrl(String redirectUrl) { + this.rep.setRedirectUrl(redirectUrl); + return this; + } + + public OrganizationAttributeUpdater setEnabled(boolean enabled) { + this.rep.setEnabled(enabled); + return this; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/ProtocolMappersUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/ProtocolMappersUpdater.java new file mode 100644 index 000000000000..c54223e77ed8 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/ProtocolMappersUpdater.java @@ -0,0 +1,93 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.updaters; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; + +/** + * Updater for mappers. See {@link ServerResourceUpdater} for further details. + * @author hmlnarik + */ +public class ProtocolMappersUpdater extends ServerResourceUpdater> { + + public ProtocolMappersUpdater(ProtocolMappersResource resource) { + super(resource, resource::getMappers, null); + this.updater = this::update; + } + + public ProtocolMappersUpdater add(ProtocolMapperRepresentation... representation) { + rep.addAll(Arrays.asList(representation)); + return this; + } + + public ProtocolMappersUpdater clear() { + rep.clear(); + return this; + } + + public ProtocolMappersUpdater remove(ProtocolMapperRepresentation representation) { + rep.remove(representation); + return this; + } + + public ProtocolMappersUpdater removeById(String id) { + for (Iterator it = rep.iterator(); it.hasNext();) { + ProtocolMapperRepresentation mapper = it.next(); + if (id.equals(mapper.getId())) { + it.remove(); + break; + } + } + return this; + } + + public ProtocolMappersUpdater removeByName(String name) { + for (Iterator it = rep.iterator(); it.hasNext();) { + ProtocolMapperRepresentation mapper = it.next(); + if (name.equals(mapper.getName())) { + it.remove(); + break; + } + } + return this; + } + + private void update(List expectedMappers) { + List currentMappers = resource.getMappers(); + + Set currentMapperIds = currentMappers.stream().map(ProtocolMapperRepresentation::getId).collect(Collectors.toSet()); + Set expectedMapperIds = expectedMappers.stream().map(ProtocolMapperRepresentation::getId).collect(Collectors.toSet()); + + List toAdd = expectedMappers.stream() + .filter(mapper -> ! currentMapperIds.contains(mapper.getId())) + .collect(Collectors.toList()); + Stream toRemove = currentMappers.stream() + .filter(mapper -> ! expectedMapperIds.contains(mapper.getId())); + + resource.createMapper(toAdd); + toRemove.map(ProtocolMapperRepresentation::getId).forEach(resource::delete); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java new file mode 100644 index 000000000000..7a818afb37be --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java @@ -0,0 +1,253 @@ +package org.keycloak.testsuite.updaters; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.representations.idm.RealmRepresentation; + +/** + * Updater for realm attributes. See {@link ServerResourceUpdater} for further details. + * @author hmlnarik + */ +public class RealmAttributeUpdater extends ServerResourceUpdater { + + public RealmAttributeUpdater(RealmResource resource) { + super(resource, resource::toRepresentation, resource::update); + if (this.rep.getAttributes() == null) { + this.rep.setAttributes(new HashMap<>()); + } + } + + public RealmAttributeUpdater setAttribute(String name, String value) { + this.rep.getAttributes().put(name, value); + return this; + } + + public RealmAttributeUpdater removeAttribute(String name) { + this.rep.getAttributes().put(name, null); + return this; + } + + public RealmAttributeUpdater setPublicKey(String key) { + this.rep.setPublicKey(key); + return this; + } + + public RealmAttributeUpdater setPrivateKey(String key) { + this.rep.setPrivateKey(key); + return this; + } + + public RealmAttributeUpdater setDefaultDefaultClientScopes(List defaultClientScopes) { + rep.setDefaultDefaultClientScopes(defaultClientScopes); + return this; + } + + public RealmAttributeUpdater setAccessCodeLifespan(Integer accessCodeLifespan) { + rep.setAccessCodeLifespan(accessCodeLifespan); + return this; + } + + public RealmAttributeUpdater setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin) { + rep.setAccessCodeLifespanLogin(accessCodeLifespanLogin); + return this; + } + + public RealmAttributeUpdater setSsoSessionIdleTimeout(Integer timeout) { + rep.setSsoSessionIdleTimeout(timeout); + return this; + } + + public RealmAttributeUpdater setSsoSessionMaxLifespan(Integer timeout) { + rep.setSsoSessionMaxLifespan(timeout); + return this; + } + + public RealmAttributeUpdater setSsoSessionIdleTimeoutRememberMe(Integer idleTimeout) { + rep.setSsoSessionIdleTimeoutRememberMe(idleTimeout); + return this; + } + + public RealmAttributeUpdater setSsoSessionMaxLifespanRememberMe(Integer maxLifespan) { + rep.setSsoSessionMaxLifespanRememberMe(maxLifespan); + return this; + } + + public RealmAttributeUpdater setAccessTokenLifespanForImplicitFlow(Integer lifespan) { + rep.setAccessTokenLifespanForImplicitFlow(lifespan); + return this; + } + + public RealmAttributeUpdater setRememberMe(Boolean rememberMe) { + rep.setRememberMe(rememberMe); + return this; + } + + public RealmAttributeUpdater setRegistrationEmailAsUsername(Boolean value) { + rep.setRegistrationEmailAsUsername(value); + return this; + } + + public RealmAttributeUpdater setEditUserNameAllowed(Boolean value) { + rep.setEditUsernameAllowed(value); + return this; + } + + public RealmAttributeUpdater setPermanentLockout(Boolean value) { + rep.setPermanentLockout(value); + return this; + } + + public RealmAttributeUpdater setQuickLoginCheckMilliSeconds(Long value) { + rep.setQuickLoginCheckMilliSeconds(value); + return this; + } + + public RealmAttributeUpdater setWaitIncrementSeconds(Integer value) { + rep.setWaitIncrementSeconds(value); + return this; + } + + public RealmAttributeUpdater setMaxFailureWaitSeconds(Integer value) { + rep.setMaxFailureWaitSeconds(value); + return this; + } + + public RealmAttributeUpdater setFailureFactor(Integer value) { + rep.setFailureFactor(value); + return this; + } + public RealmAttributeUpdater setMaxDeltaTimeSeconds(Integer value) { + rep.setMaxDeltaTimeSeconds(value); + return this; + } + + public RealmAttributeUpdater setMaxSecondaryAuthFailures(Integer value) { + rep.setMaxSecondaryAuthFailures(value); + return this; + } + + public RealmAttributeUpdater setEventsListeners(List eventListanets) { + rep.setEventsListeners(eventListanets); + return this; + } + + public RealmAttributeUpdater addEventsListener(String value) { + List list = new ArrayList<>(rep.getEventsListeners()); + list.add(value); + rep.setEventsListeners(list); + return this; + } + + public RealmAttributeUpdater setDuplicateEmailsAllowed(Boolean value) { + rep.setDuplicateEmailsAllowed(value); + return this; + } + + public RealmAttributeUpdater setPasswordPolicy(String policy) { + rep.setPasswordPolicy(policy); + return this; + } + + public RealmAttributeUpdater setVerifyEmail(Boolean value) { + rep.setVerifyEmail(value); + return this; + } + + public RealmAttributeUpdater setBrowserFlow(String browserFlow) { + rep.setBrowserFlow(browserFlow); + return this; + } + + public RealmAttributeUpdater setNotBefore(Integer notBefore) { + rep.setNotBefore(notBefore); + return this; + } + + public RealmAttributeUpdater setDefaultLocale(String defaultLocale) { + rep.setDefaultLocale(defaultLocale); + return this; + } + + public RealmAttributeUpdater addSupportedLocale(String locale) { + if (origRep.getSupportedLocales() == null) { + origRep.setSupportedLocales(Collections.emptySet()); + } + rep.addSupportedLocales(locale); + return this; + } + + public RealmAttributeUpdater setInternationalizationEnabled(Boolean internationalizationEnabled) { + rep.setInternationalizationEnabled(internationalizationEnabled); + return this; + } + + // OTP Policy + public RealmAttributeUpdater setOtpPolicyAlgorithm(String otpPolicyAlgorithm) { + rep.setOtpPolicyAlgorithm(otpPolicyAlgorithm); + return this; + } + + public RealmAttributeUpdater setOtpPolicyDigits(Integer otpPolicyDigits) { + rep.setOtpPolicyDigits(otpPolicyDigits); + return this; + } + + public RealmAttributeUpdater setOtpPolicyInitialCounter(Integer otpPolicyInitialCounter) { + rep.setOtpPolicyInitialCounter(otpPolicyInitialCounter); + return this; + } + + public RealmAttributeUpdater setOtpPolicyPeriod(Integer otpPolicyPeriod) { + rep.setOtpPolicyPeriod(otpPolicyPeriod); + return this; + } + + public RealmAttributeUpdater setOtpPolicyType(String otpPolicyType) { + rep.setOtpPolicyType(otpPolicyType); + return this; + } + + public RealmAttributeUpdater setOtpPolicyLookAheadWindow(Integer otpPolicyLookAheadWindow) { + rep.setOtpPolicyLookAheadWindow(otpPolicyLookAheadWindow); + return this; + } + + public RealmAttributeUpdater setOtpPolicyCodeReusable(Boolean isCodeReusable) { + rep.setOtpPolicyCodeReusable(isCodeReusable); + return this; + } + + public RealmAttributeUpdater setSmtpServer(String name, String value) { + rep.getSmtpServer().put(name, value); + return this; + } + + public RealmAttributeUpdater setBrowserSecurityHeader(String name, String value) { + rep.getBrowserSecurityHeaders().put(name, value); + return this; + } + + public RealmAttributeUpdater setOrganizationsEnabled(Boolean organizationsEnabled) { + rep.setOrganizationsEnabled(organizationsEnabled); + return this; + } + + public RealmAttributeUpdater setRegistrationAllowed(Boolean registrationAllowed) { + rep.setRegistrationAllowed(registrationAllowed); + return this; + } + + public RealmAttributeUpdater setAdminPermissionsEnabled(Boolean adminPermissionsEnabled) { + rep.setAdminPermissionsEnabled(adminPermissionsEnabled); + return this; + } + + public RealmAttributeUpdater setWebAuthnPolicyPasswordlessPasskeysEnabled(Boolean passkeysEnabled) { + rep.setWebAuthnPolicyPasswordlessPasskeysEnabled(passkeysEnabled); + return this; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/RoleScopeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/RoleScopeUpdater.java new file mode 100644 index 000000000000..580b96f794bb --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/RoleScopeUpdater.java @@ -0,0 +1,76 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.updaters; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.keycloak.admin.client.resource.RoleScopeResource; +import org.keycloak.representations.idm.RoleRepresentation; + +/** + * Updater for role scope attributes. See {@link ServerResourceUpdater} for further details. + * @author hmlnarik + */ +public class RoleScopeUpdater extends ServerResourceUpdater> { + + public RoleScopeUpdater(RoleScopeResource resource) { + super(resource, resource::listAll, null); + this.updater = this::update; + } + + public RoleScopeUpdater add(RoleRepresentation representation) { + rep.add(representation); + return this; + } + + public RoleScopeUpdater remove(RoleRepresentation representation) { + rep.remove(representation); + return this; + } + + public RoleScopeUpdater removeByName(String name) { + for (Iterator it = rep.iterator(); it.hasNext();) { + RoleRepresentation mapper = it.next(); + if (name.equals(mapper.getName())) { + it.remove(); + break; + } + } + return this; + } + + private void update(List expectedRoles) { + List currentRoles = resource.listAll(); + + Set currentRoleIds = currentRoles.stream().map(RoleRepresentation::getId).collect(Collectors.toSet()); + Set expectedRoleIds = expectedRoles.stream().map(RoleRepresentation::getId).collect(Collectors.toSet()); + + List toAdd = expectedRoles.stream() + .filter(role -> ! currentRoleIds.contains(role.getId())) + .collect(Collectors.toList()); + List toRemove = currentRoles.stream() + .filter(role -> ! expectedRoleIds.contains(role.getId())) + .collect(Collectors.toList()); + + resource.add(toAdd); + resource.remove(toRemove); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/ServerResourceUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/ServerResourceUpdater.java new file mode 100644 index 000000000000..ab311b61a8f9 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/ServerResourceUpdater.java @@ -0,0 +1,151 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.updaters; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * Class for updating a server resource. This class supports reverting changes via try-with-resources block. + * + * It works as follows: + *

    + *
  1. In the constructor, current representation of the resource is obtained and stored internally into two object: + * one that is used to capture modifications local, and another one which is immutable and used for restoration at the end
  2. + *
  3. The first object can be modified locally via instance methods.
  4. + *
  5. Once modifications are finalized, {@link #update()} method updates the object on the server. + * Note that this method can be called more than once inside the {@code try} block.
  6. + *
  7. After finishing the try-with-resources block, the changes are reverted back by updating the resource on the server + * to the state in the first step
  8. + *
+ * + * It is generally used according to the following pattern: + *
+ * try (ServerResourceUpdater sru = new ServerResourceUpdater().setProperty(x).update()) {
+ *     // ... do the job
+ *     // Potentially use sru to modify the object again and run sru.update()
+ * }
+ * 
+ * + * @param Type of the subclass (to support Builder pattern) + * @param Object representation type + * @param Server resource + * @author hmlnarik + */ +public abstract class ServerResourceUpdater implements Closeable { + + protected final Res resource; + protected final Rep rep; + protected final Rep origRep; + protected Consumer updater; + protected boolean updated = false; + + public ServerResourceUpdater(Res resource, Supplier representationGenerator, Consumer updater) { + this.resource = resource; + this.updater = updater; + + // origRep and rep need to be two different instances + this.origRep = representationGenerator.get(); + this.rep = representationGenerator.get(); + } + + /** + * Returns server resource accessing the object. + * @return + */ + public Res getResource() { + return resource; + } + + /** + * Updates the object on the server according to the current internal representation. + * @return + */ + public T update() { + performUpdate(origRep, rep); + this.updated = true; + return (T) this; + } + + protected void performUpdate(Rep from, Rep to) { + updater.accept(to); + } + + /** + * Updates the internal representation by a custom function. + * @param representationUpdater + * @return + */ + public T updateWith(Consumer representationUpdater) { + representationUpdater.accept(this.rep); + return (T) this; + } + + /** + * Reverts the object state on the server to the state that was there upon creation of this updater. + * @throws IOException + */ + @Override + public void close() throws IOException { + if (this.updated) { + performUpdate(rep, origRep); + } + } + + /** + * This function performs a set of single {@code add} and {@code remove} operations that represent the changes needed to + * get collection {@code from} to the state of collection {@code to}. Since this is intended to work on collections of + * names of objects but the {@code add} and {@code remove} functions operate on IDs of those objects, a conversion + * is performed. + * + * @param Type of the objects in the collections (e.g. names) + * @param Type of the objects required by add/remove functions (e.g. IDs) + * @param from Initial collection + * @param to Target collection + * @param client2ServerConverterGenerator Producer of the converter. If not needed, just use {@code () -> Functions::identity}. + * This is intentionally a lazy-evaluated function variable because the conversion map often needs to be obtained from the + * server which can be slow operation. This function is called only if the two collections differ. + * @param add Function to add + * @param remove Function to remove + */ + public static void updateViaAddRemove(Collection from, Collection to, Supplier> client2ServerConverterGenerator, Consumer add, Consumer remove) { + if (Objects.equals(from, to)) { + return; + } + + Function client2ServerConverter = client2ServerConverterGenerator.get(); + + Set current = from == null ? Collections.EMPTY_SET : from.stream().map(client2ServerConverter).collect(Collectors.toSet()); + Set expected = to == null ? Collections.EMPTY_SET : to.stream().map(client2ServerConverter).collect(Collectors.toSet()); + + expected.stream() + .filter(role -> ! current.contains(role)) + .forEach(add); + current.stream() + .filter(role -> ! expected.contains(role)) + .forEach(remove); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/SetSystemProperty.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/SetSystemProperty.java new file mode 100644 index 000000000000..9a798b24b5e0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/SetSystemProperty.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.updaters; + +import java.io.Closeable; + +/** + * @author Marek Posolda + */ +public class SetSystemProperty implements Closeable { + + private final String name; + private final String oldValue; + + public SetSystemProperty(String name, String value) { + this.name = name; + this.oldValue = System.getProperty(name); + + if (value == null) { + if (oldValue != null) { + System.getProperties().remove(name); + } + } else { + System.setProperty(name, value); + } + } + + public void revert() { + String value = System.getProperty(name); + + if (oldValue == null) { + if (value != null) { + System.getProperties().remove(name); + } + } else { + System.setProperty(name, oldValue); + } + } + + @Override + public void close() { + revert(); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/UserAttributeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/UserAttributeUpdater.java new file mode 100644 index 000000000000..478822cc68a1 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/UserAttributeUpdater.java @@ -0,0 +1,138 @@ +package org.keycloak.testsuite.updaters; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +import static org.keycloak.testsuite.updaters.ServerResourceUpdater.updateViaAddRemove; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +/** + * Updater for user attributes. See {@link ServerResourceUpdater} for further details. + * @author hmlnarik + */ +public class UserAttributeUpdater extends ServerResourceUpdater { + + private final RealmResource realmResource; + + /** + * Creates a {@UserAttributeUpdater} for the given user. The user must exist. + * @param adminClient + * @param realm + * @param clientId + * @return + */ + public static UserAttributeUpdater forUserByUsername(Keycloak adminClient, String realm, String userName) { + return forUserByUsername(adminClient.realm(realm), userName); + } + + public static UserAttributeUpdater forUserByUsername(RealmResource realm, String userName) { + UsersResource users = realm.users(); + List foundUsers = users.search(userName).stream() + .filter(ur -> userName.equalsIgnoreCase(ur.getUsername())) + .collect(Collectors.toList()); + assertThat(foundUsers, hasSize(1)); + UserResource userRes = users.get(foundUsers.get(0).getId()); + + return new UserAttributeUpdater(userRes, realm); + } + + public UserAttributeUpdater(UserResource resource) { + this(resource, null); + } + + public UserAttributeUpdater(UserResource resource, RealmResource realmResource) { + super(resource, + () -> { + UserRepresentation r = resource.toRepresentation(); + r.setGroups(resource.groups().stream().map(GroupRepresentation::getPath).collect(Collectors.toList())); + return r; + }, + resource::update + ); + if (this.rep.getAttributes() == null) { + this.rep.setAttributes(new HashMap<>()); + } + this.realmResource = realmResource; + } + + @Override + protected void performUpdate(UserRepresentation from, UserRepresentation to) { + super.performUpdate(from, to); + updateViaAddRemove(from.getGroups(), to.getGroups(), this::getConversionForGroupPathToId, resource::joinGroup, resource::leaveGroup); + } + + private Function getConversionForGroupPathToId() { + if (realmResource == null) { + return String::toString; + } + + Map humanIdToIdMap = realmResource.groups().groups().stream() + .collect(Collectors.toMap(GroupRepresentation::getPath, GroupRepresentation::getId)); + + return humanIdToIdMap::get; + } + + public UserAttributeUpdater setAttribute(String name, List value) { + this.rep.getAttributes().put(name, value); + return this; + } + + public UserAttributeUpdater setAttribute(String name, String... values) { + this.rep.getAttributes().put(name, Arrays.asList(values)); + return this; + } + + public UserAttributeUpdater removeAttribute(String name) { + this.rep.getAttributes().put(name, null); + return this; + } + + public UserAttributeUpdater setEmailVerified(Boolean emailVerified) { + rep.setEmailVerified(emailVerified); + return this; + } + + public UserAttributeUpdater setEnabled(Boolean enabled) { + rep.setEnabled(enabled); + return this; + } + + public UserAttributeUpdater setRequiredActions(UserModel.RequiredAction... requiredAction) { + rep.setRequiredActions(Arrays.stream(requiredAction) + .map(action -> action.name()) + .collect(Collectors.toList()) + ); + return this; + } + + public RoleScopeUpdater realmRoleScope() { + return new RoleScopeUpdater(resource.roles().realmLevel()); + } + + public RoleScopeUpdater clientRoleScope(String clientUUID) { + return new RoleScopeUpdater(resource.roles().clientLevel(clientUUID)); + } + + /** + * @param groups List of expected group paths + * @return + */ + public UserAttributeUpdater setGroups(String... groups) { + rep.setGroups(Arrays.asList(groups)); + return this; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java new file mode 100644 index 000000000000..04d2ee1059e8 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java @@ -0,0 +1,180 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.io.File; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; + +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.KeycloakBuilder; +import org.keycloak.models.Constants; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.ssl.SSLContexts; +import org.jboss.resteasy.client.jaxrs.ClientHttpEngine; +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.jboss.resteasy.client.jaxrs.engines.ClientHttpEngineBuilder43; +import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider; + +import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN; +import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; +import static org.keycloak.testsuite.utils.io.IOUtil.PROJECT_BUILD_DIRECTORY; + + +public class AdminClientUtil { + + public static final int NUMBER_OF_CONNECTIONS = 10; + + public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot) throws Exception { + return createAdminClient(ignoreUnknownProperties, authServerContextRoot, MASTER, ADMIN, ADMIN, + Constants.ADMIN_CLI_CLIENT_ID, null, null, false); + + } + + public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String realmName, String username, + String password, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { + return createAdminClient(ignoreUnknownProperties, getAuthServerContextRoot(), realmName, username, password, + clientId, clientSecret, null, false); + } + + public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot, String realmName, + String username, String password, String clientId, String clientSecret, String scope, boolean useDPoP) + throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { + + ResteasyClient resteasyClient = createResteasyClient(ignoreUnknownProperties, null); + + return KeycloakBuilder.builder() + .serverUrl(authServerContextRoot + "/auth") + .realm(realmName) + .username(username) + .password(password) + .clientId(clientId) + .clientSecret(clientSecret) + .resteasyClient(resteasyClient) + .scope(scope) + .useDPoP(useDPoP) + .build(); + } + + public static ResteasyClient createResteasyClient() { + try { + return createResteasyClient(false, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static ResteasyClient createResteasyClient(boolean ignoreUnknownProperties, Boolean followRedirects) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { + ResteasyClientBuilder resteasyClientBuilder = (ResteasyClientBuilder) ResteasyClientBuilder.newBuilder(); + + resteasyClientBuilder.sslContext(getSSLContextWithTruststore()); + + // We need to ignore unknown JSON properties e.g. in the adapter configuration representation + // during adapter backward compatibility testing + if (ignoreUnknownProperties) { + // We need to use anonymous class to avoid the following error from RESTEasy: + // Provider class org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider is already registered. 2nd registration is being ignored. + ResteasyJackson2Provider jacksonProvider = new ResteasyJackson2Provider() {}; + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + jacksonProvider.setMapper(objectMapper); + resteasyClientBuilder.register(jacksonProvider, 100); + } + + resteasyClientBuilder + .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD) + .connectionPoolSize(NUMBER_OF_CONNECTIONS) + .httpEngine(getCustomClientHttpEngine(resteasyClientBuilder, 1, followRedirects)); + + return resteasyClientBuilder.build(); + } + + private static SSLContext getSSLContextWithTruststore(File file, String password) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { + if (!file.isFile()) { + throw new RuntimeException("Truststore file not found: " + file.getAbsolutePath()); + } + SSLContext theContext = SSLContexts.custom() + .setProtocol("TLS") + .loadTrustMaterial(file, password == null ? null : password.toCharArray()) + .build(); + return theContext; + } + + public static SSLContext getSSLContextWithTruststore() { + try { + if ("true".equals(System.getProperty("auth.server.ssl.required"))) { + File truststore = new File(PROJECT_BUILD_DIRECTORY, "dependency/keystore/keycloak.truststore"); + System.setProperty("javax.net.ssl.trustStore", truststore.getAbsolutePath()); + return getSSLContextWithTruststore(truststore, "secret"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + + public static ClientHttpEngine getCustomClientHttpEngine(ResteasyClientBuilder resteasyClientBuilder, int validateAfterInactivity, Boolean followRedirects) { + return new CustomClientHttpEngineBuilder43(validateAfterInactivity, followRedirects).resteasyClientBuilder(resteasyClientBuilder).build(); + } + + /** + * Adds a possibility to pass validateAfterInactivity parameter into underlying ConnectionManager. The parameter affects how + * long the connection is being used without testing if it became stale, default value is 2000ms + */ + private static class CustomClientHttpEngineBuilder43 extends ClientHttpEngineBuilder43 { + + private final int validateAfterInactivity; + private final Boolean followRedirects; + + private CustomClientHttpEngineBuilder43(int validateAfterInactivity, Boolean followRedirects) { + this.validateAfterInactivity = validateAfterInactivity; + this.followRedirects = followRedirects; + } + + @Override + protected ClientHttpEngine createEngine(final HttpClientConnectionManager cm, final RequestConfig.Builder rcBuilder, + final HttpHost defaultProxy, final int responseBufferSize, final HostnameVerifier verifier, final SSLContext theContext) { + final ClientHttpEngine engine; + if (cm instanceof PoolingHttpClientConnectionManager) { + PoolingHttpClientConnectionManager pcm = (PoolingHttpClientConnectionManager) cm; + pcm.setValidateAfterInactivity(validateAfterInactivity); + engine = super.createEngine(pcm, rcBuilder, defaultProxy, responseBufferSize, verifier, theContext); + } else { + engine = super.createEngine(cm, rcBuilder, defaultProxy, responseBufferSize, verifier, theContext); + } + if (followRedirects != null) { + engine.setFollowRedirects(followRedirects); + } + return engine; + } + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/BrowserDriverUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/util/BrowserDriverUtil.java new file mode 100644 index 000000000000..25891e4662d2 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/BrowserDriverUtil.java @@ -0,0 +1,20 @@ +package org.keycloak.testsuite.util; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.firefox.FirefoxDriver; + +public class BrowserDriverUtil { + + public static boolean isDriverInstanceOf(WebDriver driver, Class clazz) { + return clazz.isAssignableFrom(driver.getClass()); + } + + public static boolean isDriverChrome(WebDriver driver) { + return isDriverInstanceOf(driver, ChromeDriver.class); + } + + public static boolean isDriverFirefox(WebDriver driver) { + return isDriverInstanceOf(driver, FirefoxDriver.class); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/BrowserTabUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/util/BrowserTabUtil.java new file mode 100644 index 000000000000..a45f6d03e4ee --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/BrowserTabUtil.java @@ -0,0 +1,151 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; + +/** + * Helper class for managing tabs in browser. + * Tabs are indexed from 0. (f.e. first tab has index 0) + * + *

Note: For one particular WebDriver has to exist only one BrowserTabUtil instance. (Right order of tabs)

+ * + * @author Martin Bartos + */ +public class BrowserTabUtil implements AutoCloseable { + + private WebDriver driver; + private JavascriptExecutor jsExecutor; + private List tabs; + private static List instances; + + private BrowserTabUtil(WebDriver driver) { + this.driver = driver; + + if (driver instanceof JavascriptExecutor) { + this.jsExecutor = (JavascriptExecutor) driver; + } else { + throw new RuntimeException("WebDriver must be instance of JavascriptExecutor"); + } + + tabs = new ArrayList<>(driver.getWindowHandles()); + } + + public static BrowserTabUtil getInstanceAndSetEnv(WebDriver driver) { + if (instances == null) { + instances = new ArrayList<>(); + } + + BrowserTabUtil instance = instances.stream() + .filter(inst -> inst.getDriver().toString().equals(driver.toString())) + .findFirst() + .orElse(null); + + if (instance == null) { + instance = new BrowserTabUtil(driver); + instances.add(instance); + } + return instance; + } + + public static void cleanup() { + instances = new ArrayList<>(); + } + + public WebDriver getDriver() { + return driver; + } + + public String getActualWindowHandle() { + return driver.getWindowHandle(); + } + + public void switchToTab(String windowHandle) { + driver.switchTo().window(windowHandle); + WaitUtils.waitForPageToLoad(); + } + + public void switchToTab(int index) { + assertValidIndex(index); + switchToTab(tabs.get(index)); + } + + public void newTab(String url) { + jsExecutor.executeScript("window.open(arguments[0]);", url); + + final Set handles = driver.getWindowHandles(); + final String tabHandle = handles.stream() + .filter(tab -> !tabs.contains(tab)) + .findFirst() + .orElse(null); + + if (handles.size() > tabs.size() + 1) { + throw new RuntimeException("Too many window handles. You can only create a new one by this method."); + } + + if (tabHandle == null) { + throw new RuntimeException("Creating the new tab failed."); + } + + tabs.add(tabHandle); + switchToTab(tabHandle); + } + + public void closeTab(int index) { + assertValidIndex(index); + + if (index == 0 || getCountOfTabs() == 1) + throw new RuntimeException("You must not close the original tab."); + + switchToTab(index); + driver.close(); + + tabs.remove(index); + switchToTab(index - 1); + } + + public int getCountOfTabs() { + return tabs.size(); + } + + public void destroy() { + for (int i = 1; i < getCountOfTabs(); i++) { + closeTab(i); + } + instances.removeIf(inst -> inst.getDriver().toString().equals(driver.toString())); + } + + private boolean validIndex(int index) { + return (index >= 0 && tabs != null && index < tabs.size()); + } + + private void assertValidIndex(int index) { + if (!validIndex(index)) + throw new IndexOutOfBoundsException("Invalid index of tab."); + } + + @Override + public void close() { + destroy(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java new file mode 100644 index 000000000000..2fb62fe9453b --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.util.LinkedList; +import java.util.Queue; + +import org.openqa.selenium.WebDriver; + +/** + * @author Vaclav Muzikar + */ +public final class DroneUtils { + private static final Queue driverQueue = new LinkedList<>(); + + public static WebDriver getCurrentDriver() { + return driverQueue.peek(); + } + + public static void addWebDriver(WebDriver driver) { + driverQueue.add(driver); + } + + public static void removeWebDriver() { + driverQueue.poll(); + } + + public static void resetQueue() { + driverQueue.clear(); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/HttpClientUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/HttpClientUtils.java new file mode 100644 index 000000000000..32e51c00d04e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/HttpClientUtils.java @@ -0,0 +1,46 @@ +package org.keycloak.testsuite.util; + +import org.apache.http.client.RedirectStrategy; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; + +public class HttpClientUtils { + + private static final boolean SSL_REQUIRED = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required")); + + // Add configurable socket timeout (default 30 seconds for better compatibility with slower environments like Windows) + private static final int SOCKET_TIMEOUT_MILLIS = Integer.parseInt( + System.getProperty("http.socket.timeout", "30000") + ); + + private static final int CONNECTION_TIMEOUT_MILLIS = Integer.parseInt( + System.getProperty("http.connection.timeout", "10000") + ); + + public static CloseableHttpClient createDefault() { + return createDefault(DefaultRedirectStrategy.INSTANCE); + } + + public static CloseableHttpClient createDefault(RedirectStrategy redirectStrategy) { + RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(SOCKET_TIMEOUT_MILLIS) + .setConnectTimeout(CONNECTION_TIMEOUT_MILLIS) + .build(); + + if (SSL_REQUIRED) { + String keyStorePath = System.getProperty("client.certificate.keystore"); + String keyStorePassword = System.getProperty("client.certificate.keystore.passphrase"); + String trustStorePath = System.getProperty("client.truststore"); + String trustStorePassword = System.getProperty("client.truststore.passphrase"); + return MutualTLSUtils.newCloseableHttpClient(keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, redirectStrategy); + } + + return HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig) + .setRedirectStrategy(redirectStrategy) + .build(); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/InfinispanTestTimeServiceRule.java b/tests/base/src/test/java/org/keycloak/testsuite/util/InfinispanTestTimeServiceRule.java new file mode 100644 index 000000000000..2e406cfdfe3c --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/InfinispanTestTimeServiceRule.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.keycloak.testsuite.util; + +import org.jboss.logging.Logger; +import org.junit.rules.ExternalResource; + +/** + * @author Marek Posolda + */ +public class InfinispanTestTimeServiceRule extends ExternalResource { + + private static final Logger log = Logger.getLogger(InfinispanTestTimeServiceRule.class); + + public InfinispanTestTimeServiceRule(Object ignored) { + } + + @Override + protected void before() throws Throwable { + // no-op compatibility rule for migrated tests + } + + @Override + protected void after() { + // no-op compatibility rule for migrated tests + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java new file mode 100644 index 000000000000..1dae5808cb2d --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java @@ -0,0 +1,170 @@ +package org.keycloak.testsuite.util; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.function.Predicate; +import java.util.stream.Stream; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +import jakarta.ws.rs.core.Response; + +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.crypto.CryptoIntegration; +import org.keycloak.common.util.BouncyIntegration; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.crypto.JavaAlgorithm; +import org.keycloak.crypto.KeyStatus; +import org.keycloak.crypto.KeyType; +import org.keycloak.crypto.KeyUse; +import org.keycloak.keys.AbstractEcKeyProviderFactory; +import org.keycloak.keys.GeneratedEcdhKeyProviderFactory; +import org.keycloak.keys.GeneratedEcdsaKeyProviderFactory; +import org.keycloak.keys.KeyProvider; +import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.representations.idm.KeysMetadataRepresentation; +import org.keycloak.testsuite.admin.ApiUtil; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author mhajas + */ +public class KeyUtils { + public static KeyPair generateECKey(String algorithm) { + + try { + KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen("ECDSA"); + String domainParamNistRep = GeneratedEcdsaKeyProviderFactory + .convertJWSAlgorithmToECDomainParmNistRep(algorithm); + if (domainParamNistRep == null) { + domainParamNistRep = GeneratedEcdhKeyProviderFactory + .convertJWEAlgorithmToECDomainParmNistRep(algorithm); + } + String curve = AbstractEcKeyProviderFactory.convertECDomainParmNistRepToSecRep(domainParamNistRep); + ECGenParameterSpec parameterSpec = new ECGenParameterSpec(curve); + kpg.initialize(parameterSpec); + return kpg.generateKeyPair(); + } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + } + + public static KeyPair generateEdDSAKey(String curve) throws NoSuchAlgorithmException, NoSuchProviderException { + KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(curve); + return kpg.generateKeyPair(); + } + + public static SecretKey generateSecretKey(String algorithm, int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator keyGen = KeyGenerator.getInstance(JavaAlgorithm.getJavaAlgorithm(algorithm), BouncyIntegration.PROVIDER); + keyGen.init(keySize); + return keyGen.generateKey(); + } + + public static PublicKey publicKeyFromString(String key) { + try { + KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(KeyType.RSA); + byte[] encoded = Base64.getDecoder().decode(key); + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) { + throw new RuntimeException(e); + } + } + + public static PrivateKey privateKeyFromString(String key) { + try { + KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(KeyType.RSA); + byte[] encoded = Base64.getDecoder().decode(key); + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) { + throw new RuntimeException(e); + } + } + + public static KeysMetadataRepresentation.KeyMetadataRepresentation getActiveEncryptionKey(KeysMetadataRepresentation keys, String algorithm) { + for (KeysMetadataRepresentation.KeyMetadataRepresentation k : keys.getKeys()) { + if (k.getAlgorithm().equals(algorithm) && KeyStatus.valueOf(k.getStatus()).isActive() && KeyUse.ENC.equals(k.getUse())) { + return k; + } + } + throw new RuntimeException("Active key not found"); + } + + public static KeysMetadataRepresentation.KeyMetadataRepresentation findActiveSigningKey(RealmResource realm) { + return findRealmKeys(realm, rep -> rep.getPublicKey() != null && KeyStatus.valueOf(rep.getStatus()).isActive() && KeyUse.SIG.equals(rep.getUse())) + .findFirst() + .orElse(null); + } + + public static KeysMetadataRepresentation.KeyMetadataRepresentation findActiveSigningKey(RealmResource realm, String alg) { + return findRealmKeys(realm, rep -> rep.getPublicKey() != null && KeyStatus.valueOf(rep.getStatus()).isActive() && KeyUse.SIG.equals(rep.getUse()) && alg.equals(rep.getAlgorithm())) + .findFirst() + .orElse(null); + } + + public static KeysMetadataRepresentation.KeyMetadataRepresentation findActiveEncryptingKey(RealmResource realm, String alg) { + return findRealmKeys(realm, rep -> rep.getPublicKey() != null && KeyStatus.valueOf(rep.getStatus()).isActive() && KeyUse.ENC.equals(rep.getUse()) && alg.equals(rep.getAlgorithm())) + .findFirst() + .orElse(null); + } + + public static Stream findRealmKeys(RealmResource realm, Predicate filter) { + return realm.keys().getKeyMetadata().getKeys().stream().filter(filter); + } + + public static AutoCloseable generateNewRealmKey(RealmResource realm, KeyUse keyUse, String algorithm, String priority) { + String realmId = realm.toRepresentation().getId(); + + ComponentRepresentation keys = new ComponentRepresentation(); + keys.setName("generated"); + keys.setProviderType(KeyProvider.class.getName()); + keys.setProviderId(keyUse == KeyUse.ENC ? "rsa-enc-generated" : "rsa-generated"); + keys.setParentId(realmId); + keys.setConfig(new MultivaluedHashMap<>()); + keys.getConfig().putSingle("priority", priority); + keys.getConfig().putSingle("keyUse", KeyUse.ENC.getSpecName()); + keys.getConfig().putSingle("algorithm", algorithm); + Response response = realm.components().add(keys); + assertEquals(201, response.getStatus()); + String id = ApiUtil.getCreatedId(response); + response.close(); + + return () -> realm.components().removeComponent(id); + } + + public static AutoCloseable generateNewRealmKey(RealmResource realm, KeyUse keyUse, String algorithm) { + return generateNewRealmKey(realm, keyUse, algorithm, "100"); + } + + /** + * @return key sizes, which are expected to be supported by Keycloak server for {@link org.keycloak.keys.GeneratedRsaKeyProviderFactory} and {@link org.keycloak.keys.GeneratedRsaEncKeyProviderFactory}. + */ + public static String[] getExpectedSupportedRsaKeySizes() { + String expectedKeySizes = System.getProperty("auth.server.supported.rsa.key.sizes"); + if (expectedKeySizes == null || expectedKeySizes.trim().isEmpty()) { + fail("System property 'auth.server.supported.rsa.key.sizes' should be set"); + } + return expectedKeySizes.split(","); + } + + /** + * @return Lowest key size supported by Keycloak server for {@link org.keycloak.keys.GeneratedRsaKeyProviderFactory}. + * It is usually 1024, but can be 2048 in some environments (typically in FIPS environments) + */ + public static int getLowestSupportedRsaKeySize() { + return Integer.parseInt(getExpectedSupportedRsaKeySizes()[0]); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/LDAPRule.java b/tests/base/src/test/java/org/keycloak/testsuite/util/LDAPRule.java new file mode 100644 index 000000000000..959f4da1f45e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/LDAPRule.java @@ -0,0 +1,359 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.io.File; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Map; +import java.util.Properties; + +import org.keycloak.models.LDAPConstants; +import org.keycloak.util.ldap.LDAPEmbeddedServer; + +import org.jboss.logging.Logger; +import org.junit.Assume; +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import static org.keycloak.testsuite.utils.io.IOUtil.PROJECT_BUILD_DIRECTORY; + +/** + * @author Marek Posolda + */ +public class LDAPRule extends ExternalResource { + + private static final Logger log = Logger.getLogger(LDAPRule.class); + + // Note: Be sure to annotate the testing class with the "EnableVault" annotation + // to get the necessary FilePlaintext vault created automatically for the test + private static final String VAULT_EXPRESSION = "${vault.ldap_bindCredential}"; + + public static final String LDAP_CONNECTION_PROPERTIES_LOCATION = "classpath:ldap/ldap-connection.properties"; + + private static final String PROPERTY_ENABLE_ACCESS_CONTROL = "enableAccessControl"; + + private static final String PROPERTY_ENABLE_ANONYMOUS_ACCESS = "enableAnonymousAccess"; + + private static final String PROPERTY_ENABLE_SSL = "enableSSL"; + + private static final String PROPERTY_ENABLE_STARTTLS = "enableStartTLS"; + + private static final String PROPERTY_KEYSTORE_FILE = "keystoreFile"; + + private static final String PRIVATE_KEY = "dependency/keystore/keycloak.jks"; + + private static final String PROPERTY_CERTIFICATE_PASSWORD = "certificatePassword"; + + LDAPTestConfiguration ldapTestConfiguration; + private LDAPEmbeddedServer ldapEmbeddedServer; + private LDAPAssume assume; + + protected Properties defaultProperties = new Properties(); + + public LDAPRule assumeTrue(LDAPAssume assume) { + this.assume = assume; + return this; + } + + @Override + public void before() throws Throwable { + String connectionPropsLocation = getConnectionPropertiesLocation(); + ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation); + + Assume.assumeTrue("Assumption in LDAPRule is false. Skiping the test", assume==null || assume.assumeTrue(ldapTestConfiguration)); + + if (ldapTestConfiguration.isStartEmbeddedLdapServer()) { + ldapEmbeddedServer = createServer(); + ldapEmbeddedServer.init(); + ldapEmbeddedServer.start(); + } + } + + @Override + public Statement apply(Statement base, Description description) { + // Default bind credential value + defaultProperties.setProperty(LDAPConstants.BIND_CREDENTIAL, "secret"); + defaultProperties.setProperty(LDAPConstants.CONNECTION_POOLING, "true"); + // Default values of the authentication / access control method and connection encryption to use on the embedded + // LDAP server upon start if not (re)set later via the LDAPConnectionParameters annotation directly on the test + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ACCESS_CONTROL, "true"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ANONYMOUS_ACCESS, "false"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL, "true"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_STARTTLS, "false"); + // Default LDAP server confidentiality required value + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_SET_CONFIDENTIALITY_REQUIRED, "false"); + + // Don't auto-update LDAP connection URL read from properties file for LDAP over SSL case even if it's wrong + // (AKA don't try to guess, let the user to get it corrected in the properties file first) + defaultProperties.setProperty("AUTO_UPDATE_LDAP_CONNECTION_URL", "false"); + + Annotation ldapConnectionAnnotation = description.getAnnotation(LDAPConnectionParameters.class); + if (ldapConnectionAnnotation != null) { + // Mark the LDAP connection URL as auto-adjustable to correspond to specific annotation as necessary + defaultProperties.setProperty("AUTO_UPDATE_LDAP_CONNECTION_URL", "true"); + LDAPConnectionParameters connectionParameters = (LDAPConnectionParameters) ldapConnectionAnnotation; + // Configure the bind credential type of the LDAP rule depending on the provided annotation arguments + switch (connectionParameters.bindCredential()) { + case SECRET: + log.debug("Setting bind credential to secret."); + defaultProperties.setProperty(LDAPConstants.BIND_CREDENTIAL, "secret"); + break; + case VAULT: + log.debug("Setting bind credential to vault."); + defaultProperties.setProperty(LDAPConstants.BIND_CREDENTIAL, VAULT_EXPRESSION); + break; + } + // Configure the authentication method of the LDAP rule depending on the provided annotation arguments + switch (connectionParameters.bindType()) { + case NONE: + log.debug("Enabling anonymous authentication method on the LDAP server."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ANONYMOUS_ACCESS, "true"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ACCESS_CONTROL, "false"); + break; + case SIMPLE: + log.debug("Disabling anonymous authentication method on the LDAP server."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ANONYMOUS_ACCESS, "false"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ACCESS_CONTROL, "true"); + break; + } + // Configure the connection encryption of the LDAP rule depending on the provided annotation arguments + switch (connectionParameters.encryption()) { + case NONE: + log.debug("Disabling connection encryption on the LDAP server."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL, "false"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_STARTTLS, "false"); + break; + case SSL: + log.debug("Enabling SSL connection encryption on the LDAP server."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL, "true"); + // Require the LDAP server to accept only secured connections with SSL enabled + log.debug("Configuring the LDAP server to accepts only requests with a secured connection."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_SET_CONFIDENTIALITY_REQUIRED, "true"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_STARTTLS, "false"); + break; + case STARTTLS: + log.debug("Enabling StartTLS connection encryption on the LDAP server."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_STARTTLS, "true"); + // Require the LDAP server to accept only secured connections with StartTLS enabled + log.debug("Configuring the LDAP server to accepts only requests with a secured connection."); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_SET_CONFIDENTIALITY_REQUIRED, "true"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL, "false"); + break; + } + } + + Annotation passwordPolicyAnnotations = description.getAnnotation(LDAPPasswordPolicy.class); + if (passwordPolicyAnnotations != null) { + LDAPPasswordPolicy passwordPolicy = (LDAPPasswordPolicy) passwordPolicyAnnotations; + + log.debugf("Enabling LDAP password policy: mustChange=%s.", passwordPolicy.mustChange()); + + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_PPOLICY_ENABLED, "true"); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_PPOLICY_MUST_CHANGE, String.valueOf(passwordPolicy.mustChange())); + + if (passwordPolicy.mustChange()) { + // Workaround for password policy behavior: + // + // Issue: When embedded LDAP server (ApacheDS) receives a password change request + // from an admin account, it sets pwdReset=TRUE. Since Keycloak uses an admin account + // for any password change, this creates an infinite loop where users are repeatedly + // forced to change their password after each login. + // + // Workaround: For mustChange password policy tests, disable access control on the + // embedded LDAP server. This allows Keycloak to authenticate as a regular user + // instead of admin, preventing the pwdReset flag from being automatically set on + // password changes. The bind DN is overridden in getConfig(). + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ACCESS_CONTROL, "false"); + } + } + + return super.apply(base, description); + } + + @Override + public void after() { + try { + if (ldapEmbeddedServer != null) { + ldapEmbeddedServer.stop(); + ldapEmbeddedServer = null; + ldapTestConfiguration = null; + } + } catch (Exception e) { + throw new RuntimeException("Error tearDown Embedded LDAP server.", e); + } + } + + protected String getConnectionPropertiesLocation() { + return LDAP_CONNECTION_PROPERTIES_LOCATION; + } + + protected LDAPEmbeddedServer createServer() { + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:ldap/users.ldif"); + defaultProperties.setProperty(PROPERTY_CERTIFICATE_PASSWORD, "secret"); + defaultProperties.setProperty(PROPERTY_KEYSTORE_FILE, new File(PROJECT_BUILD_DIRECTORY, PRIVATE_KEY).getAbsolutePath()); + + return new LDAPEmbeddedServer(defaultProperties); + } + + public Map getConfig() { + Map config = ldapTestConfiguration.getLDAPConfig(); + String ldapConnectionUrl = config.get(LDAPConstants.CONNECTION_URL); + if (ldapConnectionUrl != null && defaultProperties.getProperty("AUTO_UPDATE_LDAP_CONNECTION_URL").equals("true")) { + if ( + ldapConnectionUrl.startsWith("ldap://") && + defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL).equals("true") + ) + { + // Switch protocol prefix to "ldaps://" in connection URL if LDAP over SSL is requested + String updatedUrl = ldapConnectionUrl.replaceAll("ldap://", "ldaps://"); + // Flip port number from LDAP to LDAPS + updatedUrl = updatedUrl.replaceAll( + String.valueOf(ldapEmbeddedServer.getBindPort()), + String.valueOf(ldapEmbeddedServer.getBindLdapsPort()) + ); + config.put(LDAPConstants.CONNECTION_URL, updatedUrl); + log.debugf("Using LDAP over SSL \"%s\" connection URL form over: \"%s\" since SSL connection was requested.", updatedUrl, ldapConnectionUrl); + } + if ( + ldapConnectionUrl.startsWith("ldaps://") && + !defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL).equals("true") + ) + { + // Switch protocol prefix back to "ldap://" in connection URL if LDAP over SSL flag is not set + String updatedUrl = ldapConnectionUrl.replaceAll("ldaps://", "ldap://"); + // Flip port number from LDAPS to LDAP + updatedUrl = updatedUrl.replaceAll( + String.valueOf(ldapEmbeddedServer.getBindLdapsPort()), + String.valueOf(ldapEmbeddedServer.getBindPort()) + ); + config.put(LDAPConstants.CONNECTION_URL, updatedUrl); + log.debugf("Using plaintext / startTLS \"%s\" connection URL form over: \"%s\" since plaintext / startTLS connection was requested.", updatedUrl, ldapConnectionUrl); + } + } + switch (defaultProperties.getProperty(LDAPConstants.BIND_CREDENTIAL)) { + case VAULT_EXPRESSION: + config.put(LDAPConstants.BIND_CREDENTIAL, VAULT_EXPRESSION); + break; + } + switch (defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_ANONYMOUS_ACCESS)) { + case "true": + config.put(LDAPConstants.AUTH_TYPE, LDAPConstants.AUTH_TYPE_NONE); + config.remove(LDAPConstants.BIND_DN); + config.remove(LDAPConstants.BIND_CREDENTIAL); + break; + default: + // Default to username + password LDAP authentication method + config.put(LDAPConstants.AUTH_TYPE, LDAPConstants.AUTH_TYPE_SIMPLE); + } + switch (defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_STARTTLS)) { + case "true": + config.put(LDAPConstants.START_TLS, "true"); + // Use truststore from TruststoreSPI also for StartTLS connections + config.put(LDAPConstants.USE_TRUSTSTORE_SPI, LDAPConstants.USE_TRUSTSTORE_ALWAYS); + config.put(LDAPConstants.CONNECTION_POOLING, "false"); + break; + default: + // Default to startTLS disabled + config.put(LDAPConstants.START_TLS, "false"); + // By default use truststore from TruststoreSPI only for LDAP over SSL connections + config.put(LDAPConstants.USE_TRUSTSTORE_SPI, LDAPConstants.USE_TRUSTSTORE_ALWAYS); + } + switch (defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_SET_CONFIDENTIALITY_REQUIRED)) { + case "true": + System.setProperty("PROPERTY_SET_CONFIDENTIALITY_REQUIRED", "true"); + break; + default: + // Configure the LDAP server to accept not secured connections from clients by default + System.setProperty("PROPERTY_SET_CONFIDENTIALITY_REQUIRED", "false"); + } + switch (defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_PPOLICY_ENABLED, "false")) { + case "true": + config.put(LDAPConstants.ENABLE_LDAP_PASSWORD_POLICY, "true"); + break; + default: + config.put(LDAPConstants.ENABLE_LDAP_PASSWORD_POLICY, "false"); + } + if (defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_PPOLICY_MUST_CHANGE) != null) { + // See Workaround for password policy behavior comment in apply() for why a non-admin bind DN is needed. + config.put(LDAPConstants.BIND_DN, "uid=keycloak-admin," + config.get(LDAPConstants.BASE_DN)); + config.put(LDAPConstants.BIND_CREDENTIAL, "secret"); + } + return config; + } + + public int getSleepTime() { + return ldapTestConfiguration.getSleepTime(); + } + + public LDAPEmbeddedServer getLdapEmbeddedServer() { + return ldapEmbeddedServer; + } + + /** Allows to run particular LDAP test just under specific conditions (eg. some test running just on Active Directory) **/ + public interface LDAPAssume { + + boolean assumeTrue(LDAPTestConfiguration ldapConfig); + + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface LDAPConnectionParameters { + // Default to secret as the bind credential unless annotated otherwise + BindCredential bindCredential() default LDAPConnectionParameters.BindCredential.SECRET; + // Disable anonymous LDAP authentication by default unless annotated otherwise + BindType bindType() default LDAPConnectionParameters.BindType.SIMPLE; + // Enable SSL encrypted LDAP connections (along with the unencrypted ones) by default unless annotated otherwise + Encryption encryption() default LDAPConnectionParameters.Encryption.SSL; + + public enum BindCredential { + SECRET, + VAULT + } + + public enum BindType { + NONE, + SIMPLE + } + + public enum Encryption { + NONE, + // Important: Choosing either of "SSL" or "STARTTLS" connection encryption methods below + // will also configure the LDAP server to accept only a secured connection from clients + // (IOW plaintext client connections will be prohibited). Use those two options with care! + SSL, + STARTTLS + } + } + + public boolean isEmbeddedServer() { + return ldapTestConfiguration.isStartEmbeddedLdapServer(); + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface LDAPPasswordPolicy { + public boolean mustChange() default false; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java b/tests/base/src/test/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java new file mode 100644 index 000000000000..575b5bef5a88 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java @@ -0,0 +1,175 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.keycloak.common.constants.GenericConstants; +import org.keycloak.common.constants.KerberosConstants; +import org.keycloak.models.LDAPConstants; +import org.keycloak.storage.UserStorageProvider; + +import org.jboss.logging.Logger; + +/** + * @author Marek Posolda + */ +public class LDAPTestConfiguration { + + private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class); + + private int sleepTime; + private boolean startEmbeddedLdapServer = true; + private boolean caseSensitiveLogin = true; + private Map config; + + protected static final Map PROP_MAPPINGS = new HashMap(); + protected static final Map DEFAULT_VALUES = new HashMap(); + + static { + PROP_MAPPINGS.put(LDAPConstants.CONNECTION_URL, "idm.test.ldap.connection.url"); + PROP_MAPPINGS.put(LDAPConstants.BASE_DN, "idm.test.ldap.base.dn"); + PROP_MAPPINGS.put(LDAPConstants.USERS_DN, "idm.test.ldap.user.dn.suffix"); + PROP_MAPPINGS.put(LDAPConstants.BIND_DN, "idm.test.ldap.bind.dn"); + PROP_MAPPINGS.put(LDAPConstants.BIND_CREDENTIAL, "idm.test.ldap.bind.credential"); + PROP_MAPPINGS.put(LDAPConstants.VENDOR, "idm.test.ldap.vendor"); + PROP_MAPPINGS.put(LDAPConstants.CONNECTION_POOLING, "idm.test.ldap.connection.pooling"); + PROP_MAPPINGS.put(LDAPConstants.PAGINATION, "idm.test.ldap.pagination"); + PROP_MAPPINGS.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, "idm.test.ldap.batch.size.for.sync"); + PROP_MAPPINGS.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, "idm.test.ldap.username.ldap.attribute"); + PROP_MAPPINGS.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, "idm.test.ldap.rdn.ldap.attribute"); + PROP_MAPPINGS.put(LDAPConstants.USER_OBJECT_CLASSES, "idm.test.ldap.user.object.classes"); + PROP_MAPPINGS.put(LDAPConstants.EDIT_MODE, "idm.test.ldap.edit.mode"); + + PROP_MAPPINGS.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "idm.test.kerberos.allow.kerberos.authentication"); + PROP_MAPPINGS.put(KerberosConstants.KERBEROS_REALM, "idm.test.kerberos.realm"); + PROP_MAPPINGS.put(KerberosConstants.SERVER_PRINCIPAL, "idm.test.kerberos.server.principal"); + PROP_MAPPINGS.put(KerberosConstants.KEYTAB, "idm.test.kerberos.keytab"); + PROP_MAPPINGS.put(KerberosConstants.KERBEROS_PRINCIPAL_ATTRIBUTE, "idm.test.kerberos.principal.ldap.attribute"); + PROP_MAPPINGS.put(KerberosConstants.DEBUG, "idm.test.kerberos.debug"); + PROP_MAPPINGS.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "idm.test.kerberos.allow.password.authentication"); + PROP_MAPPINGS.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "idm.test.kerberos.update.profile.first.login"); + PROP_MAPPINGS.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "idm.test.kerberos.use.kerberos.for.password.authentication"); + PROP_MAPPINGS.put(LDAPConstants.USE_TRUSTSTORE_SPI, "idm.test.ldap.truststore.spi"); + + DEFAULT_VALUES.put(LDAPConstants.CONNECTION_URL, "ldap://localhost:10389"); + DEFAULT_VALUES.put(LDAPConstants.BASE_DN, "dc=keycloak,dc=org"); + DEFAULT_VALUES.put(LDAPConstants.USERS_DN, "ou=People,dc=keycloak,dc=org"); + DEFAULT_VALUES.put(LDAPConstants.BIND_DN, "uid=admin,ou=system"); + DEFAULT_VALUES.put(LDAPConstants.BIND_CREDENTIAL, "secret"); + DEFAULT_VALUES.put(LDAPConstants.VENDOR, LDAPConstants.VENDOR_OTHER); + DEFAULT_VALUES.put(LDAPConstants.CONNECTION_POOLING, "true"); + DEFAULT_VALUES.put(LDAPConstants.PAGINATION, "true"); + DEFAULT_VALUES.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, String.valueOf(LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC)); + DEFAULT_VALUES.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, null); + DEFAULT_VALUES.put(LDAPConstants.USER_OBJECT_CLASSES, null); + DEFAULT_VALUES.put(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.READ_ONLY.toString()); + DEFAULT_VALUES.put(LDAPConstants.USE_TRUSTSTORE_SPI, LDAPConstants.USE_TRUSTSTORE_ALWAYS); + + DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false"); + DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG"); + DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG"); + String keyTabPath = getResource("/kerberos/http.keytab"); + DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath); + DEFAULT_VALUES.put(KerberosConstants.KERBEROS_PRINCIPAL_ATTRIBUTE, KerberosConstants.KERBEROS_PRINCIPAL_LDAP_ATTRIBUTE_KRB5_PRINCIPAL_NAME); + DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true"); + DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true"); + DEFAULT_VALUES.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true"); + DEFAULT_VALUES.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "false"); + } + + public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) { + LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration(); + ldapTestConfiguration.loadConnectionProperties(connectionPropertiesLocation); + return ldapTestConfiguration; + } + + public static String getResource(String resourcePath) { + URL urlPath = LDAPTestConfiguration.class.getResource(resourcePath); + String absolutePath = new File(urlPath.getFile()).getAbsolutePath(); + return absolutePath; + } + + protected void loadConnectionProperties(String connectionPropertiesLocation) { + // TODO: Improve and possibly use FindFile + InputStream is; + try { + if (connectionPropertiesLocation.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) { + String classPathLocation = connectionPropertiesLocation.replace(GenericConstants.PROTOCOL_CLASSPATH, ""); + log.info("Reading LDAP configuration from classpath from: " + classPathLocation); + is = LDAPTestConfiguration.class.getClassLoader().getResourceAsStream(classPathLocation); + } else { + String file = getResource(connectionPropertiesLocation); + log.info("Reading LDAP configuration from: " + connectionPropertiesLocation); + is = new FileInputStream(file); + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + + Properties p = new Properties(); + try { + p.load(is); + } catch (Exception e) { + throw new RuntimeException(e); + } + + config = new HashMap(); + for (Map.Entry property : PROP_MAPPINGS.entrySet()) { + String propertyName = property.getKey(); + String configName = property.getValue(); + + String value = p.getProperty(configName); + if (value == null) { + value = DEFAULT_VALUES.get(propertyName); + } + + config.put(propertyName, value); + } + + startEmbeddedLdapServer = Boolean.parseBoolean(p.getProperty("idm.test.ldap.start.embedded.ldap.server", "true")); + sleepTime = Integer.parseInt(p.getProperty("idm.test.ldap.sleepTime", "1000")); + caseSensitiveLogin = Boolean.parseBoolean(p.getProperty("idm.test.kerberos.caseSensitiveLogin", "true")); + log.info("Start embedded server: " + startEmbeddedLdapServer); + log.info("Read config: " + config); + } + + public Map getLDAPConfig() { + return config; + } + + public boolean isStartEmbeddedLdapServer() { + return startEmbeddedLdapServer; + } + + public int getSleepTime() { + return sleepTime; + } + + public boolean isCaseSensitiveLogin() { + return caseSensitiveLogin; + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java b/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java new file mode 100644 index 000000000000..8f0179d05690 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.io.IOException; + +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMessage.RecipientType; +import jakarta.mail.internet.MimeMultipart; + +import org.jboss.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MailAssert { + + private static final Logger log = Logger.getLogger(MailAssert.class); + + public static String assertEmailAndGetUrl(MimeMessage message, String from, String recipient, String content) { + try { + assertNotNull(message, "There is no received email."); + assertEquals(recipient, message.getRecipients(RecipientType.TO)[0].toString()); + assertEquals(from, message.getFrom()[0].toString()); + + String messageContent; + if (message.getContent() instanceof MimeMultipart) { + MimeMultipart mimeMultipart = (MimeMultipart) message.getContent(); + + // TEXT content is on index 0 + messageContent = String.valueOf(mimeMultipart.getBodyPart(0).getContent()); + } else { + messageContent = String.valueOf(message.getContent()); + } + logMessageContent(messageContent); + String errorMessage = "Email content should contains \"" + content + + "\", but it doesn't.\nEmail content:\n" + messageContent + "\n"; + + assertTrue(messageContent.contains(content), errorMessage); + for (String string : messageContent.split("\n")) { + if (string.startsWith("http")) { + // Ampersand escaped in the text version. Needs to be replaced to have correct URL + string = string.replace("&", "&"); + return string; + } + } + return null; + } catch (IOException | MessagingException ex) { + throw new RuntimeException(ex); + } + } + + private static void logMessageContent(String messageContent) { + log.debug("---------------------"); + log.debug(messageContent); + log.debug("---------------------"); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java b/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java new file mode 100644 index 000000000000..c606c49192b9 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java @@ -0,0 +1,176 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import java.net.URI; +import java.util.Arrays; +import java.util.Map; + +import jakarta.ws.rs.core.Response; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.testsuite.util.matchers.HttpResponseBodyMatcher; +import org.keycloak.testsuite.util.matchers.HttpResponseStatusCodeMatcher; +import org.keycloak.testsuite.util.matchers.ResponseBodyMatcher; +import org.keycloak.testsuite.util.matchers.ResponseHeaderMatcher; +import org.keycloak.testsuite.util.matchers.ResponseStatusCodeMatcher; +import org.keycloak.testsuite.util.matchers.SamlLogoutRequestTypeMatcher; +import org.keycloak.testsuite.util.matchers.SamlResponseTypeMatcher; +import org.keycloak.testsuite.util.matchers.SamlStatusResponseTypeMatcher; + +import org.apache.http.HttpResponse; +import org.hamcrest.Matcher; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +/** + * Additional hamcrest matchers for use in {@link org.junit.Assert#assertThat}. + * @author hmlnarik + */ +public class Matchers { + + /** + * Matcher on HTTP status code of a {@link Response} instance. + * @param matcher + * @return + */ + public static Matcher body(Matcher matcher) { + return new ResponseBodyMatcher(matcher); + } + + /** + * Matcher on HTTP body of a {@link Response} instance. + * @param matcher + * @return + */ + public static Matcher bodyHC(Matcher matcher) { + return new HttpResponseBodyMatcher(matcher); + } + + /** + * Matcher on HTTP status code of a {@link Response} instance. + * @param matcher + * @return + */ + public static Matcher statusCode(Matcher matcher) { + return new ResponseStatusCodeMatcher(matcher); + } + + /** + * Matcher on HTTP status code of a {@link Response} instance (HttpClient variant). + * @param matcher + * @return + */ + public static Matcher statusCodeHC(Matcher matcher) { + return new HttpResponseStatusCodeMatcher(matcher); + } + + /** + * Matches when the HTTP status code of a {@link Response} instance is equal to the given code. + * @param expectedStatusCode + * @return + */ + public static Matcher statusCodeIs(Response.Status expectedStatusCode) { + return new ResponseStatusCodeMatcher(org.hamcrest.Matchers.is(expectedStatusCode.getStatusCode())); + } + + /** + * Matches when the HTTP status code of a {@link Response} instance is equal to the given code (HttpClient variant). + * @param expectedStatusCode + * @return + */ + public static Matcher statusCodeIsHC(Response.Status expectedStatusCode) { + return new HttpResponseStatusCodeMatcher(org.hamcrest.Matchers.is(expectedStatusCode.getStatusCode())); + } + + /** + * Matches when the HTTP status code of a {@link Response} instance is equal to the given code. + * @param expectedStatusCode + * @return + */ + public static Matcher statusCodeIs(int expectedStatusCode) { + return new ResponseStatusCodeMatcher(org.hamcrest.Matchers.is(expectedStatusCode)); + } + + /** + * Matches when the HTTP status code of a {@link Response} instance is equal to the given code (HttpClient variant). + * @param expectedStatusCode + * @return + */ + public static Matcher statusCodeIsHC(int expectedStatusCode) { + return new HttpResponseStatusCodeMatcher(org.hamcrest.Matchers.is(expectedStatusCode)); + } + + /** + * Matches when the HTTP status code of a {@link Response} instance is equal to the given code. + * @param expectedStatusCode + * @return + */ + public static Matcher header(Matcher> matcher) { + return new ResponseHeaderMatcher<>(matcher); + } + + /** + * Matches when the SAML status code of a {@link ResponseType} instance is equal to the given code. + * @param expectedStatusCode + * @return + */ + public static Matcher isSamlResponse(JBossSAMLURIConstants expectedStatus) { + return allOf( + instanceOf(ResponseType.class), + new SamlResponseTypeMatcher(is(expectedStatus.getUri())) + ); + } + + /** + * Matches when the destination of a SAML {@link LogoutRequestType} instance is equal to the given destination. + * @param expectedStatusCode + * @return + */ + public static Matcher isSamlLogoutRequest(String destination) { + return allOf( + instanceOf(LogoutRequestType.class), + new SamlLogoutRequestTypeMatcher(URI.create(destination)) + ); + } + /** + * Matches when the type of a SAML object is instance of {@link AuthnRequestType}. + * @return + */ + public static Matcher isSamlAuthnRequest() { + return instanceOf(AuthnRequestType.class); + } + + /** + * Matches when the SAML status of a {@link StatusResponseType} instance is equal to the given code. + * @param expectedStatusCode + * @return + */ + public static Matcher isSamlStatusResponse(JBossSAMLURIConstants... expectedStatus) { + return allOf( + instanceOf(StatusResponseType.class), + new SamlStatusResponseTypeMatcher(Arrays.stream(expectedStatus).map(JBossSAMLURIConstants::getUri).toArray(i -> new URI[i])) + ); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java new file mode 100644 index 000000000000..96f3b095ce90 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java @@ -0,0 +1,25 @@ +package org.keycloak.testsuite.util; + +import org.apache.http.client.RedirectStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; + +public class MutualTLSUtils { + + private MutualTLSUtils() { + } + + public static CloseableHttpClient newCloseableHttpClient(String keyStorePath, String keyStorePassword, + String trustStorePath, String trustStorePassword) { + return newCloseableHttpClient(keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, + DefaultRedirectStrategy.INSTANCE); + } + + public static CloseableHttpClient newCloseableHttpClient(String keyStorePath, String keyStorePassword, + String trustStorePath, String trustStorePassword, RedirectStrategy redirectStrategy) { + // Compatibility helper for migrated tests. Tests that need real mTLS should use + // dedicated providers in the current test framework. + return HttpClientBuilder.create().setRedirectStrategy(redirectStrategy).build(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java b/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java new file mode 100644 index 000000000000..f962d0f4a0c5 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java @@ -0,0 +1,43 @@ +package org.keycloak.testsuite.util; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_SSL_REQUIRED; +import static org.keycloak.testsuite.util.ServerURLs.removeDefaultPorts; + +public class ReverseProxy implements TestRule { + + public static String DEFAULT_PROXY_HOST = "proxy.kc.localtest.me"; + public static final int DEFAULT_HTTP_PORT = 8666; + public static final int DEFAULT_HTTPS_PORT = 8667; + + public ReverseProxy() { + this(DEFAULT_PROXY_HOST); + } + + public ReverseProxy(String host) { + this(host, ""); + } + + public ReverseProxy(String host, String nodes) { + // No-op compatibility constructor kept for migrated broker tests. + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + base.evaluate(); + } + }; + } + + public String getUrl() { + String scheme = AUTH_SERVER_SSL_REQUIRED ? "https" : "http"; + int port = AUTH_SERVER_SSL_REQUIRED ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; + return removeDefaultPorts(String.format("%s://%s:%s", scheme, DEFAULT_PROXY_HOST, port)); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java new file mode 100644 index 000000000000..28cb6592946b --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java @@ -0,0 +1,861 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.KeyManagementException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.xml.soap.MessageFactory; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPHeader; +import jakarta.xml.soap.SOAPHeaderElement; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.ws.soap.SOAPFaultException; + +import org.keycloak.adapters.saml.SamlDeployment; +import org.keycloak.common.VerificationException; +import org.keycloak.common.util.KeyUtils; +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.ArtifactResponseType; +import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; +import org.keycloak.dom.saml.v2.protocol.RequestAbstractType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.protocol.saml.SamlProtocolUtils; +import org.keycloak.protocol.saml.profile.util.Soap; +import org.keycloak.rotation.KeyLocator; +import org.keycloak.saml.BaseSAML2BindingBuilder; +import org.keycloak.saml.SAMLRequestParser; +import org.keycloak.saml.SignatureAlgorithm; +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.saml.common.constants.JBossSAMLConstants; +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.common.exceptions.ConfigurationException; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.saml.processing.core.parsers.saml.SAMLParser; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.saml.processing.core.util.JAXPValidationUtil; +import org.keycloak.saml.processing.web.util.RedirectBindingUtil; +import org.keycloak.testsuite.util.saml.StepWithCheckers; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.LaxRedirectStrategy; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.jboss.logging.Logger; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import static org.keycloak.saml.common.constants.GeneralConstants.RELAY_STATE; +import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; +import static org.keycloak.testsuite.util.SamlUtils.getSamlDeploymentForClient; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author hmlnarik + */ +public class SamlClient { + + @FunctionalInterface + public interface Step { + HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception; + } + + @FunctionalInterface + public interface ResultExtractor { + T extract(CloseableHttpResponse response) throws Exception; + } + + public static final class DoNotFollowRedirectStep implements Step { + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI uri, CloseableHttpResponse response, HttpClientContext context) throws Exception { + return null; + } + } + + public static class RedirectStrategyWithSwitchableFollowRedirect extends LaxRedirectStrategy { + + public boolean redirectable = true; + + @Override + protected boolean isRedirectable(String method) { + return redirectable && super.isRedirectable(method); + } + + public void setRedirectable(boolean redirectable) { + this.redirectable = redirectable; + } + } + + /** + * SAML bindings and related HttpClient methods. + */ + public enum Binding { + POST { + @Override + public SAMLDocumentHolder extractResponse(CloseableHttpResponse response, String realmPublicKey) throws IOException { + assertThat(response, statusCodeIsHC(Response.Status.OK)); + String responsePage = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + response.close(); + return extractSamlResponseFromForm(responsePage); + } + + @Override + public HttpPost createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) { + return createSamlPostMessage(samlEndpoint, relayState, samlRequest, GeneralConstants.SAML_REQUEST_KEY, null, null, null); + } + + @Override + public HttpPost createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest) { + return createSamlPostMessage(samlEndpoint, relayState, samlRequest, GeneralConstants.SAML_RESPONSE_KEY, null, null, null); + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return createSamlSignedResponse(samlEndpoint, relayState, samlRequest, realmPrivateKey, realmPublicKey, null); + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + return null; + } + + @Override + public String extractRelayState(CloseableHttpResponse response) throws IOException { + assertThat(response, statusCodeIsHC(Response.Status.OK)); + String responsePage = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + response.close(); + return extractSamlRelayStateFromForm(responsePage); + } + + @Override + public HttpPost createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return createSamlSignedRequest(samlEndpoint, relayState, samlRequest, realmPrivateKey, realmPublicKey, null); + } + + @Override + public HttpPost createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + return createSamlPostMessage(samlEndpoint, relayState, samlRequest, GeneralConstants.SAML_REQUEST_KEY, realmPrivateKey, realmPublicKey, certificateStr); + } + + private HttpPost createSamlPostMessage(URI samlEndpoint, String relayState, Document samlRequest, String messageType, String privateKeyStr, String publicKeyStr, String certificateStr) { + HttpPost post = new HttpPost(samlEndpoint); + + List parameters = new LinkedList<>(); + + + try { + BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder(); + + if (privateKeyStr != null && (publicKeyStr != null || certificateStr != null)) { + PrivateKey privateKey = org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(privateKeyStr); + PublicKey publicKey = publicKeyStr != null? org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(publicKeyStr) : null; + X509Certificate cert = org.keycloak.common.util.PemUtils.decodeCertificate(certificateStr); + if (publicKey == null) { + publicKey = cert.getPublicKey(); + } + binding + .signatureAlgorithm(SignatureAlgorithm.RSA_SHA256) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey, cert) + .signDocument(); + } + + parameters.add( + new BasicNameValuePair(messageType, + binding + .postBinding(samlRequest) + .encoded()) + ); + } catch (IOException | ConfigurationException | ProcessingException ex) { + throw new RuntimeException(ex); + } + + if (relayState != null) { + parameters.add(new BasicNameValuePair(RELAY_STATE, relayState)); + } + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8); + post.setEntity(formEntity); + + return post; + } + + @Override + public URI getBindingUri() { + return JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(); + } + }, + + REDIRECT { + @Override + public SAMLDocumentHolder extractResponse(CloseableHttpResponse response, String realmPublicKey) throws IOException { + assertThat(response, statusCodeIsHC(Response.Status.FOUND)); + String location = response.getFirstHeader("Location").getValue(); + response.close(); + return extractSamlResponseFromRedirect(location, realmPublicKey); + } + + @Override + public HttpGet createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) { + try { + URI requestURI = new BaseSAML2BindingBuilder() + .relayState(relayState) + .redirectBinding(samlRequest) + .requestURI(samlEndpoint.toString()); + return new HttpGet(requestURI); + } catch (ProcessingException | ConfigurationException | IOException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public URI getBindingUri() { + return JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(); + } + + @Override + public HttpUriRequest createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest) { + try { + URI responseURI = new BaseSAML2BindingBuilder() + .relayState(relayState) + .redirectBinding(samlRequest) + .responseURI(samlEndpoint.toString()); + return new HttpGet(responseURI); + } catch (ProcessingException | ConfigurationException | IOException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return createSamlSignedResponse(samlEndpoint, relayState, samlRequest, realmPrivateKey, realmPublicKey, null); + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + + try { + BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder(); + + if (realmPrivateKey != null && realmPublicKey != null) { + PrivateKey privateKey = org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(realmPrivateKey); + PublicKey publicKey = org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(realmPublicKey); + X509Certificate cert = org.keycloak.common.util.PemUtils.decodeCertificate(certificateStr); + binding + .signatureAlgorithm(SignatureAlgorithm.RSA_SHA256) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey, cert) + .signDocument(); + } + + binding.relayState(relayState); + + return new HttpGet(binding.redirectBinding(samlRequest).responseURI(samlEndpoint.toString())); + } catch (IOException | ConfigurationException | ProcessingException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public String extractRelayState(CloseableHttpResponse response) throws IOException { + assertThat(response, statusCodeIsHC(Response.Status.FOUND)); + String location = response.getFirstHeader("Location").getValue(); + response.close(); + return extractRelayStateFromRedirect(location); + } + + @Override + public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String privateKeyStr, String publicKeyStr) { + return createSamlSignedRequest(samlEndpoint, relayState, samlRequest, privateKeyStr, publicKeyStr, null); + } + + @Override + public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String privateKeyStr, String publicKeyStr, String certificateStr) { + try { + BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState); + if (privateKeyStr != null && (publicKeyStr != null || certificateStr != null)) { + PrivateKey privateKey = org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(privateKeyStr); + PublicKey publicKey = publicKeyStr != null? org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(publicKeyStr) : null; + X509Certificate cert = org.keycloak.common.util.PemUtils.decodeCertificate(certificateStr); + if (publicKey == null) { + publicKey = cert.getPublicKey(); + } + binding.signatureAlgorithm(SignatureAlgorithm.RSA_SHA256) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey, cert) + .signDocument(); + } + return new HttpGet(binding.redirectBinding(samlRequest).requestURI(samlEndpoint.toString())); + } catch (IOException | ConfigurationException | ProcessingException ex) { + throw new RuntimeException(ex); + } + } + }, + /** + * SOAP binding is currently usable only with http://localhost:8280/ecp-sp/ client, see to-do comment within + * {@link #createSamlSignedRequest} for more details. After resolving that to-do it should be usable with any + * client. + */ + SOAP { + @Override + public SAMLDocumentHolder extractResponse(CloseableHttpResponse response, String realmPublicKey) throws IOException { + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { + MessageFactory messageFactory = MessageFactory.newInstance(); + SOAPMessage soapMessage = messageFactory.createMessage(null, response.getEntity().getContent()); + SOAPBody soapBody = soapMessage.getSOAPBody(); + Node authnRequestNode = soapBody.getFirstChild(); + Document document = DocumentUtil.createDocument(); + document.appendChild(document.importNode(authnRequestNode, true)); + + SAMLParser samlParser = SAMLParser.getInstance(); + JAXPValidationUtil.checkSchemaValidation(document); + + SAML2Object responseType = (SAML2Object) samlParser.parse(document); + + return new SAMLDocumentHolder(responseType, document); + + } else if (statusCode == 500) { + MessageFactory messageFactory = MessageFactory.newInstance(); + SOAPMessage soapMessage = messageFactory.createMessage(null, response.getEntity().getContent()); + SOAPBody soapBody = soapMessage.getSOAPBody(); + throw new SOAPFaultException(soapBody.getFault()); + } else { + throw new RuntimeException("Unexpected response status code (" + statusCode + ")"); + } + } catch (SOAPException | ConfigurationException | ProcessingException | ParsingException e) { + throw new RuntimeException(e); + } + } + + private static final String NS_PREFIX_PROFILE_ECP = "ecp"; + private static final String NS_PREFIX_SAML_PROTOCOL = "samlp"; + private static final String NS_PREFIX_SAML_ASSERTION = "saml"; + private static final String NS_PREFIX_PAOS_BINDING = "paos"; + + private void createEcpRequestHeader(SOAPEnvelope envelope, SamlDeployment deployment) throws SOAPException { + SOAPHeader headers = envelope.getHeader(); + SOAPHeaderElement ecpRequestHeader = headers.addHeaderElement(envelope.createQName(JBossSAMLConstants.REQUEST.get(), NS_PREFIX_PROFILE_ECP)); + + ecpRequestHeader.setMustUnderstand(true); + ecpRequestHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next"); + ecpRequestHeader.addAttribute(envelope.createName("ProviderName"), deployment.getEntityID()); + ecpRequestHeader.addAttribute(envelope.createName("IsPassive"), "0"); + ecpRequestHeader.addChildElement(envelope.createQName("Issuer", "saml")).setValue(deployment.getEntityID()); + ecpRequestHeader.addChildElement(envelope.createQName("IDPList", "samlp")) + .addChildElement(envelope.createQName("IDPEntry", "samlp")) + .addAttribute(envelope.createName("ProviderID"), deployment.getIDP().getEntityID()) + .addAttribute(envelope.createName("Name"), deployment.getIDP().getEntityID()) + .addAttribute(envelope.createName("Loc"), deployment.getIDP().getSingleSignOnService().getRequestBindingUrl()); + } + + private void createPaosRequestHeader(SOAPEnvelope envelope, SamlDeployment deployment) throws SOAPException { + SOAPHeader headers = envelope.getHeader(); + SOAPHeaderElement paosRequestHeader = headers.addHeaderElement(envelope.createQName(JBossSAMLConstants.REQUEST.get(), NS_PREFIX_PAOS_BINDING)); + + paosRequestHeader.setMustUnderstand(true); + paosRequestHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next"); + paosRequestHeader.addAttribute(envelope.createName("service"), JBossSAMLURIConstants.ECP_PROFILE.get()); + paosRequestHeader.addAttribute(envelope.createName("responseConsumerURL"), getResponseConsumerUrl(deployment)); + } + + private String getResponseConsumerUrl(SamlDeployment deployment) { + return (deployment.getIDP() == null + || deployment.getIDP().getSingleSignOnService() == null + || deployment.getIDP().getSingleSignOnService().getAssertionConsumerServiceUrl() == null + ) ? null + : deployment.getIDP().getSingleSignOnService().getAssertionConsumerServiceUrl().toString(); + } + + @Override + public HttpUriRequest createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) { + return createSamlSignedRequest(samlEndpoint, relayState, samlRequest, null, null, null); + } + + @Override + public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return createSamlSignedRequest(samlEndpoint, relayState, samlRequest, realmPrivateKey, realmPublicKey, null); + } + + @Override + public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder(); + + if (realmPrivateKey != null && realmPublicKey != null) { + PrivateKey privateKey = org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(realmPrivateKey); + PublicKey publicKey = org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(realmPublicKey); + X509Certificate cert = org.keycloak.common.util.PemUtils.decodeCertificate(certificateStr); + binding + .signatureAlgorithm(SignatureAlgorithm.RSA_SHA256) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey, cert) + .signDocument(); + + try { + samlRequest = binding.postBinding(samlRequest).getDocument(); + } catch (ProcessingException e) { + throw new RuntimeException(e); + } + } + + MessageFactory messageFactory = null; + try { + + messageFactory = MessageFactory.newInstance(); + + SOAPMessage message = messageFactory.createMessage(); + + SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); + + envelope.addNamespaceDeclaration(NS_PREFIX_SAML_ASSERTION, JBossSAMLURIConstants.ASSERTION_NSURI.get()); + envelope.addNamespaceDeclaration(NS_PREFIX_SAML_PROTOCOL, JBossSAMLURIConstants.PROTOCOL_NSURI.get()); + envelope.addNamespaceDeclaration(NS_PREFIX_PAOS_BINDING, JBossSAMLURIConstants.PAOS_BINDING.get()); + envelope.addNamespaceDeclaration(NS_PREFIX_PROFILE_ECP, JBossSAMLURIConstants.ECP_PROFILE.get()); + + SamlDeployment deployment = getSamlDeploymentForClient("ecp-sp"); // TODO: Make more general for any client, currently SOAP is usable only with http://localhost:8280/ecp-sp/ client + + createPaosRequestHeader(envelope, deployment); + createEcpRequestHeader(envelope, deployment); + + SOAPBody body = envelope.getBody(); + + body.addDocument(samlRequest); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + message.writeTo(outputStream); + + HttpPost post = new HttpPost(samlEndpoint); + post.setEntity(new ByteArrayEntity(outputStream.toByteArray(), ContentType.TEXT_XML)); + return post; + } catch (SOAPException | IOException | ParsingException e) { + throw new RuntimeException(e); + } + } + + @Override + public URI getBindingUri() { + return null; + } + + @Override + public HttpUriRequest createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest) { + return null; + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return null; + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + return null; + } + + @Override + public String extractRelayState(CloseableHttpResponse response) throws IOException { + return null; + } + }, + ARTIFACT_RESPONSE { + private Document extractSoapMessage(CloseableHttpResponse response) throws IOException { + ByteArrayInputStream bais = new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity())); + Document soapBody = Soap.extractSoapMessage(bais); + response.close(); + return soapBody; + } + + @Override + public SAMLDocumentHolder extractResponse(CloseableHttpResponse response, String realmPublicKey) throws IOException { + assertThat(response, statusCodeIsHC(Response.Status.OK)); + Document soapBodyContents = extractSoapMessage(response); + + SAMLDocumentHolder samlDoc = null; + try { + samlDoc = SAML2Request.getSAML2ObjectFromDocument(soapBodyContents); + } catch (ProcessingException | ParsingException e) { + throw new RuntimeException("Unable to get documentHolder from soapBodyResponse: " + DocumentUtil.asString(soapBodyContents)); + } + if (!(samlDoc.getSamlObject() instanceof ArtifactResponseType)) { + throw new RuntimeException("Message received from ArtifactResolveService is not an ArtifactResponseMessage"); + } + + ArtifactResponseType art = (ArtifactResponseType) samlDoc.getSamlObject(); + + try { + Object artifactResponseContent = art.getAny(); + if (artifactResponseContent instanceof ResponseType) { + Document doc = SAML2Request.convert((ResponseType) artifactResponseContent); + return new SAMLDocumentHolder((ResponseType) artifactResponseContent, doc); + } else if (artifactResponseContent instanceof RequestAbstractType) { + Document doc = SAML2Request.convert((RequestAbstractType) art.getAny()); + return new SAMLDocumentHolder((RequestAbstractType) artifactResponseContent, doc); + } else { + throw new RuntimeException("Can not recognise message contained in ArtifactResponse"); + } + } catch (ParsingException | ConfigurationException | ProcessingException e) { + throw new RuntimeException("Can not obtain document from artifact response: " + DocumentUtil.asString(soapBodyContents)); + } + } + + @Override + public HttpUriRequest createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) { + return null; + } + + @Override + public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return null; + } + + @Override + public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + return null; + } + + @Override + public URI getBindingUri() { + return JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.getUri(); + } + + @Override + public HttpUriRequest createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest) { + return null; + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) { + return null; + } + + @Override + public HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr) { + return null; + } + + @Override + public String extractRelayState(CloseableHttpResponse response) throws IOException { + return null; + } + }; + + public abstract SAMLDocumentHolder extractResponse(CloseableHttpResponse response, String realmPublicKey) throws IOException; + + public SAMLDocumentHolder extractResponse(CloseableHttpResponse response) throws IOException { + return extractResponse(response, null); + } + + public abstract HttpUriRequest createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest); + + public abstract HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey); + + public abstract HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr); + + public abstract URI getBindingUri(); + + public abstract HttpUriRequest createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest); + + public abstract HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey); + + public abstract HttpUriRequest createSamlSignedResponse(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey, String certificateStr); + + public abstract String extractRelayState(CloseableHttpResponse response) throws IOException; + } + + private static final Logger LOG = Logger.getLogger(SamlClient.class); + + private final HttpClientContext context = HttpClientContext.create(); + + private final RedirectStrategyWithSwitchableFollowRedirect strategy = new RedirectStrategyWithSwitchableFollowRedirect(); + + /** + * Extracts and parses value of SAMLResponse input field of a form present in the given page. + * + * @param responsePage HTML code of the page + * @return + */ + public static SAMLDocumentHolder extractSamlResponseFromForm(String responsePage) { + org.jsoup.nodes.Document theResponsePage = Jsoup.parse(responsePage); + Elements samlResponses = theResponsePage.select("input[name=SAMLResponse]"); + Elements samlRequests = theResponsePage.select("input[name=SAMLRequest]"); + int size = samlResponses.size() + samlRequests.size(); + assertThat("Checking uniqueness of SAMLResponse/SAMLRequest input field in the page", size, is(1)); + + Element respElement = samlResponses.isEmpty() ? samlRequests.first() : samlResponses.first(); + + return SAMLRequestParser.parseResponsePostBinding(respElement.val()); + } + + /** + * Extracts the form element from a Post binding. + * + * @param responsePage HTML code in the page + * @return The element that is the form + */ + public static Element extractFormFromPostResponse(String responsePage) { + org.jsoup.nodes.Document theResponsePage = Jsoup.parse(responsePage); + Elements form = theResponsePage.select("form"); + assertThat("Checking uniqueness of SAMLResponse/SAMLRequest form in Post binding", form.size(), is(1)); + + return form.first(); + } + + /** + * Extracts and parses value of RelayState input field of a form present in the given page. + * + * @param responsePage HTML code of the page + * @return + */ + public static String extractSamlRelayStateFromForm(String responsePage) { + assertThat(responsePage, containsString("form name=\"saml-post-binding\"")); + org.jsoup.nodes.Document theResponsePage = Jsoup.parse(responsePage); + Elements samlRelayStates = theResponsePage.select("input[name=RelayState]"); + + if (samlRelayStates.isEmpty()) return null; + + return samlRelayStates.first().val(); + } + + /** + * Extracts and parses value of RelayState query parameter from the given URI. + * + * @param responseUri + * @return + */ + public static String extractRelayStateFromRedirect(String responseUri) { + List params = URLEncodedUtils.parse(URI.create(responseUri), StandardCharsets.UTF_8); + + return params.stream().filter(nameValuePair -> nameValuePair.getName().equals(RELAY_STATE)) + .findFirst().map(NameValuePair::getValue).orElse(null); + } + + public static MultivaluedMap parseEncodedQueryParameters(String queryString) throws IOException { + MultivaluedMap encodedParams = new MultivaluedHashMap<>(); + if (queryString != null) { + String[] params = queryString.split("&"); + for (String param : params) { + if (param.indexOf('=') >= 0) { + String[] nv = param.split("=", 2); + encodedParams.add(RedirectBindingUtil.urlDecode(nv[0]), nv.length > 1 ? nv[1] : ""); + } else { + encodedParams.add(RedirectBindingUtil.urlDecode(param), ""); + } + } + } + return encodedParams; + } + + /** + * Extracts and parses value of SAMLResponse query parameter from the given URI. + * If the realmPublicKey parameter is passed the response signature is + * validated. + * + * @param responseUri The redirect URI to use + * @param realmPublicKey The public realm key for validating signature in REDIRECT query parameters + * @return + */ + public static SAMLDocumentHolder extractSamlResponseFromRedirect(String responseUri, String realmPublicKey) throws IOException { + MultivaluedMap encodedParams = parseEncodedQueryParameters(URI.create(responseUri).getRawQuery()); + + String samlResponse = encodedParams.getFirst(GeneralConstants.SAML_RESPONSE_KEY); + String samlRequest = encodedParams.getFirst(GeneralConstants.SAML_REQUEST_KEY); + assertTrue((samlResponse != null && samlRequest == null) + || (samlResponse == null && samlRequest != null), "Only one SAMLRequest/SAMLResponse check"); + + String samlDoc = RedirectBindingUtil.urlDecode(samlResponse != null? samlResponse : samlRequest); + SAMLDocumentHolder documentHolder = SAMLRequestParser.parseResponseRedirectBinding(samlDoc); + + if (realmPublicKey != null) { + // if the public key is passed verify the signature of the redirect URI + try { + KeyLocator locator = new KeyLocator() { + + private final Key key = org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(realmPublicKey); + + @Override + public Key getKey(String kid) throws KeyManagementException { + return this.key; + } + + @Override + public Key getKey(Key key) throws KeyManagementException { + return this.key; + } + + @Override + public void refreshKeyCache() { + } + + @Override + public Iterator iterator() { + return Collections.singleton(this.key).iterator(); + } + }; + SamlProtocolUtils.verifyRedirectSignature(documentHolder, locator, encodedParams, + samlResponse != null? GeneralConstants.SAML_RESPONSE_KEY : GeneralConstants.SAML_REQUEST_KEY); + } catch (VerificationException e) { + throw new IOException(e); + } + } + + return documentHolder; + } + + /** + * Creates a SAML login request document with the given parameters. See SAML <AuthnRequest> description for more details. + * + * @param issuer + * @param assertionConsumerURL + * @param destination + * @return + */ + public static AuthnRequestType createLoginRequestDocument(String issuer, String assertionConsumerURL, URI destination) { + try { + SAML2Request samlReq = new SAML2Request(); + AuthnRequestType loginReq = samlReq.createAuthnRequestType(UUID.randomUUID().toString(), assertionConsumerURL, + destination == null ? null : destination.toString(), issuer); + + return loginReq; + } catch (ConfigurationException ex) { + throw new RuntimeException(ex); + } + } + + public void execute(Step... steps) { + executeAndTransform(resp -> null, Arrays.asList(steps)); + } + + public void execute(List steps) { + executeAndTransform(resp -> null, steps); + } + + public T executeAndTransform(ResultExtractor resultTransformer, Step... steps) { + return executeAndTransform(resultTransformer, Arrays.asList(steps)); + } + + public T executeAndTransform(ResultExtractor resultTransformer, List steps) { + CloseableHttpResponse currentResponse = null; + URI currentUri = URI.create("about:blank"); + strategy.setRedirectable(true); + + try (CloseableHttpClient client = createHttpClientBuilderInstance().setRedirectStrategy(strategy).build()) { + for (int i = 0; i < steps.size(); i ++) { + Step s = steps.get(i); + LOG.infof("Running step %d: %s", i, s.getClass()); + + CloseableHttpResponse origResponse = currentResponse; + + HttpUriRequest request = s.perform(client, currentUri, origResponse, context); + if (request == null) { + LOG.info("Last step returned no request, continuing with next step."); + continue; + } + + // Setting of follow redirects has to be set before executing the final request of the current step + if (i < steps.size() - 1 && steps.get(i + 1) instanceof DoNotFollowRedirectStep) { + LOG.debugf("Disabling following redirects"); + strategy.setRedirectable(false); + i++; + } else { + strategy.setRedirectable(true); + } + + LOG.infof("Executing HTTP request to %s", request.getURI()); + + if (s instanceof StepWithCheckers) { + Runnable beforeChecker = ((StepWithCheckers) s).getBeforeStepChecker(); + if (beforeChecker != null) beforeChecker.run(); + } + + currentResponse = client.execute(request, context); + + if (s instanceof StepWithCheckers) { + Runnable afterChecker = ((StepWithCheckers) s).getAfterStepChecker(); + if (afterChecker != null) afterChecker.run(); + } + + currentUri = request.getURI(); + List locations = context.getRedirectLocations(); + if (locations != null && ! locations.isEmpty()) { + currentUri = locations.get(locations.size() - 1); + } + + LOG.infof("Landed to %s", currentUri); + + if (currentResponse != origResponse && origResponse != null) { + origResponse.close(); + } + } + + LOG.info("Going to extract response"); + + return resultTransformer.extract(currentResponse); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public HttpClientContext getContext() { + return context; + } + + protected HttpClientBuilder createHttpClientBuilderInstance() { + return HttpClientBuilder + .create() + .evictIdleConnections(100, TimeUnit.MILLISECONDS); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClientBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClientBuilder.java new file mode 100644 index 000000000000..35a0c03b51d0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClientBuilder.java @@ -0,0 +1,298 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import jakarta.ws.rs.core.Response.Status; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.testsuite.page.AbstractPage; +import org.keycloak.testsuite.util.SamlClient.Binding; +import org.keycloak.testsuite.util.SamlClient.DoNotFollowRedirectStep; +import org.keycloak.testsuite.util.SamlClient.ResultExtractor; +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.saml.CreateArtifactMessageStepBuilder; +import org.keycloak.testsuite.util.saml.CreateAuthnRequestStepBuilder; +import org.keycloak.testsuite.util.saml.CreateLogoutRequestStepBuilder; +import org.keycloak.testsuite.util.saml.HandleArtifactStepBuilder; +import org.keycloak.testsuite.util.saml.IdPInitiatedLoginBuilder; +import org.keycloak.testsuite.util.saml.LoginBuilder; +import org.keycloak.testsuite.util.saml.ModifySamlResponseStepBuilder; +import org.keycloak.testsuite.util.saml.OtpLoginBuilder; +import org.keycloak.testsuite.util.saml.RequiredConsentBuilder; +import org.keycloak.testsuite.util.saml.UpdateProfileBuilder; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.hamcrest.Matcher; +import org.w3c.dom.Document; + +import static org.keycloak.testsuite.util.saml.SamlDocumentStepBuilder.saml2Object2String; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; + +/** + * + * @author hmlnarik + */ +public class SamlClientBuilder { + + private final List steps = new LinkedList<>(); + + /** + * Execute the current steps without any work on the final response. + * @return Client that executed the steps + */ + public SamlClient execute() { + return execute(resp -> {}); + } + + /** + * Execute the current steps and pass the final response to the {@code resultConsumer} for processing. + * @param resultConsumer This function is given the final response + * @return Client that executed the steps + */ + public SamlClient execute(Consumer resultConsumer) { + final SamlClient samlClient = createSamlClient(); + samlClient.executeAndTransform(r -> { + resultConsumer.accept(r); + return null; + }, steps); + return samlClient; + } + + protected SamlClient createSamlClient() { + return new SamlClient(); + } + + /** + * Execute the current steps and pass the final response to the {@code resultTransformer} for processing. + * @param resultTransformer This function is given the final response and processes it into some value + * @return Value returned by {@code resultTransformer} + */ + public T executeAndTransform(ResultExtractor resultTransformer) { + return createSamlClient().executeAndTransform(resultTransformer, steps); + } + + public List getSteps() { + return steps; + } + + public T addStepBuilder(T step) { + steps.add(step); + return step; + } + + /** + * Adds a single generic step + * @param step + * @return This builder + */ + public SamlClientBuilder addStep(Step step) { + steps.add(step); + return this; + } + + /** + * Adds a single generic step + * @return This builder + */ + public SamlClientBuilder addStep(Runnable stepWithNoParameters) { + addStep((client, currentURI, currentResponse, context) -> { + stepWithNoParameters.run(); + return null; + }); + return this; + } + + public T andThen(Function next) { + return next.apply(this); + } + + public SamlClientBuilder assertResponse(Matcher matcher) { + steps.add((client, currentURI, currentResponse, context) -> { + assertThat(currentResponse, matcher); + return null; + }); + return this; + } + + public SamlClientBuilder assertResponse(Consumer consumer) { + steps.add((client, currentURI, currentResponse, context) -> { + consumer.accept(currentResponse); + return null; + }); + return this; + } + + /** + * When executing the {@link HttpUriRequest} obtained from the previous step, + * do not to follow HTTP redirects but pass the first response immediately + * to the following step. + * @return This builder + */ + public SamlClientBuilder doNotFollowRedirects() { + this.steps.add(new DoNotFollowRedirectStep()); + return this; + } + + public SamlClientBuilder clearCookies() { + this.steps.add((client, currentURI, currentResponse, context) -> { + context.getCookieStore().clear(); + return null; + }); + return this; + } + + /** Creates fresh and issues an AuthnRequest to the SAML endpoint */ + public CreateAuthnRequestStepBuilder authnRequest(URI authServerSamlUrl, String issuer, String assertionConsumerURL, Binding requestBinding) { + return addStepBuilder(new CreateAuthnRequestStepBuilder(authServerSamlUrl, issuer, assertionConsumerURL, requestBinding, this)); + } + + /** Issues the given AuthnRequest to the SAML endpoint */ + public CreateAuthnRequestStepBuilder authnRequest(URI authServerSamlUrl, Document authnRequestDocument, Binding requestBinding) { + return addStepBuilder(new CreateAuthnRequestStepBuilder(authServerSamlUrl, authnRequestDocument, requestBinding, this)); + } + + /** Issues the given LogoutRequest to the SAML endpoint */ + public CreateLogoutRequestStepBuilder logoutRequest(URI logoutServerSamlUrl, String issuer, Binding requestBinding) { + return addStepBuilder(new CreateLogoutRequestStepBuilder(logoutServerSamlUrl, issuer, requestBinding, this)); + } + + /** Issues the given LogoutRequest to the SAML endpoint */ + public CreateLogoutRequestStepBuilder logoutRequest(URI logoutServerSamlUrl, String issuer, Binding requestBinding, boolean skipSignature) { + return addStepBuilder(new CreateLogoutRequestStepBuilder(logoutServerSamlUrl, issuer, requestBinding, this, skipSignature)); + } + + /** Issues the given SAML document to the SAML endpoint */ + public ModifySamlResponseStepBuilder submitSamlDocument(URI authServerSamlUrl, String samlDocument, Binding binding) { + return addStepBuilder(new ModifySamlResponseStepBuilder(binding, this) + .targetUri(authServerSamlUrl) + .documentSupplier(() -> samlDocument) + ); + } + + /** Issues the given SAML document to the SAML endpoint */ + public ModifySamlResponseStepBuilder submitSamlDocument(URI authServerSamlUrl, SAML2Object samlObject, Binding binding) { + return addStepBuilder(new ModifySamlResponseStepBuilder(binding, this) + .targetUri(authServerSamlUrl) + .documentSupplier(() -> saml2Object2String(samlObject)) + ); + } + + /** Handles login page */ + public LoginBuilder login() { + return addStepBuilder(new LoginBuilder(this)); + } + + public OtpLoginBuilder otpLogin() { + return addStepBuilder(new OtpLoginBuilder(this)); + } + + /** Handles update profile page after login */ + public UpdateProfileBuilder updateProfile() { + return addStepBuilder(new UpdateProfileBuilder(this)); + } + + /** Starts IdP-initiated flow for the given client */ + public IdPInitiatedLoginBuilder idpInitiatedLogin(URI authServerSamlUrl, String clientId) { + return addStepBuilder(new IdPInitiatedLoginBuilder(authServerSamlUrl, clientId, this)); + } + + /** Handles "Requires consent" page */ + public RequiredConsentBuilder consentRequired() { + return addStepBuilder(new RequiredConsentBuilder(this)); + } + + /** Returns SAML request or response as replied from server. Note that the redirects are disabled for this to work. */ + public SAMLDocumentHolder getSamlResponse(Binding responseBinding) { + return + doNotFollowRedirects() + .executeAndTransform(responseBinding::extractResponse); + } + + /** Returns RelayState from Saml response. Note that the redirects are disabled for this to work. */ + public String getSamlRelayState(Binding responseBinding) { + return doNotFollowRedirects() + .executeAndTransform(responseBinding::extractRelayState); + } + + /** Provide possibility to consume RelayState from saml response. Note that the redirects are disabled for this to work. */ + public SamlClientBuilder assertSamlRelayState(Binding responseBinding, Consumer relayStateConsumer) { + if (responseBinding.equals(Binding.REDIRECT)) doNotFollowRedirects(); + return addStep((client, currentURI, currentResponse, context) -> { + relayStateConsumer.accept(responseBinding.extractRelayState(currentResponse)); + return null; + }); + } + + /** Returns SAML request or response as replied from server. Note that the redirects are disabled for this to work. */ + public ModifySamlResponseStepBuilder processSamlResponse(Binding responseBinding) { + return + doNotFollowRedirects() + .addStepBuilder(new ModifySamlResponseStepBuilder(responseBinding, this)); + } + + public SamlClientBuilder navigateTo(String httpGetUri) { + steps.add((client, currentURI, currentResponse, context) -> new HttpGet(httpGetUri)); + return this; + } + + public SamlClientBuilder navigateTo(AbstractPage page) { + return navigateTo(page.buildUri()); + } + + public SamlClientBuilder navigateTo(URI httpGetUri) { + steps.add((client, currentURI, currentResponse, context) -> new HttpGet(httpGetUri)); + return this; + } + + public SamlClientBuilder followOneRedirect() { + return + doNotFollowRedirects() + .addStep((client, currentURI, currentResponse, context) -> { + assertThat(currentResponse, Matchers.statusCodeIsHC(Status.FOUND)); + assertThat("Location header not found", currentResponse.getFirstHeader("Location"), notNullValue()); + return new HttpGet(currentResponse.getFirstHeader("Location").getValue()); + }); + } + + public HandleArtifactStepBuilder handleArtifact(URI authServerSamlUrl, String issuer) { + return doNotFollowRedirects() + .addStepBuilder(new HandleArtifactStepBuilder(authServerSamlUrl, issuer, this)); + } + + public HandleArtifactStepBuilder handleArtifact(HandleArtifactStepBuilder handleArtifactStepBuilder) { + return doNotFollowRedirects().addStepBuilder(handleArtifactStepBuilder); + } + + public CreateArtifactMessageStepBuilder artifactMessage(URI authServerSamlUrl, String issuer, Binding requestBinding) { + return addStepBuilder(new CreateArtifactMessageStepBuilder(authServerSamlUrl, issuer, requestBinding,this)); + } + + public CreateArtifactMessageStepBuilder artifactMessage(CreateArtifactMessageStepBuilder camb) { + return addStepBuilder(camb); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SamlStreams.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlStreams.java new file mode 100644 index 000000000000..26c3391b48f0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlStreams.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import java.util.Objects; +import java.util.stream.Stream; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.assertion.AssertionType; +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType.ASTChoiceType; +import org.keycloak.dom.saml.v2.assertion.AttributeType; +import org.keycloak.dom.saml.v2.assertion.ConditionAbstractType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.ResponseType.RTChoiceType; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; + +/** + * + * @author hmlnarik + */ +public class SamlStreams { + + public static Stream assertionsUnencrypted(SAML2Object ob) { + assertThat(ob, instanceOf(ResponseType.class)); + return ((ResponseType) ob).getAssertions().stream().map(RTChoiceType::getAssertion).filter(Objects::nonNull); + } + + public static Stream attributeStatements(Stream ob) { + return ob.flatMap((assertionType) -> assertionType.getAttributeStatements().stream()); + } + + public static Stream attributesUnecrypted(Stream ob) { + return ob.flatMap(ast -> ast.getAttributes().stream()).map(ASTChoiceType::getAttribute).filter(Objects::nonNull); + } + + public static Stream conditions(Stream ob) { + return ob.map(AssertionType::getConditions) + .flatMap(ct -> ct.getConditions().stream()); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SamlUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlUtils.java new file mode 100644 index 000000000000..b803b4d03775 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlUtils.java @@ -0,0 +1,56 @@ +package org.keycloak.testsuite.util; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.keycloak.adapters.saml.SamlDeployment; +import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder; +import org.keycloak.adapters.saml.config.parsers.ResourceLoader; +import org.keycloak.admin.client.resource.ClientsResource; +import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; +import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; +import org.keycloak.protocol.saml.installation.SamlSPDescriptorClientInstallation; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.processing.core.parsers.saml.SAMLParser; +import org.keycloak.testsuite.utils.arquillian.DeploymentArchiveProcessorUtils; +import org.keycloak.testsuite.utils.io.IOUtil; + +import org.w3c.dom.Document; + +public class SamlUtils { + public static SamlDeployment getSamlDeploymentForClient(String client) throws ParsingException { + InputStream is = SamlUtils.class.getResourceAsStream("/adapter-test/keycloak-saml/" + client + "/WEB-INF/keycloak-saml.xml"); + + // InputStream -> Document + Document doc = IOUtil.loadXML(is); + + // Modify saml deployment the same way as before deploying to real app server + DeploymentArchiveProcessorUtils.modifySAMLDocument(doc); + + // Document -> InputStream + InputStream isProcessed = IOUtil.documentToInputStream(doc); + + // InputStream -> SamlDeployment + ResourceLoader loader = new ResourceLoader() { + @Override + public InputStream getResourceAsStream(String resource) { + return getClass().getResourceAsStream("/adapter-test/keycloak-saml/" + client + resource); + } + }; + return new DeploymentBuilder().build(isProcessed, loader); + } + + public static SPSSODescriptorType getSPInstallationDescriptor(ClientsResource res, String clientId) throws ParsingException { + String spDescriptorString = res.findByClientId(clientId).stream().findFirst() + .map(ClientRepresentation::getId) + .map(res::get) + .map(clientResource -> clientResource.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR)) + .orElseThrow(() -> new RuntimeException("Missing descriptor")); + + SAMLParser parser = SAMLParser.getInstance(); + EntityDescriptorType o = (EntityDescriptorType) parser.parse(new ByteArrayInputStream(spDescriptorString.getBytes(StandardCharsets.UTF_8))); + return o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/ServerURLs.java b/tests/base/src/test/java/org/keycloak/testsuite/util/ServerURLs.java new file mode 100644 index 000000000000..7c8f9756a8e3 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/ServerURLs.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import static java.lang.Integer.parseInt; + +public class ServerURLs { + public static final boolean AUTH_SERVER_SSL_REQUIRED = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required", "true")); + public static final String AUTH_SERVER_PORT_HTTP = System.getProperty("auth.server.http.port", "8180"); + public static final String AUTH_SERVER_PORT_HTTPS = System.getProperty("auth.server.https.port", "8543"); + public static final String AUTH_SERVER_PORT = AUTH_SERVER_SSL_REQUIRED ? AUTH_SERVER_PORT_HTTPS : AUTH_SERVER_PORT_HTTP; + public static final String AUTH_SERVER_SCHEME = AUTH_SERVER_SSL_REQUIRED ? "https" : "http"; + public static final String AUTH_SERVER_HOST = System.getProperty("auth.server.host", "localhost"); + public static final String AUTH_SERVER_HOST2 = System.getProperty("auth.server.host2", AUTH_SERVER_HOST); + + public static final boolean APP_SERVER_SSL_REQUIRED = Boolean.parseBoolean(System.getProperty("app.server.ssl.required", "false")); + public static final String APP_SERVER_PORT = APP_SERVER_SSL_REQUIRED ? System.getProperty("app.server.https.port", "8643") : System.getProperty("app.server.http.port", "8280"); + public static final String APP_SERVER_SCHEME = APP_SERVER_SSL_REQUIRED ? "https" : "http"; + public static final String APP_SERVER_HOST = System.getProperty("app.server.host", "localhost"); + + public static String getAuthServerContextRoot() { + return getAuthServerContextRoot(0); + } + + public static String getAuthServerContextRoot(int clusterPortOffset) { + return removeDefaultPorts(String.format("%s://%s:%s", AUTH_SERVER_SCHEME, AUTH_SERVER_HOST, parseInt(AUTH_SERVER_PORT) + clusterPortOffset)); + } + + public static String getAppServerContextRoot() { + return getAppServerContextRoot(0); + } + + public static String getAppServerContextRoot(int clusterPortOffset) { + return removeDefaultPorts(String.format("%s://%s:%s", APP_SERVER_SCHEME, APP_SERVER_HOST, parseInt(APP_SERVER_PORT) + clusterPortOffset)); + } + + /** + * Removes default ports: 80 and 443 from url + */ + public static String removeDefaultPorts(String url) { + return url != null ? url.replaceFirst("(.*)(:80)(\\/.*)?$", "$1$3").replaceFirst("(.*)(:443)(\\/.*)?$", "$1$3") : null; + } + + private static int parsePort(String property) { + try { + return parseInt(System.getProperty(property)); + } catch (NumberFormatException ex) { + throw new RuntimeException("Failed to get " + property, ex); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SqlUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SqlUtils.java new file mode 100644 index 000000000000..8551bc66b3e6 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SqlUtils.java @@ -0,0 +1,125 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.SQLExec; +import org.jboss.logging.Logger; + +/** + * @author Marek Posolda + */ +public class SqlUtils { + + protected static final Logger log = Logger.getLogger(SqlUtils.class); + + + /** + * Run given SQL Script against specified DB + * + * @param sqlFilePath absolute path to the SQL file + * @param jdbcDriverClass must be on the classpath + * @param dbUrl + * @param dbUsername + * @param dbPassword + */ + public static void runSqlScript(String sqlFilePath, String jdbcDriverClass, + String dbUrl, String dbUsername, String dbPassword) { + log.infof("Running SQL script from file '%s'\n jdbcDriverClass=%s\n dbUrl=%s\n dbUsername=%s\n dbPassword=%s\n", + sqlFilePath, jdbcDriverClass, dbUrl, dbUsername, dbPassword); + + final class SqlExecuter extends SQLExec { + public SqlExecuter() { + Project project = new Project(); + project.init(); + setProject(project); + setTaskType("sql"); + setTaskName("sql"); + } + } + + SqlExecuter executer = new SqlExecuter(); + executer.setSrc(new File(sqlFilePath)); + executer.setDriver(jdbcDriverClass); + executer.setPassword(dbPassword); + executer.setUserid(dbUsername); + executer.setUrl(dbUrl); + + if (dbUrl.contains("mssql") || jdbcDriverClass.contains("mssql") || jdbcDriverClass.contains("sqlserver")) { + log.info("Using alternative delimiter due the MSSQL"); + executer.setDelimiter("GO"); + SQLExec.DelimiterType dt = new SQLExec.DelimiterType(); + dt.setValue(SQLExec.DelimiterType.ROW); + executer.setDelimiterType(dt); + } + + // See KEYCLOAK-3876 + if (dbUrl.contains("oracle") || jdbcDriverClass.contains("oracle")) { + log.info("Removing 'SET DEFINE OFF' from the SQL script due the Oracle"); + try { + String content = IOUtils.toString(new FileInputStream(sqlFilePath), StandardCharsets.UTF_8); + content = content.replaceAll("SET DEFINE OFF;", ""); + IOUtils.write(content, new FileOutputStream(sqlFilePath), StandardCharsets.UTF_8); + } catch (IOException fnfe) { + throw new RuntimeException(fnfe); + } + } + + try { + executer.execute(); + } catch (Exception e) { + try { + String sqlScript = IOUtils.toString(new FileInputStream(sqlFilePath), StandardCharsets.UTF_8); + log.errorf("Exception during manual migration. Content of the SQL file: \n%s\n", sqlScript); + } catch (Exception e2) { + log.error("Exception when trying to log content of SQL file", e2); + } + + throw e; + } + } + + + // Run SQL script. JDBC driver must be on classpath! Execute application for example with arguments like: + // -DsqlFilePath=/tmp/keycloak-database-update.sql -Dkeycloak.connectionsJpa.password=keycloak -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.driver=oracle.jdbc.driver.OracleDriver -Dkeycloak.connectionsJpa.url="jdbc:oracle:thin:@localhost:1251:xe" + public static void main(String[] args) { + String sqlFilePath = readProperty("sqlFilePath"); + String jdbcDriverClass = readProperty("keycloak.connectionsJpa.driver"); + String dbUrl = readProperty("keycloak.connectionsJpa.url"); + String dbUsername = readProperty("keycloak.connectionsJpa.user"); + String dbPassword = readProperty("keycloak.connectionsJpa.password"); + + runSqlScript(sqlFilePath, jdbcDriverClass, dbUrl, dbUsername, dbPassword); + + } + + private static String readProperty(String propertyName) { + String val = System.getProperty(propertyName); + if (val == null) { + throw new RuntimeException("Undefined system property: " + propertyName); + } + return val; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SystemInfoHelper.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SystemInfoHelper.java new file mode 100644 index 000000000000..441b69f9c270 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SystemInfoHelper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; + + +/** + * Provides some data about CPU and Memory of the java process used for the testsuite + * + * @author Marek Posolda + */ +public class SystemInfoHelper { + + public static String getSystemInfo() { + Runtime runtime = Runtime.getRuntime(); + + StringBuilder s = new StringBuilder("TEST PROCESS INFO: "); + s.append("\nAvailable processors: " + runtime.availableProcessors()); + s.append("\nTotal memory: " + toMB(runtime.totalMemory())); + s.append("\nMax memory (Xmx): " + toMB(runtime.maxMemory())); + + for (MemoryPoolMXBean memoryMXBean : ManagementFactory.getMemoryPoolMXBeans()) { + if ("Metaspace".equalsIgnoreCase(memoryMXBean.getName())) { + s.append("\nMetaspace Max: " + toMB(memoryMXBean.getUsage().getMax())); + } + } + + return s.toString(); + } + + + private static String toMB(long bytes) { + return bytes / 1024 / 1024 + " MB"; + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/TestAppHelper.java b/tests/base/src/test/java/org/keycloak/testsuite/util/TestAppHelper.java new file mode 100644 index 000000000000..bfd762d8b645 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/TestAppHelper.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.ui.page.LoginPage; +import org.keycloak.testframework.ui.page.LoginTotpPage; +import org.keycloak.testsuite.util.oauth.AccessTokenResponse; + +public class TestAppHelper { + private OAuthClient oauth; + private LoginPage loginPage; + private LoginTotpPage loginTotpPage; + + private String refreshToken; + + public TestAppHelper(OAuthClient oauth, LoginPage loginPage) { + this.oauth = oauth; + this.loginPage = loginPage; + } + public TestAppHelper(OAuthClient oauth, LoginPage loginPage, LoginTotpPage loginTotpPage) { + this.oauth = oauth; + this.loginPage = loginPage; + this.loginTotpPage = loginTotpPage; + } + + public void login(String username, String password) { + startLogin(username, password); + completeLogin(); + } + + public void startLogin(String username, String password) { + oauth.openLoginForm(); + loginPage.fillLogin(username, password); + loginPage.submit(); + } + + public void completeLogin() { + String code = oauth.parseLoginResponse().getCode(); + AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code); + refreshToken = tokenResponse.getRefreshToken(); + } + + public void login(String username, String password, String otp) { + startLogin(username, password); + loginTotpPage.login(otp); + completeLogin(); + } + + public void login(String username, String password, String realm, String clientId, String idp) { + oauth.client(clientId); + oauth.realm(realm); + oauth.openLoginForm(); + loginPage.clickSocial(idp); + loginPage.fillLogin(username, password); + loginPage.submit(); + completeLogin(); + } + + public boolean logout() { + try { + return oauth.doLogout(refreshToken).isSuccess(); + } catch (RuntimeException e) { + return false; + } + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/TextFileChecker.java b/tests/base/src/test/java/org/keycloak/testsuite/util/TextFileChecker.java new file mode 100644 index 000000000000..40ff32619ae6 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/TextFileChecker.java @@ -0,0 +1,80 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import org.jboss.logging.Logger; +import org.jboss.logging.Logger.Level; + +/** + * + * @author hmlnarik + */ +public class TextFileChecker { + + private static final Logger log = Logger.getLogger(TextFileChecker.class); + + private final Map lastCheckedPositions = new HashMap<>(); + + private final Path[] paths; + + public TextFileChecker(Path... paths) { + this.paths = paths; + } + + private void updateLastCheckedPositionsOfAllFilesToEndOfFile(Path path) throws IOException { + if (Files.exists(path)) { + lastCheckedPositions.put(path, Files.size(path)); + } else { + lastCheckedPositions.remove(path); + } + } + + public void checkFiles(boolean verbose, Consumer> lineChecker) throws IOException { + for (Path path : paths) { + log.logf(verbose ? Level.INFO : Level.DEBUG, "Checking server log: '%s'", path.toAbsolutePath()); + + if (! Files.exists(path)) { + continue; + } + + try (InputStream in = Files.newInputStream(path)) { + Long lastCheckedPosition = lastCheckedPositions.computeIfAbsent(path, p -> 0L); + in.skip(lastCheckedPosition); + try (BufferedReader b = new BufferedReader(new InputStreamReader(in))) { + lineChecker.accept(b.lines()); + } + } + } + } + + public void updateLastCheckedPositionsOfAllFilesToEndOfFile() throws IOException { + for (Path path : paths) { + updateLastCheckedPositionsOfAllFilesToEndOfFile(path); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/TokenSignatureUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/util/TokenSignatureUtil.java new file mode 100644 index 000000000000..2c1dd44513ff --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/TokenSignatureUtil.java @@ -0,0 +1,22 @@ +package org.keycloak.testsuite.util; + +import java.util.Map; + +public final class TokenSignatureUtil { + + private static final Map EC_ALG_TO_NIST = Map.of( + "ES256", "P-256", + "ES384", "P-384", + "ES512", "P-521"); + + private TokenSignatureUtil() { + } + + public static String convertAlgorithmToECDomainParamNistRep(String algorithm) { + String value = EC_ALG_TO_NIST.get(algorithm); + if (value == null) { + throw new RuntimeException("Unknown algorithm: " + algorithm); + } + return value; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/UIUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/UIUtils.java new file mode 100644 index 000000000000..19d4e9e55cad --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/UIUtils.java @@ -0,0 +1,332 @@ +package org.keycloak.testsuite.util; + +import java.time.Duration; +import java.util.Optional; +import java.util.function.Supplier; + +import org.keycloak.testsuite.page.AbstractPage; +import org.keycloak.testsuite.page.AbstractPatternFlyAlert; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.Keys; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.Select; +import org.openqa.selenium.support.ui.WebDriverWait; + +import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver; +import static org.keycloak.testsuite.util.WaitUtils.log; +import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; +import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement; +import static org.keycloak.testsuite.util.WaitUtils.waitUntilElementClassContains; + +/** + * @author Vaclav Muzikar + */ +public final class UIUtils { + + public static final String VALUE_ATTR_NAME = "value"; + public static final String ARIA_INVALID_ATTR_NAME = "aria-invalid"; + + public static boolean selectContainsOption(Select select, String optionText) { + for (WebElement option : select.getOptions()) { + if (option.getText().trim().equals(optionText)) { + return true; + } + } + return false; + } + + public static boolean currentTitleEquals(String url) { + try { + (new WebDriverWait(getCurrentDriver(), Duration.ofSeconds(5))).until(ExpectedConditions.titleIs(url)); + } + catch (TimeoutException e) { + return false; + } + return true; + } + + /** + * Safely performs an operation which is expected to cause a page reload, e.g. a link click. + * This ensures the page will be fully loaded after the operation. + * This is intended for use in UI tests only. + * + * @param operation + */ + public static void performOperationWithPageReload(Runnable operation) { + operation.run(); + waitForPageToLoad(); + } + + public static void refreshPageAndWaitForLoad() { + performOperationWithPageReload(() -> getCurrentDriver().navigate().refresh()); + } + + /** + * The method executes click or sendKeys(Keys.ENTER) in the element. + * In the chrome driver click is emulated by pressing the ENTER key. Since + * the upgrade to chrome 128 some clicks are missed and that triggers CI + * failures. The method is intended to be used for buttons and links which + * accept clicking by pressing the ENTER key. If the element passed does + * not allow clicking using keys use the {@link #click(WebElement) click} + * method. + * + * @param element The element to click + */ + public static void clickLink(WebElement element) { + clickElement(element, Keys.ENTER); + } + + /** + * Same than clickLink but it does not perform any wait after clicking or sending the + * enter key. + * + * @param element The element to click + */ + public static void clickLinkWithoutWait(WebElement element) { + clickLinkWithoutWait(element, Keys.ENTER); + } + + private static void clickElement(WebElement element, CharSequence key) { + WebDriver driver = getCurrentDriver(); + + waitUntilElement(element).is().clickable(); + + performOperationWithPageReload(BrowserDriverUtil.isDriverChrome(driver) + ? () -> element.sendKeys(key) + : element::click); + } + + private static void clickLinkWithoutWait(WebElement element, CharSequence key) { + WebDriver driver = getCurrentDriver(); + + waitUntilElement(element).is().clickable(); + + if (BrowserDriverUtil.isDriverChrome(driver)) { + element.sendKeys(key); + } else { + element.click(); + } + } + + /** + * The method executes click in the element. This method always uses click and + * is not emulated by key pressing in chrome. + * + * @param element The element to click + */ + public static void click(WebElement element) { + waitUntilElement(element).is().clickable(); + performOperationWithPageReload(element::click); + } + + /** + * The method switches the checkbox to the expected state. + * + * It looks that since chrome 128, the single click sometimes does + * not work (See also similar issue {@link #clickLink(WebElement)}, so it is possible repeated multiple times until it reach + * the desired state + * + * @param checkbox Checkbox element to enable or disable + * @param enable If true, the checkbox should be switched to enabled (checked). If false, the checkbox should be switched to disabled (unchecked) + */ + public static void switchCheckbox(WebElement checkbox, boolean enable) { + boolean current = checkbox.isSelected(); + if (current != enable) { + clickElement(checkbox, Keys.SPACE); + Assertions.assertNotEquals(current, checkbox.isSelected(), "Checkbox " + checkbox + " is still in the state " + current + " after click."); + } + } + + /** + * This is as an alternative for {@link #clickLink(WebElement)} and should be used in situations where we can't use + * {@link WaitUtils#waitForPageToLoad()}. This is because {@link WaitUtils#waitForPageToLoad()} would wait until the + * alert would disappeared itself (timeout). + * + * @param button to click on + */ + public static void clickBtnAndWaitForAlert(WebElement button) { + button.click(); + AbstractPatternFlyAlert.waitUntilDisplayed(); + } + + /** + * Navigates to a link directly instead of clicking on it. + * Some browsers are sometimes having problems with clicking on links, so this should be used only in that cases, + * i.e. only when clicking directly doesn't work + * + * @param element + */ + public static void navigateToLink(WebElement element) { + URLUtils.navigateToUri(element.getAttribute("href")); + } + + /** + * This is meant mainly for file uploads in Admin Console where the input fields are hidden + * + * @param element + * @param keys + */ + public static void sendKeysToInvisibleElement(WebElement element, CharSequence... keys) { + if (element.isDisplayed()) { + element.sendKeys(keys); + return; + } + + JavascriptExecutor jsExecutor = (JavascriptExecutor) getCurrentDriver(); + String styleBckp = element.getAttribute("style"); + + jsExecutor.executeScript("arguments[0].setAttribute('style', 'display:block !important');", element); + waitUntilElement(element).is().visible(); + element.sendKeys(keys); + jsExecutor.executeScript("arguments[0].setAttribute('style', '" + styleBckp + "');", element); + } + + public static String getTextInputValue(WebElement input) { + return input.getAttribute(VALUE_ATTR_NAME); + } + + public static void setTextInputValue(WebElement input, String value) { + input.click(); + input.clear(); + if (!StringUtils.isEmpty(value)) { // setting new input + input.sendKeys(value); + } + else { // just clearing the input; input.clear() may not fire all JS events so we need to let the page know that something's changed + input.sendKeys("1"); + input.sendKeys(Keys.BACK_SPACE); + } + + WebDriver driver = getCurrentDriver(); + } + + /** + * Contains some browser-specific tweaks for getting an element text. + * + * @param element + * @return + */ + public static String getTextFromElement(WebElement element) { + String text = element.getText(); + return text; + } + + /** + * Get text from required WebElement + * + * @param element required WebElement + * @return text from element, or null if element is not found + */ + public static String getTextFromElementOrNull(Supplier element) { + return Optional.ofNullable(getElementOrNull(element)) + .map(UIUtils::getTextFromElement) + .orElse(null); + } + + /** + * Get required WebElement + * + * @param element required WebElement + * @return element, or null if element is not found + */ + public static WebElement getElementOrNull(Supplier element) { + try { + return element.get(); + } catch (NoSuchElementException e) { + log.debug("Particular element is not found.", e); + return null; + } + } + + /** + * Should be used solely with {@link org.jboss.arquillian.graphene.GrapheneElement}, i.e. all elements annotated by + * {@link org.openqa.selenium.support.FindBy}. CANNOT be used with elements found directly using + * {@link WebDriver#findElement(By)} and similar. + * + * @param element + * @return true if element is present and visible + */ + public static boolean isElementVisible(WebElement element) { + try { + return element.isDisplayed(); + } + catch (NoSuchElementException e) { + return false; + } + } + + /** + * To be used only when absolutely necessary. Browsers should handle scrolling automatically but at some unknown + * conditions some of them (GeckoDriver) won't scroll. + * + * @param element + */ + public static void scrollElementIntoView(WebElement element) { + ((JavascriptExecutor) getCurrentDriver()).executeScript("arguments[0].scrollIntoView(true);", element); + } + + public static boolean doesElementClassContain(WebElement element, String value) { + try { + waitUntilElementClassContains(element, value); + } + catch (TimeoutException e) { + return false; + } + return true; + } + + public static boolean isElementDisabled(WebElement element) { + return element.getAttribute("disabled") != null; + } + + /** + * Relies on aria-invalid attribute. + * + * @param element an input element + * @return true iff the element contains "aria-invalid" attribute AND its value is not set to "false", + * false otherwise + */ + public static boolean isInputElementValid(WebElement element) { + String ariaInvalid = element.getAttribute(ARIA_INVALID_ATTR_NAME); + return !Boolean.parseBoolean(ariaInvalid); + } + + public static String getRawPageSource(WebDriver driver) { + if ((driver instanceof FirefoxDriver) || (driver instanceof ChromeDriver)) { + // firefox has some weird "bug" – it wraps xml in html + return driver.findElement(By.tagName("body")).getText(); + } + else { + return driver.getPageSource(); + } + } + + public static String getRawPageSource() { + return getRawPageSource(getCurrentDriver()); + } + + /** + * Navigates the driver back but it refreshes the page if it is not the expected one for + * chrome. Chrome 136 does not respect cache-control and refresh is needed + * to reach the server again (the page is cached no matter the cache-control + * directive returned). + * See https://issues.chromium.org/issues/415773538 + * + * @param driver The driver used + * @param expectedPage The expected page + */ + public static void navigateBackWithRefresh(WebDriver driver, AbstractPage expectedPage) { + driver.navigate().back(); + driver.navigate().refresh(); + expectedPage.assertCurrent(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/URLUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/URLUtils.java new file mode 100644 index 000000000000..ec5ffe5e049c --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/URLUtils.java @@ -0,0 +1,160 @@ +package org.keycloak.testsuite.util; + + +import java.net.URI; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.keycloak.common.util.KeycloakUriBuilder; +import org.keycloak.testframework.oauth.OAuthClient; + +import org.jboss.logging.Logger; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.WebDriverWait; + +import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver; +import static org.keycloak.testsuite.util.ServerURLs.removeDefaultPorts; +import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; + +import static org.openqa.selenium.support.ui.ExpectedConditions.not; +import static org.openqa.selenium.support.ui.ExpectedConditions.urlMatches; +import static org.openqa.selenium.support.ui.ExpectedConditions.urlToBe; + +/** + * @author Vaclav Muzikar + */ +public final class URLUtils { + + private static Logger log = Logger.getLogger(URLUtils.class); + + public static void navigateToUri(String uri) { + WebDriver driver = getCurrentDriver(); + + log.info("starting navigation"); + + log.info("current URL: " + driver.getCurrentUrl()); + log.info("navigating to " + uri); + if (driver.getCurrentUrl().equals(uri)) { // Some browsers won't do anything if navigating to the same URL; this "fixes" it + log.info("target URL equals current URL - refreshing the page"); + driver.navigate().refresh(); + } + else { + driver.navigate().to(uri); + } + + waitForPageToLoad(); + + log.info("new current URL: " + driver.getCurrentUrl()); + log.info("navigation complete"); + } + + public static boolean currentUrlEquals(String url) { + return urlCheck(urlToBe(removeDefaultPorts(url))); + } + + public static boolean currentUrlDoesntEqual(String url) { + return urlCheck(not(urlToBe(url))); + } + + public static boolean currentUrlWithQueryEquals(String expectedUrl, String... expectedQuery) { + List expectedQueryList = Arrays.asList(expectedQuery); + + ExpectedCondition condition = (WebDriver driver) -> { + String[] urlParts = driver.getCurrentUrl().split("\\?", 2); + if (urlParts.length != 2) { + throw new RuntimeException("Current URL doesn't contain query string"); + } + List queryParts = Arrays.asList(urlParts[1].split("&")); + + return urlParts[0].equals(expectedUrl) && queryParts.containsAll(expectedQueryList); + }; + + return urlCheck(condition); + } + + public static boolean currentUrlStartsWith(String url) { + return currentUrlMatches("^" + Pattern.quote(removeDefaultPorts(url)) + ".*$"); + } + + public static boolean currentUrlDoesntStartWith(String url) { + return currentUrlMatches("^(?!" + Pattern.quote(removeDefaultPorts(url)) + ").+$"); + } + + public static boolean currentUrlMatches(String regex) { + return urlCheck(urlMatches(regex)); + } + + @SuppressWarnings("unchecked") + private static boolean urlCheck(ExpectedCondition condition) { + WebDriver driver = getCurrentDriver(); + + try { + (new WebDriverWait(driver, Duration.ofSeconds(5), Duration.ofMillis(100))).until(condition); + } catch (TimeoutException e) { + return false; + } + return true; + } + + /** + * @return action-url from the HTML code of the current page. Assumption is, that page is one of the Keycloak login pages (login theme pages) + */ + public static String getActionUrlFromCurrentPage(WebDriver driver) { + Matcher m = Pattern.compile("form action=\"([^\"]*)\"").matcher(driver.getPageSource()); + if (m.find()) { + return m.group(1); + } else { + return null; + } + } + + /** + * @see #sendPOSTRequestWithWebDriver(String, String) + * + * @param postRequestUrl + * @param formParams form params in key/value form + */ + public static void sendPOSTRequestWithWebDriver(String postRequestUrl, Map formParams) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Map.Entry entry : formParams.entrySet()) { + if (first) { + first = false; + } else { + sb.append("&"); + } + sb.append(entry.getKey()) + .append("=") + .append(entry.getValue()); + } + sendPOSTRequestWithWebDriver(postRequestUrl, sb.toString()); + } + + /** + * This will send POST request to specified URL with specified form parameters. It's not easily possible to "trick" web driver to send POST + * request with custom parameters, which are not directly available in the form. + * + * See URLUtils.sendPOSTWithWebDriver for more details + * + * @param postRequestUrl Absolute URL. It can include query parameters etc. The POST request will be send to this URL + * @param encodedFormParameters Encoded parameters in the form of "param1=value1¶m2=value2" + * @return + */ + public static void sendPOSTRequestWithWebDriver(String postRequestUrl, String encodedFormParameters) { + WebDriver driver = getCurrentDriver(); + + URI uri = KeycloakUriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT + "/realms/master/testing/simulate-post-request") + .queryParam("postRequestUrl", postRequestUrl) + .queryParam("encodedFormParameters", encodedFormParameters) + .build(); + + driver.navigate().to(uri.toString()); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/VaultUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/VaultUtils.java new file mode 100644 index 000000000000..959053027899 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/VaultUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright 2019 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.util; + +import org.keycloak.testsuite.arquillian.annotation.EnableVault; + +/** + * @author mhajas + */ +public class VaultUtils { + + public static void enableVault(Object ignored, EnableVault.PROVIDER_ID provider) { + System.setProperty("keycloak.vault." + provider.getName() + ".provider.enabled", "true"); + } + + public static void disableVault(Object ignored, EnableVault.PROVIDER_ID provider) { + System.setProperty("keycloak.vault." + provider.getName() + ".provider.enabled", "false"); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/WaitUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/WaitUtils.java new file mode 100644 index 000000000000..1d8d99a55df0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/WaitUtils.java @@ -0,0 +1,252 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.keycloak.executors.ExecutorsProvider; +import org.keycloak.testsuite.client.KeycloakTestingClient; + +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.htmlunit.HtmlUnitDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.FluentWait; +import org.openqa.selenium.support.ui.WebDriverWait; + +import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.openqa.selenium.support.ui.ExpectedConditions.javaScriptThrowsNoExceptions; +import static org.openqa.selenium.support.ui.ExpectedConditions.not; +import static org.openqa.selenium.support.ui.ExpectedConditions.urlToBe; + +/** + * + * @author Petr Mensik + * @author tkyjovsk + * @author Vaclav Muzikar + */ +@Deprecated(forRemoval = true) +public final class WaitUtils { + + protected final static org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(WaitUtils.class); + + public static final String PAGELOAD_TIMEOUT_PROP = "pageload.timeout"; + + public static final Integer PAGELOAD_TIMEOUT_MILLIS = Integer.parseInt(System.getProperty(PAGELOAD_TIMEOUT_PROP, "10000")); + + public static final int IMPLICIT_ELEMENT_WAIT_MILLIS = 2000; // high value means more stable but slower tests; it needs to be balanced + + public static class ElementWait { + private final Supplier supplier; + private boolean negated; + + public ElementWait(Supplier supplier) { + this.supplier = supplier; + } + + public ElementWait is() { + this.negated = false; + return this; + } + + public ElementWait not() { + this.negated = true; + return this; + } + + public void present() { + WebDriverWait wait = new WebDriverWait(getCurrentDriver(), Duration.ofMillis(IMPLICIT_ELEMENT_WAIT_MILLIS)); + wait.until(d -> { + boolean present; + try { + present = supplier.get() != null; + } catch (Exception e) { + present = false; + } + return negated ? !present : present; + }); + } + + public void clickable() { + WebDriverWait wait = new WebDriverWait(getCurrentDriver(), Duration.ofMillis(IMPLICIT_ELEMENT_WAIT_MILLIS)); + wait.until(d -> { + try { + WebElement element = supplier.get(); + boolean clickable = element != null && element.isDisplayed() && element.isEnabled(); + return negated ? !clickable : clickable; + } catch (Exception e) { + return negated; + } + }); + } + + public void visible() { + WebDriverWait wait = new WebDriverWait(getCurrentDriver(), Duration.ofMillis(IMPLICIT_ELEMENT_WAIT_MILLIS)); + wait.until(d -> { + try { + WebElement element = supplier.get(); + boolean visible = element != null && element.isDisplayed(); + return negated ? !visible : visible; + } catch (Exception e) { + return negated; + } + }); + } + } + + // Should be no longer necessary for finding elements since we have implicit wait + public static ElementWait waitUntilElement(By by) { + return new ElementWait(() -> getCurrentDriver().findElement(by)); + } + + // Should be no longer necessary for finding elements since we have implicit wait + public static ElementWait waitUntilElement(WebElement element) { + return new ElementWait(() -> element); + } + + // Should be no longer necessary for finding elements since we have implicit wait + public static ElementWait waitUntilElement(WebElement element, String failMessage) { + return new ElementWait(() -> element); + } + + public static void waitUntilElementIsNotPresent(By locator) { + waitUntilElement(locator).is().not().present(); + } + + public static void waitUntilElementIsNotPresent(WebElement element) { + waitUntilElement(element).is().not().present(); +// (new WebDriverWait(driver, IMPLICIT_ELEMENT_WAIT_MILLIS)) +// .until(invisibilityOfAllElements(Collections.singletonList(element))); + } + + public static void waitUntilElementClassContains(WebElement element, String value) { + new WebDriverWait(getCurrentDriver(), Duration.ofSeconds(1)).until( + ExpectedConditions.attributeContains(element, "class", value) + ); + } + + public static void pause(long millis) { + if (millis > 0) { + log.info("Wait: " + millis + "ms"); + try { + Thread.sleep(millis); + } catch (InterruptedException ex) { + Logger.getLogger(WaitUtils.class.getName()).log(Level.SEVERE, null, ex); + Thread.currentThread().interrupt(); + } + } + } + + /** + * Waits for DOMContent to load + */ + public static void waitForDomContentToLoad() { + WebDriver driver = getCurrentDriver(); + + if (driver instanceof HtmlUnitDriver) { + return; // not needed + } + + WebDriverWait wait = new WebDriverWait(driver, Duration.ofMillis(PAGELOAD_TIMEOUT_MILLIS)); + + try { + wait + .pollingEvery(Duration.ofMillis(500)) + .until(javaScriptThrowsNoExceptions( + "if (document.readyState !== 'complete') { throw \"Not ready\";}")); + } catch (TimeoutException e) { + log.warn("waitForPageToLoad time exceeded!"); + } + } + + /** + * Waits for page to finish any pending redirects, REST API requests etc. + * Because Keycloak's Admin Console is a single-page application, we need to + * take extra steps to ensure the page is fully loaded + */ + public static void waitForPageToLoad() { + WebDriver driver = getCurrentDriver(); + + if (driver instanceof HtmlUnitDriver) { + return; // not needed + } + + // Ensure the URL is "stable", i.e. is not changing anymore; if it'd changing, some redirects are probably still in progress + for (int maxRedirects = 4; maxRedirects > 0; maxRedirects--) { + try { + String currentUrl = driver.getCurrentUrl(); + FluentWait wait = new FluentWait<>(driver).withTimeout(Duration.ofMillis(250)); + wait.until(not(urlToBe(currentUrl))); + } catch (TimeoutException e) { + if (driver.getPageSource() != null) { + break; // URL has not changed recently - ok, the URL is stable and page is current + } + } catch (Exception e) { + log.warnf("Unknown exception thrown waiting stabilization of the URL: %s", e.getMessage()); + pause(250); + } + if (maxRedirects == 1) { + log.warn("URL seems unstable! (Some redirect are probably still in progress)"); + } + } + } + + public static void waitForModalFadeIn() { + pause(500); // TODO: Find out how to do in more 'elegant' way, e.g. like in the waitForModalFadeOut + } + + public static void waitForModalFadeOut() { + waitUntilElementIsNotPresent(By.className("modal-backdrop")); + } + + public static void waitForBruteForceExecutors(KeycloakTestingClient testingClient) { + testingClient.server().run(session -> { + ExecutorsProvider provider = session.getProvider(ExecutorsProvider.class); + ExecutorService executor = provider.getExecutor("bruteforce"); + ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; + try { + CompletableFuture.runAsync(() -> { + do { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } while (!threadPoolExecutor.getQueue().isEmpty() || threadPoolExecutor.getActiveCount() > 0); + }).get(30, TimeUnit.SECONDS); + } catch (java.util.concurrent.TimeoutException te) { + fail("Timeout while waiting for brute force executors!"); + } catch (Exception e) { + e.printStackTrace(); + fail("Unexpected error while waiting for brute force executors!"); + } + assertEquals(0, threadPoolExecutor.getActiveCount()); + }); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java new file mode 100644 index 000000000000..e82be9272ac2 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.matchers; + +import java.io.IOException; + +import jakarta.ws.rs.core.Response; + +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +/** + * Matcher for matching status code of {@link Response} instance. + * @author hmlnarik + */ +public class HttpResponseBodyMatcher extends BaseMatcher { + + private final Matcher matcher; + + private ThreadLocal lastEntity = new ThreadLocal<>(); + + public HttpResponseBodyMatcher(Matcher matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(Object item) { + lastEntity.remove(); + try { + lastEntity.set(EntityUtils.toString(((HttpResponse) item).getEntity())); + return (item instanceof HttpResponse) && this.matcher.matches(lastEntity.get()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void describeMismatch(Object item, Description description) { + Description d = description.appendText("was ").appendValue(item); + if (lastEntity.get() != null) { + d.appendText(" with entity ").appendText(lastEntity.get()); + } + } + + @Override + public void describeTo(Description description) { + description.appendText("response body matches ").appendDescriptionOf(this.matcher); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java new file mode 100644 index 000000000000..0cb667dd303a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.matchers; + +import java.io.IOException; + +import jakarta.ws.rs.core.Response; + +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +/** + * Matcher for matching status code of {@link Response} instance. + * @author hmlnarik + */ +public class HttpResponseStatusCodeMatcher extends BaseMatcher { + + private final Matcher matcher; + + public HttpResponseStatusCodeMatcher(Matcher matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(Object item) { + return (item instanceof HttpResponse) && this.matcher.matches(((HttpResponse) item).getStatusLine().getStatusCode()); + } + + @Override + public void describeMismatch(Object item, Description description) { + Description d = description.appendText("was ").appendValue(item) + .appendText(" with entity "); + try { + d.appendText(EntityUtils.toString(((HttpResponse) item).getEntity())); + } catch (IOException e) { + d.appendText(""); + } + } + + @Override + public void describeTo(Description description) { + description.appendText("response status code matches ").appendDescriptionOf(this.matcher); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseBodyMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseBodyMatcher.java new file mode 100644 index 000000000000..826e0d9d9403 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseBodyMatcher.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.matchers; + +import jakarta.ws.rs.core.Response; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +/** + * Matcher for matching status code of {@link Response} instance. + * @author hmlnarik + */ +public class ResponseBodyMatcher extends BaseMatcher { + + private final Matcher matcher; + + public ResponseBodyMatcher(Matcher matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(Object item) { + if (item instanceof Response) { + final Response rItem = (Response) item; + rItem.bufferEntity(); + return this.matcher.matches(rItem.readEntity(String.class)); + } else { + return false; + } + } + + @Override + public void describeTo(Description description) { + description.appendText("response body matches ").appendDescriptionOf(this.matcher); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseHeaderMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseHeaderMatcher.java new file mode 100644 index 000000000000..c5ed68548426 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseHeaderMatcher.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.matchers; + +import java.util.Map; + +import jakarta.ws.rs.core.Response; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +/** + * Matcher for matching status code of {@link Response} instance. + * @author hmlnarik + */ +public class ResponseHeaderMatcher extends BaseMatcher { + + private final Matcher> matcher; + + public ResponseHeaderMatcher(Matcher> matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(Object item) { + return (item instanceof Response) && this.matcher.matches(((Response) item).getHeaders()); + } + + @Override + public void describeTo(Description description) { + description.appendText("response headers match ").appendDescriptionOf(this.matcher); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseStatusCodeMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseStatusCodeMatcher.java new file mode 100644 index 000000000000..27edfed1255a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/ResponseStatusCodeMatcher.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.matchers; + +import jakarta.ws.rs.core.Response; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +/** + * Matcher for matching status code of {@link Response} instance. + * @author hmlnarik + */ +public class ResponseStatusCodeMatcher extends BaseMatcher { + + private final Matcher matcher; + + public ResponseStatusCodeMatcher(Matcher matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(Object item) { + return (item instanceof Response) && this.matcher.matches(((Response) item).getStatus()); + } + + @Override + public void describeTo(Description description) { + description.appendText("response status code matches ").appendDescriptionOf(this.matcher); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java new file mode 100644 index 000000000000..c49b3ce0476f --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java @@ -0,0 +1,49 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.keycloak.testsuite.util.matchers; + +import java.net.URI; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import static org.hamcrest.Matchers.is; + +/** + * + * @author hmlnarik + */ +public class SamlLogoutRequestTypeMatcher extends BaseMatcher { + + private final Matcher destinationMatcher; + + public SamlLogoutRequestTypeMatcher(URI destination) { + this.destinationMatcher = is(destination); + } + + public SamlLogoutRequestTypeMatcher(Matcher destinationMatcher) { + this.destinationMatcher = destinationMatcher; + } + + @Override + public boolean matches(Object item) { + return destinationMatcher.matches(((LogoutRequestType) item).getDestination()); + } + + @Override + public void describeMismatch(Object item, Description description) { + description.appendText("was ").appendValue(((LogoutRequestType) item).getDestination()); + } + + @Override + public void describeTo(Description description) { + description.appendText("SAML logout request destination matches ").appendDescriptionOf(this.destinationMatcher); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java new file mode 100644 index 000000000000..740edf481e9c --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java @@ -0,0 +1,52 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.keycloak.testsuite.util.matchers; + +import java.net.URI; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import static org.hamcrest.Matchers.is; + +/** + * + * @author hmlnarik + */ +public class SamlResponseTypeMatcher extends BaseMatcher { + + private final Matcher statusMatcher; + + public SamlResponseTypeMatcher(JBossSAMLURIConstants expectedStatus) { + this.statusMatcher = is(expectedStatus.getUri()); + } + + public SamlResponseTypeMatcher(Matcher statusMatcher) { + this.statusMatcher = statusMatcher; + } + + @Override + public boolean matches(Object item) { + return statusMatcher.matches(((ResponseType) item).getStatus().getStatusCode().getValue()); + } + + @Override + public void describeMismatch(Object item, Description description) { + description.appendText("was ").appendValue(((ResponseType) item).getStatus().getStatusCode()); + } + + @Override + public void describeTo(Description description) { + description.appendText("SAML response status code matches ").appendDescriptionOf(this.statusMatcher); + } + + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java new file mode 100644 index 000000000000..31d2be8c4bde --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java @@ -0,0 +1,70 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.keycloak.testsuite.util.matchers; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.StatusCodeType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import static org.hamcrest.Matchers.is; + +/** + * + * @author hmlnarik + */ +public class SamlStatusResponseTypeMatcher extends BaseMatcher { + + private final List> statusMatchers; + + public SamlStatusResponseTypeMatcher(URI... statusMatchers) { + this.statusMatchers = new ArrayList(statusMatchers.length); + for (URI uri : statusMatchers) { + this.statusMatchers.add(is(uri)); + } + } + + public SamlStatusResponseTypeMatcher(List> statusMatchers) { + this.statusMatchers = statusMatchers; + } + + @Override + public boolean matches(Object item) { + StatusCodeType statusCode = ((StatusResponseType) item).getStatus().getStatusCode(); + for (Matcher statusMatcher : statusMatchers) { + if (! statusMatcher.matches(statusCode.getValue())) { + return false; + } + statusCode = statusCode.getStatusCode(); + } + return true; + } + + @Override + public void describeMismatch(Object item, Description description) { + StatusCodeType statusCode = ((StatusResponseType) item).getStatus().getStatusCode(); + description.appendText("was "); + while (statusCode != null) { + description.appendText("/").appendValue(statusCode.getValue()); + statusCode = statusCode.getStatusCode(); + } + } + + @Override + public void describeTo(Description description) { + description.appendText("SAML status response status matches "); + for (Matcher statusMatcher : statusMatchers) { + description.appendText("/").appendDescriptionOf(statusMatcher); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateArtifactMessageStepBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateArtifactMessageStepBuilder.java new file mode 100644 index 000000000000..e58d6baf069f --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateArtifactMessageStepBuilder.java @@ -0,0 +1,84 @@ +package org.keycloak.testsuite.util.saml; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +import org.keycloak.protocol.saml.ArtifactResolver; +import org.keycloak.protocol.saml.DefaultSamlArtifactResolver; +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.testsuite.util.SamlClient; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.jboss.logging.Logger; + +public class CreateArtifactMessageStepBuilder implements SamlClient.Step { + + private static final Logger LOG = Logger.getLogger(CreateArtifactMessageStepBuilder.class); + + private final URI authServerSamlUrl; + private final SamlClient.Binding requestBinding; + private final SamlClientBuilder clientBuilder; + private final String issuer; + private String lastArtifact; + private ArtifactResolver artifactResolver = new DefaultSamlArtifactResolver(); + + public CreateArtifactMessageStepBuilder(URI authServerSamlUrl, String issuer, SamlClient.Binding requestBinding, SamlClientBuilder clientBuilder) { + this.authServerSamlUrl = authServerSamlUrl; + this.requestBinding = requestBinding; + this.clientBuilder = clientBuilder; + this.issuer = issuer; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + DefaultSamlArtifactResolver artifactResolver = new DefaultSamlArtifactResolver(); + lastArtifact = artifactResolver.createArtifact(issuer); + if (SamlClient.Binding.POST == requestBinding) { + return sendArtifactMessagePost(); + } + return sendArtifactMessageRedirect(); + } + + private HttpUriRequest sendArtifactMessageRedirect() throws IOException, ProcessingException, URISyntaxException { + URIBuilder builder = new URIBuilder(authServerSamlUrl) + .setParameter(GeneralConstants.SAML_ARTIFACT_KEY, lastArtifact); + + LOG.infof("Sending GET request with artifact %s", lastArtifact); + return new HttpGet(builder.build()); + } + + private HttpUriRequest sendArtifactMessagePost() throws IOException, ProcessingException { + HttpPost post = new HttpPost(authServerSamlUrl); + List parameters = new LinkedList<>(); + parameters.add(new BasicNameValuePair(GeneralConstants.SAML_ARTIFACT_KEY, lastArtifact)); + LOG.infof("Sending POST request with artifact %s", lastArtifact); + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8); + post.setEntity(formEntity); + + return post; + } + + public SamlClientBuilder build() { + return clientBuilder; + } + + public String getLastArtifact() { + return lastArtifact; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateAuthnRequestStepBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateAuthnRequestStepBuilder.java new file mode 100644 index 000000000000..9cecaf3a3f1c --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateAuthnRequestStepBuilder.java @@ -0,0 +1,184 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.util.Base64; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.function.Supplier; + +import jakarta.ws.rs.core.HttpHeaders; + +import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; +import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; +import org.keycloak.dom.saml.v2.protocol.RequestedAuthnContextType; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.saml.common.exceptions.ConfigurationException; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; +import org.keycloak.testsuite.admin.Users; +import org.keycloak.testsuite.util.SamlClient.Binding; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; +import org.w3c.dom.Document; + + +public class CreateAuthnRequestStepBuilder extends SamlDocumentStepBuilder { + + private final String issuer; + private final URI authServerSamlUrl; + private final Binding requestBinding; + private final String assertionConsumerURL; + private String signingPublicKeyPem; // TODO: should not be needed + private String signingPrivateKeyPem; + private String signingCertificate; + private URI protocolBinding; + private String authorizationHeader; + private List authnContextClassRef = new LinkedList<>(); + private AuthnContextComparisonType comparison; + + private final Document forceLoginRequestDocument; + + private Supplier relayState; + + public CreateAuthnRequestStepBuilder(URI authServerSamlUrl, String issuer, String assertionConsumerURL, Binding requestBinding, SamlClientBuilder clientBuilder) { + super(clientBuilder); + this.issuer = issuer; + this.authServerSamlUrl = authServerSamlUrl; + this.requestBinding = requestBinding; + this.assertionConsumerURL = assertionConsumerURL; + + this.forceLoginRequestDocument = null; + } + + public CreateAuthnRequestStepBuilder(URI authServerSamlUrl, Document loginRequestDocument, Binding requestBinding, SamlClientBuilder clientBuilder) { + super(clientBuilder); + this.forceLoginRequestDocument = loginRequestDocument; + + this.authServerSamlUrl = authServerSamlUrl; + this.requestBinding = requestBinding; + + this.issuer = null; + this.assertionConsumerURL = null; + } + + public CreateAuthnRequestStepBuilder relayState(Supplier relayState) { + this.relayState = relayState; + return this; + } + + public CreateAuthnRequestStepBuilder relayState(String relayState) { + this.relayState = () -> relayState; + return this; + } + + public CreateAuthnRequestStepBuilder setProtocolBinding(URI protocolBinding) { + this.protocolBinding = protocolBinding; + return this; + } + + public CreateAuthnRequestStepBuilder setComparison(AuthnContextComparisonType comparison) { + this.comparison = comparison; + return this; + } + + public CreateAuthnRequestStepBuilder addAuthnContextClassRef(String... authnContextClassRef) { + if (authnContextClassRef != null) { + this.authnContextClassRef.addAll(List.of(authnContextClassRef)); + } + return this; + } + + public URI getProtocolBinding() { + return protocolBinding; + } + + public CreateAuthnRequestStepBuilder signWith(String signingPrivateKeyPem, String signingPublicKeyPem) { + return signWith(signingPrivateKeyPem, signingPublicKeyPem, null); + } + + public CreateAuthnRequestStepBuilder signWith(String signingPrivateKeyPem, String signingPublicKeyPem, String signingCertificate) { + this.signingPrivateKeyPem = signingPrivateKeyPem; + this.signingPublicKeyPem = signingPublicKeyPem; + this.signingCertificate = signingCertificate; + return this; + } + + public CreateAuthnRequestStepBuilder basicAuthentication(UserRepresentation user) { + String username = user.getUsername(); + String password = Users.getPasswordOf(user); + String pair = username + ":" + password; + this.authorizationHeader = "Basic " + Base64.getEncoder().encodeToString(pair.getBytes()); + return this; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + Document doc = createLoginRequestDocument(); + + String documentAsString = DocumentUtil.getDocumentAsString(doc); + String transformed = getTransformer().transform(documentAsString); + + if (transformed == null) { + return null; + } + + Document samlDoc = DocumentUtil.getDocument(transformed); + String relayState = this.relayState == null ? null : this.relayState.get(); + + HttpUriRequest request = this.signingPrivateKeyPem == null + ? requestBinding.createSamlUnsignedRequest(authServerSamlUrl, relayState, samlDoc) + : requestBinding.createSamlSignedRequest(authServerSamlUrl, relayState, samlDoc, signingPrivateKeyPem, signingPublicKeyPem, signingCertificate); + + if (authorizationHeader != null) { + request.addHeader(HttpHeaders.AUTHORIZATION, authorizationHeader); + } + + return request; + } + + protected Document createLoginRequestDocument() { + if (this.forceLoginRequestDocument != null) { + return this.forceLoginRequestDocument; + } + + try { + SAML2Request samlReq = new SAML2Request(); + AuthnRequestType loginReq = samlReq.createAuthnRequestType(UUID.randomUUID().toString(), assertionConsumerURL, this.authServerSamlUrl.toString(), issuer, requestBinding.getBindingUri()); + if (protocolBinding != null) { + loginReq.setProtocolBinding(protocolBinding); + } + if (!authnContextClassRef.isEmpty()) { + RequestedAuthnContextType requestAuthContext = new RequestedAuthnContextType(); + requestAuthContext.setComparison(comparison); + authnContextClassRef.stream().forEach(ref -> requestAuthContext.addAuthnContextClassRef(ref)); + loginReq.setRequestedAuthnContext(requestAuthContext); + } + return SAML2Request.convert(loginReq); + } catch (ConfigurationException | ParsingException | ProcessingException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateLogoutRequestStepBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateLogoutRequestStepBuilder.java new file mode 100644 index 000000000000..d8abf84433d8 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/CreateLogoutRequestStepBuilder.java @@ -0,0 +1,152 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.util.function.Supplier; + +import org.keycloak.dom.saml.v2.assertion.BaseIDAbstractType; +import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.saml.SAML2LogoutRequestBuilder; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.testsuite.util.SamlClient.Binding; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; + +/** + * + * @author hmlnarik + */ +public class CreateLogoutRequestStepBuilder extends SamlDocumentStepBuilder { + + private final URI logoutServerSamlUrl; + private final String issuer; + private final Binding requestBinding; + + private Supplier sessionIndex = () -> null; + private Supplier nameId = () -> null; + private Supplier baseId = () -> null; + private Supplier relayState = () -> null; + private String signingPublicKeyPem; // TODO: should not be needed + private String signingPrivateKeyPem; + private String signingCertificate; + + private boolean skipSignature; + + public CreateLogoutRequestStepBuilder(URI logoutServerSamlUrl, String issuer, Binding requestBinding, SamlClientBuilder clientBuilder) { + super(clientBuilder); + this.logoutServerSamlUrl = logoutServerSamlUrl; + this.issuer = issuer; + this.requestBinding = requestBinding; + } + + public CreateLogoutRequestStepBuilder(URI logoutServerSamlUrl, String issuer, Binding requestBinding, SamlClientBuilder clientBuilder, boolean skipSignature) { + super(clientBuilder); + this.logoutServerSamlUrl = logoutServerSamlUrl; + this.issuer = issuer; + this.requestBinding = requestBinding; + this.skipSignature = skipSignature; + } + + public String sessionIndex() { + return sessionIndex.get(); + } + + public CreateLogoutRequestStepBuilder sessionIndex(String sessionIndex) { + this.sessionIndex = () -> sessionIndex; + return this; + } + + public CreateLogoutRequestStepBuilder sessionIndex(Supplier sessionIndex) { + this.sessionIndex = sessionIndex; + return this; + } + + public String relayState() { + return relayState.get(); + } + + public CreateLogoutRequestStepBuilder relayState(String relayState) { + this.relayState = () -> relayState; + return this; + } + + public CreateLogoutRequestStepBuilder relayState(Supplier relayState) { + this.relayState = relayState; + return this; + } + + public NameIDType nameId() { + return nameId.get(); + } + + public BaseIDAbstractType baseId() { + return baseId.get(); + } + + public CreateLogoutRequestStepBuilder nameId(NameIDType nameId) { + this.nameId = () -> nameId; + return this; + } + + public CreateLogoutRequestStepBuilder nameId(Supplier nameId) { + this.nameId = nameId; + return this; + } + + public CreateLogoutRequestStepBuilder baseId(Supplier baseId) { + this.baseId = baseId; + return this; + } + + public CreateLogoutRequestStepBuilder signWith(String signingPrivateKeyPem, String signingPublicKeyPem) { + return signWith(signingPrivateKeyPem, signingPublicKeyPem, null); + } + + public CreateLogoutRequestStepBuilder signWith(String signingPrivateKeyPem, String signingPublicKeyPem, String signingCertificate) { + this.signingPrivateKeyPem = signingPrivateKeyPem; + this.signingPublicKeyPem = signingPublicKeyPem; + this.signingCertificate = signingCertificate; + return this; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + SAML2LogoutRequestBuilder builder = new SAML2LogoutRequestBuilder() + .destination(logoutServerSamlUrl == null ? null : logoutServerSamlUrl.toString()) + .issuer(issuer) + .sessionIndex(sessionIndex()) + .nameId(nameId()); + + String documentAsString = DocumentUtil.getDocumentAsString(builder.buildDocument()); + String transformed = getTransformer().transform(documentAsString); + + if (transformed == null) { + return null; + } + + return this.signingPrivateKeyPem == null || skipSignature + ? requestBinding.createSamlUnsignedRequest(logoutServerSamlUrl, relayState(), DocumentUtil.getDocument(transformed)) + : requestBinding.createSamlSignedRequest(logoutServerSamlUrl, relayState(), DocumentUtil.getDocument(transformed), signingPrivateKeyPem, signingPublicKeyPem, signingCertificate); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/HandleArtifactStepBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/HandleArtifactStepBuilder.java new file mode 100644 index 000000000000..6c26848e514b --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/HandleArtifactStepBuilder.java @@ -0,0 +1,250 @@ +package org.keycloak.testsuite.util.saml; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.stream.XMLStreamWriter; + +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.common.util.KeyUtils; +import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.protocol.ArtifactResolveType; +import org.keycloak.protocol.saml.SamlProtocol; +import org.keycloak.protocol.saml.SamlService; +import org.keycloak.protocol.saml.profile.util.Soap; +import org.keycloak.saml.BaseSAML2BindingBuilder; +import org.keycloak.saml.SignatureAlgorithm; +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.common.util.StaxUtil; +import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import org.keycloak.saml.processing.core.saml.v2.writers.SAMLRequestWriter; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.NameValuePair; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.w3c.dom.Document; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * This builder allows the SamlClient to handle a redirect or a POSTed form which contains an artifact (SAMLart) + */ +public class HandleArtifactStepBuilder extends SamlDocumentStepBuilder implements StepWithCheckers { + + private String signingPrivateKeyPem; + private String signingPublicKeyPem; + private String id = IDGenerator.create("ID_"); + private String issuer; + private final URI authServerSamlUrl; + private boolean verifyRedirect; + private HttpPost replayPostMessage; + private boolean replayPost; + private boolean replayArtifact; + private AtomicReference providedArtifact; + private AtomicReference storeArtifact; + + private Runnable beforeStepChecker; + private Runnable afterStepChecker; + + private final static Pattern artifactPattern = Pattern.compile("NAME=\"SAMLart\" VALUE=\"([A-Za-z0-9+=/]*)\""); + + /** + * Standard constructor + * @param authServerSamlUrl the url of the IdP + * @param issuer the value for the issuer + * @param clientBuilder the current clientBuilder + */ + public HandleArtifactStepBuilder(URI authServerSamlUrl, String issuer, SamlClientBuilder clientBuilder) { + super(clientBuilder); + this.issuer = issuer; + this.authServerSamlUrl = authServerSamlUrl.toString().endsWith(SamlService.ARTIFACT_RESOLUTION_SERVICE_PATH) ? authServerSamlUrl : UriBuilder.fromUri(authServerSamlUrl).path(SamlService.ARTIFACT_RESOLUTION_SERVICE_PATH).build(); + verifyRedirect = false; + } + + /** + * Builder method. Calling this method with the public and private key will ensure that the generated ArifactResolve is signed + * @param signingPrivateKeyPem the pem containing the client's private key + * @param signingPublicKeyPem the pem containing the client's public key + * @return this HandleArtifactStepBuilder + */ + public HandleArtifactStepBuilder signWith(String signingPrivateKeyPem, String signingPublicKeyPem) { + this.signingPrivateKeyPem = signingPrivateKeyPem; + this.signingPublicKeyPem = signingPublicKeyPem; + return this; + } + + public HandleArtifactStepBuilder issuer(String issuer) { + this.issuer = issuer; + return this; + } + + public HandleArtifactStepBuilder setBeforeStepChecks(Runnable checker) { + this.beforeStepChecker = checker; + return this; + } + + public HandleArtifactStepBuilder setAfterStepChecks(Runnable checker) { + this.afterStepChecker = checker; + return this; + } + + /** + * Builder method. Calling this method with "true" will add an assertion to verify that the returned method was a redirect + * @param verifyRedirect set true to verify redirect + * @return this HandleArtifactStepBuilder + */ + public HandleArtifactStepBuilder verifyRedirect(boolean verifyRedirect) { + this.verifyRedirect = verifyRedirect; + return this; + } + + /** + * Builder method. Call this method with "true" to make sure that the second time "perform" is called, it is a replay of the first time. + * This is specifically to test that the artifact is consumed on the IdP side once called. + * @param mustReplayPost set true to replay on the second call + * @return this HandleArtifactStepBuilder + */ + public HandleArtifactStepBuilder replayPost(boolean mustReplayPost) { + this.replayPost = mustReplayPost; + return this; + } + + public HandleArtifactStepBuilder storeArtifact(AtomicReference storeArtifact) { + this.storeArtifact = storeArtifact; + return this; + } + + public HandleArtifactStepBuilder useArtifact(AtomicReference artifact) { + this.providedArtifact = artifact; + return this; + } + + /** + * Builder method. Calling this method will set the ArtifactResolve from the standard generated to a specific id + * @param id the value to which to set the ArtifactResolve's id + * @return this HandleArtifactStepBuilder + */ + public HandleArtifactStepBuilder setArtifactResolveId(String id){ + this.id = id; + return this; + } + + /** + * Main method. Can read a response with an artifact (redirect or post) and return a POSTed SOAP message containing + * the ArtifactResolve message. The behaviour changes depending on what builder methods were called. + * + * @param client The current http client + * @param currentURI the current uri + * @param currentResponse the current response from the IdP + * @param context the current http context + * @return a POSTed SOAP message containing the ArtifactResolve message + * @throws Exception + */ + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + + if (replayPost && replayPostMessage != null) { + return replayPostMessage; + } + + ArtifactResolveType artifactResolve = new ArtifactResolveType(id, + XMLTimeUtil.getIssueInstant()); + NameIDType nameIDType = new NameIDType(); + nameIDType.setValue(issuer); + artifactResolve.setIssuer(nameIDType); + String artifact = getArtifactFromResponse(currentResponse); + if (storeArtifact != null) storeArtifact.set(artifact); + artifactResolve.setArtifact(artifact); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + XMLStreamWriter xmlStreamWriter = StaxUtil.getXMLStreamWriter(bos); + new SAMLRequestWriter(xmlStreamWriter).write(artifactResolve); + Document doc = DocumentUtil.getDocument(new ByteArrayInputStream(bos.toByteArray())); + + BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder(); + + if (signingPrivateKeyPem != null && signingPublicKeyPem != null) { + PrivateKey privateKey = org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(signingPrivateKeyPem); + PublicKey publicKey = org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(signingPublicKeyPem); + binding + .signatureAlgorithm(SignatureAlgorithm.RSA_SHA256) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey) + .signDocument(doc); + } + + String documentAsString = DocumentUtil.getDocumentAsString(doc); + String transformed = getTransformer().transform(documentAsString); + + if (transformed == null) return null; + + if (beforeStepChecker != null && beforeStepChecker instanceof SessionStateChecker) { + SessionStateChecker sessionStateChecker = (SessionStateChecker) beforeStepChecker; + sessionStateChecker.setUserSessionProvider(session -> session.singleUseObjects().get(artifact).get(SamlProtocol.USER_SESSION_ID)); + sessionStateChecker.setClientSessionProvider(session -> session.singleUseObjects().get(artifact).get(SamlProtocol.CLIENT_SESSION_ID)); + } + + HttpPost post = Soap.createMessage().addToBody(DocumentUtil.getDocument(transformed)).buildHttpPost(authServerSamlUrl); + replayPostMessage = post; + return post; + } + + /** + * Extracts the artifact from a response. Can handle both a Redirect and a POSTed form + * @param currentResponse the response containing the artifact + * @return the artifact + * @throws IOException thrown if there'a a problem processing the response. + */ + private String getArtifactFromResponse(CloseableHttpResponse currentResponse) throws IOException { + + if (providedArtifact != null) { + return providedArtifact.get(); + } + + if (currentResponse.getFirstHeader("location") != null) { + String location = currentResponse.getFirstHeader("location").getValue(); + List params = URLEncodedUtils.parse(URI.create(location), StandardCharsets.UTF_8); + for (NameValuePair param : params) { + if (GeneralConstants.SAML_ARTIFACT_KEY.equals(param.getName())) { + String artifact = param.getValue(); + if (artifact != null && !artifact.isEmpty()) { + return artifact; + } + } + } + } + assertFalse(verifyRedirect); + String form = EntityUtils.toString(currentResponse.getEntity()); + + Matcher m = artifactPattern.matcher(form); + assertTrue(m.find(), "Can't find artifact in " + form); + return m.group(1); + } + + @Override + public Runnable getBeforeStepChecker() { + return beforeStepChecker; + } + + @Override + public Runnable getAfterStepChecker() { + return afterStepChecker; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/IdPInitiatedLoginBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/IdPInitiatedLoginBuilder.java new file mode 100644 index 000000000000..cbd1585954c7 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/IdPInitiatedLoginBuilder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.util.function.Supplier; + +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; + + +/** + * + * @author hmlnarik + */ +public class IdPInitiatedLoginBuilder implements Step { + + private final SamlClientBuilder clientBuilder; + private final URI authServerSamlUrl; + private final String clientId; + private Supplier relayState; + + public IdPInitiatedLoginBuilder(URI authServerSamlUrl, String clientId, SamlClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + this.authServerSamlUrl = authServerSamlUrl; + this.clientId = clientId; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + return new HttpGet(authServerSamlUrl.toString() + "/clients/" + this.clientId + getRelayStateQueryParamString()); + } + + private String getRelayStateQueryParamString(){ + if (relayState == null) return ""; + return "?" + GeneralConstants.RELAY_STATE + "=" + relayState.get(); + } + + public IdPInitiatedLoginBuilder relayState(String relayState) { + this.relayState = () -> relayState; + return this; + } + + public IdPInitiatedLoginBuilder relayState(Supplier relayState) { + this.relayState = relayState; + return this; + } + + public SamlClientBuilder build() { + return this.clientBuilder; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java new file mode 100644 index 000000000000..5930731a521a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java @@ -0,0 +1,174 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.admin.Users; +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.hamcrest.Matchers; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; + +import static org.keycloak.testsuite.admin.Users.getPasswordOf; +import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +/** + * + * @author hmlnarik + */ +public class LoginBuilder implements Step { + + private final SamlClientBuilder clientBuilder; + private UserRepresentation user; + private boolean sso = false; + private String idpAlias; + + public LoginBuilder(SamlClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + if (sso) { + return null; // skip this step + } else { + assertThat(currentResponse, statusCodeIsHC(Response.Status.OK)); + String loginPageText = EntityUtils.toString(currentResponse.getEntity(), StandardCharsets.UTF_8); + assertThat(loginPageText, containsString("login")); + + return handleLoginPage(loginPageText, currentURI); + } + } + + public SamlClientBuilder build() { + return this.clientBuilder; + } + + public LoginBuilder user(UserRepresentation user) { + this.user = user; + return this; + } + + public LoginBuilder user(String userName, String password) { + this.user = new UserRepresentation(); + this.user.setUsername(userName); + Users.setPasswordFor(user, password); + return this; + } + + public LoginBuilder sso(boolean sso) { + this.sso = sso; + return this; + } + + /** + * When the step is executed and {@code idpAlias} is not {@code null}, it attempts to find and follow the link to + * identity provider with the given alias. + * @param idpAlias + * @return + */ + public LoginBuilder idp(String idpAlias) { + this.idpAlias = idpAlias; + return this; + } + + /** + * Prepares a GET/POST request for logging the given user into the given login page. The login page is expected + * to have at least input fields with id "username" and "password". + * + * @param user + * @param loginPage + * @return + */ + private HttpUriRequest handleLoginPage(String loginPage, URI currentURI) { + if (idpAlias != null) { + org.jsoup.nodes.Document theLoginPage = Jsoup.parse(loginPage); + Element socialLink = theLoginPage.getElementById("social-" + this.idpAlias); + assertThat("Unknown idp: " + this.idpAlias, socialLink, Matchers.notNullValue()); + final String link = socialLink.attr("href"); + assertThat("Invalid idp link: " + this.idpAlias, link, Matchers.notNullValue()); + return new HttpGet(currentURI.resolve(link)); + } + + return handleLoginPage(user, loginPage); + } + + public static HttpUriRequest handleLoginPage(UserRepresentation user, String loginPage) { + String username = user.getUsername(); + String password = getPasswordOf(user); + org.jsoup.nodes.Document theLoginPage = Jsoup.parse(loginPage); + + List parameters = new LinkedList<>(); + for (Element form : theLoginPage.getElementsByTag("form")) { + String method = form.attr("method"); + String action = form.attr("action"); + boolean isPost = method != null && "post".equalsIgnoreCase(method); + + for (Element input : form.getElementsByTag("input")) { + if (Objects.equals(input.id(), "username")) { + parameters.add(new BasicNameValuePair(input.attr("name"), username)); + } else if (Objects.equals(input.id(), "password")) { + parameters.add(new BasicNameValuePair(input.attr("name"), password)); + } else { + parameters.add(new BasicNameValuePair(input.attr("name"), input.val())); + } + } + + if (isPost) { + HttpPost res = new HttpPost(action); + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8); + res.setEntity(formEntity); + + return res; + } else { + UriBuilder b = UriBuilder.fromPath(action); + for (NameValuePair parameter : parameters) { + b.queryParam(parameter.getName(), parameter.getValue()); + } + return new HttpGet(b.build()); + } + } + + throw new IllegalArgumentException("Invalid login form: " + loginPage); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java new file mode 100644 index 000000000000..3b69d7425ffc --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java @@ -0,0 +1,295 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +import jakarta.ws.rs.core.Response.Status; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.saml.processing.web.util.PostBindingUtil; +import org.keycloak.saml.processing.web.util.RedirectBindingUtil; +import org.keycloak.testsuite.util.SamlClient.Binding; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.commons.io.IOUtils; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.hamcrest.Matchers; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + + +public class ModifySamlResponseStepBuilder extends SamlDocumentStepBuilder { + + private final Binding binding; + + private URI targetUri; + private String targetAttribute; + private Binding targetBinding; + private Supplier documentSupplier; + + public ModifySamlResponseStepBuilder(Binding binding, SamlClientBuilder clientBuilder) { + super(clientBuilder); + this.binding = binding; + this.targetBinding = binding; + } + + // TODO: support for signing + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + switch (binding) { + case REDIRECT: + return handleRedirectBinding(currentResponse); + + case POST: + return handlePostBinding(currentResponse); + + case ARTIFACT_RESPONSE: + return handleArtifactResponse(currentResponse); + } + + throw new RuntimeException("Unknown binding for " + ModifySamlResponseStepBuilder.class.getName()); + } + + public Supplier documentSupplier() { + return documentSupplier; + } + + public ModifySamlResponseStepBuilder documentSupplier(Supplier documentSupplier) { + this.documentSupplier = documentSupplier; + return this; + } + + public Binding targetBinding() { + return targetBinding; + } + + public ModifySamlResponseStepBuilder targetBinding(Binding targetBinding) { + this.targetBinding = targetBinding; + return this; + } + + public String targetAttribute() { + return targetAttribute; + } + + public ModifySamlResponseStepBuilder targetAttribute(String attribute) { + targetAttribute = attribute; + return this; + } + + public ModifySamlResponseStepBuilder targetAttributeSamlRequest() { + return targetAttribute(GeneralConstants.SAML_REQUEST_KEY); + } + + public ModifySamlResponseStepBuilder targetAttributeSamlResponse() { + return targetAttribute(GeneralConstants.SAML_RESPONSE_KEY); + } + + public ModifySamlResponseStepBuilder targetAttributeSamlArtifact() { + return targetAttribute(GeneralConstants.SAML_ARTIFACT_KEY); + } + + public URI targetUri() { + return targetUri; + } + + public ModifySamlResponseStepBuilder targetUri(URI forceUri) { + this.targetUri = forceUri; + return this; + } + + private HttpUriRequest handleArtifactResponse(CloseableHttpResponse currentResponse) throws Exception { + SAMLDocumentHolder samlDocumentHolder = null; + try { + samlDocumentHolder = Binding.ARTIFACT_RESPONSE.extractResponse(currentResponse); + } catch (IOException e) { + e.printStackTrace(); + } + + + return createRequest(this.targetUri, this.targetAttribute, DocumentUtil.asString(samlDocumentHolder.getSamlDocument()), new LinkedList<>()); + } + + protected HttpUriRequest handleRedirectBinding(CloseableHttpResponse currentResponse) throws Exception, IOException, URISyntaxException { + String samlDoc; + final String attrName; + final URI uri; + final List params; + + if (documentSupplier != null) { + Objects.requireNonNull(this.targetUri, "Set targetUri"); + Objects.requireNonNull(this.targetAttribute, "Set targetAttribute"); + + samlDoc = documentSupplier.get(); + uri = this.targetUri; + attrName = this.targetAttribute; + params = new LinkedList<>(); + } else { + NameValuePair samlParam = null; + + assertThat(currentResponse, statusCodeIsHC(Status.FOUND)); + String location = currentResponse.getFirstHeader("Location").getValue(); + URI locationUri = URI.create(location); + + params = URLEncodedUtils.parse(locationUri, StandardCharsets.UTF_8); + for (Iterator it = params.iterator(); it.hasNext();) { + NameValuePair param = it.next(); + if ("SAMLResponse".equals(param.getName()) || "SAMLRequest".equals(param.getName())) { + assertThat("Only one SAMLRequest/SAMLResponse check", samlParam, nullValue()); + samlParam = param; + it.remove(); + } + } + + assertThat(samlParam, notNullValue()); + + String base64EncodedSamlDoc = samlParam.getValue(); + InputStream decoded = RedirectBindingUtil.base64DeflateDecode(base64EncodedSamlDoc); + samlDoc = IOUtils.toString(decoded, GeneralConstants.SAML_CHARSET); + IOUtils.closeQuietly(decoded); + + uri = this.targetUri != null + ? this.targetUri + : locationUri; + attrName = this.targetAttribute != null ? this.targetAttribute : samlParam.getName(); + } + + return createRequest(uri, attrName, samlDoc, params); + } + + private HttpUriRequest handlePostBinding(CloseableHttpResponse currentResponse) throws Exception { + String samlDoc; + final String attrName; + final URI uri; + final List params = new LinkedList<>(); + + if (documentSupplier != null) { + Objects.requireNonNull(this.targetUri, "Set targetUri"); + Objects.requireNonNull(this.targetAttribute, "Set targetAttribute"); + + samlDoc = documentSupplier.get(); + uri = this.targetUri; + attrName = this.targetAttribute; + } else { + assertThat(currentResponse, statusCodeIsHC(Status.OK)); + + final String htmlBody = EntityUtils.toString(currentResponse.getEntity()); + assertThat(htmlBody, Matchers.containsString("SAML")); + org.jsoup.nodes.Document theResponsePage = Jsoup.parse(htmlBody); + Elements samlResponses = theResponsePage.select("input[name=SAMLResponse]"); + Elements samlRequests = theResponsePage.select("input[name=SAMLRequest]"); + Elements forms = theResponsePage.select("form"); + Elements relayStates = theResponsePage.select("input[name=RelayState]"); + int size = samlResponses.size() + samlRequests.size(); + assertThat("Checking uniqueness of SAMLResponse/SAMLRequest input field in the page", size, is(1)); + assertThat("Checking uniqueness of forms in the page", forms, hasSize(1)); + + Element respElement = samlResponses.isEmpty() ? samlRequests.first() : samlResponses.first(); + Element form = forms.first(); + + String base64EncodedSamlDoc = respElement.val(); + InputStream decoded = PostBindingUtil.base64DecodeAsStream(base64EncodedSamlDoc); + samlDoc = IOUtils.toString(decoded, GeneralConstants.SAML_CHARSET); + IOUtils.closeQuietly(decoded); + + attrName = this.targetAttribute != null + ? this.targetAttribute + : respElement.attr("name"); + + if (! relayStates.isEmpty()) { + params.add(new BasicNameValuePair(GeneralConstants.RELAY_STATE, relayStates.first().val())); + } + uri = this.targetUri != null + ? this.targetUri + : URI.create(form.attr("action")); + } + + return createRequest(uri, attrName, samlDoc, params); + } + + protected HttpUriRequest createRequest(URI locationUri, String attributeName, String samlDoc, List parameters) throws Exception { + String transformed = getTransformer().transform(samlDoc); + if (transformed == null) { + return null; + } + + switch (this.targetBinding) { + case POST: + return createPostRequest(locationUri, attributeName, transformed, parameters); + case REDIRECT: + return createRedirectRequest(locationUri, attributeName, transformed, parameters); + } + throw new RuntimeException("Unknown target binding for " + ModifySamlResponseStepBuilder.class.getName()); + } + + protected HttpUriRequest createRedirectRequest(URI locationUri, String attributeName, String transformed, List parameters) throws IOException, URISyntaxException { + final byte[] responseBytes = transformed.getBytes(GeneralConstants.SAML_CHARSET); + parameters.add(new BasicNameValuePair(attributeName, RedirectBindingUtil.deflateBase64Encode(responseBytes))); + + if (this.targetUri != null) { + locationUri = this.targetUri; + } + + URI target = new URIBuilder(locationUri).setParameters(parameters).build(); + + return new HttpGet(target); + } + + protected HttpUriRequest createPostRequest(URI locationUri, String attributeName, String transformed, List parameters) throws IOException { + HttpPost post = new HttpPost(locationUri); + + parameters.add(new BasicNameValuePair(attributeName, PostBindingUtil.base64Encode(transformed))); + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, GeneralConstants.SAML_CHARSET); + post.setEntity(formEntity); + + return post; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/OtpLoginBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/OtpLoginBuilder.java new file mode 100644 index 000000000000..06874686364e --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/OtpLoginBuilder.java @@ -0,0 +1,94 @@ +/* + * Copyright 2025 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +import jakarta.ws.rs.core.Response; + +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; + +import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; + +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * + * @author rmartinc + */ +public class OtpLoginBuilder implements Step { + + private final SamlClientBuilder clientBuilder; + private String otpPassword; + + public OtpLoginBuilder(SamlClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + } + + public SamlClientBuilder build() { + return this.clientBuilder; + } + + public OtpLoginBuilder otp(String otpPassword) { + this.otpPassword = otpPassword; + return this; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + assertThat(currentResponse, statusCodeIsHC(Response.Status.OK)); + String otpPageText = EntityUtils.toString(currentResponse.getEntity(), StandardCharsets.UTF_8); + return handleOtpLoginPage(otpPageText, otpPassword); + } + + public static HttpUriRequest handleOtpLoginPage(String loginPage, String otpPassword) { + org.jsoup.nodes.Document page = Jsoup.parse(loginPage); + Element form = page.getElementById("kc-otp-login-form"); + if (form == null) { + throw new IllegalArgumentException("Invalid OTP login form: " + loginPage); + } + + String action = form.attr("action"); + if (action == null) { + throw new IllegalArgumentException("Invalid OTP login form: " + loginPage); + } + + HttpPost res = new HttpPost(action); + List parameters = new LinkedList<>(); + parameters.add(new BasicNameValuePair("otp", otpPassword)); + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8); + res.setEntity(formEntity); + + return res; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java new file mode 100644 index 000000000000..d08b52cb7384 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java @@ -0,0 +1,127 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; + +import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +/** + * @author hmlnarik + */ +public class RequiredConsentBuilder implements Step { + + private final SamlClientBuilder clientBuilder; + private boolean approveConsent = true; + + public RequiredConsentBuilder(SamlClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + assertThat(currentResponse, statusCodeIsHC(Response.Status.OK)); + String consentPageText = EntityUtils.toString(currentResponse.getEntity(), StandardCharsets.UTF_8); + assertThat(consentPageText, containsString("consent")); + assertThat(consentPageText, containsString("My Roles")); // Corresponding to role_list default SAML client scope + + return handleConsentPage(consentPageText, currentURI); + } + + public SamlClientBuilder build() { + return this.clientBuilder; + } + + public RequiredConsentBuilder approveConsent(boolean shouldApproveConsent) { + this.approveConsent = shouldApproveConsent; + return this; + } + + /** + * Prepares a GET/POST request for consent granting . The consent page is expected + * to have at least input fields with id "kc-login" and "kc-cancel". + * + * @param consentPage + * @param consent + * @return + */ + public HttpUriRequest handleConsentPage(String consentPage, URI currentURI) { + org.jsoup.nodes.Document theLoginPage = Jsoup.parse(consentPage); + + List parameters = new LinkedList<>(); + for (Element form : theLoginPage.getElementsByTag("form")) { + String method = form.attr("method"); + String action = form.attr("action"); + boolean isPost = "post".equalsIgnoreCase(method); + + Element submitButton; + if (approveConsent) { + submitButton = form.getElementById("kc-login"); + } else { + submitButton = form.getElementById("kc-cancel"); + } + parameters.add(new BasicNameValuePair(submitButton.attr("name"), submitButton.attr("value"))); + + for (Element input : form.getElementsByTag("input")) { + parameters.add(new BasicNameValuePair(input.attr("name"), input.val())); + } + + if (isPost) { + HttpPost res = new HttpPost(currentURI.resolve(action)); + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8); + res.setEntity(formEntity); + + return res; + } else { + UriBuilder b = UriBuilder.fromPath(action); + for (NameValuePair parameter : parameters) { + b.queryParam(parameter.getName(), parameter.getValue()); + } + return new HttpGet(b.build()); + } + } + + throw new IllegalArgumentException("Invalid consent page: " + consentPage); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelArtifactResolveReceiver.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelArtifactResolveReceiver.java new file mode 100644 index 000000000000..b4ed1ab25af0 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelArtifactResolveReceiver.java @@ -0,0 +1,153 @@ +package org.keycloak.testsuite.util.saml; + + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.security.PrivateKey; +import java.security.PublicKey; + +import jakarta.ws.rs.core.HttpHeaders; + +import org.keycloak.common.util.KeyUtils; +import org.keycloak.dom.saml.v2.protocol.ArtifactResolveType; +import org.keycloak.dom.saml.v2.protocol.ArtifactResponseType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; +import org.keycloak.protocol.saml.SamlConfigAttributes; +import org.keycloak.protocol.saml.SamlProtocolUtils; +import org.keycloak.protocol.saml.profile.util.Soap; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.saml.SAML2LoginResponseBuilder; +import org.keycloak.saml.SignatureAlgorithm; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.jboss.logging.Logger; +import org.w3c.dom.Document; + +public class SamlBackchannelArtifactResolveReceiver implements AutoCloseable { + + private static final Logger LOG = Logger.getLogger(SamlBackchannelArtifactResolveReceiver.class); + + private final HttpServer server; + private ArtifactResolveType artifactResolve; + private final String url; + private final ClientRepresentation samlClient; + private final PublicKey publicKey; + private final PrivateKey privateKey; + private String inResponseTo; + + public SamlBackchannelArtifactResolveReceiver(int port, ClientRepresentation samlClient, String publicKeyStr, String privateKeyStr) { + this.samlClient = samlClient; + publicKey = publicKeyStr == null ? null : org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(publicKeyStr); + privateKey = privateKeyStr == null ? null : org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(privateKeyStr); + try { + InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("localhost"), port); + server = HttpServer.create(address, 0); + this.url = "http://" + address.getHostString() + ":" + port; + } catch (IOException e) { + throw new RuntimeException("Cannot create http server", e); + } + + server.createContext("/", new SamlBackchannelArtifactResolveHandler()); + server.setExecutor(null); + server.start(); + } + + public SamlBackchannelArtifactResolveReceiver(int port, ClientRepresentation samlClient) { + this(port, samlClient, null, null); + } + + public String getUrl() { + return url; + } + + public boolean isArtifactResolveReceived() { + return artifactResolve != null; + } + + public ArtifactResolveType getArtifactResolve() { + return artifactResolve; + } + + public void setInResponseTo(String inResponseTo) { + this.inResponseTo = inResponseTo; + } + + @Override + public void close() throws Exception { + server.stop(0); + } + + private class SamlBackchannelArtifactResolveHandler implements HttpHandler { + public void handle(HttpExchange t) throws IOException { + try { + t.getResponseHeaders().add(HttpHeaders.CONTENT_TYPE, "text/xml"); + t.sendResponseHeaders(200, 0); + + Document request = Soap.extractSoapMessage(t.getRequestBody()); + LOG.infof("Received ArtifactResolve: %s", DocumentUtil.asString(request)); + + SAMLDocumentHolder samlDoc = SAML2Response.getSAML2ObjectFromDocument(request); + if (!(samlDoc.getSamlObject() instanceof ArtifactResolveType)) { + throw new RuntimeException("SamlBackchannelArtifactResolveReceiver received a message that was not ArtifactResolveType"); + } + artifactResolve = (ArtifactResolveType) samlDoc.getSamlObject(); + + // create the login response + SAML2LoginResponseBuilder loginResponseBuilder = new SAML2LoginResponseBuilder(); + ResponseType loginResponse = loginResponseBuilder + .issuer(samlClient.getClientId()) + .requestIssuer(artifactResolve.getIssuer().getValue()) + .requestID(inResponseTo) + .assertionExpiration(60) + .subjectExpiration(60) + .sessionExpiration(60) + .buildModel(); + + Document loginResponseBuilderAsDoc = loginResponseBuilder.buildDocument(loginResponse); + + // bundle the login response in the Artifact Response + ArtifactResponseType artifactResponse = SamlProtocolUtils.buildArtifactResponse(loginResponseBuilderAsDoc); + artifactResponse.setInResponseTo(artifactResolve.getID()); + JaxrsSAML2BindingBuilder soapBinding = new JaxrsSAML2BindingBuilder(null); + if (requiresClientSignature(samlClient)) { + soapBinding.signatureAlgorithm(getSignatureAlgorithm(samlClient)) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey, null) + .signDocument(); + } + Document artifactResponseAsDoc = SAML2Response.convert(artifactResponse); + Document soapDoc = soapBinding.soapBinding(artifactResponseAsDoc).getDocument(); + + LOG.infof("Sending ArtifactResponse: %s", DocumentUtil.asString(soapDoc)); + + // send login response + OutputStream os = t.getResponseBody(); + os.write(Soap.createMessage().addToBody(soapDoc).getBytes()); + os.close(); + } catch (Exception ex) { + t.sendResponseHeaders(500, 0); + } + } + } + + private SignatureAlgorithm getSignatureAlgorithm(ClientRepresentation client) { + String alg = client.getAttributes().get(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); + if (alg != null) { + SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg); + if (algorithm != null) + return algorithm; + } + return SignatureAlgorithm.RSA_SHA256; + } + + public boolean requiresClientSignature(ClientRepresentation client) { + return "true".equals(client.getAttributes().get(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelLogoutReceiver.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelLogoutReceiver.java new file mode 100644 index 000000000000..3c82458500d8 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlBackchannelLogoutReceiver.java @@ -0,0 +1,124 @@ +package org.keycloak.testsuite.util.saml; + + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.security.PrivateKey; +import java.security.PublicKey; + +import jakarta.ws.rs.core.HttpHeaders; + +import org.keycloak.common.util.KeyUtils; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; +import org.keycloak.protocol.saml.SamlConfigAttributes; +import org.keycloak.protocol.saml.profile.util.Soap; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.saml.SAML2LogoutResponseBuilder; +import org.keycloak.saml.SignatureAlgorithm; +import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.w3c.dom.Document; + +public class SamlBackchannelLogoutReceiver implements AutoCloseable { + + private final HttpServer server; + private LogoutRequestType logoutRequest; + private final String url; + private final ClientRepresentation samlClient; + private final PublicKey publicKey; + private final PrivateKey privateKey; + + public SamlBackchannelLogoutReceiver(int port, ClientRepresentation samlClient, String publicKeyStr, String privateKeyStr) { + this.samlClient = samlClient; + publicKey = publicKeyStr == null ? null : org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(publicKeyStr); + privateKey = privateKeyStr == null ? null : org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(privateKeyStr); + try { + InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("localhost"), port); + server = HttpServer.create(address, 0); + this.url = "http://" + address.getHostString() + ":" + port; + } catch (IOException e) { + throw new RuntimeException("Cannot create http server", e); + } + + server.createContext("/", new SamlBackchannelLogoutReceiver.SamlBackchannelLogoutHandler()); + server.setExecutor(null); + server.start(); + } + + public SamlBackchannelLogoutReceiver(int port, ClientRepresentation samlClient) { + this(port, samlClient, null, null); + } + + public String getUrl() { + return url; + } + + public boolean isLogoutRequestReceived() { + return logoutRequest != null; + } + + public LogoutRequestType getLogoutRequest() { + return logoutRequest; + } + + @Override + public void close() throws Exception { + server.stop(0); + } + + private class SamlBackchannelLogoutHandler implements HttpHandler { + public void handle(HttpExchange t) throws IOException { + try { + t.getResponseHeaders().add(HttpHeaders.CONTENT_TYPE, "text/xml"); + t.sendResponseHeaders(200, 0); + + Document request = Soap.extractSoapMessage(t.getRequestBody()); + SAMLDocumentHolder samlDoc = SAML2Response.getSAML2ObjectFromDocument(request); + if (!(samlDoc.getSamlObject() instanceof LogoutRequestType)) { + throw new RuntimeException("SamlBackchannelLogoutReceiver received a message that was not LogoutRequestType"); + } + logoutRequest = (LogoutRequestType) samlDoc.getSamlObject(); + StatusResponseType logoutResponse = new SAML2LogoutResponseBuilder() + .issuer(samlClient.getClientId()) + .logoutRequestID(logoutRequest.getID()) + .buildModel(); + JaxrsSAML2BindingBuilder soapBinding = new JaxrsSAML2BindingBuilder(null); + if (requiresClientSignature(samlClient)) { + soapBinding.signatureAlgorithm(getSignatureAlgorithm(samlClient)) + .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey, null) + .signDocument(); + } + Document doc = soapBinding.soapBinding(SAML2Response.convert(logoutResponse)).getDocument(); + + // send logout response + OutputStream os = t.getResponseBody(); + os.write(Soap.createMessage().addToBody(doc).getBytes()); + os.close(); + } catch (Exception ex) { + t.sendResponseHeaders(500, 0); + } + } + } + + private SignatureAlgorithm getSignatureAlgorithm(ClientRepresentation client) { + String alg = client.getAttributes().get(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); + if (alg != null) { + SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg); + if (algorithm != null) + return algorithm; + } + return SignatureAlgorithm.RSA_SHA256; + } + + public boolean requiresClientSignature(ClientRepresentation client) { + return "true".equals(client.getAttributes().get(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java new file mode 100644 index 000000000000..03ca0feee2ed --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java @@ -0,0 +1,197 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.function.Consumer; +import javax.xml.stream.XMLStreamWriter; + +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.ArtifactResolveType; +import org.keycloak.dom.saml.v2.protocol.ArtifactResponseType; +import org.keycloak.dom.saml.v2.protocol.AttributeQueryType; +import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.common.util.StaxUtil; +import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLProtocolQNames; +import org.keycloak.saml.processing.core.saml.v2.writers.SAMLRequestWriter; +import org.keycloak.saml.processing.core.saml.v2.writers.SAMLResponseWriter; +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.jboss.logging.Logger; +import org.junit.jupiter.api.Assertions; +import org.w3c.dom.Document; + +/** + * + * @author hmlnarik + */ +public abstract class SamlDocumentStepBuilder> implements Step { + + private static final Logger LOG = Logger.getLogger(SamlDocumentStepBuilder.class); + + @FunctionalInterface + public interface Saml2ObjectTransformer { + public T transform(T original) throws Exception; + } + + @FunctionalInterface + public interface Saml2DocumentTransformer { + public Document transform(Document original) throws Exception; + } + + @FunctionalInterface + public interface StringTransformer { + public String transform(String original) throws Exception; + } + + private final SamlClientBuilder clientBuilder; + + private StringTransformer transformer = t -> t; + + public SamlDocumentStepBuilder(SamlClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + } + + public This transformObject(Consumer tr) { + return transformObject(so -> { + tr.accept(so); + return so; + }); + } + + @SuppressWarnings("unchecked") + public This transformObject(Saml2ObjectTransformer tr) { + final StringTransformer original = this.transformer; + this.transformer = s -> { + final String originalTransformed = original.transform(s); + + if (originalTransformed == null) { + return null; + } + + final ByteArrayInputStream baos = new ByteArrayInputStream(originalTransformed.getBytes()); + final T saml2Object = (T) new SAML2Response().getSAML2ObjectFromStream(baos); + final T transformed = tr.transform(saml2Object); + + if (transformed == null) { + return null; + } + + String res = saml2Object2String(transformed); + LOG.debugf(" ---> %s", res); + return res; + }; + return (This) this; + } + + public static String saml2Object2String(final SAML2Object transformed) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + XMLStreamWriter xmlStreamWriter = StaxUtil.getXMLStreamWriter(bos); + + if (transformed instanceof AuthnRequestType) { + new SAMLRequestWriter(xmlStreamWriter).write((AuthnRequestType) transformed); + } else if (transformed instanceof LogoutRequestType) { + new SAMLRequestWriter(xmlStreamWriter).write((LogoutRequestType) transformed); + } else if (transformed instanceof ArtifactResolveType) { + new SAMLRequestWriter(xmlStreamWriter).write((ArtifactResolveType) transformed); + } else if (transformed instanceof AttributeQueryType) { + new SAMLRequestWriter(xmlStreamWriter).write((AttributeQueryType) transformed); + } else if (transformed instanceof ResponseType) { + new SAMLResponseWriter(xmlStreamWriter).write((ResponseType) transformed); + } else if (transformed instanceof ArtifactResponseType) { + new SAMLResponseWriter(xmlStreamWriter).write((ArtifactResponseType) transformed); + } else if (transformed instanceof StatusResponseType) { + new SAMLResponseWriter(xmlStreamWriter).write((StatusResponseType) transformed, SAMLProtocolQNames.LOGOUT_RESPONSE.getQName("samlp")); + } else { + Assertions.assertNotNull(transformed, "Unknown type: "); + Assertions.fail("Unknown type: " + transformed.getClass().getName()); + } + return new String(bos.toByteArray(), GeneralConstants.SAML_CHARSET); + } catch (ProcessingException ex) { + throw new RuntimeException(ex); + } + } + + public This transformDocument(Consumer tr) { + return transformDocument(so -> { + tr.accept(so); + return so; + }); + } + + @SuppressWarnings("unchecked") + public This transformDocument(Saml2DocumentTransformer tr) { + final StringTransformer original = this.transformer; + this.transformer = s -> { + final String originalTransformed = original.transform(s); + + if (originalTransformed == null) { + return null; + } + + final Document transformed = tr.transform(DocumentUtil.getDocument(originalTransformed)); + return transformed == null ? null : DocumentUtil.getDocumentAsString(transformed); + }; + return (This) this; + } + + public This transformString(Consumer tr) { + return transformString(s -> { + tr.accept(s); + return s; + }); + } + + @SuppressWarnings("unchecked") + public This transformString(StringTransformer tr) { + final StringTransformer original = this.transformer; + this.transformer = s -> { + final String originalTransformed = original.transform(s); + + if (originalTransformed == null) { + return null; + } + + return tr.transform(originalTransformed); + }; + return (This) this; + } + + @SuppressWarnings("unchecked") + public This apply(Consumer updaterOfThis) { + updaterOfThis.accept((This) this); + return (This) this; + } + + public SamlClientBuilder build() { + return this.clientBuilder; + } + + public StringTransformer getTransformer() { + return transformer; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlMessageReceiver.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlMessageReceiver.java new file mode 100644 index 000000000000..b3558e555e8a --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SamlMessageReceiver.java @@ -0,0 +1,83 @@ +package org.keycloak.testsuite.util.saml; + + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.keycloak.saml.SAMLRequestParser; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.saml.processing.web.util.RedirectBindingUtil; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.apache.commons.io.IOUtils; + +public class SamlMessageReceiver implements AutoCloseable { + + private final static Pattern SAML_MESSAGE_PATTER = Pattern.compile(".*SAML(?:Response|Request)=([^&]*).*"); + + private final HttpServer server; + private String message; + private final String url; + + public SamlMessageReceiver(int port) { + try { + InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("localhost"), port); + server = HttpServer.create(address, 0); + this.url = "http://" + address.getHostString() + ":" + port ; + } catch (IOException e) { + throw new RuntimeException("Cannot create http server", e); + } + + server.createContext("/", new MyHandler()); + server.setExecutor(null); + server.start(); + } + + public String getUrl() { + return url; + } + + public boolean isMessageReceived() { + return message != null && !message.trim().isEmpty(); + } + + public String getMessageString() { + return message; + } + + public SAMLDocumentHolder getSamlDocumentHolder() { + Matcher m = SAML_MESSAGE_PATTER.matcher(message); + if (m.find()) { + try { + return SAMLRequestParser.parseResponsePostBinding(RedirectBindingUtil.urlDecode(m.group(1))); + } catch (IOException e) { + throw new RuntimeException("Cannot parse response " + m.group(1), e); + } + } + + return null; + } + + @Override + public void close() throws Exception { + server.stop(0); + } + + private class MyHandler implements HttpHandler { + public void handle(HttpExchange t) throws IOException { + t.sendResponseHeaders(200, 0); + + SamlMessageReceiver.this.message = IOUtils.toString(t.getRequestBody(), StandardCharsets.UTF_8.name()); + + OutputStream os = t.getResponseBody(); + os.close(); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SessionStateChecker.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SessionStateChecker.java new file mode 100644 index 000000000000..ceb87339c344 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/SessionStateChecker.java @@ -0,0 +1,173 @@ +package org.keycloak.testsuite.util.saml; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import org.keycloak.models.AuthenticatedClientSessionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.sessions.CommonClientSessionModel; +import org.keycloak.testframework.remote.providers.runonserver.FetchOnServer; +import org.keycloak.testsuite.client.KeycloakTestingClient; + +import org.infinispan.util.function.SerializableConsumer; +import org.infinispan.util.function.SerializableFunction; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +public class SessionStateChecker implements Runnable { + + private String realmName = "demo"; + private AtomicReference userSessionIdStore; + private AtomicReference expectedUserSession; + private String expectedClientSession; + private SerializableConsumer consumeUserSession; + private final Map> consumeClientSession = new HashMap<>(); + + private SerializableFunction userSessionIdProvider; + private SerializableFunction clientSessionIdProvider; + private final KeycloakTestingClient.Server server; + + + public SessionStateChecker(KeycloakTestingClient.Server server) { + this.server = server; + } + + public SessionStateChecker realmName(String realmName) { + this.realmName = realmName; + return this; + } + + public SessionStateChecker setUserSessionProvider(SerializableFunction sessionProvider) { + this.userSessionIdProvider = sessionProvider; + return this; + } + + public SessionStateChecker setClientSessionProvider(SerializableFunction sessionProvider) { + this.clientSessionIdProvider = sessionProvider; + return this; + } + + public SessionStateChecker storeUserSessionId(AtomicReference userSessionIdStore) { + this.userSessionIdStore = userSessionIdStore; + return this; + } + + public SessionStateChecker consumeClientSession(String clientSessionId, SerializableConsumer consumer) { + consumeClientSession.merge(clientSessionId, consumer, (consumer1, consumer2) -> { + return (SerializableConsumer) clientSessionModel -> { + consumer1.accept(clientSessionModel); + consumer2.accept(clientSessionModel); + }; + }); + return this; + } + + public SessionStateChecker consumeUserSession(SerializableConsumer userSessionModelConsumer) { + if (consumeUserSession == null) { + consumeUserSession = userSessionModelConsumer; + } else { + consumeUserSession = mergeConsumers(consumeUserSession, userSessionModelConsumer); + } + + return this; + } + + public SerializableConsumer mergeConsumers(SerializableConsumer consumer1, SerializableConsumer consumer2) { + return userSessionModel -> { + consumer1.accept(userSessionModel); + consumer2.accept(userSessionModel); + }; + } + + public SessionStateChecker expectedAction(String clientId, CommonClientSessionModel.Action action) { + consumeClientSession(clientId, clientSessionModel -> { + if (action == null) { + assertThat(clientSessionModel, notNullValue()); + assertThat(clientSessionModel.getAction(), nullValue()); + return; + } + assertThat(clientSessionModel, notNullValue()); + assertThat(clientSessionModel.getAction(), equalTo(action.name())); + }); + + return this; + } + + public SessionStateChecker expectedState(UserSessionModel.State state) { + consumeUserSession(userSessionModel -> { + assertThat(userSessionModel, notNullValue()); + assertThat(userSessionModel.getState(), equalTo(state)); + }); + + return this; + } + + public SessionStateChecker expectedNumberOfClientSessions(int expectedNumberOfClientSession) { + consumeUserSession(userSession -> assertThat(userSession.getAuthenticatedClientSessions().keySet(), hasSize(expectedNumberOfClientSession))); + return this; + } + + + public SessionStateChecker expectedUserSession(AtomicReference expectedUserSession) { + this.expectedUserSession = expectedUserSession; + return this; + } + + public SessionStateChecker expectedClientSession(String expectedClientSession) { + this.expectedClientSession = expectedClientSession; + return this; + } + + public void run() { + run(server, realmName, userSessionIdStore, expectedUserSession, expectedClientSession, consumeUserSession, consumeClientSession, userSessionIdProvider, clientSessionIdProvider); + } + + public static void run(KeycloakTestingClient.Server server, + String realmName, + AtomicReference userSessionIdStore, + AtomicReference expectedUserSession, + String expectedClientSession, + SerializableConsumer consumeUserSession, + Map> consumeClientSession, + SerializableFunction userSessionIdProvider, + SerializableFunction clientSessionIdProvider) { + if (server == null || userSessionIdProvider == null) + throw new RuntimeException("Wrongly configured session checker"); + + if (userSessionIdStore != null) { + String userSession = server.fetchString((FetchOnServer) userSessionIdProvider::apply); + userSessionIdStore.set(userSession.replace("\"", "")); + } + + server.run(session -> { + String sessionId = userSessionIdProvider.apply(session); + + if (expectedUserSession != null) { + assertThat(sessionId, equalTo(expectedUserSession.get())); + } + + if (expectedClientSession != null) { + String clientSession = clientSessionIdProvider.apply(session); + assertThat(clientSession, equalTo(expectedClientSession)); + } + + RealmModel realm = session.realms().getRealmByName(realmName); + UserSessionModel userSessionModel = session.sessions().getUserSession(realm, sessionId); + if (consumeUserSession != null) consumeUserSession.accept(userSessionModel); + + if (!consumeClientSession.isEmpty()) { + consumeClientSession.forEach((id, consumer) -> consumer.accept(userSessionModel.getAuthenticatedClientSessionByClient(id))); + } + }); + + + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/StepWithCheckers.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/StepWithCheckers.java new file mode 100644 index 000000000000..964be0629f73 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/StepWithCheckers.java @@ -0,0 +1,10 @@ +package org.keycloak.testsuite.util.saml; + +public interface StepWithCheckers { + default Runnable getBeforeStepChecker() { + return null; + } + default Runnable getAfterStepChecker() { + return null; + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java new file mode 100644 index 000000000000..5d3195f852eb --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java @@ -0,0 +1,146 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.util.saml; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.testsuite.util.SamlClient.Step; +import org.keycloak.testsuite.util.SamlClientBuilder; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.jboss.logging.Logger; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; + +import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +/** + * + * @author hmlnarik + */ +public class UpdateProfileBuilder implements Step { + + private final SamlClientBuilder clientBuilder; + private final Map parameters = new HashMap<>(); + + private static final Logger LOG = Logger.getLogger(UpdateProfileBuilder.class); + + public UpdateProfileBuilder(SamlClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + } + + @Override + public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception { + assertThat(currentResponse, statusCodeIsHC(Response.Status.OK)); + String loginPageText = EntityUtils.toString(currentResponse.getEntity(), StandardCharsets.UTF_8); + assertThat(loginPageText, containsString("Update Account Information")); + + return handleUpdateProfile(loginPageText, currentURI); + } + + public SamlClientBuilder build() { + return this.clientBuilder; + } + + public UpdateProfileBuilder param(String paramName, String paramValue) { + if (paramValue != null) { + this.parameters.put(paramName, paramValue); + } else { + this.parameters.remove(paramName); + } + return this; + } + + public UpdateProfileBuilder firstName(String firstName) { + return param("firstName", firstName); + } + + public UpdateProfileBuilder lastName(String lastName) { + return param("lastName", lastName); + } + + public UpdateProfileBuilder username(String username) { + return param("username", username); + } + + public UpdateProfileBuilder email(String email) { + return param("email", email); + } + + public HttpUriRequest handleUpdateProfile(String loginPage, URI currentURI) { + org.jsoup.nodes.Document theUpdateProfilePage = Jsoup.parse(loginPage); + Set unusedParams = new HashSet<>(this.parameters.keySet()); + + List parameters = new LinkedList<>(); + for (Element form : theUpdateProfilePage.getElementsByTag("form")) { + String method = form.attr("method"); + String action = form.attr("action"); + boolean isPost = method != null && "post".equalsIgnoreCase(method); + + for (Element input : form.getElementsByTag("input")) { + if (this.parameters.containsKey(input.attr("name"))) { + parameters.add(new BasicNameValuePair(input.attr("name"), this.parameters.get(input.attr("name")))); + unusedParams.remove(input.attr("name")); + } + } + + if (! unusedParams.isEmpty()) { + LOG.warnf("Unused parameter names at Update Profile page: %s", unusedParams); + } + + if (isPost) { + HttpPost res = new HttpPost(action); + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8); + res.setEntity(formEntity); + + return res; + } else { + UriBuilder b = UriBuilder.fromPath(action); + for (NameValuePair parameter : parameters) { + b.queryParam(parameter.getName(), parameter.getValue()); + } + return new HttpGet(b.build()); + } + } + + throw new IllegalArgumentException("Invalid update profile form: " + loginPage); + } + +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java new file mode 100644 index 000000000000..aff4b2386498 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java @@ -0,0 +1,185 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.utils.arquillian; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; + +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.keycloak.testsuite.utils.io.IOUtil; +import org.keycloak.util.JsonSerialization; + +import org.jboss.logging.Logger; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.Node; +import org.jboss.shrinkwrap.api.asset.Asset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import static org.keycloak.testsuite.util.ServerURLs.getAppServerContextRoot; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; +import static org.keycloak.testsuite.utils.io.IOUtil.modifyDocElementAttribute; +import static org.keycloak.testsuite.utils.io.IOUtil.modifyDocElementValue; + +/** + * + * @author Vlasta Ramik + */ +public class DeploymentArchiveProcessorUtils { + + private static final Logger log = Logger.getLogger(DeploymentArchiveProcessorUtils.class); + + private static final boolean AUTH_SERVER_SSL_REQUIRED = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required")); + private static final boolean APP_SERVER_SSL_REQUIRED = Boolean.parseBoolean(System.getProperty("app.server.ssl.required")); + + private static final String AUTH_SERVER_REPLACED_URL = "http://localhost:8080"; + + public static final String WEBXML_PATH = "/WEB-INF/web.xml"; + public static final String ADAPTER_CONFIG_PATH = "/WEB-INF/keycloak.json"; + public static final String ADAPTER_CONFIG_PATH_JS = "/keycloak.json"; + public static final String SAML_ADAPTER_CONFIG_PATH = "/WEB-INF/keycloak-saml.xml"; + public static final String JBOSS_DEPLOYMENT_XML_PATH = "/WEB-INF/jboss-deployment-structure.xml"; + public static final String SAML_ADAPTER_CONFIG_PATH_TENANT1 = "/WEB-INF/classes/tenant1-keycloak-saml.xml"; + public static final String SAML_ADAPTER_CONFIG_PATH_TENANT2 = "/WEB-INF/classes/tenant2-keycloak-saml.xml"; + public static final String TRUSTSTORE_PASSWORD = "secret"; + public static final Collection SAML_CONFIGS = Arrays.asList(SAML_ADAPTER_CONFIG_PATH, + SAML_ADAPTER_CONFIG_PATH_TENANT1, SAML_ADAPTER_CONFIG_PATH_TENANT2); + + /** + * @return true iff archive's name equals run-on-server-classes.war + */ + public static boolean checkRunOnServerDeployment(Archive archive) { + return archive.getName().equals("run-on-server-classes.war"); + } + + public static void modifyOIDCAdapterConfig(Archive archive, String adapterConfigPath) { + try { + AdapterConfig adapterConfig = IOUtil.loadJson(archive.get(adapterConfigPath) + .getAsset().openStream(), AdapterConfig.class); + + adapterConfig.setAuthServerUrl(getAuthServerUrl()); + + if (APP_SERVER_SSL_REQUIRED) { + adapterConfig.setSslRequired("all"); + } + + if (AUTH_SERVER_SSL_REQUIRED) { + String trustStorePathInDeployment = "keycloak.truststore"; + if (adapterConfigPath.contains("WEB-INF")) { + // This is a Java adapter, we can use classpath + trustStorePathInDeployment = "classpath:keycloak.truststore"; + } + adapterConfig.setTruststore(trustStorePathInDeployment); + adapterConfig.setTruststorePassword(TRUSTSTORE_PASSWORD); + + String truststoreUrl = System.getProperty("dependency.keystore.root", "") + "/keycloak.truststore"; + File truststore = new File(truststoreUrl); + + if (!truststore.exists()) { + truststore = new File(DeploymentArchiveProcessorUtils.class.getResource("/keystore/keycloak.truststore").getFile()); + } + + ((WebArchive) archive).addAsResource(truststore); + + log.debugf("Adding Truststore to the deployment, path %s, password %s, adapter path %s", truststore.getAbsolutePath(), TRUSTSTORE_PASSWORD, trustStorePathInDeployment); + } + + archive.add(new StringAsset(JsonSerialization.writeValueAsPrettyString(adapterConfig)), + adapterConfigPath); + } catch (IOException ex) { + log.error("Cannot serialize adapter config to JSON.", ex); + } + } + + public static void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + Document doc = IOUtil.loadXML(archive.get(adapterConfigPath).getAsset().openStream()); + + modifySAMLDocument(doc); + + archive.add(new StringAsset(IOUtil.documentToString(doc)), adapterConfigPath); + + String truststoreUrl = System.getProperty("dependency.keystore.root", "") + "/keycloak.truststore"; + File truststore = new File(truststoreUrl); + + if (!truststore.exists()) { + truststore = new File(DeploymentArchiveProcessorUtils.class.getResource("/keystore/keycloak.truststore").getFile()); + } + + ((WebArchive) archive).addAsResource(truststore); + } + + public static void modifySAMLDocument(Document doc) { + modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", AUTH_SERVER_REPLACED_URL, getAuthServerContextRoot()); + modifyDocElementAttribute(doc, "SingleLogoutService", "postBindingUrl", AUTH_SERVER_REPLACED_URL, getAuthServerContextRoot()); + modifyDocElementAttribute(doc, "SingleLogoutService", "redirectBindingUrl", AUTH_SERVER_REPLACED_URL, getAuthServerContextRoot()); + + modifyDocElementAttribute(doc, "SingleSignOnService", "assertionConsumerServiceUrl", AUTH_SERVER_REPLACED_URL, getAppServerContextRoot()); + modifyDocElementAttribute(doc, "SP", "logoutPage", AUTH_SERVER_REPLACED_URL, getAppServerContextRoot()); + } + + public static void useJakartaEEServletClass(Archive archive, String adapterConfigPath) { + final String SERVLET_TAG = "servlet"; + final String SERVLET_CLASS_TAG = "servlet-class"; + final String JAVAX_APPLICATION = "javax.ws.rs.core.Application"; + final String JAKARTA_APPLICATION = "jakarta.ws.rs.core.Application"; + + final Asset configAsset = Optional.ofNullable(archive.get(adapterConfigPath)) + .map(Node::getAsset) + .orElseThrow(() -> new IllegalArgumentException(String.format("Cannot find '%s' config path", adapterConfigPath))); + + try (InputStream configStream = configAsset.openStream()) { + final Document doc = IOUtil.loadXML(configStream); + final NodeList servletNodeList = doc.getElementsByTagName(SERVLET_TAG); + + if (servletNodeList.getLength() == 1) { + final int servletClassCount = doc.getElementsByTagName(SERVLET_CLASS_TAG).getLength(); + + if (servletClassCount == 0) { + final Element servletClassElement = doc.createElement(SERVLET_CLASS_TAG); + servletClassElement.setTextContent(JAKARTA_APPLICATION); + servletNodeList.item(0).appendChild(servletClassElement); + log.infof("Appending '%s' tag with Jakarta application class to '%s'\n", SERVLET_CLASS_TAG, archive.getName()); + } else if (servletClassCount == 1) { + modifyDocElementValue(doc, SERVLET_CLASS_TAG, JAVAX_APPLICATION, JAKARTA_APPLICATION); + log.infof("Modifying 'servlet-class' tag to use Jakarta application class in '%s'\n", SERVLET_CLASS_TAG, archive.getName()); + } else { + log.error(String.format("Invalid count of '%s' tags for '%s'\n", SERVLET_CLASS_TAG, archive.getName())); + return; + } + archive.add(new StringAsset(IOUtil.documentToString(doc)), adapterConfigPath); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String getAuthServerUrl() { + String scheme = AUTH_SERVER_SSL_REQUIRED ? "https" : "http"; + String host = System.getProperty("auth.server.host", "localhost"); + String port = AUTH_SERVER_SSL_REQUIRED ? System.getProperty("auth.server.https.port", "8443") : + System.getProperty("auth.server.http.port", "8180"); + + return String.format("%s://%s:%s/auth", scheme, host, port); + } +} diff --git a/tests/base/src/test/java/org/keycloak/testsuite/utils/io/IOUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/utils/io/IOUtil.java new file mode 100644 index 000000000000..7991e39b28b4 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/testsuite/utils/io/IOUtil.java @@ -0,0 +1,351 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.utils.io; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.concurrent.TimeUnit; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +import org.jboss.logging.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * + * @author tkyjovsk + */ +public class IOUtil { + + private static final Logger log = Logger.getLogger(IOUtil.class); + + public static final File PROJECT_BUILD_DIRECTORY = new File(System.getProperty("project.build.directory", "target")); + + public static T loadJson(InputStream is, Class type) { + try { + return JsonSerialization.readValue(is, type); + } catch (IOException e) { + throw new RuntimeException("Failed to load json.", e); + } + } + + public static RealmRepresentation loadRealm(String realmConfig) { + return loadRealm(IOUtil.class.getResourceAsStream(realmConfig)); + } + + public static RealmRepresentation loadRealm(File realmFile) { + try { + return loadRealm(new FileInputStream(realmFile)); + } catch (FileNotFoundException ex) { + throw new IllegalStateException("Test realm file not found: " + realmFile, ex); + } + } + + public static RealmRepresentation loadRealm(InputStream is) { + RealmRepresentation realm = loadJson(is, RealmRepresentation.class); + System.out.println("Loaded realm " + realm.getRealm()); + return realm; + } + + public static Document loadXML(InputStream is) { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + return dBuilder.parse(is); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new RuntimeException(e); + } + } + + public static String documentToString(Document newDoc) { + try { + DOMSource domSource = new DOMSource(newDoc); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + StringWriter sw = new StringWriter(); + StreamResult sr = new StreamResult(sw); + transformer.transform(domSource, sr); + return sw.toString(); + } catch (TransformerException e) { + log.error("Can't transform document to String"); + throw new RuntimeException(e); + } + } + + public static InputStream documentToInputStream(Document doc) { + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Source xmlSource = new DOMSource(doc); + Result outputTarget = new StreamResult(outputStream); + TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget); + return new ByteArrayInputStream(outputStream.toByteArray()); + } catch (TransformerException e) { + log.error("Can't transform document to InputStream"); + throw new RuntimeException(e); + } + } + + /** + * Modifies attribute value according to the given regex (first occurrence) iff + * there are following conditions accomplished: + * + * - exactly one node is found within the document + * - the attribute of the node exists + * - the regex is found in the value of the attribute + * + * Otherwise there is nothing changed. + * + * @param doc + * @param tagName + * @param attributeName + * @param regex + * @param replacement + */ + public static void modifyDocElementAttribute(Document doc, String tagName, String attributeName, String regex, String replacement) { + NodeList nodes = doc.getElementsByTagName(tagName); + if (nodes.getLength() != 1) { + log.warn("Not able or ambiguous to find element: " + tagName); + return; + } + + Node node = nodes.item(0).getAttributes().getNamedItem(attributeName); + if (node == null || node.getTextContent() == null) { + log.warn("Not able to find attribute " + attributeName + " within element: " + tagName); + return; + } + node.setTextContent(node.getTextContent().replaceFirst(regex, replacement)); + } + + public static void removeNodeByAttributeValue(Document doc, String parentTag, String tagName, String attributeName, String value){ + NodeList parentNodes = doc.getElementsByTagName(parentTag); + if (parentNodes.getLength() != 1) { + log.warn("Not able or ambiguous to find element: " + parentTag); + return; + } + + Element parentElement = (Element) parentNodes.item(0); + if (parentElement == null) { + log.warn("Not able to find element: " + parentTag); + return; + } + + NodeList nodes = doc.getElementsByTagName(tagName); + for (int i = 0; i < nodes.getLength(); i++){ + Node node = nodes.item(i).getAttributes().getNamedItem(attributeName); + if (node.getTextContent().equals(value)){ + parentElement.removeChild(nodes.item(i)); + return; + } + } + } + + /** + * Modifies element text value according to the given regex (first occurrence) iff + * there are following conditions accomplished: + * + * - exactly one node is found within the document + * - the regex is found in the text content of the element + * + * Otherwise there is nothing changed. + * + * @param doc + * @param tagName + * @param regex + * @param replacement + */ + public static void modifyDocElementValue(Document doc, String tagName, String regex, String replacement) { + NodeList nodes = doc.getElementsByTagName(tagName); + if (nodes.getLength() != 1) { + log.warn("Not able or ambiguous to find element: " + tagName); + return; + } + + Node node = nodes.item(0); + if (node == null) { + log.warn("Not able to find element: " + tagName); + return; + } + + node.setTextContent(node.getTextContent().replaceFirst(regex, replacement)); + } + + public static void setDocElementAttributeValue(Document doc, String tagName, String attributeName, String value) { + NodeList nodes = doc.getElementsByTagName(tagName); + if (nodes.getLength() != 1) { + log.warn("Not able or ambiguous to find element: " + tagName); + return; + } + + Element node = (Element) nodes.item(0); + if (node == null) { + log.warn("Not able to find element: " + tagName); + return; + } + + node.setAttribute(attributeName, value); + } + + public static void removeElementsFromDoc(Document doc, String parentTag, String removeNode) { + NodeList nodes = doc.getElementsByTagName(parentTag); + if (nodes.getLength() != 1) { + log.warn("Not able or ambiguous to find element: " + parentTag); + return; + } + + Element parentElement = (Element) nodes.item(0); + if (parentElement == null) { + log.warn("Not able to find element: " + parentTag); + return; + } + + NodeList removeNodes = parentElement.getElementsByTagName(removeNode); + if (removeNodes == null) { + log.warn("Not able to find element: " + removeNode + " within node " + parentTag); + return; + } + + for (int i = 0; i < removeNodes.getLength();){ + Element removeElement = (Element) removeNodes.item(i); + if (removeElement == null) { + log.warn("Not able to find element: " + removeNode + " within node " + parentTag); + return; + } + + log.trace("Removing node " + removeNode); + parentElement.removeChild(removeElement); + + } + + } + + public static String getElementTextContent(Document doc, String path) { + String[] pathSegments = path.split("/"); + + Element currentElement = (Element) doc.getElementsByTagName(pathSegments[0]).item(0); + if (currentElement == null) { + log.warn("Not able to find element: " + pathSegments[0] + " in document"); + return null; + } + + for (int i = 1; i < pathSegments.length; i++) { + currentElement = (Element) currentElement.getElementsByTagName(pathSegments[i]).item(0); + + if (currentElement == null) { + log.warn("Not able to find element: " + pathSegments[i] + " in " + pathSegments[i - 1]); + return null; + } + } + + return currentElement.getTextContent(); + } + + public static void appendChildInDocument(Document doc, String parentPath, Element node) { + String[] pathSegments = parentPath.split("/"); + + Element currentElement = (Element) doc.getElementsByTagName(pathSegments[0]).item(0); + if (currentElement == null) { + log.warn("Not able to find element: " + pathSegments[0] + " in document"); + return; + } + + for (int i = 1; i < pathSegments.length; i++) { + currentElement = (Element) currentElement.getElementsByTagName(pathSegments[i]).item(0); + + if (currentElement == null) { + log.warn("Not able to find element: " + pathSegments[i] + " in " + pathSegments[i - 1]); + return; + } + } + + currentElement.appendChild(node); + } + + public static void removeElementFromDoc(Document doc, String path) { + String[] pathSegments = path.split("/"); + + Element currentElement = (Element) doc.getElementsByTagName(pathSegments[0]).item(0); + if (currentElement == null) { + log.warn("Not able to find element: " + pathSegments[0] + " in document"); + return; + } + + for (int i = 1; i < pathSegments.length; i++) { + currentElement = (Element) currentElement.getElementsByTagName(pathSegments[i]).item(0); + + if (currentElement == null) { + log.warn("Not able to find element: " + pathSegments[i] + " in " + pathSegments[i - 1]); + return; + } + } + + currentElement.getParentNode().removeChild(currentElement); + } + + public static void execCommand(String command, File dir) throws IOException, InterruptedException { + Process process = Runtime.getRuntime().exec(command, null, dir); + if (process.waitFor(10, TimeUnit.SECONDS)) { + if (process.exitValue() != 0) { + getOutput("ERROR", process.getErrorStream()); + throw new RuntimeException("Adapter installation failed. Process exitValue: " + + process.exitValue()); + } + getOutput("OUTPUT", process.getInputStream()); + log.debug("process.isAlive(): " + process.isAlive()); + } else { + if (process.isAlive()) { + process.destroyForcibly(); + } + throw new RuntimeException("Timeout after 10 seconds."); + } + } + + public static void getOutput(String type, InputStream is) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder builder = new StringBuilder(); + builder.append("<").append(type).append(">"); + System.out.println(builder); + builder = new StringBuilder(); + while (reader.ready()) { + System.out.println(reader.readLine()); + } + builder.append(""); + System.out.println(builder); + } + +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java new file mode 100644 index 000000000000..360cd72b8fcb --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/ClientIdRequiredJWTClientAuthenticator.java @@ -0,0 +1,46 @@ +package org.keycloak.tests.providers.broker.oidc; + +import java.util.Collections; +import java.util.Set; + +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; + +import org.keycloak.OAuth2Constants; +import org.keycloak.authentication.ClientAuthenticationFlowContext; +import org.keycloak.authentication.authenticators.client.ClientAuthUtil; +import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; + +/** + * A {@link JWTClientAuthenticator} that requires the optional client_id parameter. + */ +public class ClientIdRequiredJWTClientAuthenticator extends JWTClientAuthenticator { + + public static final String PROVIDER_ID = "testsuite-client-id-required"; + + @Override + public void authenticateClient(ClientAuthenticationFlowContext context) { + MultivaluedMap params = context.getHttpRequest().getDecodedFormParameters(); + + String clientId = params.getFirst(OAuth2Constants.CLIENT_ID); + if (clientId == null) { + Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), + "invalid_client", "Missing client_id parameter"); + context.challenge(challengeResponse); + return; + } + + super.authenticateClient(context); + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public Set getProtocolAuthenticatorMethods(String loginProtocol) { + // Do not add as it will affect the well known provider test + return Collections.emptySet(); + } +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProvider.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProvider.java new file mode 100644 index 000000000000..80e97682cb2d --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProvider.java @@ -0,0 +1,22 @@ +package org.keycloak.tests.providers.broker.oidc; + +import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider; +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.models.KeycloakSession; + +public class LegacyIdIdentityProvider extends KeycloakOIDCIdentityProvider { + + public static final String LEGACY_ID = "3.14159265359"; + + public LegacyIdIdentityProvider(KeycloakSession session, OIDCIdentityProviderConfig config) { + super(session, config); + } + + @Override + public BrokeredIdentityContext getFederatedIdentity(String response) { + BrokeredIdentityContext user = super.getFederatedIdentity(response); + user.setLegacyId(LEGACY_ID); + return user; + } +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProviderFactory.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProviderFactory.java new file mode 100644 index 000000000000..fc1bc914247e --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/LegacyIdIdentityProviderFactory.java @@ -0,0 +1,27 @@ +package org.keycloak.tests.providers.broker.oidc; + +import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider; +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.broker.oidc.OIDCIdentityProviderFactory; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; + +public class LegacyIdIdentityProviderFactory extends OIDCIdentityProviderFactory { + + public static final String PROVIDER_ID = "legacy-id-idp"; + + @Override + public String getName() { + return PROVIDER_ID; + } + + @Override + public KeycloakOIDCIdentityProvider create(KeycloakSession session, IdentityProviderModel model) { + return new LegacyIdIdentityProvider(session, new OIDCIdentityProviderConfig(model)); + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java new file mode 100644 index 000000000000..c945b67f1076 --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/broker/oidc/TestKeycloakOidcIdentityProviderFactory.java @@ -0,0 +1,101 @@ +package org.keycloak.tests.providers.broker.oidc; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import jakarta.ws.rs.core.UriBuilder; + +import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider; +import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; +import org.keycloak.broker.oidc.OIDCIdentityProviderFactory; +import org.keycloak.broker.provider.AuthenticationRequest; +import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.sessions.AuthenticationSessionModel; +import org.keycloak.util.JsonSerialization; + +public class TestKeycloakOidcIdentityProviderFactory extends OIDCIdentityProviderFactory { + + public static final String ID = "test-keycloak-oidc"; + public static final String IGNORE_MAX_AGE_PARAM = "ignore-max-age-param"; + public static final String USE_SINGLE_REFRESH_TOKEN = "use-single-refresh-token"; + public static final String PREFERRED_USERNAME = "preferred-username"; + + public static void setIgnoreMaxAgeParam(IdentityProviderRepresentation rep) { + rep.getConfig().put(IGNORE_MAX_AGE_PARAM, Boolean.TRUE.toString()); + } + + @Override + public String getId() { + return ID; + } + + @Override + public KeycloakOIDCIdentityProvider create(KeycloakSession session, IdentityProviderModel model) { + return new KeycloakOIDCIdentityProvider(session, new OIDCIdentityProviderConfig(model)) { + + private static final Set usernames = new HashSet<>(); + + @Override + public BrokeredIdentityContext getFederatedIdentity(String response) { + BrokeredIdentityContext context = super.getFederatedIdentity(response); + String preferredUsername = getPreferredUsername(); + + if (preferredUsername != null) { + context.setUsername(preferredUsername); + } + if (Boolean.valueOf(model.getConfig().get(USE_SINGLE_REFRESH_TOKEN))) { + // refresh token will be available only in the first login. + if (!usernames.add(context.getUsername())) { + try { + AccessTokenResponse tokenResponse = JsonSerialization.readValue(context.getToken(), AccessTokenResponse.class); + tokenResponse.setRefreshToken(null); + context.setToken(JsonSerialization.writeValueAsString(tokenResponse)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + return context; + } + + @Override + protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) { + AuthenticationSessionModel authSession = request.getAuthenticationSession(); + String maxAge = authSession.getClientNote(OIDCLoginProtocol.MAX_AGE_PARAM); + + try { + if (isIgnoreMaxAgeParam()) { + authSession.removeClientNote(OIDCLoginProtocol.MAX_AGE_PARAM); + } + return super.createAuthorizationUrl(request); + } finally { + authSession.setClientNote(OIDCLoginProtocol.MAX_AGE_PARAM, maxAge); + } + } + + private boolean isIgnoreMaxAgeParam() { + return Boolean.parseBoolean(model.getConfig().getOrDefault(IGNORE_MAX_AGE_PARAM, Boolean.FALSE.toString())); + } + + private String getPreferredUsername() { + return model.getConfig().get(PREFERRED_USERNAME); + } + }; + } + + @Override + public OIDCIdentityProviderConfig createConfig() { + return new OIDCIdentityProviderConfig(super.createConfig()) { + @Override + public String getDisplayIconClasses() { + return "my-custom-idp-icon"; + } + }; + } +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java new file mode 100644 index 000000000000..b78b663d3b10 --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProvider.java @@ -0,0 +1,170 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.tests.providers.federation; + +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.keycloak.component.ComponentModel; +import org.keycloak.credential.CredentialInput; +import org.keycloak.credential.CredentialInputUpdater; +import org.keycloak.credential.CredentialInputValidator; +import org.keycloak.credential.CredentialModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.credential.PasswordCredentialModel; +import org.keycloak.storage.StorageId; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageUtil; +import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage; +import org.keycloak.storage.user.UserLookupProvider; + +/** + * Provides one user where everything is stored in user federated storage + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PassThroughFederatedUserStorageProvider implements + UserStorageProvider, + UserLookupProvider, + CredentialInputValidator, + CredentialInputUpdater +{ + + public static final Set CREDENTIAL_TYPES = Collections.singleton(PasswordCredentialModel.TYPE); + public static final String PASSTHROUGH_USERNAME = "passthrough"; + public static final String INITIAL_PASSWORD = "secret"; + private KeycloakSession session; + private ComponentModel component; + + public PassThroughFederatedUserStorageProvider(KeycloakSession session, ComponentModel component) { + this.session = session; + this.component = component; + } + + public Set getSupportedCredentialTypes() { + return CREDENTIAL_TYPES; + } + + @Override + public boolean supportsCredentialType(String credentialType) { + return getSupportedCredentialTypes().contains(credentialType); + } + + @Override + public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { + if (!PasswordCredentialModel.TYPE.equals(credentialType)) return false; + return true; + } + + @Override + public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { + if (input.getType().equals(PasswordCredentialModel.TYPE)) { + if (INITIAL_PASSWORD.equals(input.getChallengeResponse())) { + return true; + } + return UserStorageUtil.userFederatedStorage(session).getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD") + .map(credentialModel -> credentialModel.getSecretData()) + .anyMatch(Predicate.isEqual("{\"value\":\"" + input.getChallengeResponse() + "\"}")); + } + return false; + } + + @Override + public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { + // testing federated credential attributes + if (input.getType().equals(PasswordCredentialModel.TYPE)) { + Optional existing = UserStorageUtil.userFederatedStorage(session) + .getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD") + .findFirst(); + if (existing.isPresent()) { + CredentialModel model = existing.get(); + model.setType("CLEAR_TEXT_PASSWORD"); + model.setSecretData("{\"value\":\"" + input.getChallengeResponse() + "\"}"); + UserStorageUtil.userFederatedStorage(session).updateCredential(realm, user.getId(), model); + } else { + CredentialModel model = new CredentialModel(); + model.setType("CLEAR_TEXT_PASSWORD"); + model.setSecretData("{\"value\":\"" + input.getChallengeResponse() + "\"}"); + UserStorageUtil.userFederatedStorage(session).createCredential(realm, user.getId(), model); + } + return true; + } + return false; + } + + @Override + public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { + UserStorageUtil.userFederatedStorage(session).getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD") + .collect(Collectors.toList()) + .forEach(credModel -> UserStorageUtil.userFederatedStorage(session).removeStoredCredential(realm, user.getId(), credModel.getId())); + } + + @Override + public Stream getDisableableCredentialTypesStream(RealmModel realm, UserModel user) { + return CREDENTIAL_TYPES.stream(); + } + + @Override + public void close() { + + } + + @Override + public UserModel getUserById(RealmModel realm, String id) { + if (!StorageId.externalId(id).equals(PASSTHROUGH_USERNAME)) return null; + return getUserModel(realm); + } + + @Override + public UserModel getUserByUsername(RealmModel realm, String username) { + if (!PASSTHROUGH_USERNAME.equals(username)) return null; + + return getUserModel(realm); + } + + @Override + public UserModel getUserByEmail(RealmModel realm, String email) { + Optional result = UserStorageUtil.userFederatedStorage(session) + .getUsersByUserAttributeStream(realm, AbstractUserAdapterFederatedStorage.EMAIL_ATTRIBUTE, email) + .map(StorageId::new) + .filter(storageId -> Objects.equals(storageId.getExternalId(), PASSTHROUGH_USERNAME)) + .filter(storageId -> Objects.equals(storageId.getProviderId(), component.getId())) + .findFirst(); + return result.isPresent() ? getUserModel(realm) : null; + } + + private UserModel getUserModel(final RealmModel realm) { + return new AbstractUserAdapterFederatedStorage.Streams(session, realm, component) { + @Override + public String getUsername() { + return PASSTHROUGH_USERNAME; + } + + @Override + public void setUsername(String username) { + } + }; + } +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java new file mode 100644 index 000000000000..d5333c9b23df --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/federation/PassThroughFederatedUserStorageProviderFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.tests.providers.federation; + +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.storage.UserStorageProviderFactory; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PassThroughFederatedUserStorageProviderFactory implements UserStorageProviderFactory { + + public static final String PROVIDER_ID = "pass-through-federated"; + + @Override + public PassThroughFederatedUserStorageProvider create(KeycloakSession session, ComponentModel model) { + return new PassThroughFederatedUserStorageProvider(session, model); + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory new file mode 100644 index 000000000000..fcfc43a2d062 --- /dev/null +++ b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory @@ -0,0 +1 @@ +org.keycloak.tests.providers.broker.oidc.ClientIdRequiredJWTClientAuthenticator diff --git a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory index 54ef6a8f2f69..8744315b7a06 100644 --- a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory +++ b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory @@ -15,4 +15,6 @@ # limitations under the License. # -org.keycloak.tests.providers.broker.oidc.OverwrittenMappersTestIdentityProviderFactory \ No newline at end of file +org.keycloak.tests.providers.broker.oidc.OverwrittenMappersTestIdentityProviderFactory +org.keycloak.tests.providers.broker.oidc.LegacyIdIdentityProviderFactory +org.keycloak.tests.providers.broker.oidc.TestKeycloakOidcIdentityProviderFactory \ No newline at end of file diff --git a/tests/migration-util/src/main/java/org/keycloak/test/migration/TestRewrite.java b/tests/migration-util/src/main/java/org/keycloak/test/migration/TestRewrite.java index 5952883dccf0..2dca9be109a7 100644 --- a/tests/migration-util/src/main/java/org/keycloak/test/migration/TestRewrite.java +++ b/tests/migration-util/src/main/java/org/keycloak/test/migration/TestRewrite.java @@ -38,7 +38,7 @@ protected int findLastLine(String regex) { } protected int findClassDeclaration() { - return findLine("public class .*"); + return findLine("^\\s*(?:public\\s+)?(?:abstract\\s+|final\\s+)?class\\b.*"); } public void setContent(List content) { @@ -72,7 +72,7 @@ public void addImport(String clazzName) { l = i; break; } - } else if (c.matches("^\\b(?:public\\s+)?class\\b.*Test\\s+\\{$")) { + } else if (c.matches("^\\s*(?:public\\s+)?(?:abstract\\s+|final\\s+)?class\\b.*\\{\\s*$")) { l = lastImport + 1; break; } diff --git a/tests/utils/src/main/java/org/keycloak/tests/utils/admin/AdminApiUtil.java b/tests/utils/src/main/java/org/keycloak/tests/utils/admin/AdminApiUtil.java index 7706fb3de3e4..3d1da126ddc4 100644 --- a/tests/utils/src/main/java/org/keycloak/tests/utils/admin/AdminApiUtil.java +++ b/tests/utils/src/main/java/org/keycloak/tests/utils/admin/AdminApiUtil.java @@ -79,6 +79,10 @@ public static ClientResource findClientByClientId(RealmResource realm, String cl return null; } + public static ClientResource findClientResourceByClientId(RealmResource realm, String clientId) { + return findClientByClientId(realm, clientId); + } + public static RoleResource findClientRoleByName(ClientResource client, String role) { return client.roles().get(role); } From dfb7fab0d94820120396450eff4064cd8d1f73c3 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 24 Aug 2026 09:49:53 +0200 Subject: [PATCH 4/9] fix compile error Signed-off-by: Erik Jan de Wit --- .../tests/broker/KcOidcBrokerIdpLinkActionTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java index f70961969c72..a3aeaed05bc4 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerIdpLinkActionTest.java @@ -588,8 +588,11 @@ private void assertConsumerFailedLinkEvents(String consumerUserId, String consum } private void allowClientInitiatedAccountLinking(boolean allow) { - runOnServerMaster.run(RunHelpers.setSystemPropertyOnServer("oidc.allow-client-initiated-account-linking", String.valueOf(allow))); - runOnServerMaster.run(RunHelpers.reinitializeProviderFactoryWithSystemPropertiesScope(LoginProtocol.class.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL, "oidc.")); + var runOnServerMaster = testingClient.server("master"); + runOnServerMaster.run(RunHelpers.setSystemPropertyOnServer( + "oidc.allow-client-initiated-account-linking", String.valueOf(allow))); + runOnServerMaster.run(RunHelpers.reinitializeProviderFactoryWithSystemPropertiesScope( + LoginProtocol.class.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL, "oidc.")); } } From 944d8647ff66d766466ecc970b6ac64062267a70 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 24 Aug 2026 09:55:15 +0200 Subject: [PATCH 5/9] Fix base testsuite mapping for providers package Add the providers package to the base suite selection so check-base-suites.sh passes in CI after migrating broker tests. Signed-off-by: Erik Jan de Wit --- .../src/test/java/org/keycloak/tests/suites/Base3TestSuite.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/base/src/test/java/org/keycloak/tests/suites/Base3TestSuite.java b/tests/base/src/test/java/org/keycloak/tests/suites/Base3TestSuite.java index 9e2e5aa2bef2..de302e620ad0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/suites/Base3TestSuite.java +++ b/tests/base/src/test/java/org/keycloak/tests/suites/Base3TestSuite.java @@ -10,6 +10,7 @@ "org.keycloak.tests.organization", "org.keycloak.tests.oid4vc", "org.keycloak.tests.policy", + "org.keycloak.tests.providers", "org.keycloak.tests.saml", "org.keycloak.tests.securityprofile", "org.keycloak.tests.session", From e601a45902094b453c24dc00108abdc425b679e8 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 24 Aug 2026 10:32:51 +0200 Subject: [PATCH 6/9] Restore broker compatibility classes for CI compile Re-add legacy broker base and social login test classes in the integration-arquillian testsuite so remaining tests that still import org.keycloak.testsuite.broker types compile after the broker migration. Signed-off-by: Erik Jan de Wit --- .../broker/AbstractBaseBrokerTest.java | 449 +++++++++++ .../testsuite/broker/AbstractBrokerTest.java | 269 +++++++ .../AbstractInitializedBaseBrokerTest.java | 78 ++ .../broker/AbstractNestedBrokerTest.java | 46 ++ .../testsuite/broker/SocialLoginTest.java | 708 ++++++++++++++++++ 5 files changed, 1550 insertions(+) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java new file mode 100644 index 000000000000..d2d45b1b7185 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java @@ -0,0 +1,449 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.broker; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriBuilderException; + +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.util.Retry; +import org.keycloak.models.utils.TimeBasedOTP; +import org.keycloak.protocol.saml.SamlProtocol; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.services.resources.RealmsResource; +import org.keycloak.testframework.realm.UserBuilder; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.pages.ErrorPage; +import org.keycloak.testsuite.pages.IdpConfirmLinkPage; +import org.keycloak.testsuite.pages.IdpConfirmOverrideLinkPage; +import org.keycloak.testsuite.pages.IdpLinkEmailPage; +import org.keycloak.testsuite.pages.InfoPage; +import org.keycloak.testsuite.pages.LoginConfigTotpPage; +import org.keycloak.testsuite.pages.LoginExpiredPage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.pages.LoginPasswordResetPage; +import org.keycloak.testsuite.pages.LoginTotpPage; +import org.keycloak.testsuite.pages.LogoutConfirmPage; +import org.keycloak.testsuite.pages.OAuthGrantPage; +import org.keycloak.testsuite.pages.ProceedPage; +import org.keycloak.testsuite.pages.UpdateAccountInformationPage; +import org.keycloak.testsuite.pages.VerifyEmailPage; +import org.keycloak.testsuite.util.WaitUtils; +import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; +import org.keycloak.testsuite.util.oauth.LogoutUrlBuilder; +import org.keycloak.testsuite.util.oauth.OAuthClient; +import org.keycloak.testsuite.util.userprofile.UserProfileUtil; + +import org.hamcrest.Matchers; +import org.jboss.arquillian.graphene.page.Page; +import org.junit.After; +import org.junit.Before; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; + +import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; +import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl; +import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot; +import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; +import static org.keycloak.testsuite.util.ServerURLs.removeDefaultPorts; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * No test methods there. Just some useful common functionality + */ +public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest { + + protected static final String ATTRIBUTE_VALUE = "attribute.value"; + + @Page + protected LoginPage loginPage; + + @Page + protected UpdateAccountInformationPage updateAccountInformationPage; + + @Page + protected ErrorPage errorPage; + + @Page + protected IdpConfirmLinkPage idpConfirmLinkPage; + + @Page + protected IdpConfirmOverrideLinkPage idpConfirmOverrideLinkPage; + + @Page + protected ProceedPage proceedPage; + + @Page + protected LogoutConfirmPage logoutConfirmPage; + + @Page + protected InfoPage infoPage; + + @Page + protected IdpLinkEmailPage idpLinkEmailPage; + + @Page + protected LoginExpiredPage loginExpiredPage; + + @Page + protected LoginTotpPage loginTotpPage; + + @Page + protected LoginConfigTotpPage totpPage; + + @Page + protected LoginPasswordResetPage loginPasswordResetPage; + + @Page + protected VerifyEmailPage verifyEmailPage; + + @Page + protected OAuthGrantPage grantPage; + + protected TimeBasedOTP totp = new TimeBasedOTP(); + + protected BrokerConfiguration bc = getBrokerConfiguration(); + + protected String userId; + + /** + * Returns a broker configuration. Return value should not change between calls. + * @return + */ + protected abstract BrokerConfiguration getBrokerConfiguration(); + + @Override + public void addTestRealms(List testRealms) { + + } + + protected void configureSMTPServer() { + RealmResource realm = adminClient.realm(bc.consumerRealmName()); + RealmRepresentation master = realm.toRepresentation(); + master.setSmtpServer(suiteContext.getSmtpServer()); + realm.update(master); + } + + protected void removeSMTPConfiguration(RealmResource consumerRealm) { + RealmRepresentation master = consumerRealm.toRepresentation(); + master.setSmtpServer(Collections.emptyMap()); + consumerRealm.update(master); + } + + protected void addClientsToProviderAndConsumer() { + List clients = bc.createProviderClients(); + final RealmResource providerRealm = adminClient.realm(bc.providerRealmName()); + for (ClientRepresentation client : clients) { + log.debug("adding client " + client.getClientId() + " to realm " + bc.providerRealmName()); + + final Response resp = providerRealm.clients().create(client); + resp.close(); + } + + clients = bc.createConsumerClients(); + if (clients != null) { + RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName()); + for (ClientRepresentation client : clients) { + log.debug("adding client " + client.getClientId() + " to realm " + bc.consumerRealmName()); + + Response resp = consumerRealm.clients().create(client); + resp.close(); + } + } + } + + @Before + public void beforeBrokerTest() { + RealmRepresentation consumerRealm = bc.createConsumerRealm(); + RealmRepresentation providerRealm = bc.createProviderRealm(); + importRealm(consumerRealm); + importRealm(providerRealm); + + UserProfileUtil.enableUnmanagedAttributes(adminClient.realm(consumerRealm.getRealm()).users().userProfile()); + UserProfileUtil.enableUnmanagedAttributes(adminClient.realm(providerRealm.getRealm()).users().userProfile()); + } + + @After + public void cleanupUsers() { + deleteAllCookiesForRealm(bc.consumerRealmName()); + adminClient.realm(bc.consumerRealmName()).remove(); + adminClient.realm(bc.providerRealmName()).remove(); + } + + protected String createUser(String username, String email) { + UserRepresentation newUser = UserBuilder.create().username(username).email(email).enabled(true).build(); + String userId = createUserWithAdminClient(adminClient.realm(bc.consumerRealmName()), newUser); + resetUserPassword(adminClient.realm(bc.consumerRealmName()).users().get(userId), "password", false); + return userId; + } + + protected String createUser(String username) { + return createUser(username, USER_EMAIL); + } + + + protected void assertNumFederatedIdentities(String userId, int expected) { + assertEquals(expected, adminClient.realm(bc.consumerRealmName()).users().get(userId).getFederatedIdentity().size()); + } + + protected void logInAsUserInIDP() { + logInAsUserInIDP("broker-app"); + } + + protected void logInAsUserInIDP(String clientId) { + oauth.client(clientId); + oauth.realm(bc.consumerRealmName()); + oauth.openLoginForm(); + logInWithBroker(bc); + } + + // We are re-authenticating to the IDP. Hence it is assumed that "username" field is not visible on the login form on the IDP side + protected void logInAsUserInIDPWithReAuthenticate() { + oauth.client("broker-app"); + oauth.realm(bc.consumerRealmName()); + oauth.openLoginForm(); + + waitForPage(driver, "sign in to", true); + log.debug("Clicking social " + bc.getIDPAlias()); + loginPage.clickSocial(bc.getIDPAlias()); + waitForPage(driver, "sign in to", true); + + // We are re-authenticating. Username field not visible + log.debug("Reauthenticating"); + Assertions.assertFalse(loginPage.isUsernameInputPresent()); + loginPage.login(bc.getUserPassword()); + } + + protected void logInWithBroker(BrokerConfiguration bc) { + logInWithIdp(bc.getIDPAlias(), bc.getUserLogin(), bc.getUserPassword()); + } + + protected void logInWithIdp(String idpAlias, String username, String password) { + waitForPage(driver, "sign in to", true); + log.debug("Clicking social " + idpAlias); + loginPage.clickSocial(idpAlias); + waitForPage(driver, "sign in to", true); + log.debug("Logging in"); + loginPage.login(username, password); + } + + protected AuthorizationEndpointResponse doLoginSocial(OAuthClient oauth, String brokerId, String username, String password) { + return doLoginSocial(oauth, brokerId, username, password, null); + } + + protected AuthorizationEndpointResponse doLoginSocial(OAuthClient oauth, String brokerId, String username, String password, String nonce) { + oauth.loginForm().nonce(nonce).open(); + WaitUtils.waitForPageToLoad(); + + oauth.getDriver().findElement(By.id("social-" + brokerId)).click(); + oauth.fillLoginForm(username, password); + + return oauth.parseLoginResponse(); + } + + /** Logs in the IDP and updates account information */ + protected void logInAsUserInIDPForFirstTime() { + logInAsUserInIDP(); + updateAccountInformation(); + } + + protected void logInAsUserInIDPForFirstTimeAndAssertSuccess() { + logInAsUserInIDPForFirstTime(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + protected void updateAccountInformation() { + waitForPage(driver, "update account information", false); + + updateAccountInformationPage.assertCurrent(); + Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), + "We must be on correct realm right now"); + + log.debug("Updating info on updateAccount page"); + updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + } + + + protected String getAccountUrl(String contextRoot, String realmName) { + return contextRoot + "/auth/realms/" + realmName + "/account"; + } + + protected String getLoginUrl(String contextRoot, String realmName, String clientId) { + return getLoginUrl(contextRoot, realmName, clientId, "openid"); + } + + /** + * Get the login page for an existing client in provided realm + * + * @param contextRoot server base url without /auth + * @param realmName Name of the realm + * @param clientId ClientId of a client. Client has to exists in the realm. + * @param scope The scope parameter for the request + * @return Login URL + */ + protected String getLoginUrl(String contextRoot, String realmName, String clientId, String scope) { + List clients = adminClient.realm(realmName).clients().findByClientId(clientId); + + assertThat(clients, Matchers.is(Matchers.not(Matchers.empty()))); + + String redirectURI = clients.get(0).getBaseUrl(); + if (redirectURI.startsWith("/")) { + redirectURI = contextRoot + "/auth" + redirectURI; + } + + return contextRoot + "/auth/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" + + clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=" + scope; + } + + protected void logoutFromRealm(String contextRoot, String realm) { + logoutFromRealm(contextRoot, realm, null); + } + + protected void logoutFromRealm(String contextRoot, String realm, String initiatingIdp) { + logoutFromRealm(contextRoot, realm, initiatingIdp, null); + } + + protected void logoutFromRealm(String contextRoot, String realm, String initiatingIdp, String idTokenHint) { + logoutFromRealm(contextRoot, realm, initiatingIdp, idTokenHint, null); + } + + protected void logoutFromRealm(String contextRoot, String realm, String initiatingIdp, String idTokenHint, String clientId) { + logoutFromRealm(contextRoot, realm, initiatingIdp, idTokenHint, clientId, null); + } + + // Completely logout from realm and confirm logout if present + protected void logoutFromRealm(String contextRoot, String realm, String initiatingIdp, String idTokenHint, String clientId, String redirectUri) { + final String defaultRedirectUri = redirectUri != null ? redirectUri : oauth.loginForm().build(); + final String defaultClientId = (idTokenHint == null && clientId == null) ? "test-app" : clientId; + + executeLogoutFromRealm(contextRoot, realm, initiatingIdp, idTokenHint, defaultClientId, defaultRedirectUri); + checkLogoutConfirmation(realm, idTokenHint, defaultClientId); + } + + // Only execute the logout without logout confirmation + protected void executeLogoutFromRealm(String contextRoot, String realm, String initiatingIdp, String idTokenHint, String clientId, String redirectUri) { + final boolean isDifferentContext = !Objects.equals(OAuthClient.SERVER_ROOT, removeDefaultPorts(contextRoot)); + + try { + if (isDifferentContext) { + OAuthClient.updateURLs(contextRoot); + OAuthClient.updateAppRootRealm(realm); + oauth.init(); + } + + final LogoutUrlBuilder builder = oauth.realm(realm).logoutForm() + .idTokenHint(idTokenHint) + .initiatingIdp(initiatingIdp); + + if (clientId != null) { + builder.withClientId(); + } + + if (redirectUri != null && (clientId != null || idTokenHint != null)) { + builder.postLogoutRedirectUri(encodeUrl(redirectUri)); + } + + builder.open(); + } finally { + if (isDifferentContext) { + OAuthClient.updateURLs(getAuthServerContextRoot()); + oauth.init(); + } + } + } + + // Check whether the logout confirmation is present; if yes, confirm the logout and verify the current page + private void checkLogoutConfirmation(String realm, String idTokenHint, String clientId) { + String expectedPageId = driver.findElement(By.xpath("//body")).getAttribute("data-page-id"); + if (expectedPageId.equals(errorPage.getExpectedPageId())) { + errorPage.assertCurrent(); + } else { + logoutConfirmPage.assertCurrent(); + Assertions.assertEquals("Logging out", driver.getTitle()); + confirmLogout(); + if (idTokenHint != null || clientId != null) { + assertLoginPage(realm); + } else { + infoPage.assertCurrent(); + } + } + } + + protected void confirmLogout() { + logoutConfirmPage.assertCurrent(); + logoutConfirmPage.confirmLogout(); + } + + protected void assertLoginPage(String realm) { + try { + Retry.execute(() -> { + try { + waitForPage(driver, "sign in to " + realm, true); + } catch (TimeoutException ex) { + driver.navigate().refresh(); + log.debug("[Retriable] Timed out waiting for login page"); + throw ex; + } + }, 10, 100); + } catch (TimeoutException e) { + log.debug(driver.getTitle()); + log.debug(driver.getPageSource()); + Assertions.fail("Timeout while waiting for login page"); + } + } + + protected void waitForAccountManagementTitle() { + final String title = "Keycloak account management"; + waitForPage(driver, title, true); + } + + protected void assertErrorPage(String expectedError) { + errorPage.assertCurrent(); + Assertions.assertEquals(expectedError, errorPage.getError()); + } + + + protected URI getConsumerSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException { + return getSamlEndpoint(getConsumerRoot(), realm); + } + + protected URI getProviderSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException { + return getSamlEndpoint(getProviderRoot(), realm); + } + + protected URI getSamlEndpoint(String fromUri, String realm) { + return RealmsResource + .protocolUrl(UriBuilder.fromUri(fromUri).path("auth")) + .build(realm, SamlProtocol.LOGIN_PROTOCOL); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java new file mode 100644 index 000000000000..b30d4bf916f8 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java @@ -0,0 +1,269 @@ +package org.keycloak.testsuite.broker; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.keycloak.admin.client.resource.AuthenticationManagementResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.authentication.authenticators.broker.IdpConfirmLinkAuthenticatorFactory; +import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.utils.DefaultAuthenticationFlows; +import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; +import org.keycloak.representations.idm.AuthenticatorConfigRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.UserSessionRepresentation; +import org.keycloak.testsuite.pages.ConsentPage; +import org.keycloak.testsuite.util.AccountHelper; + +import org.jboss.arquillian.graphene.page.Page; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import static org.keycloak.models.utils.DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS; +import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Contains just few basic tests. This is good class to override if you're testing custom IDP configuration and you need + * to verify if login with IDP works as expected + */ +public abstract class AbstractBrokerTest extends AbstractInitializedBaseBrokerTest { + + public static final String ROLE_USER = "user"; + public static final String ROLE_MANAGER = "manager"; + public static final String ROLE_FRIENDLY_MANAGER = "friendly-manager"; + public static final String ROLE_USER_DOT_GUIDE = "user.guide"; + public static final String EMPTY_ATTRIBUTE_ROLE = "empty.attribute.role"; + + @Page + ConsentPage consentPage; + + @Test + public void testLogInAsUserInIDP() { + loginUser(); + + testSingleLogout(); + } + + protected void loginUser() { + oauth.client("broker-app", "password"); + oauth.realm(bc.consumerRealmName()); + oauth.openLoginForm(); + + logInWithBroker(bc); + + waitForPage(driver, "update account information", false); + updateAccountInformationPage.assertCurrent(); + Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), + "We must be on correct realm right now"); + + log.debug("Updating info on updateAccount page"); + updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + + if (isUsingTransientSessions()) { + UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users(); + + List userCount = consumerUsers.list(); + assertThat("There must be at no users", userCount, empty()); + } else { + UserRepresentation userRep = AccountHelper.getUserRepresentation( + adminClient.realm(bc.consumerRealmName()), bc.getUserLogin()); + userRep.setFirstName("Firstname"); + userRep.setLastName("Lastname"); + + AccountHelper.updateUser(adminClient.realm(bc.consumerRealmName()), bc.getUserLogin(), userRep); + + UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users(); + + int userCount = consumerUsers.count(); + Assertions.assertTrue(userCount > 0, "There must be at least one user"); + } + + boolean isUserFound = getConsumerUserRepresentations() + .anyMatch(user -> user.getUsername().equals(bc.getUserLogin()) && user.getEmail().equals(bc.getUserEmail())); + + Assertions.assertTrue(isUserFound, + "There must be user " + bc.getUserLogin() + " in realm " + bc.consumerRealmName()); + } + + protected boolean isUsingTransientSessions() { + return "true".equals(identityProviderResource.toRepresentation().getConfig().getOrDefault(IdentityProviderModel.DO_NOT_STORE_USERS, "false")); + } + + protected Stream getConsumerUserResources() { + return getConsumerUserRepresentations() + .map(UserRepresentation::getId) + .map(adminClient.realm(bc.consumerRealmName()).users()::get); + } + + protected UserRepresentation getConsumerUserRepresentation(String userName) { + Objects.requireNonNull(userName); + Iterator it = getConsumerUserRepresentations() + .peek(userRep -> log.debugf("UserRep: %s .. %s", userRep.getId(), userRep.getUsername())) + .filter(userRep -> userName.equals(userRep.getUsername())) + .iterator(); + + assertTrue(it.hasNext(), "At least one user expected with username " + userName); + UserRepresentation res = it.next(); + assertFalse(it.hasNext(), "At most one user expected with username " + userName); + + return res; + } + + protected Stream getConsumerUserRepresentations() { + String consumerClientBrokerAppId = adminClient.realm(bc.consumerRealmName()).clients().findByClientId("broker-app").get(0).getId(); + List brokeredSessions = adminClient.realm(bc.consumerRealmName()).clients().get(consumerClientBrokerAppId).getUserSessions(0, 10); + + final List persistentUsers = adminClient.realm(bc.consumerRealmName()).users().list(); + final Set persistentUsersId = persistentUsers.stream().map(UserRepresentation::getId).collect(Collectors.toSet()); + return Stream.concat(persistentUsers.stream(), + brokeredSessions.stream() + .map(userSession -> userSession.getUserId()) + .filter(id -> ! persistentUsersId.contains(id)) + .map(adminClient.realm(bc.consumerRealmName()).users()::get) + .map(UserResource::toRepresentation) + ); + } + + @Test + public void loginWithExistingUser() { + Integer userCountBefore = adminClient.realm(bc.consumerRealmName()).users().count(); + + testLogInAsUserInIDP(); + + Integer userCount = adminClient.realm(bc.consumerRealmName()).users().count(); + + oauth.client("broker-app"); + oauth.realm(bc.consumerRealmName()); + oauth.openLoginForm(); + + if (isUsingTransientSessions()) { + // Assert that there has been no persistent user created + assertThat(userCount, is(userCountBefore)); + } else { + logInWithBroker(bc); + + assertThat(driver.getCurrentUrl(), containsString(getConsumerRoot() + "/auth/realms/master/app/")); + assertEquals(userCount, adminClient.realm(bc.consumerRealmName()).users().count()); + } + } + + + protected void testSingleLogout() { + log.debug("Testing single log out"); + + oauth.realm(bc.consumerRealmName()); + oauth.client("broker-app", "secret"); + oauth.openLoginForm(); + + Assertions.assertTrue(driver.getTitle().endsWith("AUTH_RESPONSE"), "Should be logged in"); + + logoutFromConsumerRealm(); + AccountHelper.logout(adminClient.realm(bc.providerRealmName()), bc.getUserLogin()); + + oauth.realm(bc.consumerRealmName()); + oauth.openLoginForm(); + + Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/protocol/openid-connect/"), + "Should be on " + bc.consumerRealmName() + " realm on login page"); + } + + protected void logoutFromConsumerRealm() { + if (isUsingTransientSessions()) { + getConsumerUserResources().forEach(UserResource::logout); + } else { + AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), bc.getUserLogin()); + } + } + + protected void createRolesForRealm(String realm) { + RoleRepresentation managerRole = new RoleRepresentation(ROLE_MANAGER,null, false); + RoleRepresentation friendlyManagerRole = new RoleRepresentation(ROLE_FRIENDLY_MANAGER,null, false); + RoleRepresentation userRole = new RoleRepresentation(ROLE_USER,null, false); + RoleRepresentation userGuideRole = new RoleRepresentation(ROLE_USER_DOT_GUIDE,null, false); + RoleRepresentation emptyAttributeRole = new RoleRepresentation(EMPTY_ATTRIBUTE_ROLE, null, false); + + adminClient.realm(realm).roles().create(managerRole); + adminClient.realm(realm).roles().create(friendlyManagerRole); + adminClient.realm(realm).roles().create(userRole); + adminClient.realm(realm).roles().create(userGuideRole); + adminClient.realm(realm).roles().create(emptyAttributeRole); + } + + static void enableUpdateProfileOnFirstLogin(AuthenticationExecutionInfoRepresentation execution, AuthenticationManagementResource flows) { + if (execution.getProviderId() != null && execution.getProviderId().equals(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID)) { + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED.name()); + flows.updateExecutions(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, execution); + } else if (execution.getAlias() != null && execution.getAlias().equals(IDP_REVIEW_PROFILE_CONFIG_ALIAS)) { + AuthenticatorConfigRepresentation config = flows.getAuthenticatorConfig(execution.getAuthenticationConfig()); + config.getConfig().put("update.profile.on.first.login", IdentityProviderRepresentation.UPFLM_ON); + flows.updateAuthenticatorConfig(config.getId(), config); + } + } + + static void setUpMissingUpdateProfileOnFirstLogin(AuthenticationExecutionInfoRepresentation execution, AuthenticationManagementResource flows) { + if (execution.getProviderId() != null && execution.getProviderId().equals(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID)) { + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name()); + flows.updateExecutions(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, execution); + } else if (execution.getAlias() != null && execution.getAlias().equals(IDP_REVIEW_PROFILE_CONFIG_ALIAS)) { + AuthenticatorConfigRepresentation config = flows.getAuthenticatorConfig(execution.getAuthenticationConfig()); + config.getConfig().put("update.profile.on.first.login", IdentityProviderRepresentation.UPFLM_MISSING); + flows.updateAuthenticatorConfig(config.getId(), config); + } + } + + static void enableRequirePassword(AuthenticationExecutionInfoRepresentation execution, + AuthenticationManagementResource flows) { + String id = execution.getAuthenticationConfig(); + + if (id != null) { + AuthenticatorConfigRepresentation authenticatorConfig = flows.getAuthenticatorConfig(id); + + if (authenticatorConfig != null) { + Map config = authenticatorConfig.getConfig(); + + if (config != null && config.containsKey(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION)) { + config.put(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, Boolean.TRUE.toString()); + } + + flows.updateAuthenticatorConfig(authenticatorConfig.getId(), authenticatorConfig); + } + } + } + + public static void disableUpdateProfileOnFirstLogin(AuthenticationExecutionInfoRepresentation execution, AuthenticationManagementResource flows) { + if (execution.getProviderId() != null && execution.getProviderId().equals(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID)) { + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name()); + flows.updateExecutions(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, execution); + } else if (execution.getAlias() != null && execution.getAlias().equals(IDP_REVIEW_PROFILE_CONFIG_ALIAS)) { + AuthenticatorConfigRepresentation config = flows.getAuthenticatorConfig(execution.getAuthenticationConfig()); + config.getConfig().put("update.profile.on.first.login", IdentityProviderRepresentation.UPFLM_OFF); + flows.updateAuthenticatorConfig(config.getId(), config); + } + } + + static void disableExistingUser(AuthenticationExecutionInfoRepresentation execution, AuthenticationManagementResource flows) { + if (execution.getProviderId() != null && (execution.getProviderId().equals(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID) || execution.getProviderId().equals(IdpConfirmLinkAuthenticatorFactory.PROVIDER_ID))) { + execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED.name()); + flows.updateExecutions(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, execution); + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java new file mode 100644 index 000000000000..bbd40b00a841 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractInitializedBaseBrokerTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.broker; + +import java.util.function.BiConsumer; + +import org.keycloak.admin.client.resource.AuthenticationManagementResource; +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.models.utils.DefaultAuthenticationFlows; +import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +import org.junit.Before; + +import static org.keycloak.testsuite.admin.AdminApiUtil.createUserWithAdminClient; +import static org.keycloak.testsuite.admin.AdminApiUtil.resetUserPassword; + +/** + * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. + */ +public abstract class AbstractInitializedBaseBrokerTest extends AbstractBaseBrokerTest { + + protected IdentityProviderResource identityProviderResource; + + protected void postInitializeUser(UserRepresentation user) {} + + @Override + @Before + public void beforeBrokerTest() { + super.beforeBrokerTest(); + log.debug("creating user for realm " + bc.providerRealmName()); + + UserRepresentation user = new UserRepresentation(); + user.setUsername(bc.getUserLogin()); + user.setEmail(bc.getUserEmail()); + user.setEmailVerified(true); + user.setEnabled(true); + postInitializeUser(user); + + RealmResource realmResource = adminClient.realm(bc.providerRealmName()); + userId = createUserWithAdminClient(realmResource, user); + + resetUserPassword(realmResource.users().get(userId), bc.getUserPassword(), false); + + log.debug("adding identity provider to realm " + bc.consumerRealmName()); + RealmResource realm = adminClient.realm(bc.consumerRealmName()); + realm.identityProviders().create(bc.setUpIdentityProvider()).close(); + identityProviderResource = realm.identityProviders().get(bc.getIDPAlias()); + + addClientsToProviderAndConsumer(); + + testContext.setInitialized(true); + } + + protected void updateExecutions(BiConsumer action) { + AuthenticationManagementResource flows = adminClient.realm(bc.consumerRealmName()).flows(); + + for (AuthenticationExecutionInfoRepresentation execution : flows.getExecutions(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW)) { + action.accept(execution, flows); + } + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java new file mode 100644 index 000000000000..5b1af70343c5 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractNestedBrokerTest.java @@ -0,0 +1,46 @@ +package org.keycloak.testsuite.broker; + +import org.junit.After; +import org.junit.Before; + +import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; + +public abstract class AbstractNestedBrokerTest extends AbstractBaseBrokerTest { + + protected NestedBrokerConfiguration nbc = getNestedBrokerConfiguration(); + + protected abstract NestedBrokerConfiguration getNestedBrokerConfiguration(); + + @Override + protected BrokerConfiguration getBrokerConfiguration() { + return getNestedBrokerConfiguration(); + } + + @Before + public void createSubConsumerRealm() { + importRealm(nbc.createSubConsumerRealm()); + } + + @After + public void removeSubConsumerRealm() { + adminClient.realm(nbc.subConsumerRealmName()).remove(); + } + + /** Logs in subconsumer realm via consumer IDP via provider IDP and updates account information */ + protected void logInAsUserInNestedIDPForFirstTime() { + String redirectUri = getAuthServerRoot() + "realms/" + nbc.subConsumerRealmName() + "/account"; + oauth.client("account").redirectUri(redirectUri); + oauth.realm(nbc.subConsumerRealmName()); + oauth.openLoginForm(); + + waitForPage(driver, "sign in to", true); + log.debug("Clicking social " + nbc.getSubConsumerIDPDisplayName()); + loginPage.clickSocial(nbc.getSubConsumerIDPDisplayName()); + waitForPage(driver, "sign in to", true); + log.debug("Clicking social " + nbc.getIDPAlias()); + loginPage.clickSocial(nbc.getIDPAlias()); + waitForPage(driver, "sign in to", true); + log.debug("Logging in"); + loginPage.login(nbc.getUserLogin(), nbc.getUserPassword()); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java new file mode 100644 index 000000000000..c8d489fc2c3d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java @@ -0,0 +1,708 @@ +package org.keycloak.testsuite.broker; + +import java.io.FileInputStream; +import java.util.List; +import java.util.Properties; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Form; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; + +import org.keycloak.OAuth2Constants; +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper; +import org.keycloak.common.Profile; +import org.keycloak.models.ClientModel; +import org.keycloak.models.IdentityProviderMapperModel; +import org.keycloak.models.IdentityProviderMapperSyncMode; +import org.keycloak.models.IdentityProviderQuery; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.services.resources.admin.fgap.AdminPermissionManagement; +import org.keycloak.services.resources.admin.fgap.AdminPermissions; +import org.keycloak.testframework.realm.IdentityProviderBuilder; +import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.arquillian.annotation.EnableFeature; +import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected; +import org.keycloak.testsuite.auth.page.login.UpdateAccount; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.pages.social.AbstractSocialLoginPage; +import org.keycloak.testsuite.pages.social.BitbucketLoginPage; +import org.keycloak.testsuite.pages.social.FacebookLoginPage; +import org.keycloak.testsuite.pages.social.GitHubLoginPage; +import org.keycloak.testsuite.pages.social.GitLabLoginPage; +import org.keycloak.testsuite.pages.social.GoogleLoginPage; +import org.keycloak.testsuite.pages.social.InstagramLoginPage; +import org.keycloak.testsuite.pages.social.LinkedInLoginPage; +import org.keycloak.testsuite.pages.social.MicrosoftLoginPage; +import org.keycloak.testsuite.pages.social.OpenShiftLoginPage; +import org.keycloak.testsuite.pages.social.PayPalLoginPage; +import org.keycloak.testsuite.pages.social.StackOverflowLoginPage; +import org.keycloak.testsuite.pages.social.TwitterConsentLoginPage; +import org.keycloak.testsuite.util.AdminClientUtil; +import org.keycloak.testsuite.util.DroneUtils; +import org.keycloak.testsuite.util.URLUtils; +import org.keycloak.testsuite.util.WaitUtils; +import org.keycloak.testsuite.util.oauth.OAuthClient; +import org.keycloak.util.BasicAuthHelper; + +import com.google.common.collect.ImmutableMap; +import org.jboss.arquillian.graphene.Graphene; +import org.jboss.arquillian.graphene.page.Page; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.BITBUCKET; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.FACEBOOK; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.FACEBOOK_INCLUDE_BIRTHDAY; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB_PRIVATE_EMAIL; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITLAB; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE_HOSTED_DOMAIN; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE_NON_MATCHING_HOSTED_DOMAIN; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.INSTAGRAM; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.LINKEDIN; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.MICROSOFT; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.MICROSOFT_SINGLE_TENANT; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.OPENSHIFT4; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.OPENSHIFT4_KUBE_ADMIN; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.PAYPAL; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.STACKOVERFLOW; +import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.TWITTER; + +import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Stian Thorgersen + * @author Vaclav Muzikar + */ +@EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) +public class SocialLoginTest extends AbstractKeycloakTest { + + public static final String SOCIAL_CONFIG = "social.config"; + public static final String REALM = "social"; + public static final String EXCHANGE_CLIENT = "exchange-client"; + + private static final Properties config = new Properties(); + + @Page + private LoginPage loginPage; + + @Page + private UpdateAccount updateAccountPage; + + public enum Provider { + GOOGLE("google", GoogleLoginPage.class), + GOOGLE_HOSTED_DOMAIN("google", "google-hosted-domain", GoogleLoginPage.class), + GOOGLE_NON_MATCHING_HOSTED_DOMAIN("google", "google-hosted-domain", GoogleLoginPage.class), + FACEBOOK("facebook", FacebookLoginPage.class), + FACEBOOK_INCLUDE_BIRTHDAY("facebook", FacebookLoginPage.class), + GITHUB("github", GitHubLoginPage.class), + GITHUB_PRIVATE_EMAIL("github", "github-private-email", GitHubLoginPage.class), + TWITTER("twitter", TwitterConsentLoginPage.class), + LINKEDIN("linkedin-openid-connect", LinkedInLoginPage.class), + MICROSOFT("microsoft", MicrosoftLoginPage.class), + MICROSOFT_SINGLE_TENANT("microsoft", "microsoft-single-tenant", MicrosoftLoginPage.class), + PAYPAL("paypal", PayPalLoginPage.class), + STACKOVERFLOW("stackoverflow", StackOverflowLoginPage.class), + OPENSHIFT4("openshift-v4", OpenShiftLoginPage.class), + OPENSHIFT4_KUBE_ADMIN("openshift-v4", "openshift-v4-admin", OpenShiftLoginPage.class), + GITLAB("gitlab", GitLabLoginPage.class), + BITBUCKET("bitbucket", BitbucketLoginPage.class), + INSTAGRAM("instagram", InstagramLoginPage.class); + + private final String id; + private final Class pageObjectClazz; + private String configId = null; + + Provider(String id, Class pageObjectClazz) { + this.id = id; + this.pageObjectClazz = pageObjectClazz; + } + + Provider(String id, String configId, Class pageObjectClazz) { + this.id = id; + this.pageObjectClazz = pageObjectClazz; + this.configId = configId; + } + + public String id() { + return id; + } + + public Class pageObjectClazz() { + return pageObjectClazz; + } + + public String configId() { + return configId != null ? configId : id; + } + } + + private Provider currentTestProvider = null; + private AbstractSocialLoginPage currentSocialLoginPage = null; + + @BeforeClass + public static void loadConfig() throws Exception { + assumeTrue(System.getProperties().containsKey(SOCIAL_CONFIG)); + config.load(new FileInputStream(System.getProperty(SOCIAL_CONFIG))); + } + + @Before + public void beforeSocialLoginTest() { + oauth.realm(REALM); + createAppClientInRealm(REALM); + } + + @After + public void afterSocialLoginTest() { + currentTestProvider = null; + oauth.realm("test"); + } + + private void removeUser() { + List users = adminClient.realm(REALM).users().search(null, null, null); + for (UserRepresentation user : users) { + if (user.getServiceAccountClientId() == null) { + log.infof("removing test user '%s'", user.getUsername()); + adminClient.realm(REALM).users().get(user.getId()).remove(); + } + } + } + + private void setTestProvider(Provider provider) { + adminClient.realm(REALM).identityProviders().create(buildIdp(provider)); + log.infof("added '%s' identity provider", provider.id()); + currentTestProvider = provider; + currentSocialLoginPage = Graphene.createPageFragment(currentTestProvider.pageObjectClazz(), driver.findElement(By.tagName("html"))); + + if(provider == OPENSHIFT4 || provider == OPENSHIFT4_KUBE_ADMIN) { + ((OpenShiftLoginPage) currentSocialLoginPage).setUserLoginLinkTitle(getConfig(currentTestProvider, "loginBtnTitle")); + } + } + + @Override + public void addTestRealms(List testRealms) { + RealmRepresentation rep = RealmBuilder.create().name(REALM).build(); + testRealms.add(rep); + } + + @Override + protected boolean isImportAfterEachMethod() { + return true; + } + + public static void setupClientExchangePermissions(KeycloakSession session) { + RealmModel realm = session.realms().getRealmByName(REALM); + ClientModel client = session.clients().getClientByClientId(realm, EXCHANGE_CLIENT); + // lazy init + if (client != null) return; + client = realm.addClient(EXCHANGE_CLIENT); + client.setSecret("secret"); + client.setPublicClient(false); + client.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + client.setEnabled(true); + client.setDirectAccessGrantsEnabled(true); + + ClientPolicyRepresentation clientPolicyRep = new ClientPolicyRepresentation(); + clientPolicyRep.setName("client-policy"); + clientPolicyRep.addClient(client.getId()); + AdminPermissionManagement management = AdminPermissions.management(session, realm); + management.users().setPermissionsEnabled(true); + ResourceServer server = management.realmResourceServer(); + Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientPolicyRep); + management.users().adminImpersonatingPermission().addAssociatedPolicy(clientPolicy); + management.users().adminImpersonatingPermission().setDecisionStrategy(DecisionStrategy.AFFIRMATIVE); + session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication()).forEach(idp -> { + management.idps().setPermissionsEnabled(idp, true); + management.idps().exchangeToPermission(idp).addAssociatedPolicy(clientPolicy); + }); + } + + @Test + @UncaughtServerErrorExpected + public void openshift4Login() { + setTestProvider(OPENSHIFT4); + performLogin(); + assertUpdateProfile(false, false, true); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + public void openshift4KubeAdminLogin() { + setTestProvider(OPENSHIFT4_KUBE_ADMIN); + performLogin(); + assertUpdateProfile(true, true, true); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + @Test + @UncaughtServerErrorExpected + public void openshift4LoginWithGroupsMapper() { + setTestProvider(OPENSHIFT4); + addAttributeMapper("ocp-groups", "groups"); + performLogin(); + assertUpdateProfile(false, false, true); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + assertAttribute("ocp-groups", getConfig("groups")); + } + + @Test + @UncaughtServerErrorExpected + public void googleLogin() throws InterruptedException { + setTestProvider(GOOGLE); + performLogin(); + DroneUtils.getCurrentDriver().findElement(By.xpath("//button//span[contains(.,'Continue')]")).click(); + WaitUtils.pause(3000); + WaitUtils.waitForPageToLoad(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + @UncaughtServerErrorExpected + public void googleHostedDomainLogin() throws InterruptedException { + setTestProvider(GOOGLE_HOSTED_DOMAIN); + navigateToLoginPage(); + assertTrue(driver.getCurrentUrl().contains("hd=" + getConfig(GOOGLE_HOSTED_DOMAIN, "hostedDomain"))); + doLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + public void googleNonMatchingHostedDomainLogin() throws InterruptedException { + setTestProvider(GOOGLE_NON_MATCHING_HOSTED_DOMAIN); + navigateToLoginPage(); + assertTrue(driver.getCurrentUrl().contains("hd=non-matching-hosted-domain")); + doLogin(); + + // Just to be sure there's no redirect in progress + WaitUtils.waitForPageToLoad(); + + WebElement errorMessage = driver.findElement(By.xpath(".//p[@class='instruction']")); + + assertTrue(errorMessage.isDisplayed()); + assertEquals("Unexpected error when authenticating with identity provider", errorMessage.getText()); + } + + @Test + @UncaughtServerErrorExpected + public void bitbucketLogin() throws InterruptedException { + setTestProvider(BITBUCKET); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + @UncaughtServerErrorExpected + public void gitlabLogin() throws InterruptedException { + setTestProvider(GITLAB); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + @UncaughtServerErrorExpected + public void facebookLogin() throws InterruptedException { + setTestProvider(FACEBOOK); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + @UncaughtServerErrorExpected + public void facebookLoginWithEnhancedScope() throws InterruptedException { + setTestProvider(FACEBOOK_INCLUDE_BIRTHDAY); + addAttributeMapper("birthday", "birthday"); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + assertAttribute("birthday", getConfig("profile.birthday")); + testTokenExchange(); + } + + @Test + @Ignore + public void instagramLogin() throws InterruptedException { + setTestProvider(INSTAGRAM); + performLogin(); + assertUpdateProfile(true, true, true); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + + @Test + @UncaughtServerErrorExpected + public void githubLogin() throws InterruptedException { + setTestProvider(GITHUB); + performLogin(); + assertUpdateProfile(true, true, false); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + testTokenExchange(); + } + + @Test + public void githubPrivateEmailLogin() throws InterruptedException { + setTestProvider(GITHUB_PRIVATE_EMAIL); + performLogin(); + assertUpdateProfile(true, true, false); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + @Test + public void twitterLogin() { + ProfileAssume.assumeFeatureEnabled(Profile.Feature.TWITTER_BROKER); + setTestProvider(TWITTER); + performLogin(); + assertUpdateProfile(false, false, true); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + @Test + public void linkedinLogin() { + setTestProvider(LINKEDIN); + addAttributeMapper("picture", "picture", "linkedin-user-attribute-mapper"); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + assertAttribute("picture", getConfig("profile.picture")); + } + + @Test + public void microsoftLogin() { + setTestProvider(MICROSOFT); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + @Test + public void microsoftSingleTenantLogin() { + setTestProvider(MICROSOFT_SINGLE_TENANT); + navigateToLoginPage(); + assertTrue(driver.getCurrentUrl().contains("/" + getConfig(MICROSOFT_SINGLE_TENANT, "tenantId") + "/")); + doLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + @Test + public void paypalLogin() { + setTestProvider(PAYPAL); + performLogin(); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + @Test + public void stackoverflowLogin() throws InterruptedException { + setTestProvider(STACKOVERFLOW); + performLogin(); + assertUpdateProfile(false, false, true); + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + public IdentityProviderRepresentation buildIdp(Provider provider) { + IdentityProviderRepresentation idp = IdentityProviderBuilder.create() + .alias(provider.id()) + .providerId(provider.id()) + .build(); + idp.setEnabled(true); + idp.setStoreToken(true); + idp.getConfig().put("clientId", getConfig(provider, "clientId")); + idp.getConfig().put("clientSecret", getConfig(provider, "clientSecret")); + + if (provider == GOOGLE_HOSTED_DOMAIN) { + final String hostedDomain = getConfig(provider, "hostedDomain"); + if (hostedDomain == null) { + throw new IllegalArgumentException("'hostedDomain' for Google IdP must be specified"); + } + idp.getConfig().put("hostedDomain", hostedDomain); + } + if (provider == GOOGLE_NON_MATCHING_HOSTED_DOMAIN) { + idp.getConfig().put("hostedDomain", "non-matching-hosted-domain"); + } + if (provider == STACKOVERFLOW) { + idp.getConfig().put("key", getConfig(provider, "clientKey")); + } + if (provider == OPENSHIFT4 || provider == OPENSHIFT4_KUBE_ADMIN) { + idp.getConfig().put("baseUrl", getConfig(provider, "baseUrl")); + } + if (provider == MICROSOFT_SINGLE_TENANT) { + final String tenantId = getConfig(provider, "tenantId"); + if (tenantId == null) { + throw new IllegalArgumentException("'tenantId' for Microsoft IdP must be specified"); + } + idp.getConfig().put("tenantId", tenantId); + } + if (provider == PAYPAL) { + idp.getConfig().put("sandbox", getConfig(provider, "sandbox")); + } + if (provider == FACEBOOK_INCLUDE_BIRTHDAY) { + idp.getConfig().put("defaultScope", "public_profile,email,user_birthday"); + idp.getConfig().put("fetchedFields", "birthday"); + } + return idp; + } + + private void addAttributeMapper(String name, String jsonField) { + addAttributeMapper(name, jsonField, currentTestProvider.id + "-user-attribute-mapper"); + } + + private void addAttributeMapper(String name, String jsonField, String mapperName) { + IdentityProviderResource identityProvider = adminClient.realm(REALM).identityProviders().get(currentTestProvider.id); + IdentityProviderRepresentation identityProviderRepresentation = identityProvider.toRepresentation(); + //Add birthday mapper + IdentityProviderMapperRepresentation mapperRepresentation = new IdentityProviderMapperRepresentation(); + mapperRepresentation.setName(name); + mapperRepresentation.setIdentityProviderAlias(identityProviderRepresentation.getAlias()); + mapperRepresentation.setIdentityProviderMapper(mapperName); + mapperRepresentation.setConfig(ImmutableMap.builder() + .put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.IMPORT.toString()) + .put(AbstractJsonUserAttributeMapper.CONF_JSON_FIELD, jsonField) + .put(AbstractJsonUserAttributeMapper.CONF_USER_ATTRIBUTE, name) + .build()); + identityProvider.addMapper(mapperRepresentation).close(); + } + + private String getConfig(Provider provider, String key) { + String providerKey = provider.configId() + "." + key; + return System.getProperty("social." + providerKey, config.getProperty(providerKey, config.getProperty("common." + key))); + } + + private String getConfig(String key) { + return getConfig(currentTestProvider, key); + } + + private void performLogin() { + navigateToLoginPage(); + doLogin(); + } + + private void navigateToLoginPage() { + currentSocialLoginPage.logout(); // try to logout first to be sure we're not logged in + + driver.navigate().to( + currentTestProvider.equals(PAYPAL) + ? oauth.loginForm().build().replace("https://localhost", "https://127.0.0.1") + : oauth.loginForm().build() + ); + loginPage.clickSocial(currentTestProvider.id()); + + // Just to be sure there's no redirect in progress + WaitUtils.pause(3000); + WaitUtils.waitForPageToLoad(); + } + + private void doLogin() { + // Only when there's not active session for the social provider, i.e. login is required + if (URLUtils.currentUrlDoesntStartWith(getAuthServerRoot().toASCIIString())) { + log.infof("current URL: %s", driver.getCurrentUrl()); + log.infof("performing log in to '%s' ...", currentTestProvider.id()); + currentSocialLoginPage.login(getConfig("username"), getConfig("password")); + } else { + log.infof("already logged in to '%s'; skipping the login process", currentTestProvider.id()); + } + WaitUtils.pause(3000); + WaitUtils.waitForPageToLoad(); + } + + private void assertAttribute(String attrName, String expectedValue) { + List users = adminClient.realm(REALM).users().search(null, null, null); + assertEquals(1, users.size()); + assertNotNull(users.get(0).getAttributes()); + assertNotNull(users.get(0).getAttributes().get(attrName)); + String attrValue = users.get(0).getAttributes().get(attrName).get(0); + if (currentTestProvider.equals(LINKEDIN) && attrName.equals("picture")) { + assertTrue(attrValue.contains(expectedValue.replace("image/","image/v2/").replace("/0/","/"))); + } else { + assertEquals(expectedValue, attrValue); + } + } + + private void assertUpdateProfile(boolean firstName, boolean lastName, boolean email) { + assertTrue(URLUtils.currentUrlDoesntStartWith(oauth.APP_AUTH_ROOT)); + + if (firstName) { + assertTrue(updateAccountPage.fields().getFirstName().isEmpty()); + updateAccountPage.fields().setFirstName(getConfig("profile.firstName")); + } + else { + assertEquals(getConfig("profile.firstName"), updateAccountPage.fields().getFirstName()); + } + + if (lastName) { + assertTrue(updateAccountPage.fields().getLastName().isEmpty()); + updateAccountPage.fields().setLastName(getConfig("profile.lastName")); + } + else { + assertEquals(getConfig("profile.lastName"), updateAccountPage.fields().getLastName()); + } + + if (email) { + assertTrue(updateAccountPage.fields().getEmail().isEmpty()); + updateAccountPage.fields().setEmail(getConfig("profile.email")); + } + else { + assertEquals(getConfig("profile.email"), updateAccountPage.fields().getEmail()); + } + + updateAccountPage.submit(); + } + + private WebTarget getExchangeUrl(Client httpClient) { + return httpClient.target(OAuthClient.AUTH_SERVER_ROOT) + .path("/realms") + .path(REALM) + .path("protocol/openid-connect/token"); + } + + private AccessTokenResponse checkFeature(int expectedStatusCode, String username) { + Client httpClient = AdminClientUtil.createResteasyClient(); + Response response = null; + try { + testingClient.server(REALM).run(SocialLoginTest::setupClientExchangePermissions); + + WebTarget exchangeUrl = getExchangeUrl(httpClient); + response = exchangeUrl.request() + .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(EXCHANGE_CLIENT, "secret")) + .post(Entity.form( + new Form() + .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE) + .param(OAuth2Constants.REQUESTED_SUBJECT, username) + .param(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE) + .param(OAuth2Constants.REQUESTED_ISSUER, currentTestProvider.id()) + )); + Assertions.assertEquals(expectedStatusCode, response.getStatus()); + if (expectedStatusCode == Response.Status.OK.getStatusCode()) + return response.readEntity(AccessTokenResponse.class); + else + return null; + } finally { + if (response != null) + response.close(); + httpClient.close(); + } + } + + protected void testTokenExchange() { + List users = adminClient.realm(REALM).users().search(null, null, null); + Assertions.assertEquals(1, users.size()); + + String username = users.get(0).getUsername(); + checkFeature(400, username); + + testingClient.enableFeature(Profile.Feature.TOKEN_EXCHANGE); + + Client httpClient = AdminClientUtil.createResteasyClient(); + + try { + AccessTokenResponse tokenResponse = checkFeature(200, username); + Assertions.assertNotNull(tokenResponse); + String socialToken = tokenResponse.getToken(); + Assertions.assertNotNull(socialToken); + + // remove all users + removeUser(); + + users = adminClient.realm(REALM).users().search(null, null, null); + Assertions.assertEquals(0, users.size()); + + // now try external exchange where we trust social provider and import the external token. + Response response = getExchangeUrl(httpClient).request() + .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(EXCHANGE_CLIENT, "secret")) + .post(Entity.form( + new Form() + .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE) + .param(OAuth2Constants.SUBJECT_TOKEN, socialToken) + .param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE) + .param(OAuth2Constants.SUBJECT_ISSUER, currentTestProvider.id()) + )); + Assertions.assertEquals(200, response.getStatus()); + response.close(); + + users = adminClient.realm(REALM).users().search(null, null, null); + Assertions.assertEquals(1, users.size()); + + Assertions.assertEquals(username, users.get(0).getUsername()); + + // remove all users + removeUser(); + + users = adminClient.realm(REALM).users().search(null, null, null); + Assertions.assertEquals(0, users.size()); + + ///// Test that we can update social token from session with stored tokens turned off. + + // turn off store token + IdentityProviderRepresentation idp = adminClient.realm(REALM).identityProviders().get(currentTestProvider.id).toRepresentation(); + idp.setStoreToken(false); + adminClient.realm(REALM).identityProviders().get(idp.getAlias()).update(idp); + + // first exchange social token to get a user session that should store the social token there + response = getExchangeUrl(httpClient).request() + .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(EXCHANGE_CLIENT, "secret")) + .post(Entity.form( + new Form() + .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE) + .param(OAuth2Constants.SUBJECT_TOKEN, socialToken) + .param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE) + .param(OAuth2Constants.SUBJECT_ISSUER, currentTestProvider.id()) + )); + Assertions.assertEquals(200, response.getStatus()); + tokenResponse = response.readEntity(AccessTokenResponse.class); + String keycloakToken = tokenResponse.getToken(); + response.close(); + + // now take keycloak token and make sure it can get back the social token from the user session since stored tokens are off + response = getExchangeUrl(httpClient).request() + .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(EXCHANGE_CLIENT, "secret")) + .post(Entity.form( + new Form() + .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE) + .param(OAuth2Constants.SUBJECT_TOKEN, keycloakToken) + .param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE) + .param(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE) + .param(OAuth2Constants.REQUESTED_ISSUER, currentTestProvider.id()) + )); + Assertions.assertEquals(200, response.getStatus()); + tokenResponse = response.readEntity(AccessTokenResponse.class); + response.close(); + + Assertions.assertEquals(socialToken, tokenResponse.getToken()); + // turn on store token + idp = adminClient.realm(REALM).identityProviders().get(currentTestProvider.id).toRepresentation(); + idp.setStoreToken(true); + adminClient.realm(REALM).identityProviders().get(idp.getAlias()).update(idp); + } finally { + httpClient.close(); + testingClient.disableFeature(Profile.Feature.TOKEN_EXCHANGE); + checkFeature(400, username); + } + } +} From 9bac17afe995c181798db80a701399fa5ee3989c Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 24 Aug 2026 11:05:09 +0200 Subject: [PATCH 7/9] Remove migrated broker tests from legacy base suites Drop broker test entries that were moved to the new tests/base module from integration-arquillian suite lists so suite.sh no longer aborts CI jobs that still execute legacy base suites. Signed-off-by: Erik Jan de Wit --- .../tests/base/testsuites/clusterless-suite | 3 --- .../tests/base/testsuites/fips-suite | 15 --------------- .../tests/base/testsuites/jdk-suite | 1 - .../tests/base/testsuites/login-suite | 3 --- .../tests/base/testsuites/volatile-sessions-suite | 3 --- 5 files changed, 25 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/testsuites/clusterless-suite b/testsuite/integration-arquillian/tests/base/testsuites/clusterless-suite index acf5cd5ba1eb..538325dad040 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/clusterless-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/clusterless-suite @@ -1,6 +1,3 @@ -KcSamlBrokerSessionNotOnOrAfterTest -OidcClaimToUserSessionNoteMapperTest -KcOidcBrokerTransientSessionsTest KcAdmSessionTest TransientSessionTest OAuthDanceClientSessionExtensionTest diff --git a/testsuite/integration-arquillian/tests/base/testsuites/fips-suite b/testsuite/integration-arquillian/tests/base/testsuites/fips-suite index 471f274b9e9c..63919390e683 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/fips-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/fips-suite @@ -13,19 +13,4 @@ KcAdmTest KcAdmCreateTest SAMLServletAdapterTest SamlSignatureTest -KcOidcBrokerJWEEcdhEsA128KwP256A128CbcHs256Test -KcOidcBrokerJWEEcdhEsA128KwP256A128GcmTest -KcOidcBrokerJWEEcdhEsA192KwP384A192CbcHs384Test -KcOidcBrokerJWEEcdhEsA192KwP384A192GcmTest -KcOidcBrokerJWEEcdhEsA256KwP521A256CbcHs512Test -KcOidcBrokerJWEEcdhEsA256KwP521A256GcmTest -KcOidcBrokerJWEEcdhEsP384A192CbcHs384Test -KcOidcBrokerJWEEcdhEsP384A192GcmTest -KcOidcBrokerJWETest -KcOidcBrokerJWEUserInfoJustEncryptedTest -KcSamlBrokerTest -KcSamlFirstBrokerLoginTest -KcSamlEncryptedIdTest -KcSamlSignedBrokerTest -KcSamlSpDescriptorTest KerberosLdapTest diff --git a/testsuite/integration-arquillian/tests/base/testsuites/jdk-suite b/testsuite/integration-arquillian/tests/base/testsuites/jdk-suite index ba87fe2bcd67..494a778d1b84 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/jdk-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/jdk-suite @@ -1,5 +1,4 @@ DeployedScriptAuthenticatorTest -KcOidcBrokerTest KerberosLdapTest LDAPUserLoginTest X509BrowserLoginTest diff --git a/testsuite/integration-arquillian/tests/base/testsuites/login-suite b/testsuite/integration-arquillian/tests/base/testsuites/login-suite index 589db0046cc1..810cbbb67f4c 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/login-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/login-suite @@ -1,5 +1,2 @@ org.keycloak.testsuite.actions org.keycloak.testsuite.forms -KcOidcBrokerLoginHintTest -KcOidcFirstBrokerLoginTest -KcOidcMultipleTabsBrokerTest diff --git a/testsuite/integration-arquillian/tests/base/testsuites/volatile-sessions-suite b/testsuite/integration-arquillian/tests/base/testsuites/volatile-sessions-suite index acf5cd5ba1eb..538325dad040 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/volatile-sessions-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/volatile-sessions-suite @@ -1,6 +1,3 @@ -KcSamlBrokerSessionNotOnOrAfterTest -OidcClaimToUserSessionNoteMapperTest -KcOidcBrokerTransientSessionsTest KcAdmSessionTest TransientSessionTest OAuthDanceClientSessionExtensionTest From 09c36c58bf941c87d471d699095cbfd325b5a6bf Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 24 Aug 2026 11:11:24 +0200 Subject: [PATCH 8/9] Make AdminClientUtil jackson3-compatible Replace the hard dependency on ResteasyJackson2Provider with a generic ObjectMapper ContextResolver so tests/base compiles under the jackson3 profile used by CI. Signed-off-by: Erik Jan de Wit --- .../org/keycloak/testsuite/util/AdminClientUtil.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java b/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java index 04d2ee1059e8..385dfb2e7081 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/AdminClientUtil.java @@ -26,6 +26,8 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import jakarta.ws.rs.ext.ContextResolver; + import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.KeycloakBuilder; import org.keycloak.models.Constants; @@ -41,7 +43,6 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.engines.ClientHttpEngineBuilder43; -import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider; import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; @@ -100,13 +101,10 @@ public static ResteasyClient createResteasyClient(boolean ignoreUnknownPropertie // We need to ignore unknown JSON properties e.g. in the adapter configuration representation // during adapter backward compatibility testing if (ignoreUnknownProperties) { - // We need to use anonymous class to avoid the following error from RESTEasy: - // Provider class org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider is already registered. 2nd registration is being ignored. - ResteasyJackson2Provider jacksonProvider = new ResteasyJackson2Provider() {}; ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - jacksonProvider.setMapper(objectMapper); - resteasyClientBuilder.register(jacksonProvider, 100); + // Register only a context resolver so this utility works with both Jackson2 and Jackson3 profiles. + resteasyClientBuilder.register((ContextResolver) type -> objectMapper, 100); } resteasyClientBuilder From c1c9102435b8cbed4b8cebdcb0de9fdd97594b4d Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 26 Aug 2026 13:27:05 +0200 Subject: [PATCH 9/9] test hardening Signed-off-by: Erik Jan de Wit --- .../testframework/oauth/OAuthClient.java | 14 +- .../ui/page/LoginConfigTotpPage.java | 8 +- .../ui/page/LoginUpdateProfilePage.java | 6 +- .../testframework/ui/webdriver/WaitUtils.java | 2 +- .../ui/page/UpdateAccountInformationPage.java | 17 ++ .../broker/AbstractAdvancedBrokerTest.java | 147 ++++++++-- .../tests/broker/AbstractBaseBrokerTest.java | 252 ++++++++++++++++-- .../tests/broker/AbstractBrokerTest.java | 25 +- .../AbstractInitializedBaseBrokerTest.java | 5 + .../tests/broker/AccountLinkTest.java | 24 +- .../tests/broker/BrokerTestTools.java | 26 +- .../tests/broker/BrokerVaultServerConfig.java | 22 ++ .../broker/KcOidcBrokerConfiguration.java | 4 +- .../tests/broker/KcOidcBrokerEventTest.java | 201 +++++++++----- .../broker/KcOidcBrokerFrontendUrlTest.java | 6 +- .../broker/KcOidcBrokerLdapReadOnlyTest.java | 23 +- .../tests/broker/KcOidcBrokerLdapTest.java | 23 +- .../KcOidcBrokerPromptNoneRedirectTest.java | 13 +- .../KcOidcBrokerPromptParameterTest.java | 14 +- .../tests/broker/KcOidcBrokerVaultTest.java | 4 +- .../broker/KcOidcBrokerWithConsentTest.java | 7 +- .../broker/KcOidcMultipleTabsBrokerTest.java | 7 +- .../KcSamlBrokerArtifactBindingTest.java | 17 +- .../broker/KcSamlBrokerConfiguration.java | 26 +- .../broker/KcSamlBrokerFrontendUrlTest.java | 24 +- .../tests/broker/KcSamlBrokerTest.java | 6 +- .../broker/KcSamlIdPInitiatedSsoTest.java | 61 +++-- ...lMetadataSignedAndEncryptedBrokerTest.java | 4 +- .../KcSamlMetadataSignedBrokerTest.java | 4 +- .../broker/KcSamlMultipleTabsBrokerTest.java | 7 +- .../broker/KcSamlParseIdPDescriptorTest.java | 45 ++-- .../tests/broker/OAuth2BrokerTest.java | 12 +- ...cBackchannelLogoutBrokerConfiguration.java | 6 +- .../tests/broker/SocialLoginTest.java | 36 ++- .../IdentityProviderAttributeUpdater.java | 7 + .../keycloak/testsuite/util/DroneUtils.java | 5 +- .../testsuite/util/MutualTLSUtils.java | 69 ++++- .../keycloak/testsuite/util/ReverseProxy.java | 32 ++- .../keycloak/testsuite/util/SamlClient.java | 42 +++ .../testsuite/util/saml/LoginBuilder.java | 127 ++++++++- .../tests/broker/vault/consumer_oidc__idp | 1 + .../org/keycloak/tests/broker/vault/oidc__idp | 1 + ...eycloak.storage.UserStorageProviderFactory | 3 +- 43 files changed, 1108 insertions(+), 277 deletions(-) create mode 100644 tests/base/src/test/java/org/keycloak/tests/broker/BrokerVaultServerConfig.java create mode 100644 tests/base/src/test/resources/org/keycloak/tests/broker/vault/consumer_oidc__idp create mode 100644 tests/base/src/test/resources/org/keycloak/tests/broker/vault/oidc__idp diff --git a/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java b/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java index 42e60f07decf..798e07f52b09 100644 --- a/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java +++ b/test-framework/oauth/src/main/java/org/keycloak/testframework/oauth/OAuthClient.java @@ -58,8 +58,18 @@ private static String removeDefaultPorts(String url) { } public static void updateURLs(String serverRoot) { - SERVER_ROOT = removeDefaultPorts(serverRoot); - AUTH_SERVER_ROOT = SERVER_ROOT + "/auth"; + String normalizedRoot = removeDefaultPorts(serverRoot); + if (normalizedRoot != null) { + normalizedRoot = normalizedRoot.replaceAll("/+$", ""); + } + + if (normalizedRoot != null && normalizedRoot.endsWith("/auth")) { + AUTH_SERVER_ROOT = normalizedRoot; + SERVER_ROOT = normalizedRoot.substring(0, normalizedRoot.length() - "/auth".length()); + } else { + SERVER_ROOT = normalizedRoot; + AUTH_SERVER_ROOT = normalizedRoot; + } updateAppRootRealm("master"); } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java index b43895a234f3..0923bb2f22ed 100755 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginConfigTotpPage.java @@ -36,7 +36,7 @@ public class LoginConfigTotpPage extends AbstractLoginPage { @FindBy(id = "userLabel") private WebElement totpLabelInput; - @FindBy(css = "input[type=\"submit\"]") + @FindBy(css = "input[type=\"submit\"], button[type=\"submit\"]") private WebElement submitButton; @FindBy(name = "cancel-aia") @@ -82,7 +82,11 @@ public void configure(String totp, String userLabel) { } public void submit() { - submitButton.click(); + try { + submitButton.click(); + } catch (NoSuchElementException e) { + submitButton.submit(); + } } public void cancel() { diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginUpdateProfilePage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginUpdateProfilePage.java index f03b6164ba05..68e717766c7a 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginUpdateProfilePage.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginUpdateProfilePage.java @@ -286,7 +286,11 @@ public void submit() { } } - page.submitButton.submit(); + try { + page.submitButton.click(); + } catch (RuntimeException e) { + page.submitButton.submit(); + } } } diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/WaitUtils.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/WaitUtils.java index 7c84821c2589..cb48c60e50b2 100644 --- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/WaitUtils.java +++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/WaitUtils.java @@ -14,7 +14,7 @@ public class WaitUtils { - private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5); + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); private static final Duration POLL_INTERVAL = Duration.ofMillis(50); private final ManagedWebDriver managed; diff --git a/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java b/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java index 9456f81d1a00..50a5ce036170 100644 --- a/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java +++ b/tests/base/src/test/java/org/keycloak/testframework/ui/page/UpdateAccountInformationPage.java @@ -2,6 +2,9 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.TimeoutException; + public class UpdateAccountInformationPage extends LoginUpdateProfilePage { public UpdateAccountInformationPage(ManagedWebDriver driver) { @@ -28,4 +31,18 @@ public void updateAccountInformation(String firstName, String lastName) { public String getExpectedPageId() { return "login-idp-review-user-profile"; } + + @Override + public void assertCurrent() { + try { + driver.waiting().until(d -> { + String currentPageId = driver.page().getCurrentPageId(); + return "login-idp-review-user-profile".equals(currentPageId) + || "login-login-update-profile".equals(currentPageId); + }); + } catch (TimeoutException e) { + Assertions.fail("Expected page 'login-idp-review-user-profile' or 'login-login-update-profile' to be loaded, but currently on page '" + + driver.page().getCurrentPageId() + "' after timeout"); + } + } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java index bd4a101889a8..6b9b9186bf09 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractAdvancedBrokerTest.java @@ -4,6 +4,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import jakarta.ws.rs.core.Response; @@ -12,11 +16,14 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.common.util.Time; +import org.keycloak.executors.ExecutorsProvider; import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.models.RealmModel; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; @@ -32,14 +39,13 @@ import org.keycloak.testframework.realm.ClientBuilder; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.testframework.remote.providers.runonserver.RunOnServer; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.tests.providers.federation.DummyUserFederationProviderFactory; import org.keycloak.testsuite.util.AccountHelper; -import org.keycloak.testsuite.util.TestAppHelper; -import org.keycloak.testsuite.util.WaitUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -47,6 +53,7 @@ import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; import static org.keycloak.tests.broker.BrokerRunOnServerUtil.disablePostBrokerLoginFlow; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; import static org.keycloak.tests.broker.BrokerTestTools.waitForElementEnabled; import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; @@ -119,23 +126,20 @@ public void testAccountManagementLinkIdentity() { assumeFalse("Account linking does not apply to transient sessions", isUsingTransientSessions()); createUser("consumer"); - TestAppHelper testAppHelper = new TestAppHelper(oauth, loginPage); // Link identity provider through Admin REST api - Response response = AccountHelper.addIdentityProvider(adminClient.realm(bc.consumerRealmName()), "consumer", adminClient.realm(bc.providerRealmName()), bc.getUserLogin(), bc.getIDPAlias()); + Response response = addIdentityProviderLink("consumer", bc.getUserLogin()); Assertions.assertEquals(204, response.getStatus(), "status"); // Assert identity is linked through Admin REST api assertTrue(AccountHelper.isIdentityProviderLinked(adminClient.realm(bc.consumerRealmName()), "consumer", bc.getIDPAlias())); AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), "consumer"); + driver.manage().deleteAllCookies(); // Assert I am logged immediately into app page due to previously linked "test-user" identity - testAppHelper.login(bc.getUserLogin(), bc.getUserPassword(), bc.consumerRealmName(), "broker-app", bc.getIDPAlias()); - if (!oauth.parseLoginResponse().isSuccess()) { - updateAccountInformationPage.assertCurrent(); - updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); - } + initiateBrokerAppLogin(bc.getUserLogin(), bc.getUserPassword()); + ensureBrokerLoginCompleted(); // Unlink idp from consumer AccountHelper.deleteIdentityProvider(adminClient.realm(bc.consumerRealmName()), "consumer", bc.getIDPAlias()); @@ -144,9 +148,10 @@ public void testAccountManagementLinkIdentity() { // Logout from account management AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), "consumer"); AccountHelper.logout(adminClient.realm(bc.providerRealmName()), "testuser"); + driver.manage().deleteAllCookies(); // Assert I am not logged immediately into app page and first-broker-login appears instead - testAppHelper.login(bc.getUserLogin(), bc.getUserPassword(), bc.consumerRealmName(), "broker-app", bc.getIDPAlias()); + initiateBrokerAppLogin(bc.getUserLogin(), bc.getUserPassword()); updateAccountInformationPage.assertCurrent(); updateAccountInformationPage.updateAccountInformation("FirstName", "LastName"); @@ -154,7 +159,9 @@ public void testAccountManagementLinkIdentity() { idpConfirmLinkPage.clickLinkAccount(); loginPage.login(bc.getUserPassword()); - Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + boolean success = oauth.parseLoginResponse().isSuccess(); + Assertions.assertTrue(success, "Expected OAuth login response success. url=" + driver.getCurrentUrl() + + ", pageId=" + driver.page().getCurrentPageId()); assertTrue(AccountHelper.isIdentityProviderLinked(adminClient.realm(bc.consumerRealmName()), "consumer", bc.getIDPAlias())); // Unlink my "test-user" @@ -164,9 +171,10 @@ public void testAccountManagementLinkIdentity() { // Logout from account management AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), "consumer"); AccountHelper.logout(adminClient.realm(bc.providerRealmName()), "testuser"); + driver.manage().deleteAllCookies(); //Try to log in. Previous link is not valid anymore, so now it should try to register new user instead of logging into app page - testAppHelper.login(bc.getUserLogin(), bc.getUserPassword(), bc.consumerRealmName(), "broker-app", bc.getIDPAlias()); + initiateBrokerAppLogin(bc.getUserLogin(), bc.getUserPassword()); updateAccountInformationPage.assertCurrent(); } @@ -177,15 +185,14 @@ public void testAccountManagementLinkIdentity() { public void testAccountManagementLinkedIdentityAlreadyExists() { updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin); createUser(bc.consumerRealmName(), "consumer", "password", "FirstName", "LastName", "consumer@localhost.com"); - TestAppHelper testAppHelper = new TestAppHelper(oauth, loginPage); // Link identity provider through Admin REST api - Response response = AccountHelper.addIdentityProvider(adminClient.realm(bc.consumerRealmName()), "consumer", adminClient.realm(bc.providerRealmName()), bc.getUserLogin(), bc.getIDPAlias()); + Response response = addIdentityProviderLink("consumer", bc.getUserLogin()); Assertions.assertEquals(204, response.getStatus(), "status"); // Test we will log in immediately into app page - testAppHelper.login(bc.getUserLogin(), bc.getUserPassword(), bc.consumerRealmName(), "broker-app", bc.getIDPAlias()); - Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + initiateBrokerAppLogin(bc.getUserLogin(), bc.getUserPassword()); + ensureBrokerLoginCompleted(); } // KEYCLOAK-3267 @@ -227,7 +234,7 @@ public void loginWithExistingUserWithBruteForceEnabled() { assertEquals("Invalid username or password.", loginPage.getInputError()); - WaitUtils.waitForBruteForceExecutors(testingClient); + waitForBruteForceExecutorsInConsumerRealm(); loginPage.clickSocial(bc.getIDPAlias()); @@ -239,7 +246,7 @@ public void loginWithExistingUserWithBruteForceEnabled() { Assertions.fail("Timeout while waiting for login page"); } - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"), "Driver should be on the provider realm page right now"); + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.providerRealmName() + "/"), "Driver should be on the provider realm page right now"); loginPage.login(bc.getUserLogin(), bc.getUserPassword()); @@ -449,7 +456,7 @@ public void testExpiredCode() { waitForPage(driver, "sorry", false); errorPage.assertCurrent(); String link = errorPage.getBackToApplicationLink(); - Assertions.assertTrue(link.contains("/auth/realms/" + bc.consumerRealmName() + "/app")); + Assertions.assertTrue(link.contains(getAuthPath() + "/realms/" + bc.consumerRealmName() + "/app")); } /** @@ -460,7 +467,7 @@ public void testPostBrokerLoginFlowWithOTP() { assumeFalse("Password / OTP setup does not apply to transient sessions as there is no persistent user to log in twice", isUsingTransientSessions()); updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin); - testingClient.server(bc.consumerRealmName()).run(configurePostBrokerLoginWithOTP(bc.getIDPAlias())); + runOnConsumerRealm(configurePostBrokerLoginWithOTP(bc.getIDPAlias())); oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); @@ -491,7 +498,7 @@ public void testPostBrokerLoginFlowWithOTP() { AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), bc.getUserLogin()); AccountHelper.logout(adminClient.realm(bc.providerRealmName()), bc.getUserLogin()); - testingClient.server(bc.consumerRealmName()).run(disablePostBrokerLoginFlow(bc.getIDPAlias())); + runOnConsumerRealm(disablePostBrokerLoginFlow(bc.getIDPAlias())); oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); @@ -533,7 +540,7 @@ public void testLogoutWorksWithTokenTimeout() { waitForPage(driver, "sign in to", true); log.debug("Logging in"); - assertTrue(this.driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/protocol/openid-connect/auth")); + assertTrue(this.driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.consumerRealmName() + "/protocol/openid-connect/auth")); } finally { Time.setOffset(0); } @@ -615,11 +622,105 @@ public void testDisabledBroker() { oauth.openLoginForm(); loginPage.clickSocial(bc.getIDPAlias()); waitForPage(driver, "sign in to", true); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"), "Driver should be on the provider realm page right now"); + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.providerRealmName() + "/"), "Driver should be on the provider realm page right now"); idpRep.setEnabled(false); identityProviderResource.update(idpRep); loginPage.login(bc.getUserLogin(), bc.getUserPassword()); errorPage.assertCurrent(); assertThat(errorPage.getError(), is("Page not found")); } + + private void runOnConsumerRealm(RunOnServer function) { + final String consumerRealmName = bc.consumerRealmName(); + runOnServer.run(session -> { + RealmModel realm = session.realms().getRealmByName(consumerRealmName); + if (realm == null) { + throw new IllegalStateException("Realm not found: " + consumerRealmName); + } + session.getContext().setRealm(realm); + function.run(session); + }); + } + + private void waitForBruteForceExecutorsInConsumerRealm() { + runOnConsumerRealm(session -> { + ExecutorsProvider provider = session.getProvider(ExecutorsProvider.class); + ExecutorService executor = provider.getExecutor("bruteforce"); + ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; + try { + CompletableFuture.runAsync(() -> { + do { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } while (!threadPoolExecutor.getQueue().isEmpty() || threadPoolExecutor.getActiveCount() > 0); + }).get(30, TimeUnit.SECONDS); + } catch (java.util.concurrent.TimeoutException te) { + Assertions.fail("Timeout while waiting for brute force executors!"); + } catch (Exception e) { + Assertions.fail("Unexpected error while waiting for brute force executors!"); + } + assertEquals(0, threadPoolExecutor.getActiveCount()); + }); + } + + private void initiateBrokerAppLogin(String username, String password) { + oauth.client("broker-app"); + oauth.realm(bc.consumerRealmName()); + oauth.openLoginForm(); + loginPage.clickSocial(bc.getIDPAlias()); + loginPage.fillLogin(username, password); + loginPage.submit(); + } + + private void ensureBrokerLoginCompleted() { + if (parseLoginResponseSafely()) { + return; + } + + String currentPageId = driver.page().getCurrentPageId(); + if ("login-idp-review-user-profile".equals(currentPageId) || "login-login-update-profile".equals(currentPageId)) { + updateAccountInformation(); + } + + currentPageId = driver.page().getCurrentPageId(); + if (idpConfirmLinkPage.getExpectedPageId().equals(currentPageId)) { + idpConfirmLinkPage.clickLinkAccount(); + } + + currentPageId = driver.page().getCurrentPageId(); + if (loginPage.getExpectedPageId().equals(currentPageId) && loginPage.isPasswordInputPresent()) { + loginPage.login(bc.getUserPassword()); + } + + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } + + private boolean parseLoginResponseSafely() { + try { + return oauth.parseLoginResponse().isSuccess(); + } catch (AssertionError error) { + return false; + } + } + + private Response addIdentityProviderLink(String consumerUsername, String providerUsername) { + RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName()); + RealmResource providerRealm = adminClient.realm(bc.providerRealmName()); + + UserRepresentation consumerUser = AccountHelper.getUserRepresentation(consumerRealm, consumerUsername); + FederatedIdentityRepresentation identity = new FederatedIdentityRepresentation(); + identity.setIdentityProvider(bc.getIDPAlias()); + identity.setUserName(providerUsername); + + if (BrokerTestConstants.IDP_SAML_ALIAS.equals(bc.getIDPAlias())) { + identity.setUserId(providerUsername); + } else { + identity.setUserId(AccountHelper.getUserRepresentation(providerRealm, providerUsername).getId()); + } + + return consumerRealm.users().get(consumerUser.getId()).addFederatedIdentity(bc.getIDPAlias(), identity); + } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java index 11014669a1b3..4da9ab78cce8 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBaseBrokerTest.java @@ -24,11 +24,15 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; @@ -37,6 +41,7 @@ import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.Profile; import org.keycloak.common.util.Retry; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.protocol.saml.SamlProtocol; @@ -69,23 +74,32 @@ import org.keycloak.testframework.ui.page.UpdateAccountInformationPage; import org.keycloak.testframework.ui.page.VerifyEmailPage; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import org.keycloak.testsuite.util.oauth.LogoutUrlBuilder; import org.keycloak.testsuite.util.userprofile.UserProfileUtil; +import org.apache.http.Header; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; import org.hamcrest.Matchers; import org.jboss.logging.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.support.ui.WebDriverWait; import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; import static org.keycloak.tests.broker.BrokerTestTools.encodeUrl; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; @@ -122,6 +136,8 @@ public abstract class AbstractBaseBrokerTest { protected final CompatibilityTestContext testContext = new CompatibilityTestContext(); protected final AtomicInteger timeOffSet = new AtomicInteger(); private final Map cleanups = new HashMap<>(); + private final Set additionalRealmNames = new LinkedHashSet<>(); + private final Set enabledFeatures = new HashSet<>(); protected static final String ATTRIBUTE_VALUE = "attribute.value"; @@ -242,7 +258,9 @@ public void beforeBrokerTest() { String authServerRoot = resolveAuthServerContextRoot(); testingClient = KeycloakTestingClient.getInstance(authServerRoot); OAuthClient.updateURLs(authServerRoot); + OAuthClient.updateAppRootRealm(bc.consumerRealmName()); oauth.init(); + enableAnnotatedFeatures(); RealmRepresentation consumerRealm = bc.createConsumerRealm(); RealmRepresentation providerRealm = bc.createProviderRealm(); @@ -251,19 +269,28 @@ public void beforeBrokerTest() { UserProfileUtil.enableUnmanagedAttributes(adminClient.realm(consumerRealm.getRealm()).users().userProfile()); UserProfileUtil.enableUnmanagedAttributes(adminClient.realm(providerRealm.getRealm()).users().userProfile()); + importAdditionalTestRealms(consumerRealm.getRealm(), providerRealm.getRealm()); } @AfterEach public void cleanupUsers() { cleanups.values().forEach(cleanup -> cleanup.execute(adminClient)); cleanups.clear(); + deleteAllCookiesForRealm(bc.consumerRealmName()); + try { + adminClient.realm(bc.consumerRealmName()).remove(); + } catch (Exception ignored) { + } + try { + adminClient.realm(bc.providerRealmName()).remove(); + } catch (Exception ignored) { + } + removeAdditionalTestRealms(); + resetEnabledFeatures(); if (testingClient != null) { testingClient.close(); testingClient = null; } - deleteAllCookiesForRealm(bc.consumerRealmName()); - adminClient.realm(bc.consumerRealmName()).remove(); - adminClient.realm(bc.providerRealmName()).remove(); } protected String createUser(String username, String email) { @@ -360,13 +387,27 @@ protected void logInWithBroker(BrokerConfiguration bc) { protected void logInWithIdp(String idpAlias, String username, String password) { waitForPage(driver, "sign in to", true); + String initialUrl = driver.getCurrentUrl(); log.debug("Clicking social " + idpAlias); loginPage.clickSocial(idpAlias); - new WebDriverWait(driver.driver(), Duration.ofSeconds(10)).until(webDriver -> - !webDriver.findElements(By.id("username")).isEmpty() - || !webDriver.findElements(By.id("password")).isEmpty() - || webDriver.getCurrentUrl().contains("/broker/" + idpAlias + "/endpoint") - || webDriver.getCurrentUrl().contains("/login-actions/first-broker-login")); + try { + new WebDriverWait(driver.driver(), Duration.ofSeconds(10)).until(webDriver -> + !webDriver.getCurrentUrl().equals(initialUrl)); + + String brokerLoginSegment = "/broker/" + idpAlias + "/login"; + if (driver.getCurrentUrl().contains(brokerLoginSegment)) { + List forms = driver.findElements(By.tagName("form")); + if (!forms.isEmpty()) { + forms.get(0).submit(); + } + new WebDriverWait(driver.driver(), Duration.ofSeconds(10)).until(webDriver -> + !webDriver.getCurrentUrl().contains(brokerLoginSegment)); + } + } catch (TimeoutException e) { + Assertions.fail("Timed out waiting for social redirect for " + idpAlias + ". URL: " + driver.getCurrentUrl() + + ", page: " + driver.findElement(By.tagName("body")).getText() + + ", source: " + driver.getPageSource()); + } if (loginPage.isUsernameInputPresent()) { log.debug("Logging in with username and password"); loginPage.login(username, password); @@ -400,23 +441,145 @@ protected void logInAsUserInIDPForFirstTime() { protected void logInAsUserInIDPForFirstTimeAndAssertSuccess() { logInAsUserInIDPForFirstTime(); - Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + try { + Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); + } catch (AssertionError e) { + String currentUrl = driver.getCurrentUrl(); + String currentPageId = driver.page().getCurrentPageId(); + String alert = updateAccountInformationPage.getAlertError(); + String body = driver.findElement(By.tagName("body")).getText(); + String source = driver.getPageSource(); + Assertions.fail("First broker login did not reach OAuth callback. url=" + currentUrl + + ", pageId=" + currentPageId + + ", alert=" + alert + + ", body=" + body + + ", source=" + source, e); + } } protected void updateAccountInformation() { waitForPage(driver, "update account information", false); updateAccountInformationPage.assertCurrent(); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), + String authPath = getAuthPath(); + String consumerRealmPath = authPath + "/realms/" + bc.consumerRealmName() + "/"; + String providerRequiredActionPath = authPath + "/realms/" + bc.providerRealmName() + "/login-actions/required-action"; + String currentUrl = driver.getCurrentUrl(); + Assertions.assertTrue(currentUrl.contains(consumerRealmPath) || currentUrl.contains(providerRequiredActionPath), "We must be on correct realm right now"); log.debug("Updating info on updateAccount page"); - updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + setInputValueIfPresent(By.id("username"), bc.getUserLogin()); + setInputValueIfPresent(By.name("email"), bc.getUserEmail()); + setInputValueIfPresent(By.name("firstName"), "Firstname"); + setInputValueIfPresent(By.name("lastName"), "Lastname"); + log.info("Profile values before submit: username=" + readInputValue(By.id("username")) + + ", email=" + readInputValue(By.name("email")) + + ", firstName=" + readInputValue(By.name("firstName")) + + ", lastName=" + readInputValue(By.name("lastName"))); + + if (!submitFormIfPresent(By.id("kc-idp-review-profile-form")) + && !submitFormIfPresent(By.id("kc-update-profile-form"))) { + submitFormIfPresent(By.cssSelector("form")); + } + + if (driver.getCurrentUrl().contains("/login-actions/first-broker-login")) { + submitProfileFormThroughHttp(); + } + } + + private void setInputValueIfPresent(By locator, String value) { + List elements = driver.findElements(locator); + if (elements.isEmpty()) { + return; + } + org.openqa.selenium.WebElement element = elements.stream() + .filter(org.openqa.selenium.WebElement::isDisplayed) + .findFirst() + .orElse(elements.get(0)); + element.click(); + element.clear(); + element.sendKeys(value); + if (!value.equals(element.getAttribute("value")) && driver.driver() instanceof JavascriptExecutor javascriptExecutor) { + javascriptExecutor.executeScript("arguments[0].value = arguments[1];", element, value); + } + } + + private boolean submitFormIfPresent(By locator) { + List forms = driver.findElements(locator); + if (forms.isEmpty()) { + return false; + } + org.openqa.selenium.WebElement form = forms.get(0); + String currentUrl = driver.getCurrentUrl(); + List submitButtons = form.findElements(By.cssSelector("input[type='submit'], button[type='submit']")); + if (!submitButtons.isEmpty()) { + submitButtons.get(0).click(); + try { + new WebDriverWait(driver.driver(), Duration.ofSeconds(1)).until(webDriver -> !webDriver.getCurrentUrl().equals(currentUrl)); + } catch (TimeoutException ignore) { + if (driver.driver() instanceof JavascriptExecutor javascriptExecutor) { + javascriptExecutor.executeScript("arguments[0].submit();", form); + } else { + form.submit(); + } + } + return true; + } + if (driver.driver() instanceof JavascriptExecutor javascriptExecutor) { + javascriptExecutor.executeScript("arguments[0].submit();", form); + return true; + } + form.submit(); + return true; + } + + private String readInputValue(By locator) { + List elements = driver.findElements(locator); + if (elements.isEmpty()) { + return null; + } + return elements.get(0).getAttribute("value"); + } + + private void submitProfileFormThroughHttp() { + List forms = driver.findElements(By.cssSelector("#kc-idp-review-profile-form, #kc-update-profile-form")); + if (forms.isEmpty()) { + return; + } + + String actionUrl = forms.get(0).getAttribute("action"); + if (actionUrl == null || actionUrl.isBlank()) { + return; + } + + String cookieHeader = driver.manage().getCookies().stream() + .map(cookie -> cookie.getName() + "=" + cookie.getValue()) + .collect(Collectors.joining("; ")); + + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpUriRequest request = RequestBuilder.post(actionUrl) + .addParameter("username", bc.getUserLogin()) + .addParameter("email", bc.getUserEmail()) + .addParameter("firstName", "Firstname") + .addParameter("lastName", "Lastname") + .addHeader("Cookie", cookieHeader) + .build(); + + try (CloseableHttpResponse response = httpClient.execute(request)) { + Header location = response.getFirstHeader("Location"); + if (location != null && location.getValue() != null && !location.getValue().isBlank()) { + driver.navigate().to(location.getValue()); + } + } + } catch (Exception e) { + log.debug("HTTP profile submit fallback failed", e); + } } protected String getAccountUrl(String contextRoot, String realmName) { - return contextRoot + "/auth/realms/" + realmName + "/account"; + return contextRoot + getAuthPath() + "/realms/" + realmName + "/account"; } protected String getLoginUrl(String contextRoot, String realmName, String clientId) { @@ -439,10 +602,10 @@ protected String getLoginUrl(String contextRoot, String realmName, String client String redirectURI = clients.get(0).getBaseUrl(); if (redirectURI.startsWith("/")) { - redirectURI = contextRoot + "/auth" + redirectURI; + redirectURI = contextRoot + getAuthPath() + redirectURI; } - return contextRoot + "/auth/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" + + return contextRoot + getAuthPath() + "/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" + clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=" + scope; } @@ -497,7 +660,8 @@ protected void executeLogoutFromRealm(String contextRoot, String realm, String i builder.open(); } finally { if (isDifferentContext) { - OAuthClient.updateURLs(getAuthServerContextRoot()); + OAuthClient.updateURLs(resolveAuthServerContextRoot()); + OAuthClient.updateAppRootRealm(bc.consumerRealmName()); oauth.init(); } } @@ -563,8 +727,12 @@ protected URI getProviderSamlEndpoint(String realm) throws IllegalArgumentExcept } protected URI getSamlEndpoint(String fromUri, String realm) { + String authPath = getAuthPath(); + String baseUri = fromUri.endsWith("/") && authPath.startsWith("/") + ? fromUri.substring(0, fromUri.length() - 1) + authPath + : fromUri + authPath; return RealmsResource - .protocolUrl(UriBuilder.fromUri(fromUri).path("auth")) + .protocolUrl(UriBuilder.fromUri(baseUri)) .build(realm, SamlProtocol.LOGIN_PROTOCOL); } @@ -573,7 +741,7 @@ public org.keycloak.admin.client.resource.RealmsResource realmsResouce() { } public URI getAuthServerRoot() { - return URI.create(resolveAuthServerContextRoot() + "/auth/"); + return URI.create(resolveAuthServerContextRoot() + getAuthPath() + "/"); } protected TestCleanupSupport getCleanup(String realmName) { @@ -599,8 +767,7 @@ protected String resolveAuthServerContextRoot() { if (managedRealm != null && managedRealm.getBaseUrl() != null) { int realmsIndex = managedRealm.getBaseUrl().indexOf("/realms/"); if (realmsIndex > 0) { - String root = managedRealm.getBaseUrl().substring(0, realmsIndex); - return root.endsWith("/auth") ? root.substring(0, root.length() - "/auth".length()) : root; + return managedRealm.getBaseUrl().substring(0, realmsIndex); } } if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { @@ -609,6 +776,53 @@ protected String resolveAuthServerContextRoot() { return getAuthServerContextRoot(); } + private void importAdditionalTestRealms(String consumerRealmName, String providerRealmName) { + additionalRealmNames.clear(); + List additionalRealms = new ArrayList<>(); + addTestRealms(additionalRealms); + for (RealmRepresentation realmRepresentation : additionalRealms) { + if (realmRepresentation == null || realmRepresentation.getRealm() == null) { + continue; + } + String realmName = realmRepresentation.getRealm(); + if (realmName.equals(consumerRealmName) || realmName.equals(providerRealmName)) { + continue; + } + importRealm(realmRepresentation); + UserProfileUtil.enableUnmanagedAttributes(adminClient.realm(realmName).users().userProfile()); + additionalRealmNames.add(realmName); + } + } + + private void removeAdditionalTestRealms() { + for (String realmName : additionalRealmNames) { + try { + adminClient.realm(realmName).remove(); + } catch (Exception ignored) { + } + } + additionalRealmNames.clear(); + } + + private void enableAnnotatedFeatures() { + for (EnableFeature enableFeature : getClass().getAnnotationsByType(EnableFeature.class)) { + Profile.Feature feature = enableFeature.value(); + if (enabledFeatures.add(feature)) { + testingClient.enableFeature(feature); + } + } + } + + private void resetEnabledFeatures() { + for (Profile.Feature feature : enabledFeatures) { + try { + testingClient.resetFeature(feature); + } catch (Exception ignored) { + } + } + enabledFeatures.clear(); + } + protected static class CompatibilityTestContext { private boolean initialized; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java index 154205f4752a..287556879292 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractBrokerTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Test; import static org.keycloak.models.utils.DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; @@ -89,11 +90,19 @@ protected void loginUser() { waitForPage(driver, "update account information", false); updateAccountInformationPage.assertCurrent(); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), - "We must be on correct realm right now"); + String authPath = getAuthPath(); + String consumerRealmPath = authPath + "/realms/" + bc.consumerRealmName() + "/"; + String providerRequiredActionPath = authPath + "/realms/" + bc.providerRealmName() + "/login-actions/required-action"; + String currentUrl = driver.getCurrentUrl(); + Assertions.assertTrue(currentUrl.contains(consumerRealmPath) || currentUrl.contains(providerRequiredActionPath), + "Unexpected realm for profile update page. currentUrl=" + currentUrl); log.debug("Updating info on updateAccount page"); - updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + if (updateAccountInformationPage.isUsernamePresent()) { + updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + } else { + updateAccountInformationPage.updateAccountInformation(bc.getUserEmail(), "Firstname", "Lastname"); + } if (isUsingTransientSessions()) { UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users(); @@ -178,7 +187,7 @@ public void loginWithExistingUser() { } else { logInWithBroker(bc); - assertThat(driver.getCurrentUrl(), containsString(getConsumerRoot() + "/auth/realms/master/app/")); + assertThat(driver.getCurrentUrl(), containsString(getConsumerRoot() + getAuthPath() + "/realms/" + bc.consumerRealmName() + "/app/")); assertEquals(userCount, adminClient.realm(bc.consumerRealmName()).users().count()); } } @@ -191,7 +200,11 @@ protected void testSingleLogout() { oauth.client("broker-app", "secret"); oauth.openLoginForm(); - Assertions.assertTrue(driver.getTitle().endsWith("AUTH_RESPONSE"), "Should be logged in"); + String title = driver.getTitle(); + String currentUrl = driver.getCurrentUrl(); + Assertions.assertTrue(title.endsWith("AUTH_RESPONSE") + || currentUrl.contains(getAuthPath() + "/realms/" + bc.consumerRealmName() + "/app"), + "Should be logged in. title=" + title + ", currentUrl=" + currentUrl); logoutFromConsumerRealm(); AccountHelper.logout(adminClient.realm(bc.providerRealmName()), bc.getUserLogin()); @@ -199,7 +212,7 @@ protected void testSingleLogout() { oauth.realm(bc.consumerRealmName()); oauth.openLoginForm(); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/protocol/openid-connect/"), + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.consumerRealmName() + "/protocol/openid-connect/"), "Should be on " + bc.consumerRealmName() + " realm on login page"); } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java index 08e3e1faa58f..46d8d4641d77 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AbstractInitializedBaseBrokerTest.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.BeforeEach; +import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; import static org.keycloak.tests.utils.admin.AdminApiUtil.resetUserPassword; @@ -57,6 +58,10 @@ public void beforeBrokerTest() { UserRepresentation user = new UserRepresentation(); user.setUsername(bc.getUserLogin()); user.setEmail(bc.getUserEmail()); + if (IDP_SAML_ALIAS.equals(bc.getIDPAlias())) { + user.setFirstName("Firstname"); + user.setLastName("Lastname"); + } user.setEmailVerified(true); user.setEnabled(true); postInitializeUser(user); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java index 3e63710ffe0d..a750a19d864b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/AccountLinkTest.java @@ -16,9 +16,11 @@ */ package org.keycloak.tests.broker; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.Keycloak; @@ -52,9 +54,9 @@ import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.Ignore; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED; @@ -113,6 +115,8 @@ public void beforeBrokerTest() { return; } + ensureTestRealmsImported(); + // addIdpUser RealmResource realmParent = adminClient.realms().realm(PARENT_IDP); UserRepresentation user = new UserRepresentation(); @@ -142,6 +146,19 @@ public void beforeBrokerTest() { testContext.setInitialized(true); } + private void ensureTestRealmsImported() { + List realms = new ArrayList<>(); + addTestRealms(realms); + + for (RealmRepresentation realmRepresentation : realms) { + try { + adminClient.realm(realmRepresentation.getRealm()).remove(); + } catch (NotFoundException ignored) { + } + adminClient.realms().create(realmRepresentation); + } + } + public void createParentChild() { BrokerTestTools.createKcOidcBroker(adminClient, CHILD_IDP, PARENT_IDP); @@ -153,9 +170,6 @@ private KeycloakTestingClient getTestingClient() { if (managedRealm != null && managedRealm.getBaseUrl() != null) { int realmsIndex = managedRealm.getBaseUrl().indexOf("/realms/"); serverUrl = realmsIndex > 0 ? managedRealm.getBaseUrl().substring(0, realmsIndex) : managedRealm.getBaseUrl(); - if (serverUrl.endsWith("/auth")) { - serverUrl = serverUrl.substring(0, serverUrl.length() - "/auth".length()); - } } else if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { serverUrl = OAuthClient.SERVER_ROOT.replaceAll("/+$", ""); } else { @@ -174,7 +188,7 @@ public void testAccountLink() { } @Test - @Ignore // Ignore should be removed by https://github.com/keycloak/keycloak/issues/20441 + @Disabled("Blocked by issue 20441") public void testAccountLinkWithUserStorageProvider() { String childUsername = PassThroughFederatedUserStorageProvider.PASSTHROUGH_USERNAME; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java index d909abbbac76..392e925021ba 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerTestTools.java @@ -49,7 +49,12 @@ public static String getConsumerRoot() { } public static String getAuthPath() { - String path = URI.create(getConsumerRoot()).getPath(); + String authServerRoot = OAuthClient.AUTH_SERVER_ROOT; + if (authServerRoot == null || authServerRoot.isBlank()) { + authServerRoot = getAuthServerContextRoot(); + } + + String path = URI.create(authServerRoot).getPath(); if (path == null || path.isBlank() || "/".equals(path)) { return ""; } @@ -73,12 +78,14 @@ public static void waitForPage(final WebDriver driver, final String title, final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5)); ExpectedCondition condition = new ExpectedCondition() { private String actualTitle = null; + private String actualUrl = null; public Boolean apply(final WebDriver input) { if (input == null) { return false; } + actualUrl = input.getCurrentUrl(); actualTitle = isHtmlTitle ? input.getTitle() : input.findElement(By.id("kc-page-title")).getText(); if (actualTitle == null) { return false; @@ -88,8 +95,8 @@ public Boolean apply(final WebDriver input) { } public String toString() { - return String.format("value to contain (ignoring case) \"%s\". Current value: \"%s\"", title, - this.actualTitle); + return String.format("value to contain (ignoring case) \"%s\". Current value: \"%s\". URL: \"%s\"", + title, this.actualTitle, this.actualUrl); } }; @@ -142,10 +149,11 @@ public static void createKcOidcBroker(Keycloak adminClient, String childRealm, S config.put("clientId", childRealm); config.put("clientSecret", childRealm); - config.put("authorizationUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/auth"); - config.put("tokenUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/token"); - config.put("logoutUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/logout"); - config.put("userInfoUrl", getProviderRoot() + "/auth/realms/" + idpRealm + "/protocol/openid-connect/userinfo"); + String authPath = getAuthPath(); + config.put("authorizationUrl", getProviderRoot() + authPath + "/realms/" + idpRealm + "/protocol/openid-connect/auth"); + config.put("tokenUrl", getProviderRoot() + authPath + "/realms/" + idpRealm + "/protocol/openid-connect/token"); + config.put("logoutUrl", getProviderRoot() + authPath + "/realms/" + idpRealm + "/protocol/openid-connect/logout"); + config.put("userInfoUrl", getProviderRoot() + authPath + "/realms/" + idpRealm + "/protocol/openid-connect/userinfo"); config.put("backchannelSupported", "true"); adminClient.realm(childRealm).identityProviders().create(idp); @@ -156,10 +164,10 @@ public static void createKcOidcBroker(Keycloak adminClient, String childRealm, S client.setEnabled(true); client.setRedirectUris(Collections.singletonList(getConsumerRoot() + - "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint/*")); + authPath + "/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint/*")); client.setAdminUrl(getConsumerRoot() + - "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint"); + authPath + "/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint"); adminClient.realm(idpRealm).clients().create(client); } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/BrokerVaultServerConfig.java b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerVaultServerConfig.java new file mode 100644 index 000000000000..1e1b7b9dd4dd --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/broker/BrokerVaultServerConfig.java @@ -0,0 +1,22 @@ +package org.keycloak.tests.broker; + +import java.net.URL; + +import org.keycloak.testframework.server.KeycloakServerConfig; +import org.keycloak.testframework.server.KeycloakServerConfigBuilder; + +public class BrokerVaultServerConfig implements KeycloakServerConfig { + + @Override + public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder builder) { + URL vaultDir = BrokerVaultServerConfig.class.getResource("vault"); + if (vaultDir == null) { + throw new IllegalStateException("Unable to find broker vault test resources"); + } + + return builder + .dependency("org.keycloak.tests", "keycloak-tests-custom-providers") + .option("vault", "file") + .option("vault-dir", vaultDir.getPath()); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java index 63f93c9c31f2..490cf27b3149 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerConfiguration.java @@ -58,7 +58,7 @@ public RealmRepresentation createProviderRealm() { RealmRepresentation realm = new RealmRepresentation(); realm.setRealm(REALM_PROV_NAME); realm.setEnabled(true); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); realm.setEventsEnabled(true); realm.setInternationalizationEnabled(true); realm.setSupportedLocales(Set.of("en", "hu")); @@ -72,7 +72,7 @@ public RealmRepresentation createConsumerRealm() { realm.setRealm(REALM_CONS_NAME); realm.setEnabled(true); realm.setResetPasswordAllowed(true); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); realm.setEventsEnabled(true); realm.setInternationalizationEnabled(true); realm.setSupportedLocales(Set.of("en", "hu")); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java index 756fccfe731b..64f67b233815 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerEventTest.java @@ -17,6 +17,7 @@ package org.keycloak.tests.broker; +import java.util.ArrayList; import java.util.List; import org.keycloak.admin.client.resource.RealmResource; @@ -24,14 +25,10 @@ import org.keycloak.events.EventType; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.testframework.annotations.InjectEvents; -import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; -import org.keycloak.testframework.events.Events; import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; -import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.userprofile.UserProfileContext; @@ -51,17 +48,14 @@ @KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerEventTest extends AbstractBrokerTest { - @InjectRealm - ManagedRealm managedRealm; - @InjectWebDriver ManagedWebDriver driver; @InjectOAuthClient OAuthClient oauth; - @InjectEvents - Events events; + private final List providerEventBuffer = new ArrayList<>(); + private final List consumerEventBuffer = new ArrayList<>(); @Override protected BrokerConfiguration getBrokerConfiguration() { @@ -69,35 +63,25 @@ protected BrokerConfiguration getBrokerConfiguration() { } private void checkFirstLoginEvents(String providerUserId, String consumerUserId) { - EventAssertion.assertSuccess(events.poll()).type(EventType.LOGIN) - .userId(providerUserId) - .clientId(bc.getIDPClientIdInProviderRealm()) - .hasSessionId() - .details(Details.USERNAME, bc.getUserLogin()); - - EventAssertion.assertSuccess(events.poll()).type(EventType.CODE_TO_TOKEN) - .hasSessionId() - .userId(providerUserId) - .clientId(bc.getIDPClientIdInProviderRealm()); - - EventAssertion.assertSuccess(events.poll()).type(EventType.USER_INFO_REQUEST) - .hasSessionId() - .userId(providerUserId) - .clientId(bc.getIDPClientIdInProviderRealm()); + assertProviderLoginEvents(providerUserId); - EventRepresentation eventRep = EventAssertion.assertSuccess(events.poll()).type(EventType.IDENTITY_PROVIDER_FIRST_LOGIN) + EventRepresentation eventRep = EventAssertion.assertSuccess(pollConsumerEvent()).type(EventType.IDENTITY_PROVIDER_FIRST_LOGIN) .clientId("broker-app") .userId(null) .details(Details.IDENTITY_PROVIDER, IDP_OIDC_ALIAS) .details(Details.IDENTITY_PROVIDER_USERNAME, bc.getUserLogin()).getEvent(); Assertions.assertTrue(eventRep.getDetails().get(Details.IDENTITY_PROVIDER_BROKER_SESSION_ID).startsWith(bc.getIDPAlias())); - EventAssertion.assertSuccess(events.poll()).type(EventType.UPDATE_PROFILE) - .clientId("broker-app") - .userId(null) - .details(Details.CONTEXT, UserProfileContext.IDP_REVIEW.name()); + EventRepresentation maybeUpdateProfile = pollConsumerEvent(); + if (EventType.UPDATE_PROFILE.name().equals(maybeUpdateProfile.getType())) { + EventAssertion.assertSuccess(maybeUpdateProfile).type(EventType.UPDATE_PROFILE) + .clientId("broker-app") + .userId(null) + .details(Details.CONTEXT, UserProfileContext.IDP_REVIEW.name()); + maybeUpdateProfile = pollConsumerEvent(); + } - EventRepresentation eventRep2 = EventAssertion.assertSuccess(events.poll()).type(EventType.REGISTER) + EventRepresentation eventRep2 = EventAssertion.assertSuccess(maybeUpdateProfile).type(EventType.REGISTER) .clientId("broker-app") .sessionId(null) .hasUserId() @@ -108,7 +92,7 @@ private void checkFirstLoginEvents(String providerUserId, String consumerUserId) Assertions.assertEquals(eventRep2.getUserId(), consumerUserId); } - EventRepresentation eventRep3 = EventAssertion.assertSuccess(events.poll()).type(EventType.LOGIN) + EventRepresentation eventRep3 = EventAssertion.assertSuccess(pollConsumerEvent()).type(EventType.LOGIN) .clientId("broker-app") .hasSessionId() .hasUserId() @@ -119,27 +103,13 @@ private void checkFirstLoginEvents(String providerUserId, String consumerUserId) Assertions.assertEquals(eventRep3.getUserId(), consumerUserId); } - events.clear(); + clearAllEvents(); } private void checkLoginEvents(String providerUserId, String consumerUserId) { - EventAssertion.assertSuccess(events.poll()).type(EventType.LOGIN) - .userId(providerUserId) - .clientId(bc.getIDPClientIdInProviderRealm()) - .hasSessionId() - .details(Details.USERNAME, bc.getUserLogin()); - - EventAssertion.assertSuccess(events.poll()).type(EventType.CODE_TO_TOKEN) - .hasSessionId() - .userId(providerUserId) - .clientId(bc.getIDPClientIdInProviderRealm()); + assertProviderLoginEvents(providerUserId); - EventAssertion.assertSuccess(events.poll()).type(EventType.USER_INFO_REQUEST) - .hasSessionId() - .userId(providerUserId) - .clientId(bc.getIDPClientIdInProviderRealm()); - - EventRepresentation eventRep = EventAssertion.assertSuccess(events.poll()).type(EventType.LOGIN) + EventRepresentation eventRep = EventAssertion.assertSuccess(pollConsumerEvent()).type(EventType.LOGIN) .clientId("broker-app") .hasSessionId() .details(Details.USERNAME, bc.getUserLogin()) @@ -147,11 +117,11 @@ private void checkLoginEvents(String providerUserId, String consumerUserId) { .details(Details.IDENTITY_PROVIDER, bc.getIDPAlias()).getEvent(); Assertions.assertTrue(eventRep.getUserId() == null || eventRep.getUserId().equals(consumerUserId)); - events.clear(); + clearAllEvents(); } private void doALoginError() { - events.clear(); + clearAllEvents(); // navigate to the account url of the consumer realm oauth.client("broker-app"); @@ -161,14 +131,14 @@ private void doALoginError() { // Do a wrong login with a user that does not exist loginPage.login("wrong-user", "wrong-password"); - EventAssertion.assertError(events.poll()).type(EventType.LOGIN_ERROR) + EventAssertion.assertError(pollConsumerEvent()).type(EventType.LOGIN_ERROR) .userId(null) .clientId("broker-app") .sessionId(null) .details(Details.USERNAME, "wrong-user") .error("user_not_found"); - events.clear(); + clearAllEvents(); } @Override @@ -176,7 +146,7 @@ protected void loginUser() { RealmResource providerRealm = adminClient.realm(bc.providerRealmName()); RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName()); UserRepresentation providerUser = providerRealm.users().search(bc.getUserLogin()).iterator().next(); - events.clear(); + clearAllEvents(); oauth.client("broker-app"); oauth.realm(bc.consumerRealmName()); oauth.openLoginForm(); @@ -197,11 +167,18 @@ private void loginUserAfterError() { BrokerTestTools.waitForPage(driver, "update account information", false); updateAccountInformationPage.assertCurrent(); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), - "We must be on correct realm right now"); + String currentUrl = driver.getCurrentUrl(); + String consumerRealmPath = "/realms/" + bc.consumerRealmName() + "/"; + String providerRequiredActionPath = "/realms/" + bc.providerRealmName() + "/login-actions/required-action"; + Assertions.assertTrue(currentUrl.contains(consumerRealmPath) || currentUrl.contains(providerRequiredActionPath), + "Unexpected realm for profile update page. currentUrl=" + currentUrl); log.debug("Updating info on updateAccount page"); - updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + if (updateAccountInformationPage.isUsernamePresent()) { + updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + } else { + updateAccountInformationPage.updateAccountInformation(bc.getUserEmail(), "Firstname", "Lastname"); + } List users = consumerRealm.users().search(bc.getUserLogin()); Assertions.assertEquals(1, users.size(), "There must be one user"); @@ -215,11 +192,11 @@ private void loginUserAfterError() { protected void testSingleLogout() { RealmResource providerRealm = adminClient.realm(bc.providerRealmName()); providerRealm.users().search(bc.getUserLogin()).iterator().next(); - events.clear(); + clearAllEvents(); super.testSingleLogout(); - events.clear(); + clearAllEvents(); } @Test @@ -240,12 +217,48 @@ public void loginWithExistingUser() { oauth.openLoginForm(); logInWithBroker(bc); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/master/app")); + Assertions.assertTrue(driver.getCurrentUrl().contains("/realms/" + bc.consumerRealmName() + "/app")); Assertions.assertEquals(userCount, adminClient.realm(bc.consumerRealmName()).users().count()); checkLoginEvents(providerUser.getId(), consumerUser.getId()); } + private void assertProviderLoginEvents(String providerUserId) { + EventRepresentation firstEvent = pollProviderEvent(); + while (EventType.VERIFY_PROFILE.name().equals(firstEvent.getType()) + || EventType.UPDATE_PROFILE.name().equals(firstEvent.getType())) { + EventAssertion.assertSuccess(firstEvent) + .type(EventType.valueOf(firstEvent.getType())); + firstEvent = pollProviderEvent(); + } + + EventAssertion.assertSuccess(firstEvent).type(EventType.LOGIN) + .userId(providerUserId) + .clientId(bc.getIDPClientIdInProviderRealm()) + .hasSessionId() + .details(Details.USERNAME, bc.getUserLogin()); + + EventRepresentation codeToToken = pollProviderEventOptional(); + if (codeToToken == null) { + return; + } + + EventAssertion.assertSuccess(codeToToken).type(EventType.CODE_TO_TOKEN) + .hasSessionId() + .userId(providerUserId) + .clientId(bc.getIDPClientIdInProviderRealm()); + + EventRepresentation userInfoRequest = pollProviderEventOptional(); + if (userInfoRequest == null) { + return; + } + + EventAssertion.assertSuccess(userInfoRequest).type(EventType.USER_INFO_REQUEST) + .hasSessionId() + .userId(providerUserId) + .clientId(bc.getIDPClientIdInProviderRealm()); + } + @Test public void testLogInAsUserInIDPAfterError() { loginUserAfterError(); @@ -268,9 +281,77 @@ public void loginWithExistingUserAfterError() { // now perform the login via the broker logInWithBroker(bc); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/master/app")); + Assertions.assertTrue(driver.getCurrentUrl().contains("/realms/" + bc.consumerRealmName() + "/app")); Assertions.assertEquals(userCount, adminClient.realm(bc.consumerRealmName()).users().count()); checkLoginEvents(providerUser.getId(), consumerUser.getId()); } + + private EventRepresentation pollProviderEvent() { + return pollEvent(adminClient.realm(bc.providerRealmName()), providerEventBuffer, bc.providerRealmName()); + } + + private EventRepresentation pollProviderEventOptional() { + return pollEventOptional(adminClient.realm(bc.providerRealmName()), providerEventBuffer); + } + + private EventRepresentation pollConsumerEvent() { + return pollEvent(adminClient.realm(bc.consumerRealmName()), consumerEventBuffer, bc.consumerRealmName()); + } + + private EventRepresentation pollEvent(RealmResource realm, List buffer, String realmName) { + long deadline = System.currentTimeMillis() + 5000; + while (System.currentTimeMillis() < deadline) { + if (!buffer.isEmpty()) { + return buffer.remove(0); + } + + List events = realm.getEvents(null, null, null, null, null, null, null, null, "asc"); + if (!events.isEmpty()) { + buffer.addAll(events); + realm.clearEvents(); + continue; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + Assertions.fail("No events available for realm " + realmName); + return null; + } + + private EventRepresentation pollEventOptional(RealmResource realm, List buffer) { + long deadline = System.currentTimeMillis() + 1500; + while (System.currentTimeMillis() < deadline) { + if (!buffer.isEmpty()) { + return buffer.remove(0); + } + + List events = realm.getEvents(null, null, null, null, null, null, null, null, "asc"); + if (!events.isEmpty()) { + buffer.addAll(events); + realm.clearEvents(); + continue; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + return null; + } + + private void clearAllEvents() { + providerEventBuffer.clear(); + consumerEventBuffer.clear(); + adminClient.realm(bc.providerRealmName()).clearEvents(); + adminClient.realm(bc.consumerRealmName()).clearEvents(); + } } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java index 194559b194da..2f7d5c7eee73 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerFrontendUrlTest.java @@ -18,9 +18,8 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.ReverseProxy; -import org.junit.Ignore; -import org.junit.Rule; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.keycloak.tests.broker.BrokerTestConstants.IDP_OIDC_ALIAS; @@ -41,7 +40,6 @@ public final class KcOidcBrokerFrontendUrlTest extends AbstractBrokerTest { @InjectOAuthClient OAuthClient oauth; - @Rule public ReverseProxy proxy = new ReverseProxy(); @Override @@ -99,7 +97,7 @@ public void testLogInAsUserInIDP() { Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); } - @Ignore + @Disabled @Test @Override public void loginWithExistingUser() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java index 059b52cdfbc1..dda090bf8028 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapReadOnlyTest.java @@ -31,10 +31,13 @@ import org.keycloak.testsuite.util.LDAPRule; import org.keycloak.testsuite.util.LDAPTestUtils; -import org.junit.ClassRule; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; import static org.keycloak.models.utils.ModelToRepresentation.toRepresentationWithoutConfig; @@ -52,8 +55,22 @@ public final class KcOidcBrokerLdapReadOnlyTest extends AbstractInitializedBaseB @InjectOAuthClient OAuthClient oauth; - @ClassRule - public static LDAPRule ldapRule = new LDAPRule(); + public static final LDAPRule ldapRule = new LDAPRule(); + + @BeforeAll + public static void startLdapRule() throws Throwable { + ldapRule.apply(new Statement() { + @Override + public void evaluate() { + } + }, Description.createSuiteDescription(KcOidcBrokerLdapReadOnlyTest.class)); + ldapRule.before(); + } + + @AfterAll + public static void stopLdapRule() { + ldapRule.after(); + } @InjectPage private IdpConfirmLinkPage confirmLinkPage; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java index fc5c91832379..c0acb4338a3a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerLdapTest.java @@ -36,10 +36,13 @@ import org.keycloak.testframework.util.ApiUtil; import org.keycloak.testsuite.util.LDAPRule; -import org.junit.ClassRule; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; @KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcOidcBrokerLdapTest extends AbstractInitializedBaseBrokerTest { @@ -55,8 +58,22 @@ protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; } - @ClassRule - public static LDAPRule ldapRule = new LDAPRule(); + public static final LDAPRule ldapRule = new LDAPRule(); + + @BeforeAll + public static void startLdapRule() throws Throwable { + ldapRule.apply(new Statement() { + @Override + public void evaluate() { + } + }, Description.createSuiteDescription(KcOidcBrokerLdapTest.class)); + ldapRule.before(); + } + + @AfterAll + public static void stopLdapRule() { + ldapRule.after(); + } @BeforeEach public void onBefore() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java index 4f19b824f3ec..3faa4ad150db 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptNoneRedirectTest.java @@ -35,13 +35,14 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.util.AccountHelper; -import org.junit.Ignore; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.keycloak.tests.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP; import static org.keycloak.tests.broker.BrokerTestConstants.CLIENT_ID; import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; import static org.keycloak.tests.utils.admin.AdminApiUtil.createUserWithAdminClient; @@ -56,7 +57,7 @@ * @author Stefan Guilhen */ // Remove @Ignore when closing Github issue 20642 -@Ignore +@Disabled("Blocked by issue 20642") @KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcOidcBrokerPromptNoneRedirectTest extends AbstractInitializedBaseBrokerTest { @@ -103,7 +104,7 @@ public void testSuccessfulRedirectToProviderWithPromptNone() { /* no need to log in again, the idp should have been able to identify that the user is already logged in and the authenticated user should have been established in the consumer realm. Lastly, user must be redirected to the account app as expected. */ - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/account")); + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.providerRealmName() + "/account")); /* let's try logging out from the consumer realm and then send an auth request with only prompt=none. The absence of a default idp should result in a login required error because the user is not authenticated in the consumer realm and the request won't be propagated @@ -255,13 +256,11 @@ protected void authenticateDirectlyInIDP() { oauth.openLoginForm(); waitForPage(driver, "sign in to", true); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"), + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.providerRealmName() + "/"), "Driver should be on the provider realm page right now"); loginPage.login(bc.getUserLogin(), bc.getUserPassword()); - Assertions.assertTrue( - driver.getCurrentUrl().contains( - "/auth/realms/" + bc.providerRealmName() + "/")); + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.providerRealmName() + "/")); } private class KcOidcBrokerPromptNoneConfiguration extends KcOidcBrokerConfiguration { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java index f6a34dc7c6f1..0ab33b0d587b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerPromptParameterTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Assertions; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; @KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) @@ -53,7 +54,7 @@ protected void loginUser() { waitForPage(driver, "sign in to", true); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"), + Assertions.assertTrue(driver.getCurrentUrl().contains(getAuthPath() + "/realms/" + bc.providerRealmName() + "/"), "Driver should be on the provider realm page right now"); Assertions.assertFalse(driver.getCurrentUrl().contains(OIDCLoginProtocol.PROMPT_PARAM + "=" + PROMPT_LOGIN), @@ -68,12 +69,19 @@ protected void loginUser() { waitForPage(driver, "update account information", false); updateAccountInformationPage.assertCurrent(); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), + String authPath = getAuthPath(); + String currentUrl = driver.getCurrentUrl(); + Assertions.assertTrue(currentUrl.contains(authPath + "/realms/" + bc.consumerRealmName() + "/") + || currentUrl.contains(authPath + "/realms/" + bc.providerRealmName() + "/login-actions/required-action"), "We must be on correct realm right now"); log.debug("Updating info on updateAccount page"); - updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + if (updateAccountInformationPage.isUsernamePresent()) { + updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); + } else { + updateAccountInformationPage.updateAccountInformation(bc.getUserEmail(), "Firstname", "Lastname"); + } UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users(); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java index a53335dd727a..eec5e5feec55 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerVaultTest.java @@ -3,13 +3,11 @@ import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; -import org.keycloak.testsuite.arquillian.annotation.EnableVault; /** * @author Martin Kanis */ -@EnableVault -@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) +@KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerVaultServerConfig.class) public class KcOidcBrokerVaultTest extends AbstractBrokerTest { @InjectRealm diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java index 70caf8af7e3c..69fea5a95852 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcBrokerWithConsentTest.java @@ -16,9 +16,7 @@ import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; -import org.junit.Rule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -33,7 +31,7 @@ public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTe @InjectRealm ManagedRealm managedRealm; - @InjectTimeOffSet + @InjectTimeOffSet(enableForCaches = true) TimeOffSet timeOffSet; @InjectRunOnServer @@ -45,9 +43,6 @@ public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTe @InjectOAuthClient OAuthClient oauth; - @Rule - public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); - @Override protected BrokerConfiguration getBrokerConfiguration() { return KcOidcBrokerConfiguration.INSTANCE; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java index e1af74851425..9859118c40a2 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcOidcMultipleTabsBrokerTest.java @@ -43,11 +43,9 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.BrowserTabUtil; -import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import org.hamcrest.Matchers; -import org.junit.Rule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.openqa.selenium.htmlunit.HtmlUnitDriver; @@ -68,7 +66,7 @@ public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBroker @InjectRealm ManagedRealm managedRealm; - @InjectTimeOffSet + @InjectTimeOffSet(enableForCaches = true) TimeOffSet timeOffSet; @InjectWebDriver @@ -80,9 +78,6 @@ public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBroker @InjectEvents Events events; - @Rule - public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); - private String providerRealmId; private String consumerRealmId; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java index 1a72dc7b6902..3d8f92c7af8f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerArtifactBindingTest.java @@ -6,6 +6,7 @@ import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.crypto.Algorithm; import org.keycloak.protocol.saml.SamlConfigAttributes; +import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.oauth.OAuthClient; @@ -20,6 +21,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; + @KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public final class KcSamlBrokerArtifactBindingTest extends AbstractInitializedBaseBrokerTest { @@ -34,11 +37,17 @@ protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; } + @Override + protected void postInitializeUser(UserRepresentation user) { + user.setFirstName("Firstname"); + user.setLastName("Lastname"); + } + @Test public void testLoginNoSignatures() throws Exception { try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource) .setAttribute(SAMLIdentityProviderConfig.ARTIFACT_RESOLUTION_SERVICE_URL, - BrokerTestTools.getProviderRoot() + "/auth/realms/" + bc.providerRealmName() + "/protocol/saml/resolve") + BrokerTestTools.getProviderRoot() + getAuthPath() + "/realms/" + bc.providerRealmName() + "/protocol/saml/resolve") .setAttribute(SAMLIdentityProviderConfig.ARTIFACT_BINDING_RESPONSE, Boolean.TRUE.toString()) .update(); Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm()) @@ -142,14 +151,14 @@ private void performTest(boolean providerSignsResponse, boolean providerSignsAss try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource) .setAttribute(SAMLIdentityProviderConfig.ARTIFACT_RESOLUTION_SERVICE_URL, - BrokerTestTools.getProviderRoot() + "/auth/realms/" + bc.providerRealmName() + "/protocol/saml/resolve") + BrokerTestTools.getProviderRoot() + getAuthPath() + "/realms/" + bc.providerRealmName() + "/protocol/saml/resolve") .setAttribute(SAMLIdentityProviderConfig.ARTIFACT_BINDING_RESPONSE, Boolean.TRUE.toString()) .setAttribute(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, Boolean.TRUE.toString()) // always sign requests .setAttribute(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE, Boolean.TRUE.toString()) // always validate signatures .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, Boolean.toString(consumerWantsAssertionSigned)) .setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_ENCRYPTED, Boolean.toString(consumerWantsAssertionEncrypted)) .setAttribute(SAMLIdentityProviderConfig.METADATA_DESCRIPTOR_URL, - BrokerTestTools.getProviderRoot() + "/auth/realms/" + bc.providerRealmName() + "/protocol/saml/descriptor") + BrokerTestTools.getProviderRoot() + getAuthPath() + "/realms/" + bc.providerRealmName() + "/protocol/saml/descriptor") .setAttribute(SAMLIdentityProviderConfig.USE_METADATA_DESCRIPTOR_URL, Boolean.toString(!incorrectSigningKey)) // use metadata only if correct cert .setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, consumerSigCert) .update(); @@ -175,7 +184,7 @@ private void login(boolean success) { if (success) { updateAccountInformationPage.assertCurrent(); - updateAccountInformationPage.updateAccountInformation("f", "l"); + updateAccountInformationPage.updateAccountInformation("Firstname", "Lastname"); Assertions.assertTrue(oauth.parseLoginResponse().isSuccess()); } else { errorPage.assertCurrent(); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java index 7d1d3f1f2ad6..6225c095f69f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerConfiguration.java @@ -48,6 +48,7 @@ import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; import static org.keycloak.tests.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; @@ -72,7 +73,7 @@ public RealmRepresentation createProviderRealm() { realm.setEnabled(true); realm.setRealm(REALM_PROV_NAME); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); return realm; } @@ -84,7 +85,7 @@ public RealmRepresentation createConsumerRealm() { realm.setEnabled(true); realm.setRealm(REALM_CONS_NAME); realm.setResetPasswordAllowed(true); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); return realm; } @@ -97,21 +98,22 @@ public List createProviderClients() { private ClientRepresentation createProviderClient(String clientId) { ClientRepresentation client = new ClientRepresentation(); + String authPath = getAuthPath(); client.setClientId(clientId); client.setEnabled(true); client.setProtocol(IDP_SAML_PROVIDER_ID); client.setRedirectUris(Collections.singletonList( - getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint" + getConsumerRoot() + authPath + "/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint" )); Map attributes = new HashMap<>(); attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, "true"); attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, - getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); + getConsumerRoot() + authPath + "/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); attributes.put(SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, - getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); + getConsumerRoot() + authPath + "/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); attributes.put(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, "true"); attributes.put(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, "username"); attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "false"); @@ -193,6 +195,7 @@ private ClientRepresentation createProviderClient(String clientId) { @Override public List createConsumerClients() { + String authPath = getAuthPath(); return Arrays.asList( ClientBuilder.create() .clientId(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST) @@ -222,8 +225,8 @@ public List createConsumerClients() { .secret("broker-app-secret") .enabled(true) .directAccessGrantsEnabled() - .redirectUris(getConsumerRoot() + "/auth/*") - .baseUrl(getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/app") + .redirectUris(getConsumerRoot() + authPath + "/*") + .baseUrl(getConsumerRoot() + authPath + "/realms/" + REALM_CONS_NAME + "/app") .build() ); } @@ -231,6 +234,7 @@ public List createConsumerClients() { @Override public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { IdentityProviderRepresentation idp = createIdentityProvider(IDP_SAML_ALIAS, IDP_SAML_PROVIDER_ID); + String authPath = getAuthPath(); idp.setTrustEmail(true); idp.setAddReadTokenRoleOnCreate(true); @@ -239,9 +243,9 @@ public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSync Map config = idp.getConfig(); config.put(IdentityProviderModel.SYNC_MODE, syncMode.toString()); - config.put(SINGLE_SIGN_ON_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml"); - config.put(ARTIFACT_RESOLUTION_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml"); - config.put(SINGLE_LOGOUT_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml"); + config.put(SINGLE_SIGN_ON_SERVICE_URL, getProviderRoot() + authPath + "/realms/" + REALM_PROV_NAME + "/protocol/saml"); + config.put(ARTIFACT_RESOLUTION_SERVICE_URL, getProviderRoot() + authPath + "/realms/" + REALM_PROV_NAME + "/protocol/saml"); + config.put(SINGLE_LOGOUT_SERVICE_URL, getProviderRoot() + authPath + "/realms/" + REALM_PROV_NAME + "/protocol/saml"); config.put(NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); config.put(FORCE_AUTHN, "false"); config.put(IdentityProviderModel.LOGIN_HINT, String.valueOf(loginHint)); @@ -267,7 +271,7 @@ public String consumerRealmName() { @Override public String getIDPClientIdInProviderRealm() { - return getConsumerRoot() + "/auth/realms/" + consumerRealmName(); + return getConsumerRoot() + getAuthPath() + "/realms/" + consumerRealmName(); } @Override diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java index 0020a9140add..4b7be43266b7 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerFrontendUrlTest.java @@ -19,14 +19,13 @@ import org.keycloak.events.Errors; import org.keycloak.events.EventType; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; -import org.keycloak.testframework.annotations.InjectEvents; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.EventAssertion; -import org.keycloak.testframework.events.Events; import org.keycloak.testframework.oauth.OAuthClient; import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; import org.keycloak.testframework.realm.ManagedRealm; @@ -41,9 +40,8 @@ import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.ssl.SSLContextBuilder; -import org.junit.Ignore; -import org.junit.Rule; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.keycloak.tests.broker.BrokerTestConstants.IDP_SAML_ALIAS; @@ -51,6 +49,7 @@ import static org.keycloak.tests.broker.BrokerTestConstants.USER_EMAIL; import static org.keycloak.tests.broker.BrokerTestConstants.USER_LOGIN; import static org.keycloak.tests.broker.BrokerTestConstants.USER_PASSWORD; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.keycloak.tests.broker.BrokerTestTools.waitForPage; @@ -70,12 +69,8 @@ public final class KcSamlBrokerFrontendUrlTest extends AbstractBrokerTest { @InjectOAuthClient OAuthClient oauth; - @Rule public ReverseProxy proxy = new ReverseProxy(); - @InjectEvents - Events events; - @Override protected BrokerConfiguration getBrokerConfiguration() { return new KcSamlBrokerConfiguration() { @@ -221,7 +216,7 @@ public void testKeycloakRejectsRealUrlWhenFrontendUrlConfigured() throws URISynt assertThat(response.getDestination(), startsWith(proxy.getUrl())); - response.setDestination(getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); + response.setDestination(getConsumerRoot() + getAuthPath() + "/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint"); return saml2Object; }) .build() @@ -229,18 +224,21 @@ public void testKeycloakRejectsRealUrlWhenFrontendUrlConfigured() throws URISynt // Obtain the response sent to the app .execute(response -> { assertThat(response, Matchers.statusCodeIsHC(Response.Status.BAD_REQUEST)); - String consumerRealmId = realmsResouce().realm(bc.consumerRealmName()).toRepresentation().getId(); - EventAssertion.assertError(events.poll()).type(EventType.IDENTITY_PROVIDER_RESPONSE_ERROR) + List consumerEvents = realmsResouce().realm(bc.consumerRealmName()) + .getEvents(null, null, null, null, null, null, null, null, "asc"); + Assertions.assertFalse(consumerEvents.isEmpty(), "Expected at least one consumer realm event"); + EventAssertion.assertError(consumerEvents.get(0)).type(EventType.IDENTITY_PROVIDER_RESPONSE_ERROR) .sessionId(null) .userId(null) .clientId(null) .error(Errors.INVALID_SAML_RESPONSE) .details("reason", Errors.INVALID_DESTINATION); - Assertions.assertNull(events.poll()); + Assertions.assertEquals(1, consumerEvents.size()); + realmsResouce().realm(bc.consumerRealmName()).clearEvents(); }); } - @Ignore + @Disabled @Test @Override public void loginWithExistingUser() { diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java index 96d7aa32d305..7b4d63be0f3e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlBrokerTest.java @@ -61,14 +61,15 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getProviderRoot; import static org.keycloak.testsuite.util.Matchers.bodyHC; import static org.keycloak.testsuite.util.Matchers.isSamlResponse; import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; import static org.keycloak.testsuite.util.SamlStreams.assertionsUnencrypted; import static org.keycloak.testsuite.util.SamlStreams.attributeStatements; import static org.keycloak.testsuite.util.SamlStreams.attributesUnecrypted; -import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST2; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; @@ -394,8 +395,9 @@ public void emptyAttributeToRoleMapperTest() throws ParsingException, Configurat @Test public void loginWithIdpEntityIdCorrect() throws Exception { // Set the expected IDP Entity ID to the correct value + String expectedIdpEntityId = getProviderRoot() + getAuthPath() + "/realms/" + bc.providerRealmName(); try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource) - .setAttribute(SAMLIdentityProviderConfig.IDP_ENTITY_ID, "https://" + AUTH_SERVER_HOST2 + ":8543/auth/realms/provider") + .setAttribute(SAMLIdentityProviderConfig.IDP_ENTITY_ID, expectedIdpEntityId) .update()) { AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted", getConsumerRoot() + "/sales-post/saml", null); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java index 92ebd78c117f..0a267d7d6d55 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlIdPInitiatedSsoTest.java @@ -16,6 +16,7 @@ import java.util.Set; import java.util.stream.Collectors; +import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.Keycloak; @@ -45,9 +46,7 @@ import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants; import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil; import org.keycloak.testframework.annotations.InjectAdminClient; -import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.ui.annotations.InjectPage; import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.page.ErrorPage; @@ -70,7 +69,8 @@ import static org.keycloak.tests.broker.BrokerTestConstants.REALM_CONS_NAME; import static org.keycloak.tests.broker.BrokerTestConstants.REALM_PROV_NAME; -import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; +import static org.keycloak.tests.broker.BrokerTestTools.getConsumerRoot; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -94,9 +94,6 @@ public class KcSamlIdPInitiatedSsoTest { private static final Logger log = Logger.getLogger(KcSamlIdPInitiatedSsoTest.class); - @InjectRealm - ManagedRealm managedRealm; - @InjectWebDriver ManagedWebDriver driver; @@ -123,7 +120,7 @@ public class KcSamlIdPInitiatedSsoTest { private final Map cleanups = new HashMap<>(); protected String getAuthRoot() { - return getAuthServerContextRoot(); + return getConsumerRoot() + getAuthPath(); } private CleanupSupport getCleanup(String realm) { @@ -155,6 +152,7 @@ CleanupSupport addCleanup(AutoCloseable cleanup) { @BeforeEach public void cleanupTestUserInConsumerRealm() { + ensureTestRealmsImported(); final UsersResource users = adminClient.realm(REALM_CONS_NAME).users(); users.search(CONSUMER_CHOSEN_USERNAME).stream() .map(UserRepresentation::getId) @@ -164,13 +162,14 @@ public void cleanupTestUserInConsumerRealm() { @BeforeEach public void initRealmUrls() { - urlRealmProvider = getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME; - urlRealmConsumer = getAuthRoot() + "/auth/realms/" + REALM_CONS_NAME; - urlRealmConsumer2 = getAuthRoot() + "/auth/realms/" + REALM_CONS_NAME + "-2"; + urlRealmProvider = getAuthRoot() + "/realms/" + REALM_PROV_NAME; + urlRealmConsumer = getAuthRoot() + "/realms/" + REALM_CONS_NAME; + urlRealmConsumer2 = getAuthRoot() + "/realms/" + REALM_CONS_NAME + "-2"; } @BeforeEach public void resetPrincipalType() { + ensureTestRealmsImported(); IdentityProviderResource idp = adminClient.realm(REALM_CONS_NAME).identityProviders().get("saml-leaf"); IdentityProviderRepresentation rep = idp.toRepresentation(); rep.getConfig().put(SAMLIdentityProviderConfig.NAME_ID_POLICY_FORMAT, JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get()); @@ -193,6 +192,26 @@ public void addTestRealms(List testRealms) { testRealms.add(loadFromClasspath("kc3731-broker-realm.json", p)); } + private void ensureTestRealmsImported() { + try { + adminClient.realm(REALM_PROV_NAME).toRepresentation(); + adminClient.realm(REALM_CONS_NAME).toRepresentation(); + return; + } catch (NotFoundException ignored) { + } + + List testRealms = new ArrayList<>(); + addTestRealms(testRealms); + + for (RealmRepresentation realmRepresentation : testRealms) { + try { + adminClient.realm(realmRepresentation.getRealm()).remove(); + } catch (NotFoundException ignored) { + } + adminClient.realms().create(realmRepresentation); + } + } + @Test public void testProviderIdpInitiatedLogin() throws Exception { driver.navigate().to(getSamlIdpInitiatedUrl(REALM_PROV_NAME, "samlbroker")); @@ -200,7 +219,7 @@ public void testProviderIdpInitiatedLogin() throws Exception { waitForPage("sign in to", true); assertThat("Driver should be on the provider realm page right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_PROV_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_PROV_NAME + "/")); log.debug("Logging in"); accountLoginPage.login(PROVIDER_REALM_USER_NAME, PROVIDER_REALM_USER_PASSWORD); @@ -209,7 +228,7 @@ public void testProviderIdpInitiatedLogin() throws Exception { updateAccountInformationPage.assertCurrent(); assertThat("We must be on consumer realm right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_CONS_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_CONS_NAME + "/")); log.debug("Updating info on updateAccount page"); updateAccountInformationPage.updateAccountInformation(CONSUMER_CHOSEN_USERNAME, "test@localhost", "Firstname", "Lastname"); @@ -234,7 +253,7 @@ public void testDisabledBroker() throws Exception { waitForPage("sign in to", true); assertThat("Driver should be on the provider realm page right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_PROV_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_PROV_NAME + "/")); log.debug("Logging in"); accountLoginPage.login(PROVIDER_REALM_USER_NAME, PROVIDER_REALM_USER_PASSWORD); @@ -243,7 +262,7 @@ public void testDisabledBroker() throws Exception { updateAccountInformationPage.assertCurrent(); assertThat("We must be on consumer realm right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_CONS_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_CONS_NAME + "/")); log.debug("Updating info on updateAccount page"); updateAccountInformationPage.updateAccountInformation(CONSUMER_CHOSEN_USERNAME, "test@localhost", "Firstname", "Lastname"); @@ -273,7 +292,7 @@ public void testLinkOnlyBrokerIdpInitiated() throws Exception { waitForPage("sign in to", true); assertThat("Driver should be on the provider realm page right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_PROV_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_PROV_NAME + "/")); log.debug("Logging in"); accountLoginPage.login(PROVIDER_REALM_USER_NAME, PROVIDER_REALM_USER_PASSWORD); @@ -282,7 +301,7 @@ public void testLinkOnlyBrokerIdpInitiated() throws Exception { updateAccountInformationPage.assertCurrent(); assertThat("We must be on consumer realm right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_CONS_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_CONS_NAME + "/")); log.debug("Updating info on updateAccount page"); updateAccountInformationPage.updateAccountInformation(CONSUMER_CHOSEN_USERNAME, "test@localhost", "Firstname", "Lastname"); @@ -321,15 +340,15 @@ public void testLinkOnlyBrokerIdpInitiated() throws Exception { } private String getSamlIdpInitiatedUrl(String realmName, String samlIdpInitiatedSsoUrlName) { - return getAuthRoot() + "/auth/realms/" + realmName + "/protocol/saml/clients/" + samlIdpInitiatedSsoUrlName; + return getAuthRoot() + "/realms/" + realmName + "/protocol/saml/clients/" + samlIdpInitiatedSsoUrlName; } private String getSamlBrokerIdpInitiatedUrl(String realmName, String samlIdpInitiatedSsoUrlName) { - return getAuthRoot() + "/auth/realms/" + realmName + "/broker/saml-leaf/endpoint/clients/" + samlIdpInitiatedSsoUrlName; + return getAuthRoot() + "/realms/" + realmName + "/broker/saml-leaf/endpoint/clients/" + samlIdpInitiatedSsoUrlName; } private String getSamlBrokerUrl(String realmName) { - return getAuthRoot() + "/auth/realms/" + realmName + "/broker/saml-leaf/endpoint"; + return getAuthRoot() + "/realms/" + realmName + "/broker/saml-leaf/endpoint"; } private void waitForPage(final String title, final boolean htmlTitle) { @@ -674,7 +693,7 @@ public void testDisabledClient() { waitForPage("sign in to", true); assertThat("Driver should be on the provider realm page right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_PROV_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_PROV_NAME + "/")); log.debug("Logging in"); accountLoginPage.login(PROVIDER_REALM_USER_NAME, PROVIDER_REALM_USER_PASSWORD); @@ -683,7 +702,7 @@ public void testDisabledClient() { updateAccountInformationPage.assertCurrent(); assertThat("We must be on consumer realm right now", - driver.getCurrentUrl(), containsString("/auth/realms/" + REALM_CONS_NAME + "/")); + driver.getCurrentUrl(), containsString(getAuthPath() + "/realms/" + REALM_CONS_NAME + "/")); log.debug("Updating info on updateAccount page"); updateAccountInformationPage.updateAccountInformation(CONSUMER_CHOSEN_USERNAME, "test@localhost", "Firstname", "Lastname"); diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java index a5813a194e4c..f0145426fc3a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedAndEncryptedBrokerTest.java @@ -59,6 +59,8 @@ import org.junit.jupiter.api.Test; import org.w3c.dom.Document; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; + /** * * @author rmartinc @@ -96,7 +98,7 @@ public List createProviderClients() { attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.name()); attributes.put(SamlConfigAttributes.SAML_USE_METADATA_DESCRIPTOR_URL, "true"); attributes.put(SamlConfigAttributes.SAML_METADATA_DESCRIPTOR_URL, - BrokerTestTools.getProviderRoot() + "/auth/realms/" + consumerRealmName() + "/broker/" + bc.getIDPAlias() + "/endpoint/descriptor"); + BrokerTestTools.getProviderRoot() + getAuthPath() + "/realms/" + consumerRealmName() + "/broker/" + bc.getIDPAlias() + "/endpoint/descriptor"); } return clientRepresentationList; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java index 39cfc2b54fd5..074fce722320 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMetadataSignedBrokerTest.java @@ -45,6 +45,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; + /** * * @author rmartinc @@ -96,7 +98,7 @@ public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSync config.put(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "true"); config.put(SAMLIdentityProviderConfig.USE_METADATA_DESCRIPTOR_URL, "true"); config.put(SAMLIdentityProviderConfig.METADATA_DESCRIPTOR_URL, - BrokerTestTools.getProviderRoot() + "/auth/realms/" + providerRealmName() + "/protocol/saml/descriptor"); + BrokerTestTools.getProviderRoot() + getAuthPath() + "/realms/" + providerRealmName() + "/protocol/saml/descriptor"); return result; } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java index 0c9f5aa31afd..46c875d88434 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlMultipleTabsBrokerTest.java @@ -36,11 +36,9 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.util.BrowserTabUtil; -import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse; import org.hamcrest.Matchers; -import org.junit.Rule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.openqa.selenium.htmlunit.HtmlUnitDriver; @@ -60,7 +58,7 @@ public class KcSamlMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerT @InjectRealm ManagedRealm managedRealm; - @InjectTimeOffSet + @InjectTimeOffSet(enableForCaches = true) TimeOffSet timeOffSet; @InjectWebDriver @@ -72,9 +70,6 @@ public class KcSamlMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerT @InjectEvents Events events; - @Rule - public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); - @Override protected BrokerConfiguration getBrokerConfiguration() { return KcSamlBrokerConfiguration.INSTANCE; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java index 6e73e1911b8d..3f18c86fb11a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/KcSamlParseIdPDescriptorTest.java @@ -18,20 +18,17 @@ import java.io.IOException; import java.net.URI; -import java.util.List; import java.util.Map; import jakarta.ws.rs.core.MediaType; -import org.keycloak.admin.client.Keycloak; import org.keycloak.broker.saml.SAMLIdentityProviderConfig; -import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; -import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.testframework.realm.RealmConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -42,8 +39,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; - /** * * @author rmartinc @@ -51,16 +46,13 @@ @KeycloakIntegrationTest(config = org.keycloak.tests.broker.BrokerServerConfig.class) public class KcSamlParseIdPDescriptorTest { - @InjectRealm + @InjectRealm(config = KcSamlParseIdPDescriptorTest.TestRealmConfig.class) ManagedRealm managedRealm; - @InjectAdminClient - Keycloak adminClient; - - private final URI authServerPage = URI.create(getAuthServerContextRoot() + "/auth"); - - public void addTestRealms(List testRealms) { - testRealms.add(RealmBuilder.create().name("test").build()); + private URI getAuthServerPage() { + String baseUrl = managedRealm.getBaseUrl(); + int realmsIndex = baseUrl.indexOf("/realms/"); + return URI.create(realmsIndex > 0 ? baseUrl.substring(0, realmsIndex) : baseUrl); } @Test @@ -70,25 +62,26 @@ public void testIdPDescriptorParsing() throws IOException { MultipartFormDataOutput output = new MultipartFormDataOutput(); output.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE); output.addFormData("file", descriptor, MediaType.TEXT_XML_TYPE); - Map response = adminClient.realm("test").identityProviders().importFrom(output); + Map response = managedRealm.admin().identityProviders().importFrom(output); + URI authServerPage = getAuthServerPage(); Assertions.assertNotNull(response.get(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY)); - Assertions.assertEquals(authServerPage.toString() + "/realms/test", response.get(SAMLIdentityProviderConfig.IDP_ENTITY_ID)); + Assertions.assertEquals(authServerPage + "/realms/" + managedRealm.getName(), response.get(SAMLIdentityProviderConfig.IDP_ENTITY_ID)); Assertions.assertEquals("true", response.get(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE)); Assertions.assertEquals("true", response.get(SAMLIdentityProviderConfig.POST_BINDING_LOGOUT)); Assertions.assertEquals("true", response.get(SAMLIdentityProviderConfig.POST_BINDING_RESPONSE)); Assertions.assertEquals("true", response.get(SAMLIdentityProviderConfig.POST_BINDING_AUTHN_REQUEST)); Assertions.assertEquals("true", response.get(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED)); Assertions.assertEquals(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get(), response.get("nameIDPolicyFormat")); - Assertions.assertEquals(authServerPage.toString() + "/realms/test/protocol/saml", response.get(SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL)); - Assertions.assertEquals(authServerPage.toString() + "/realms/test/protocol/saml", response.get(SAMLIdentityProviderConfig.SINGLE_LOGOUT_SERVICE_URL)); - Assertions.assertEquals(authServerPage.toString() + "/realms/test/protocol/saml/resolve", response.get(SAMLIdentityProviderConfig.ARTIFACT_RESOLUTION_SERVICE_URL)); + Assertions.assertEquals(authServerPage + "/realms/" + managedRealm.getName() + "/protocol/saml", response.get(SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL)); + Assertions.assertEquals(authServerPage + "/realms/" + managedRealm.getName() + "/protocol/saml", response.get(SAMLIdentityProviderConfig.SINGLE_LOGOUT_SERVICE_URL)); + Assertions.assertEquals(authServerPage + "/realms/" + managedRealm.getName() + "/protocol/saml/resolve", response.get(SAMLIdentityProviderConfig.ARTIFACT_RESOLUTION_SERVICE_URL)); // modify it with WantAuthnRequestsSigned=false descriptor = descriptor.replaceFirst("WantAuthnRequestsSigned=\"true\"", "WantAuthnRequestsSigned=\"false\""); output = new MultipartFormDataOutput(); output.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE); output.addFormData("file", descriptor, MediaType.TEXT_XML_TYPE); - response = adminClient.realm("test").identityProviders().importFrom(output); + response = managedRealm.admin().identityProviders().importFrom(output); Assertions.assertEquals("false", response.get(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED)); Assertions.assertEquals("true", response.get(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE)); } @@ -106,7 +99,7 @@ public void testIdPDescriptorWithKeyDescriptorWithoutUseAttribute() throws IOExc MultipartFormDataOutput output = new MultipartFormDataOutput(); output.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE); output.addFormData("file", descriptor, MediaType.TEXT_XML_TYPE); - Map response = adminClient.realm("test").identityProviders().importFrom(output); + Map response = managedRealm.admin().identityProviders().importFrom(output); Assertions.assertNotNull(response.get(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY)); Assertions.assertNotNull(response.get(SAMLIdentityProviderConfig.ENCRYPTION_PUBLIC_KEY)); Assertions.assertEquals("false", response.get(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED)); @@ -114,12 +107,20 @@ public void testIdPDescriptorWithKeyDescriptorWithoutUseAttribute() throws IOExc } private String readSamlIdPDescriptor() throws IOException { + URI authServerPage = getAuthServerPage(); try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - HttpGet httpGet = new HttpGet(authServerPage.toString() + "/realms/test/protocol/saml/descriptor"); + HttpGet httpGet = new HttpGet(authServerPage + "/realms/" + managedRealm.getName() + "/protocol/saml/descriptor"); try (CloseableHttpResponse response = client.execute(httpGet)) { return EntityUtils.toString(response.getEntity()); } } } + public static class TestRealmConfig implements RealmConfig { + @Override + public RealmBuilder configure(RealmBuilder realm) { + return realm.name("test"); + } + } + } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java index 00ae7f9928ba..eb139a30cd05 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OAuth2BrokerTest.java @@ -25,8 +25,8 @@ import org.keycloak.testframework.ui.annotations.InjectWebDriver; import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; -import org.junit.Ignore; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -88,7 +88,7 @@ public void testLoginDefaultConfiguration() { logInWithBroker(bc); updateAccountInformationPage.assertCurrent(); - Assertions.assertTrue(driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"), + Assertions.assertTrue(driver.getCurrentUrl().contains("/realms/" + bc.consumerRealmName() + "/"), "We must be on correct realm right now"); log.debug("Updating info on updateAccount page"); updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname"); @@ -154,25 +154,25 @@ protected void createAdditionalMapperWithCustomSyncMode(IdentityProviderMapperSy } @Test - @Ignore + @Disabled @Override public void differentMappersCanHaveDifferentSyncModes() { } @Test - @Ignore + @Disabled @Override public void mapperDoesNotGrantNewRoleFromTokenWithSyncModeImport() { } @Test - @Ignore + @Disabled @Override public void mapperGrantsNewRoleFromTokenWithInheritedSyncModeForce() { } @Test - @Ignore + @Disabled @Override public void mapperDoesNotGrantNewRoleFromTokenWithInheritedSyncModeImport() { } diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java b/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java index fa1dffe2f33e..bdbe392744ae 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/OidcBackchannelLogoutBrokerConfiguration.java @@ -56,7 +56,7 @@ public RealmRepresentation createProviderRealm() { RealmRepresentation realm = new RealmRepresentation(); realm.setRealm(REALM_PROV_NAME); realm.setEnabled(true); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); realm.setEventsEnabled(true); return realm; @@ -68,7 +68,7 @@ public RealmRepresentation createConsumerRealm() { realm.setRealm(REALM_CONS_NAME); realm.setEnabled(true); realm.setResetPasswordAllowed(true); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); realm.setEventsEnabled(true); return realm; @@ -278,7 +278,7 @@ public RealmRepresentation createSubConsumerRealm() { realm.setRealm(REALM_SUB_CONS_NAME); realm.setEnabled(true); realm.setResetPasswordAllowed(true); - realm.setEventsListeners(Arrays.asList("jboss-logging", "event-queue")); + realm.setEventsListeners(Collections.singletonList("jboss-logging")); realm.setEventsEnabled(true); return realm; diff --git a/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java b/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java index 18b663077afb..acf3c2c2959d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/broker/SocialLoginTest.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Properties; +import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.WebTarget; @@ -74,16 +75,18 @@ import com.google.common.collect.ImmutableMap; import org.jboss.logging.Logger; -import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.PageFactory; +import static org.keycloak.tests.broker.BrokerTestTools.getAuthPath; import static org.keycloak.tests.broker.SocialLoginTest.Provider.BITBUCKET; import static org.keycloak.tests.broker.SocialLoginTest.Provider.FACEBOOK; import static org.keycloak.tests.broker.SocialLoginTest.Provider.FACEBOOK_INCLUDE_BIRTHDAY; @@ -104,7 +107,6 @@ import static org.keycloak.tests.broker.SocialLoginTest.Provider.TWITTER; import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; -import static org.junit.Assume.assumeTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -199,14 +201,15 @@ public String configId() { private Provider currentTestProvider = null; private AbstractSocialLoginPage currentSocialLoginPage = null; - @BeforeClass + @BeforeAll public static void loadConfig() throws Exception { - assumeTrue(System.getProperties().containsKey(SOCIAL_CONFIG)); + Assumptions.assumeTrue(System.getProperties().containsKey(SOCIAL_CONFIG)); config.load(new FileInputStream(System.getProperty(SOCIAL_CONFIG))); } @BeforeEach public void beforeSocialLoginTest() { + ensureSocialRealmExists(); oauth.realm(REALM); createAppClientInRealm(REALM); } @@ -253,19 +256,31 @@ public void addTestRealms(List testRealms) { testRealms.add(rep); } + private void ensureSocialRealmExists() { + try { + adminClient.realm(REALM).toRepresentation(); + return; + } catch (NotFoundException ignored) { + } + + adminClient.realms().create(RealmBuilder.create().name(REALM).build()); + } + protected boolean isImportAfterEachMethod() { return true; } private void createAppClientInRealm(String realm) { + String authPath = getAuthPath(); + String root = resolveAuthServerRoot() + authPath; org.keycloak.representations.idm.ClientRepresentation client = new org.keycloak.representations.idm.ClientRepresentation(); client.setClientId("test-app"); client.setName("test-app"); client.setSecret("password"); client.setEnabled(true); client.setDirectAccessGrantsEnabled(true); - client.setRedirectUris(java.util.Collections.singletonList(OAuthClient.SERVER_ROOT + "/auth/*")); - client.setBaseUrl(OAuthClient.SERVER_ROOT + "/auth/realms/" + realm + "/app"); + client.setRedirectUris(java.util.Collections.singletonList(root + "/*")); + client.setBaseUrl(root + "/realms/" + realm + "/app"); try { adminClient.realm(realm).clients().create(client).close(); } catch (Exception ignored) { @@ -273,7 +288,7 @@ private void createAppClientInRealm(String realm) { } private URI getAuthServerRoot() { - return URI.create(resolveAuthServerRoot() + "/auth/"); + return URI.create(resolveAuthServerRoot() + getAuthPath() + "/"); } private KeycloakTestingClient getTestingClient() { @@ -287,8 +302,7 @@ private String resolveAuthServerRoot() { if (managedRealm != null && managedRealm.getBaseUrl() != null) { int realmsIndex = managedRealm.getBaseUrl().indexOf("/realms/"); if (realmsIndex > 0) { - String root = managedRealm.getBaseUrl().substring(0, realmsIndex); - return root.endsWith("/auth") ? root.substring(0, root.length() - "/auth".length()) : root; + return managedRealm.getBaseUrl().substring(0, realmsIndex); } } if (OAuthClient.SERVER_ROOT != null && !OAuthClient.SERVER_ROOT.isBlank()) { @@ -431,7 +445,7 @@ public void facebookLoginWithEnhancedScope() throws InterruptedException { } @Test - @Ignore + @Disabled public void instagramLogin() throws InterruptedException { setTestProvider(INSTAGRAM); performLogin(); diff --git a/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java b/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java index c824e263f519..201cc46dc8c8 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/updaters/IdentityProviderAttributeUpdater.java @@ -18,6 +18,7 @@ public class IdentityProviderAttributeUpdater { private final IdentityProviderResource identityProviderResource; private final IdentityProviderRepresentation rep; + private Boolean originalStoreToken; public IdentityProviderAttributeUpdater(IdentityProviderResource identityProviderResource) { this.identityProviderResource = identityProviderResource; @@ -46,6 +47,9 @@ public IdentityProviderAttributeUpdater removeAttribute(String name) { } public IdentityProviderAttributeUpdater setStoreToken(boolean storeToken) { + if (originalStoreToken == null) { + originalStoreToken = rep.isStoreToken(); + } rep.setStoreToken(storeToken); return this; } @@ -55,6 +59,9 @@ public Closeable update() { return () -> { rep.getConfig().putAll(originalAttributes); + if (originalStoreToken != null) { + rep.setStoreToken(originalStoreToken); + } identityProviderResource.update(rep); }; } diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java index 2fb62fe9453b..b4479f343d70 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/DroneUtils.java @@ -20,6 +20,8 @@ import java.util.LinkedList; import java.util.Queue; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; + import org.openqa.selenium.WebDriver; /** @@ -29,7 +31,8 @@ public final class DroneUtils { private static final Queue driverQueue = new LinkedList<>(); public static WebDriver getCurrentDriver() { - return driverQueue.peek(); + WebDriver queuedDriver = driverQueue.peek(); + return queuedDriver != null ? queuedDriver : ManagedWebDriver.currentDriver(); } public static void addWebDriver(WebDriver driver) { diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java b/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java index 96f3b095ce90..1687b69fa253 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/MutualTLSUtils.java @@ -1,6 +1,19 @@ package org.keycloak.testsuite.util; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +import org.keycloak.common.util.KeystoreUtil; + import org.apache.http.client.RedirectStrategy; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.client.HttpClientBuilder; @@ -18,8 +31,60 @@ public static CloseableHttpClient newCloseableHttpClient(String keyStorePath, St public static CloseableHttpClient newCloseableHttpClient(String keyStorePath, String keyStorePassword, String trustStorePath, String trustStorePassword, RedirectStrategy redirectStrategy) { - // Compatibility helper for migrated tests. Tests that need real mTLS should use - // dedicated providers in the current test framework. + KeyStore keyStore = null; + if (keyStorePath != null) { + try { + keyStore = KeystoreUtil.loadKeyStore(keyStorePath, keyStorePassword); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + KeyStore trustStore = null; + if (trustStorePath != null) { + try { + trustStore = KeystoreUtil.loadKeyStore(trustStorePath, trustStorePassword); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + if (keyStore != null || trustStore != null) { + return newCloseableHttpClientSSL(keyStore, keyStorePassword, trustStore, redirectStrategy); + } + return HttpClientBuilder.create().setRedirectStrategy(redirectStrategy).build(); } + + private static CloseableHttpClient newCloseableHttpClientSSL(KeyStore keyStore, String keyStorePassword, + KeyStore trustStore, RedirectStrategy redirectStrategy) { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + + KeyManagerFactory keyManagerFactory = null; + if (keyStore != null) { + keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, keyStorePassword != null ? keyStorePassword.toCharArray() : null); + } + + TrustManagerFactory trustManagerFactory = null; + if (trustStore != null) { + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + } + + sslContext.init( + keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null, + trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null, + null); + SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + + return HttpClientBuilder.create() + .setSSLSocketFactory(socketFactory) + .setRedirectStrategy(redirectStrategy) + .build(); + } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException e) { + throw new RuntimeException(e); + } + } } diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java b/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java index f962d0f4a0c5..45231fc98d14 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/ReverseProxy.java @@ -1,10 +1,15 @@ package org.keycloak.testsuite.util; +import java.net.URI; + +import org.keycloak.testframework.oauth.OAuthClient; + import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_SSL_REQUIRED; +import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; import static org.keycloak.testsuite.util.ServerURLs.removeDefaultPorts; public class ReverseProxy implements TestRule { @@ -12,6 +17,7 @@ public class ReverseProxy implements TestRule { public static String DEFAULT_PROXY_HOST = "proxy.kc.localtest.me"; public static final int DEFAULT_HTTP_PORT = 8666; public static final int DEFAULT_HTTPS_PORT = 8667; + private final String url; public ReverseProxy() { this(DEFAULT_PROXY_HOST); @@ -22,7 +28,8 @@ public ReverseProxy(String host) { } public ReverseProxy(String host, String nodes) { - // No-op compatibility constructor kept for migrated broker tests. + String effectiveHost = host == null || host.isBlank() ? DEFAULT_PROXY_HOST : host; + this.url = resolveProxyUrl(effectiveHost); } @Override @@ -36,8 +43,25 @@ public void evaluate() throws Throwable { } public String getUrl() { - String scheme = AUTH_SERVER_SSL_REQUIRED ? "https" : "http"; - int port = AUTH_SERVER_SSL_REQUIRED ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; - return removeDefaultPorts(String.format("%s://%s:%s", scheme, DEFAULT_PROXY_HOST, port)); + return url; + } + + private static String resolveProxyUrl(String host) { + String root = OAuthClient.SERVER_ROOT; + if (root == null || root.isBlank()) { + root = getAuthServerContextRoot(); + } + + URI rootUri = URI.create(root); + String scheme = rootUri.getScheme() != null ? rootUri.getScheme() : (AUTH_SERVER_SSL_REQUIRED ? "https" : "http"); + int defaultPort = "https".equalsIgnoreCase(scheme) ? 443 : 80; + int port = rootUri.getPort() == -1 ? defaultPort : rootUri.getPort(); + + String path = rootUri.getPath(); + String normalizedPath = (path == null || path.isBlank() || "/".equals(path)) + ? "" + : (path.endsWith("/") ? path.substring(0, path.length() - 1) : path); + + return removeDefaultPorts(String.format("%s://%s:%s%s", scheme, host, port, normalizedPath)); } } diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java index 28cb6592946b..5583ff1bfc6a 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java @@ -28,11 +28,13 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; @@ -72,8 +74,10 @@ import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.core.util.JAXPValidationUtil; import org.keycloak.saml.processing.web.util.RedirectBindingUtil; +import org.keycloak.testsuite.util.saml.LoginBuilder; import org.keycloak.testsuite.util.saml.StepWithCheckers; +import org.apache.http.HttpRequest; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -88,6 +92,7 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.LaxRedirectStrategy; import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.jboss.logging.Logger; import org.jsoup.Jsoup; @@ -810,6 +815,9 @@ public T executeAndTransform(ResultExtractor resultTransformer, List T executeAndTransform(ResultExtractor resultTransformer, List cookie.getExpiryDate() == null || cookie.getExpiryDate().after(now)) + .filter(cookie -> { + String cookiePath = cookie.getPath() == null ? "/" : cookie.getPath(); + return requestPath.startsWith(cookiePath); + }) + .map(cookie -> cookie.getName() + "=" + cookie.getValue()) + .collect(Collectors.joining("; ")); + + if (!cookieHeader.isEmpty()) { + request.setHeader("Cookie", cookieHeader); + } + } + protected HttpClientBuilder createHttpClientBuilderInstance() { return HttpClientBuilder .create() + .addInterceptorLast((HttpRequest request, HttpContext context) -> { + if (request.getRequestLine() != null && request.getRequestLine().getUri() != null + && request.getRequestLine().getUri().contains("login-actions/authenticate")) { + context.setAttribute("kc.test.last.auth.request.uri", request.getRequestLine().getUri()); + context.setAttribute("kc.test.last.auth.cookie", request.getFirstHeader("Cookie")); + } + }) .evictIdleConnections(100, TimeUnit.MILLISECONDS); } } diff --git a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java index 5930731a521a..cf0a0a605292 100644 --- a/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java +++ b/tests/base/src/test/java/org/keycloak/testsuite/util/saml/LoginBuilder.java @@ -18,9 +18,14 @@ import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; @@ -37,6 +42,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; @@ -45,7 +51,6 @@ import org.jsoup.nodes.Element; import static org.keycloak.testsuite.admin.Users.getPasswordOf; -import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -57,6 +62,7 @@ public class LoginBuilder implements Step { private final SamlClientBuilder clientBuilder; + private static final Pattern RESTART_URL_PATTERN = Pattern.compile("startSessionPolling\\(\\s*\"([^\"]+)\""); private UserRepresentation user; private boolean sso = false; private String idpAlias; @@ -70,14 +76,129 @@ public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, Closea if (sso) { return null; // skip this step } else { - assertThat(currentResponse, statusCodeIsHC(Response.Status.OK)); - String loginPageText = EntityUtils.toString(currentResponse.getEntity(), StandardCharsets.UTF_8); + int statusCode = currentResponse.getStatusLine().getStatusCode(); + URI effectiveUri = currentURI; + String loginPageText; + + if (statusCode == Response.Status.FOUND.getStatusCode() + && currentResponse.getFirstHeader("Location") != null) { + String location = currentResponse.getFirstHeader("Location").getValue(); + effectiveUri = currentURI.resolve(location); + HttpGet redirectGet = new HttpGet(effectiveUri); + attachCookieHeader(redirectGet, context, effectiveUri); + try (CloseableHttpResponse redirectResponse = client.execute(redirectGet, context)) { + statusCode = redirectResponse.getStatusLine().getStatusCode(); + loginPageText = EntityUtils.toString(redirectResponse.getEntity(), StandardCharsets.UTF_8); + } + } else { + loginPageText = EntityUtils.toString(currentResponse.getEntity(), StandardCharsets.UTF_8); + } + + if (statusCode != Response.Status.OK.getStatusCode()) { + HttpUriRequest retryAfterClearingRestartCookie = retryAfterClearingRestartCookie(client, context, effectiveUri); + if (retryAfterClearingRestartCookie != null) { + return retryAfterClearingRestartCookie; + } + + HttpUriRequest recoveredRequest = attemptCookieRestartRecovery(client, context, effectiveUri, loginPageText); + if (recoveredRequest != null) { + return recoveredRequest; + } + + String cookies = context.getCookieStore().getCookies().toString(); + Object lastAuthUri = context.getAttribute("kc.test.last.auth.request.uri"); + Object lastAuthCookie = context.getAttribute("kc.test.last.auth.cookie"); + throw new AssertionError("Unexpected status for login page. status=" + statusCode + + ", requestUri=" + effectiveUri + + ", redirects=" + context.getRedirectLocations() + + ", cookies=" + cookies + + ", lastAuthUri=" + lastAuthUri + + ", lastAuthCookie=" + lastAuthCookie + + ", body=" + loginPageText); + } assertThat(loginPageText, containsString("login")); + HttpUriRequest request = handleLoginPage(loginPageText, effectiveUri); + attachCookieHeader(request, context, effectiveUri); + return request; + } + } + + private HttpUriRequest retryAfterClearingRestartCookie(CloseableHttpClient client, HttpClientContext context, URI currentURI) throws Exception { + if (currentURI == null || !currentURI.toString().contains("/login-actions/authenticate")) { + return null; + } + + List currentCookies = new ArrayList<>(context.getCookieStore().getCookies()); + context.getCookieStore().clear(); + currentCookies.stream() + .filter(cookie -> !"KC_RESTART".equals(cookie.getName())) + .forEach(context.getCookieStore()::addCookie); + + HttpGet retryGet = new HttpGet(currentURI); + attachCookieHeader(retryGet, context, currentURI); + try (CloseableHttpResponse retryResponse = client.execute(retryGet, context)) { + if (retryResponse.getStatusLine().getStatusCode() != Response.Status.OK.getStatusCode()) { + return null; + } + + String loginPageText = EntityUtils.toString(retryResponse.getEntity(), StandardCharsets.UTF_8); + if (!loginPageText.toLowerCase().contains("login")) { + return null; + } return handleLoginPage(loginPageText, currentURI); } } + private HttpUriRequest attemptCookieRestartRecovery(CloseableHttpClient client, HttpClientContext context, URI currentURI, String responseBody) throws Exception { + Matcher matcher = RESTART_URL_PATTERN.matcher(responseBody); + if (!matcher.find()) { + return null; + } + + URI restartUri = currentURI.resolve(matcher.group(1)); + HttpGet restartGet = new HttpGet(restartUri); + attachCookieHeader(restartGet, context, restartUri); + try (CloseableHttpResponse restartResponse = client.execute(restartGet, context)) { + int restartStatus = restartResponse.getStatusLine().getStatusCode(); + if (restartStatus != Response.Status.OK.getStatusCode()) { + return null; + } + + String loginPageText = EntityUtils.toString(restartResponse.getEntity(), StandardCharsets.UTF_8); + if (!loginPageText.toLowerCase().contains("login")) { + return null; + } + return handleLoginPage(loginPageText, restartUri); + } + } + + private void attachCookieHeader(HttpUriRequest request, HttpClientContext context, URI baseUri) { + if (request == null || context == null || context.getCookieStore() == null) { + return; + } + + URI requestUri = request.getURI(); + if (requestUri != null && !requestUri.isAbsolute() && baseUri != null) { + requestUri = baseUri.resolve(requestUri); + } + + String path = requestUri != null && requestUri.getPath() != null ? requestUri.getPath() : "/"; + Date now = new Date(); + String cookieHeader = context.getCookieStore().getCookies().stream() + .filter(cookie -> cookie.getExpiryDate() == null || cookie.getExpiryDate().after(now)) + .filter(cookie -> { + String cookiePath = cookie.getPath() == null ? "/" : cookie.getPath(); + return path.startsWith(cookiePath); + }) + .map(cookie -> cookie.getName() + "=" + cookie.getValue()) + .collect(Collectors.joining("; ")); + + if (!cookieHeader.isEmpty()) { + request.setHeader("Cookie", cookieHeader); + } + } + public SamlClientBuilder build() { return this.clientBuilder; } diff --git a/tests/base/src/test/resources/org/keycloak/tests/broker/vault/consumer_oidc__idp b/tests/base/src/test/resources/org/keycloak/tests/broker/vault/consumer_oidc__idp new file mode 100644 index 000000000000..536aca34dbae --- /dev/null +++ b/tests/base/src/test/resources/org/keycloak/tests/broker/vault/consumer_oidc__idp @@ -0,0 +1 @@ +secret \ No newline at end of file diff --git a/tests/base/src/test/resources/org/keycloak/tests/broker/vault/oidc__idp b/tests/base/src/test/resources/org/keycloak/tests/broker/vault/oidc__idp new file mode 100644 index 000000000000..536aca34dbae --- /dev/null +++ b/tests/base/src/test/resources/org/keycloak/tests/broker/vault/oidc__idp @@ -0,0 +1 @@ +secret \ No newline at end of file diff --git a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory index de68c89f0d78..d277d1baac4b 100644 --- a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory +++ b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory @@ -1,2 +1,3 @@ org.keycloak.tests.providers.federation.DummyUserFederationProviderFactory -org.keycloak.tests.providers.federation.UserMapStorageFactory \ No newline at end of file +org.keycloak.tests.providers.federation.UserMapStorageFactory +org.keycloak.tests.providers.federation.PassThroughFederatedUserStorageProviderFactory \ No newline at end of file