From 191ef1874eafb6f85d77b6e3bba623edfb63b318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Barto=C5=A1?= Date: Mon, 31 Jan 2022 11:26:53 +0100 Subject: [PATCH] Complete support for Passwordless tests Closes #9850 --- .../webauthn/utils/WebAuthnRealmData.java | 81 +++ .../AbstractWebAuthnPolicySettingsTest.java | 422 -------------- .../admin/PwdLessPolicySettingsTest.java | 29 + ...ebAuthnPolicyPasswordlessSettingsTest.java | 219 -------- .../admin/WebAuthnPolicySettingsTest.java | 524 +++++++++++++++--- .../AppInitiatedActionPwdLessTest.java | 5 +- .../WebAuthnPwdLessPropertyTest.java | 39 ++ 7 files changed, 588 insertions(+), 731 deletions(-) delete mode 100644 testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/AbstractWebAuthnPolicySettingsTest.java create mode 100644 testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/PwdLessPolicySettingsTest.java delete mode 100644 testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicyPasswordlessSettingsTest.java rename testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/{ => passwordless}/AppInitiatedActionPwdLessTest.java (86%) create mode 100644 testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/WebAuthnPwdLessPropertyTest.java diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/utils/WebAuthnRealmData.java b/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/utils/WebAuthnRealmData.java index 92e2271637..977d0c6c72 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/utils/WebAuthnRealmData.java +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/utils/WebAuthnRealmData.java @@ -20,6 +20,7 @@ package org.keycloak.testsuite.webauthn.utils; import org.keycloak.representations.idm.RealmRepresentation; import java.util.List; +import java.util.function.Consumer; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -79,4 +80,84 @@ public class WebAuthnRealmData { public List getAcceptableAaguids() { return isPasswordless ? realm.getWebAuthnPolicyPasswordlessAcceptableAaguids() : realm.getWebAuthnPolicyAcceptableAaguids(); } + + public RealmRepresentation getRealm() { + return realm; + } + + public Builder builder() { + return new Builder(realm, isPasswordless); + } + + public static class Builder { + private final RealmRepresentation realm; + private final boolean isPasswordless; + + public Builder(RealmRepresentation realm, boolean isPasswordless) { + this.realm = realm; + this.isPasswordless = isPasswordless; + } + + public Builder rpEntityName(String entityName) { + setProperty(entityName, realm::setWebAuthnPolicyRpEntityName, realm::setWebAuthnPolicyPasswordlessRpEntityName); + return this; + } + + public Builder signatureAlgorithms(List list) { + setProperty(list, realm::setWebAuthnPolicySignatureAlgorithms, realm::setWebAuthnPolicyPasswordlessSignatureAlgorithms); + return this; + } + + public Builder rpId(String rpId) { + setProperty(rpId, realm::setWebAuthnPolicyRpId, realm::setWebAuthnPolicyPasswordlessRpId); + return this; + } + + public Builder attestationConveyancePreference(String preference) { + setProperty(preference, realm::setWebAuthnPolicyAttestationConveyancePreference, realm::setWebAuthnPolicyPasswordlessAttestationConveyancePreference); + return this; + } + + public Builder authenticatorAttachment(String attachment) { + setProperty(attachment, realm::setWebAuthnPolicyAuthenticatorAttachment, realm::setWebAuthnPolicyPasswordlessAuthenticatorAttachment); + return this; + } + + public Builder requireResidentKey(String requirement) { + setProperty(requirement, realm::setWebAuthnPolicyRequireResidentKey, realm::setWebAuthnPolicyPasswordlessRequireResidentKey); + return this; + } + + public Builder userVerificationRequirement(String requirement) { + setProperty(requirement, realm::setWebAuthnPolicyUserVerificationRequirement, realm::setWebAuthnPolicyPasswordlessUserVerificationRequirement); + return this; + } + + public Builder timeout(Integer timeout) { + setProperty(timeout, realm::setWebAuthnPolicyCreateTimeout, realm::setWebAuthnPolicyPasswordlessCreateTimeout); + return this; + } + + public Builder avoidSameAuthenticatorRegister(Boolean state) { + setProperty(state, realm::setWebAuthnPolicyAvoidSameAuthenticatorRegister, realm::setWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister); + return this; + } + + public Builder acceptableAaguids(List aaguids) { + setProperty(aaguids, realm::setWebAuthnPolicyAcceptableAaguids, realm::setWebAuthnPolicyPasswordlessAcceptableAaguids); + return this; + } + + public RealmRepresentation build() { + return realm; + } + + private void setProperty(T value, Consumer webauthnSetter, Consumer passwordlessSetter) { + if (isPasswordless) { + passwordlessSetter.accept(value); + } else { + webauthnSetter.accept(value); + } + } + } } diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/AbstractWebAuthnPolicySettingsTest.java b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/AbstractWebAuthnPolicySettingsTest.java deleted file mode 100644 index 4cfece6c11..0000000000 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/AbstractWebAuthnPolicySettingsTest.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * 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.webauthn.admin; - -import com.webauthn4j.data.AttestationConveyancePreference; -import com.webauthn4j.data.AuthenticatorAttachment; -import com.webauthn4j.data.UserVerificationRequirement; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.keycloak.models.Constants; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.console.AbstractConsoleTest; -import org.keycloak.testsuite.page.AbstractPatternFlyAlert; -import org.keycloak.testsuite.util.UIUtils; -import org.keycloak.testsuite.webauthn.pages.WebAuthnPolicyPage; -import org.keycloak.testsuite.webauthn.updaters.AbstractWebAuthnRealmUpdater; -import org.keycloak.testsuite.webauthn.utils.PropertyRequirement; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.ui.Select; - -import java.io.Closeable; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.keycloak.testsuite.util.WaitUtils.pause; -import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; -import static org.keycloak.testsuite.webauthn.utils.PropertyRequirement.NO; -import static org.keycloak.testsuite.webauthn.utils.PropertyRequirement.YES; - -/** - * @author Martin Bartos - */ -public abstract class AbstractWebAuthnPolicySettingsTest extends AbstractConsoleTest { - - protected static final String ALL_ZERO_AAGUID = "00000000-0000-0000-0000-000000000000"; - protected static final String ALL_ONE_AAGUID = "11111111-1111-1111-1111-111111111111"; - - @Rule - public AssertEvents events = new AssertEvents(this); - - @Before - public void navigateToPolicy() { - driver.manage().window().maximize(); - getPolicyPage().navigateTo(); - waitForPageToLoad(); - getPolicyPage().assertCurrent(); - } - - protected abstract WebAuthnPolicyPage getPolicyPage(); - - protected abstract AbstractWebAuthnRealmUpdater getWebAuthnRealmUpdater(); - - protected AbstractWebAuthnRealmUpdater updateWebAuthnPolicy( - String rpName, - List algorithms, - String attestationPreference, - String authenticatorAttachment, - String requireResidentKey, - String rpId, - String userVerification, - List acceptableAaguids) { - - AbstractWebAuthnRealmUpdater updater = getWebAuthnRealmUpdater().setWebAuthnPolicyRpEntityName(rpName); - - checkAndSet(algorithms, updater::setWebAuthnPolicySignatureAlgorithms); - checkAndSet(attestationPreference, updater::setWebAuthnPolicyAttestationConveyancePreference); - checkAndSet(authenticatorAttachment, updater::setWebAuthnPolicyAuthenticatorAttachment); - checkAndSet(requireResidentKey, updater::setWebAuthnPolicyRequireResidentKey); - checkAndSet(rpId, updater::setWebAuthnPolicyRpId); - checkAndSet(userVerification, updater::setWebAuthnPolicyUserVerificationRequirement); - checkAndSet(acceptableAaguids, updater::setWebAuthnPolicyAcceptableAaguids); - - return updater.update(); - } - - private void checkAndSet(T value, Consumer consumer) { - if (value != null) { - consumer.accept(value); - } - } - - protected void checkRpEntityValues() { - String rpEntityName = getPolicyPage().getRpEntityName(); - assertThat(rpEntityName, notNullValue()); - assertThat(rpEntityName, is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); - - getPolicyPage().setRpEntityName("newEntityName"); - getPolicyPage().clickSaveButton(); - AbstractPatternFlyAlert.waitUntilHidden(); - - rpEntityName = getPolicyPage().getRpEntityName(); - assertThat(rpEntityName, notNullValue()); - assertThat(rpEntityName, is("newEntityName")); - - getPolicyPage().setRpEntityName(""); - getPolicyPage().clickSaveButton(); - AbstractPatternFlyAlert.waitUntilHidden(); - - rpEntityName = getPolicyPage().getRpEntityName(); - assertThat(rpEntityName, notNullValue()); - assertThat(rpEntityName, is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); - - String rpEntityId = getPolicyPage().getRpEntityId(); - assertThat(rpEntityId, notNullValue()); - assertThat(rpEntityId, is("")); - - getPolicyPage().setRpEntityId("rpId123"); - getPolicyPage().clickSaveButton(); - AbstractPatternFlyAlert.waitUntilHidden(); - - rpEntityId = getPolicyPage().getRpEntityId(); - assertThat(rpEntityId, notNullValue()); - assertThat(rpEntityId, is("rpId123")); - } - - protected void checkWrongSignatureAlgorithm() throws IOException { - try (Closeable c = getWebAuthnRealmUpdater() - .setWebAuthnPolicySignatureAlgorithms(Collections.singletonList("something-bad")) - .update()) { - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - final List signatureAlgorithms = realm.getWebAuthnPolicySignatureAlgorithms(); - assertThat(signatureAlgorithms, notNullValue()); - assertThat(signatureAlgorithms.size(), is(1)); - - getPolicyPage().navigateTo(); - waitForPageToLoad(); - - Select selectedAlg = getPolicyPage().getSignatureAlgorithms(); - assertThat(selectedAlg, notNullValue()); - - try { - // should throw an exception - selectedAlg.getFirstSelectedOption(); - } catch (NoSuchElementException e) { - assertThat(e.getMessage(), containsString("No options are selected")); - } - } - } - - protected void checkSignatureAlgorithms() { - getPolicyPage().assertCurrent(); - - final Select algorithms = getPolicyPage().getSignatureAlgorithms(); - assertThat(algorithms, notNullValue()); - - algorithms.selectByValue("ES256"); - algorithms.selectByValue("ES384"); - algorithms.selectByValue("RS1"); - - final List selectedAlgs = algorithms.getAllSelectedOptions() - .stream() - .map(WebElement::getText) - .collect(Collectors.toList()); - - assertThat(selectedAlgs, notNullValue()); - assertThat(selectedAlgs, hasSize(3)); - - try { - algorithms.selectByValue("something-bad"); - } catch (NoSuchElementException e) { - assertThat(e.getMessage(), containsString("Cannot locate option with value: something-bad")); - } - - assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); - assertThat(getPolicyPage().isCancelButtonEnabled(), is(true)); - - getPolicyPage().clickSaveButton(); - - assertThat(getPolicyPage().isSaveButtonEnabled(), is(false)); - assertThat(getPolicyPage().isCancelButtonEnabled(), is(false)); - } - - public void checkAttestationConveyancePreference() { - // default not specified - AttestationConveyancePreference attestation = getPolicyPage().getAttestationConveyancePreference(); - assertThat(attestation, nullValue()); - - // Direct - getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.DIRECT); - getPolicyPage().clickSaveButton(); - - attestation = getPolicyPage().getAttestationConveyancePreference(); - assertThat(attestation, notNullValue()); - assertThat(attestation, is(AttestationConveyancePreference.DIRECT)); - - // Indirect - getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.INDIRECT); - getPolicyPage().clickSaveButton(); - - attestation = getPolicyPage().getAttestationConveyancePreference(); - assertThat(attestation, notNullValue()); - assertThat(attestation, is(AttestationConveyancePreference.INDIRECT)); - - // None - getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.NONE); - getPolicyPage().clickSaveButton(); - - attestation = getPolicyPage().getAttestationConveyancePreference(); - assertThat(attestation, notNullValue()); - assertThat(attestation, is(AttestationConveyancePreference.NONE)); - - try { - getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.ENTERPRISE); - Assert.fail("We don't support 'Enterprise' mode at this moment"); - } catch (NoSuchElementException e) { - } - } - - protected void checkAuthenticatorAttachment() { - AuthenticatorAttachment attachment = getPolicyPage().getAuthenticatorAttachment(); - assertThat(attachment, nullValue()); - - // Cross-platform - getPolicyPage().setAuthenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM); - getPolicyPage().clickSaveButton(); - - attachment = getPolicyPage().getAuthenticatorAttachment(); - assertThat(attachment, notNullValue()); - assertThat(attachment, is(AuthenticatorAttachment.CROSS_PLATFORM)); - - // Platform - getPolicyPage().setAuthenticatorAttachment(AuthenticatorAttachment.PLATFORM); - getPolicyPage().clickSaveButton(); - - attachment = getPolicyPage().getAuthenticatorAttachment(); - assertThat(attachment, notNullValue()); - assertThat(attachment, is(AuthenticatorAttachment.PLATFORM)); - } - - protected void checkResidentKey() { - PropertyRequirement requireResidentKey = getPolicyPage().requireResidentKey(); - assertThat(requireResidentKey, notNullValue()); - assertThat(requireResidentKey, is(PropertyRequirement.NOT_SPECIFIED)); - - getPolicyPage().requireResidentKey(YES); - getPolicyPage().clickSaveButton(); - - // Yes - requireResidentKey = getPolicyPage().requireResidentKey(); - assertThat(requireResidentKey, notNullValue()); - assertThat(requireResidentKey, is(YES)); - - getPolicyPage().requireResidentKey(NO); - getPolicyPage().clickSaveButton(); - - // Null - getPolicyPage().requireResidentKey(null); - assertThat(getPolicyPage().isSaveButtonEnabled(), is(false)); - - // Not specified - getPolicyPage().requireResidentKey(PropertyRequirement.NOT_SPECIFIED); - assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); - getPolicyPage().clickSaveButton(); - - // No - getPolicyPage().requireResidentKey(NO); - getPolicyPage().clickSaveButton(); - - requireResidentKey = getPolicyPage().requireResidentKey(); - assertThat(requireResidentKey, notNullValue()); - assertThat(requireResidentKey, is(NO)); - } - - protected void checkUserVerification() { - UserVerificationRequirement userVerification = getPolicyPage().getUserVerification(); - assertThat(userVerification, nullValue()); - - // Preferred - getPolicyPage().setUserVerification(UserVerificationRequirement.PREFERRED); - getPolicyPage().clickSaveButton(); - - userVerification = getPolicyPage().getUserVerification(); - assertThat(userVerification, notNullValue()); - assertThat(userVerification, is(UserVerificationRequirement.PREFERRED)); - - // Required - getPolicyPage().setUserVerification(UserVerificationRequirement.REQUIRED); - getPolicyPage().clickSaveButton(); - - userVerification = getPolicyPage().getUserVerification(); - assertThat(userVerification, notNullValue()); - assertThat(userVerification, is(UserVerificationRequirement.REQUIRED)); - - // Discouraged - getPolicyPage().setUserVerification(UserVerificationRequirement.DISCOURAGED); - getPolicyPage().clickSaveButton(); - - userVerification = getPolicyPage().getUserVerification(); - assertThat(userVerification, notNullValue()); - assertThat(userVerification, is(UserVerificationRequirement.DISCOURAGED)); - } - - protected void checkTimeout() { - int timeout = getPolicyPage().getTimeout(); - assertThat(timeout, is(0)); - - getPolicyPage().setTimeout(10); - getPolicyPage().clickSaveButton(); - - timeout = getPolicyPage().getTimeout(); - assertThat(timeout, is(10)); - - getPolicyPage().setTimeout(-10); - getPolicyPage().clickSaveButton(); - assertAlertDanger(); - - timeout = getPolicyPage().getTimeout(); - assertThat(timeout, is(-10)); - - getPolicyPage().navigateTo(); - waitForPageToLoad(); - - timeout = getPolicyPage().getTimeout(); - assertThat(timeout, is(10)); - - getPolicyPage().setTimeout(1000000); - getPolicyPage().clickSaveButton(); - assertAlertDanger(); - - getPolicyPage().setTimeout(500); - getPolicyPage().clickSaveButton(); - - timeout = getPolicyPage().getTimeout(); - assertThat(timeout, is(500)); - } - - protected void checkAvoidSameAuthenticatorRegistration() { - boolean avoidSameAuthenticatorRegistration = getPolicyPage().avoidSameAuthenticatorRegistration(); - assertThat(avoidSameAuthenticatorRegistration, is(false)); - - getPolicyPage().avoidSameAuthenticatorRegister(true); - assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); - getPolicyPage().clickSaveButton(); - - avoidSameAuthenticatorRegistration = getPolicyPage().avoidSameAuthenticatorRegistration(); - assertThat(avoidSameAuthenticatorRegistration, is(true)); - - getPolicyPage().avoidSameAuthenticatorRegister(false); - getPolicyPage().clickSaveButton(); - - avoidSameAuthenticatorRegistration = getPolicyPage().avoidSameAuthenticatorRegistration(); - assertThat(avoidSameAuthenticatorRegistration, is(false)); - } - - protected void checkAcceptableAaguid() { - WebAuthnPolicyPage.MultivaluedAcceptableAaguid acceptableAaguid = getPolicyPage().getAcceptableAaguid(); - assertThat(acceptableAaguid, notNullValue()); - - List items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); - assertThat(items, notNullValue()); - - acceptableAaguid.addItem(ALL_ONE_AAGUID); - getPolicyPage().clickSaveButton(); - - items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); - - assertThat(items, notNullValue()); - assertThat(items.isEmpty(), is(false)); - assertThat(items.contains(ALL_ONE_AAGUID), is(true)); - - final String YUBIKEY_5_AAGUID = "cb69481e-8ff7-4039-93ec-0a2729a154a8"; - final String YUBICO_AAGUID = "f8a011f3-8c0a-4d15-8006-17111f9edc7d"; - - acceptableAaguid.addItem(YUBIKEY_5_AAGUID); - acceptableAaguid.addItem(YUBICO_AAGUID); - items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); - - assertThat(items, notNullValue()); - assertThat(items, hasSize(3)); - - getPolicyPage().clickSaveButton(); - acceptableAaguid.removeItem(0); - items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); - - assertThat(items, notNullValue()); - assertThat(items, hasSize(2)); - assertThat(items.contains(YUBICO_AAGUID), is(true)); - assertThat(items.contains(YUBIKEY_5_AAGUID), is(true)); - assertThat(items.contains(ALL_ONE_AAGUID), is(false)); - - assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); - getPolicyPage().clickSaveButton(); - pause(100); - } - - protected List getAcceptableAaguid(WebAuthnPolicyPage.MultivaluedAcceptableAaguid acceptableAaguid) { - return acceptableAaguid.getItems() - .stream() - .map(UIUtils::getTextInputValue) - .collect(Collectors.toList()); - } -} diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/PwdLessPolicySettingsTest.java b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/PwdLessPolicySettingsTest.java new file mode 100644 index 0000000000..b3dc2a5340 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/PwdLessPolicySettingsTest.java @@ -0,0 +1,29 @@ +/* + * 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.webauthn.admin; + +/** + * @author Martin Bartos + */ +public class PwdLessPolicySettingsTest extends WebAuthnPolicySettingsTest { + + @Override + protected boolean isPasswordless() { + return true; + } +} diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicyPasswordlessSettingsTest.java b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicyPasswordlessSettingsTest.java deleted file mode 100644 index 594eb1bada..0000000000 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicyPasswordlessSettingsTest.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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.webauthn.admin; - -import com.webauthn4j.data.AttestationConveyancePreference; -import com.webauthn4j.data.AuthenticatorAttachment; -import com.webauthn4j.data.UserVerificationRequirement; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Test; -import org.keycloak.models.Constants; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.testsuite.updaters.RealmAttributeUpdater; -import org.keycloak.testsuite.webauthn.pages.WebAuthnPolicyPage; -import org.keycloak.testsuite.webauthn.pages.WebAuthnPolicyPasswordlessPage; -import org.keycloak.testsuite.webauthn.updaters.AbstractWebAuthnRealmUpdater; -import org.keycloak.testsuite.webauthn.updaters.PasswordLessRealmAttributeUpdater; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import static com.webauthn4j.data.AttestationConveyancePreference.DIRECT; -import static com.webauthn4j.data.AuthenticatorAttachment.PLATFORM; -import static com.webauthn4j.data.UserVerificationRequirement.PREFERRED; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasSize; -import static org.keycloak.models.Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - -/** - * @author Martin Bartos - */ -public class WebAuthnPolicyPasswordlessSettingsTest extends AbstractWebAuthnPolicySettingsTest { - - @Page - WebAuthnPolicyPasswordlessPage webAuthnPolicyPasswordlessPage; - - @Override - protected WebAuthnPolicyPage getPolicyPage() { - return webAuthnPolicyPasswordlessPage; - } - - @Override - protected AbstractWebAuthnRealmUpdater getWebAuthnRealmUpdater() { - return new PasswordLessRealmAttributeUpdater(testRealmResource()); - } - - @Test - public void policySettingsWithExternalProperties() throws IOException { - try (RealmAttributeUpdater rau = updateWebAuthnPolicy( - "rpNamePasswordless", - Collections.singletonList("RS256"), - DIRECT.getValue(), - PLATFORM.getValue(), - "Yes", - "1234", - PREFERRED.getValue(), - Collections.singletonList(ALL_ZERO_AAGUID)) - ) { - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessSignatureAlgorithms(), hasItems("RS256")); - assertThat(realm.getWebAuthnPolicyPasswordlessAttestationConveyancePreference(), is(DIRECT.getValue())); - assertThat(realm.getWebAuthnPolicyPasswordlessAuthenticatorAttachment(), is(PLATFORM.getValue())); - assertThat(realm.getWebAuthnPolicyPasswordlessRequireResidentKey(), is("Yes")); - assertThat(realm.getWebAuthnPolicyPasswordlessRpId(), is("1234")); - assertThat(realm.getWebAuthnPolicyPasswordlessUserVerificationRequirement(), is(PREFERRED.getValue())); - assertThat(realm.getWebAuthnPolicyPasswordlessAcceptableAaguids(), hasItems(ALL_ZERO_AAGUID)); - } - } - - @Test - public void wrongSignatureAlgorithm() throws IOException { - checkWrongSignatureAlgorithm(); - } - - @Test - public void algorithmsValuesSetUpInAdminConsole() { - checkSignatureAlgorithms(); - - final RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - final List realmSignatureAlgs = realm.getWebAuthnPolicyPasswordlessSignatureAlgorithms(); - assertThat(realmSignatureAlgs, notNullValue()); - assertThat(realmSignatureAlgs, hasSize(3)); - assertThat(realmSignatureAlgs, contains("ES256", "ES384", "RS1")); - } - - @Test - public void rpValuesSetUpInAdminConsole() { - checkRpEntityValues(); - - final RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessRpEntityName(), is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); - assertThat(realm.getWebAuthnPolicyPasswordlessRpId(), is("rpId123")); - } - - @Test - public void attestationConveyancePreferenceSettings() { - checkAttestationConveyancePreference(); - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessAttestationConveyancePreference(), is(AttestationConveyancePreference.NONE.getValue())); - - realm.setWebAuthnPolicyPasswordlessAttestationConveyancePreference(null); - testRealmResource().update(realm); - - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessAttestationConveyancePreference(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); - } - - @Test - public void authenticatorAttachmentSettings() { - checkAuthenticatorAttachment(); - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessAuthenticatorAttachment(), is(AuthenticatorAttachment.PLATFORM.getValue())); - - realm.setWebAuthnPolicyPasswordlessAuthenticatorAttachment(null); - testRealmResource().update(realm); - - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessAuthenticatorAttachment(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); - } - - @Test - public void requireResidentKeySettings() { - checkResidentKey(); - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessRequireResidentKey(), is("No")); - - realm.setWebAuthnPolicyPasswordlessRequireResidentKey(null); - testRealmResource().update(realm); - - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessRequireResidentKey(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); - } - - @Test - public void userVerificationRequirementSettings() { - checkUserVerification(); - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessUserVerificationRequirement(), is(UserVerificationRequirement.DISCOURAGED.getValue())); - - realm.setWebAuthnPolicyPasswordlessUserVerificationRequirement(null); - testRealmResource().update(realm); - - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessUserVerificationRequirement(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); - } - - @Test - public void timeoutSettings() { - checkTimeout(); - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyPasswordlessCreateTimeout(), is(500)); - } - - @Test - public void avoidSameAuthenticatorRegistrationSettings() { - checkAvoidSameAuthenticatorRegistration(); - - final RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - assertThat(realm.isWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister(), is(false)); - } - - @Test - public void acceptableAaguidSettings() { - checkAcceptableAaguid(); - - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - assertThat(realm.getWebAuthnPolicyPasswordlessAcceptableAaguids(), is(getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()))); - } -} diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicySettingsTest.java b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicySettingsTest.java index 6a1e66dc6d..70616f13cb 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicySettingsTest.java +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/admin/WebAuthnPolicySettingsTest.java @@ -21,45 +21,117 @@ import com.webauthn4j.data.AttestationConveyancePreference; import com.webauthn4j.data.AuthenticatorAttachment; import com.webauthn4j.data.UserVerificationRequirement; import org.jboss.arquillian.graphene.page.Page; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.keycloak.models.Constants; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.console.AbstractConsoleTest; +import org.keycloak.testsuite.page.AbstractPatternFlyAlert; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; +import org.keycloak.testsuite.util.UIUtils; import org.keycloak.testsuite.webauthn.pages.WebAuthnPolicyPage; +import org.keycloak.testsuite.webauthn.pages.WebAuthnPolicyPasswordlessPage; import org.keycloak.testsuite.webauthn.updaters.AbstractWebAuthnRealmUpdater; +import org.keycloak.testsuite.webauthn.updaters.PasswordLessRealmAttributeUpdater; import org.keycloak.testsuite.webauthn.updaters.WebAuthnRealmAttributeUpdater; +import org.keycloak.testsuite.webauthn.utils.PropertyRequirement; +import org.keycloak.testsuite.webauthn.utils.WebAuthnRealmData; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.Select; +import java.io.Closeable; import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; import static com.webauthn4j.data.AttestationConveyancePreference.INDIRECT; import static com.webauthn4j.data.AuthenticatorAttachment.CROSS_PLATFORM; import static com.webauthn4j.data.UserVerificationRequirement.PREFERRED; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; import static org.keycloak.models.Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; +import static org.keycloak.testsuite.util.WaitUtils.pause; +import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; +import static org.keycloak.testsuite.webauthn.utils.PropertyRequirement.NO; +import static org.keycloak.testsuite.webauthn.utils.PropertyRequirement.YES; /** * @author Martin Bartos */ -public class WebAuthnPolicySettingsTest extends AbstractWebAuthnPolicySettingsTest { +public class WebAuthnPolicySettingsTest extends AbstractConsoleTest { + + protected static final String ALL_ZERO_AAGUID = "00000000-0000-0000-0000-000000000000"; + protected static final String ALL_ONE_AAGUID = "11111111-1111-1111-1111-111111111111"; + + @Rule + public AssertEvents events = new AssertEvents(this); @Page WebAuthnPolicyPage webAuthnPolicyPage; - @Override - protected WebAuthnPolicyPage getPolicyPage() { - return webAuthnPolicyPage; + @Page + WebAuthnPolicyPasswordlessPage webAuthnPolicyPasswordlessPage; + + @Before + public void navigateToPolicy() { + driver.manage().window().maximize(); + getPolicyPage().navigateTo(); + waitForPageToLoad(); + getPolicyPage().assertCurrent(); } - @Override - protected AbstractWebAuthnRealmUpdater getWebAuthnRealmUpdater() { - return new WebAuthnRealmAttributeUpdater(testRealmResource()); + protected boolean isPasswordless() { + return false; + } + + protected WebAuthnPolicyPage getPolicyPage() { + return isPasswordless() ? webAuthnPolicyPasswordlessPage : webAuthnPolicyPage; + } + + protected AbstractWebAuthnRealmUpdater getWebAuthnRealmUpdater() { + return isPasswordless() ? new PasswordLessRealmAttributeUpdater(testRealmResource()) : new WebAuthnRealmAttributeUpdater(testRealmResource()); + } + + protected AbstractWebAuthnRealmUpdater updateWebAuthnPolicy( + String rpName, + List algorithms, + String attestationPreference, + String authenticatorAttachment, + String requireResidentKey, + String rpId, + String userVerification, + List acceptableAaguids) { + + AbstractWebAuthnRealmUpdater updater = getWebAuthnRealmUpdater().setWebAuthnPolicyRpEntityName(rpName); + + checkAndSet(algorithms, updater::setWebAuthnPolicySignatureAlgorithms); + checkAndSet(attestationPreference, updater::setWebAuthnPolicyAttestationConveyancePreference); + checkAndSet(authenticatorAttachment, updater::setWebAuthnPolicyAuthenticatorAttachment); + checkAndSet(requireResidentKey, updater::setWebAuthnPolicyRequireResidentKey); + checkAndSet(rpId, updater::setWebAuthnPolicyRpId); + checkAndSet(userVerification, updater::setWebAuthnPolicyUserVerificationRequirement); + checkAndSet(acceptableAaguids, updater::setWebAuthnPolicyAcceptableAaguids); + + return updater.update(); + } + + private void checkAndSet(T value, Consumer consumer) { + if (value != null) { + consumer.accept(value); + } } @Test @@ -74,150 +146,424 @@ public class WebAuthnPolicySettingsTest extends AbstractWebAuthnPolicySettingsTe PREFERRED.getValue(), Collections.singletonList(ALL_ZERO_AAGUID)) ) { - RealmRepresentation realm = testRealmResource().toRepresentation(); + + WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); assertThat(realm, notNullValue()); - assertThat(realm.getWebAuthnPolicySignatureAlgorithms(), hasItems("ES256")); - assertThat(realm.getWebAuthnPolicyAttestationConveyancePreference(), is(INDIRECT.getValue())); - assertThat(realm.getWebAuthnPolicyAuthenticatorAttachment(), is(CROSS_PLATFORM.getValue())); - assertThat(realm.getWebAuthnPolicyRequireResidentKey(), is("No")); - assertThat(realm.getWebAuthnPolicyRpId(), is("")); - assertThat(realm.getWebAuthnPolicyUserVerificationRequirement(), is(PREFERRED.getValue())); - assertThat(realm.getWebAuthnPolicyAcceptableAaguids(), hasItems(ALL_ZERO_AAGUID)); + assertThat(realm.getSignatureAlgorithms(), hasItems("ES256")); + assertThat(realm.getAttestationConveyancePreference(), is(INDIRECT.getValue())); + assertThat(realm.getAuthenticatorAttachment(), is(CROSS_PLATFORM.getValue())); + assertThat(realm.getRequireResidentKey(), is("No")); + assertThat(realm.getRpId(), is("")); + assertThat(realm.getUserVerificationRequirement(), is(PREFERRED.getValue())); + assertThat(realm.getAcceptableAaguids(), hasItems(ALL_ZERO_AAGUID)); } } @Test - public void wrongSignatureAlgorithm() throws IOException { - checkWrongSignatureAlgorithm(); + public void rpEntityValues() { + String rpEntityName = getPolicyPage().getRpEntityName(); + assertThat(rpEntityName, notNullValue()); + assertThat(rpEntityName, is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); + + getPolicyPage().setRpEntityName("newEntityName"); + getPolicyPage().clickSaveButton(); + AbstractPatternFlyAlert.waitUntilHidden(); + + rpEntityName = getPolicyPage().getRpEntityName(); + assertThat(rpEntityName, notNullValue()); + assertThat(rpEntityName, is("newEntityName")); + + getPolicyPage().setRpEntityName(""); + getPolicyPage().clickSaveButton(); + AbstractPatternFlyAlert.waitUntilHidden(); + + rpEntityName = getPolicyPage().getRpEntityName(); + assertThat(rpEntityName, notNullValue()); + assertThat(rpEntityName, is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); + + String rpEntityId = getPolicyPage().getRpEntityId(); + assertThat(rpEntityId, notNullValue()); + assertThat(rpEntityId, is("")); + + getPolicyPage().setRpEntityId("rpId123"); + getPolicyPage().clickSaveButton(); + AbstractPatternFlyAlert.waitUntilHidden(); + + rpEntityId = getPolicyPage().getRpEntityId(); + assertThat(rpEntityId, notNullValue()); + assertThat(rpEntityId, is("rpId123")); + + final WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); + assertThat(realm, notNullValue()); + + assertThat(realm.getRpEntityName(), is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); + assertThat(realm.getRpId(), is("rpId123")); } @Test - public void algorithmsValuesSetUpInAdminConsole() { - checkSignatureAlgorithms(); + public void wrongSignatureAlgorithm() throws IOException { + try (Closeable c = getWebAuthnRealmUpdater() + .setWebAuthnPolicySignatureAlgorithms(Collections.singletonList("something-bad")) + .update()) { - final RealmRepresentation realm = testRealmResource().toRepresentation(); + RealmRepresentation realm = testRealmResource().toRepresentation(); + assertThat(realm, notNullValue()); + + final List signatureAlgorithms = realm.getWebAuthnPolicySignatureAlgorithms(); + assertThat(signatureAlgorithms, notNullValue()); + assertThat(signatureAlgorithms.size(), is(1)); + + getPolicyPage().navigateTo(); + waitForPageToLoad(); + + Select selectedAlg = getPolicyPage().getSignatureAlgorithms(); + assertThat(selectedAlg, notNullValue()); + + try { + // should throw an exception + selectedAlg.getFirstSelectedOption(); + } catch (NoSuchElementException e) { + assertThat(e.getMessage(), containsString("No options are selected")); + } + } + } + + @Test + public void signatureAlgorithms() { + getPolicyPage().assertCurrent(); + + final Select algorithms = getPolicyPage().getSignatureAlgorithms(); + assertThat(algorithms, notNullValue()); + + algorithms.selectByValue("ES256"); + algorithms.selectByValue("ES384"); + algorithms.selectByValue("RS1"); + + final List selectedAlgs = algorithms.getAllSelectedOptions() + .stream() + .map(WebElement::getText) + .collect(Collectors.toList()); + + assertThat(selectedAlgs, notNullValue()); + assertThat(selectedAlgs, hasSize(3)); + + try { + algorithms.selectByValue("something-bad"); + } catch (NoSuchElementException e) { + assertThat(e.getMessage(), containsString("Cannot locate option with value: something-bad")); + } + + assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); + assertThat(getPolicyPage().isCancelButtonEnabled(), is(true)); + + getPolicyPage().clickSaveButton(); + + assertThat(getPolicyPage().isSaveButtonEnabled(), is(false)); + assertThat(getPolicyPage().isCancelButtonEnabled(), is(false)); + + final WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); assertThat(realm, notNullValue()); - final List realmSignatureAlgs = realm.getWebAuthnPolicySignatureAlgorithms(); + final List realmSignatureAlgs = realm.getSignatureAlgorithms(); assertThat(realmSignatureAlgs, notNullValue()); assertThat(realmSignatureAlgs, hasSize(3)); assertThat(realmSignatureAlgs, contains("ES256", "ES384", "RS1")); } @Test - public void rpValuesSetUpInAdminConsole() { - checkRpEntityValues(); + public void attestationConveyancePreference() { + // default not specified + AttestationConveyancePreference attestation = getPolicyPage().getAttestationConveyancePreference(); + assertThat(attestation, nullValue()); - final RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + // Direct + getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.DIRECT); + getPolicyPage().clickSaveButton(); - assertThat(realm.getWebAuthnPolicyRpEntityName(), is(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME)); - assertThat(realm.getWebAuthnPolicyRpId(), is("rpId123")); + attestation = getPolicyPage().getAttestationConveyancePreference(); + assertThat(attestation, notNullValue()); + assertThat(attestation, is(AttestationConveyancePreference.DIRECT)); + + // Indirect + getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.INDIRECT); + getPolicyPage().clickSaveButton(); + + attestation = getPolicyPage().getAttestationConveyancePreference(); + assertThat(attestation, notNullValue()); + assertThat(attestation, is(AttestationConveyancePreference.INDIRECT)); + + // None + getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.NONE); + getPolicyPage().clickSaveButton(); + + attestation = getPolicyPage().getAttestationConveyancePreference(); + assertThat(attestation, notNullValue()); + assertThat(attestation, is(AttestationConveyancePreference.NONE)); + + try { + getPolicyPage().setAttestationConveyancePreference(AttestationConveyancePreference.ENTERPRISE); + Assert.fail("We don't support 'Enterprise' mode at this moment"); + } catch (NoSuchElementException e) { + // Expected - NOP + } + + assertDataAfterModification(AttestationConveyancePreference.NONE.getValue(), + DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED, + WebAuthnRealmData::getAttestationConveyancePreference, + (builder) -> builder.attestationConveyancePreference(null) + ); } @Test - public void attestationConveyancePreferenceSettings() { - checkAttestationConveyancePreference(); + public void authenticatorAttachment() { + AuthenticatorAttachment attachment = getPolicyPage().getAuthenticatorAttachment(); + assertThat(attachment, nullValue()); - // Realm - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + // Cross-platform + getPolicyPage().setAuthenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM); + getPolicyPage().clickSaveButton(); - assertThat(realm.getWebAuthnPolicyAttestationConveyancePreference(), is(AttestationConveyancePreference.NONE.getValue())); + attachment = getPolicyPage().getAuthenticatorAttachment(); + assertThat(attachment, notNullValue()); + assertThat(attachment, is(AuthenticatorAttachment.CROSS_PLATFORM)); - realm.setWebAuthnPolicyAttestationConveyancePreference(null); - testRealmResource().update(realm); + // Platform + getPolicyPage().setAuthenticatorAttachment(AuthenticatorAttachment.PLATFORM); + getPolicyPage().clickSaveButton(); - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + attachment = getPolicyPage().getAuthenticatorAttachment(); + assertThat(attachment, notNullValue()); + assertThat(attachment, is(AuthenticatorAttachment.PLATFORM)); - assertThat(realm.getWebAuthnPolicyAttestationConveyancePreference(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); + assertDataAfterModification(AuthenticatorAttachment.PLATFORM.getValue(), + DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED, + WebAuthnRealmData::getAuthenticatorAttachment, + (builder) -> builder.authenticatorAttachment(null) + ); } @Test - public void authenticatorAttachmentSettings() { - checkAuthenticatorAttachment(); + public void residentKey() { + PropertyRequirement requireResidentKey = getPolicyPage().requireResidentKey(); + assertThat(requireResidentKey, notNullValue()); + assertThat(requireResidentKey, is(PropertyRequirement.NOT_SPECIFIED)); - // Realm - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + getPolicyPage().requireResidentKey(YES); + getPolicyPage().clickSaveButton(); - assertThat(realm.getWebAuthnPolicyAuthenticatorAttachment(), is(AuthenticatorAttachment.PLATFORM.getValue())); + // Yes + requireResidentKey = getPolicyPage().requireResidentKey(); + assertThat(requireResidentKey, notNullValue()); + assertThat(requireResidentKey, is(YES)); - realm.setWebAuthnPolicyAuthenticatorAttachment(null); - testRealmResource().update(realm); + getPolicyPage().requireResidentKey(NO); + getPolicyPage().clickSaveButton(); - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + // Null + getPolicyPage().requireResidentKey(null); + assertThat(getPolicyPage().isSaveButtonEnabled(), is(false)); - assertThat(realm.getWebAuthnPolicyAuthenticatorAttachment(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); + // Not specified + getPolicyPage().requireResidentKey(PropertyRequirement.NOT_SPECIFIED); + assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); + getPolicyPage().clickSaveButton(); + + // No + getPolicyPage().requireResidentKey(NO); + getPolicyPage().clickSaveButton(); + + requireResidentKey = getPolicyPage().requireResidentKey(); + assertThat(requireResidentKey, notNullValue()); + assertThat(requireResidentKey, is(NO)); + + assertDataAfterModification(NO.getValue(), + DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED, + WebAuthnRealmData::getRequireResidentKey, + (builder) -> builder.requireResidentKey(null) + ); } @Test - public void requireResidentKeySettings() { - checkResidentKey(); + public void userVerification() { + UserVerificationRequirement userVerification = getPolicyPage().getUserVerification(); + assertThat(userVerification, nullValue()); - // Realm - RealmRepresentation realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + // Preferred + getPolicyPage().setUserVerification(UserVerificationRequirement.PREFERRED); + getPolicyPage().clickSaveButton(); - assertThat(realm.getWebAuthnPolicyRequireResidentKey(), is("No")); + userVerification = getPolicyPage().getUserVerification(); + assertThat(userVerification, notNullValue()); + assertThat(userVerification, is(UserVerificationRequirement.PREFERRED)); - realm.setWebAuthnPolicyRequireResidentKey(null); - testRealmResource().update(realm); + // Required + getPolicyPage().setUserVerification(UserVerificationRequirement.REQUIRED); + getPolicyPage().clickSaveButton(); - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); + userVerification = getPolicyPage().getUserVerification(); + assertThat(userVerification, notNullValue()); + assertThat(userVerification, is(UserVerificationRequirement.REQUIRED)); - assertThat(realm.getWebAuthnPolicyRequireResidentKey(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); + // Discouraged + getPolicyPage().setUserVerification(UserVerificationRequirement.DISCOURAGED); + getPolicyPage().clickSaveButton(); + + userVerification = getPolicyPage().getUserVerification(); + assertThat(userVerification, notNullValue()); + assertThat(userVerification, is(UserVerificationRequirement.DISCOURAGED)); + + assertDataAfterModification(UserVerificationRequirement.DISCOURAGED.getValue(), + DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED, + WebAuthnRealmData::getUserVerificationRequirement, + (builder) -> builder.userVerificationRequirement(null) + ); } @Test - public void userVerificationRequirementSettings() { - checkUserVerification(); + public void timeout() { + int timeout = getPolicyPage().getTimeout(); + assertThat(timeout, is(0)); - // Realm - RealmRepresentation realm = testRealmResource().toRepresentation(); + getPolicyPage().setTimeout(10); + getPolicyPage().clickSaveButton(); + + timeout = getPolicyPage().getTimeout(); + assertThat(timeout, is(10)); + + getPolicyPage().setTimeout(-10); + getPolicyPage().clickSaveButton(); + assertAlertDanger(); + + timeout = getPolicyPage().getTimeout(); + assertThat(timeout, is(-10)); + + getPolicyPage().navigateTo(); + waitForPageToLoad(); + + timeout = getPolicyPage().getTimeout(); + assertThat(timeout, is(10)); + + getPolicyPage().setTimeout(1000000); + getPolicyPage().clickSaveButton(); + assertAlertDanger(); + + getPolicyPage().setTimeout(500); + getPolicyPage().clickSaveButton(); + + timeout = getPolicyPage().getTimeout(); + assertThat(timeout, is(500)); + + final WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyUserVerificationRequirement(), is(UserVerificationRequirement.DISCOURAGED.getValue())); - - realm.setWebAuthnPolicyUserVerificationRequirement(null); - testRealmResource().update(realm); - - realm = testRealmResource().toRepresentation(); - assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyUserVerificationRequirement(), is(DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)); + assertThat(realm.getCreateTimeout(), is(500)); } @Test - public void timeoutSettings() { - checkTimeout(); + public void avoidSameAuthenticatorRegistration() { + boolean avoidSameAuthenticatorRegistration = getPolicyPage().avoidSameAuthenticatorRegistration(); + assertThat(avoidSameAuthenticatorRegistration, is(false)); - // Realm - RealmRepresentation realm = testRealmResource().toRepresentation(); + getPolicyPage().avoidSameAuthenticatorRegister(true); + assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); + getPolicyPage().clickSaveButton(); + + avoidSameAuthenticatorRegistration = getPolicyPage().avoidSameAuthenticatorRegistration(); + assertThat(avoidSameAuthenticatorRegistration, is(true)); + + getPolicyPage().avoidSameAuthenticatorRegister(false); + getPolicyPage().clickSaveButton(); + + avoidSameAuthenticatorRegistration = getPolicyPage().avoidSameAuthenticatorRegistration(); + assertThat(avoidSameAuthenticatorRegistration, is(false)); + + final WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); assertThat(realm, notNullValue()); - - assertThat(realm.getWebAuthnPolicyCreateTimeout(), is(500)); + assertThat(realm.isAvoidSameAuthenticatorRegister(), is(false)); } @Test - public void avoidSameAuthenticatorRegistrationSettings() { - checkAvoidSameAuthenticatorRegistration(); + public void acceptableAaguid() { + WebAuthnPolicyPage.MultivaluedAcceptableAaguid acceptableAaguid = getPolicyPage().getAcceptableAaguid(); + assertThat(acceptableAaguid, notNullValue()); - final RealmRepresentation realm = testRealmResource().toRepresentation(); + List items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); + assertThat(items, notNullValue()); + + acceptableAaguid.addItem(ALL_ONE_AAGUID); + getPolicyPage().clickSaveButton(); + + items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); + + assertThat(items, notNullValue()); + assertThat(items.isEmpty(), is(false)); + assertThat(items.contains(ALL_ONE_AAGUID), is(true)); + + final String YUBIKEY_5_AAGUID = "cb69481e-8ff7-4039-93ec-0a2729a154a8"; + final String YUBICO_AAGUID = "f8a011f3-8c0a-4d15-8006-17111f9edc7d"; + + acceptableAaguid.addItem(YUBIKEY_5_AAGUID); + acceptableAaguid.addItem(YUBICO_AAGUID); + items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); + + assertThat(items, notNullValue()); + assertThat(items, hasSize(3)); + + getPolicyPage().clickSaveButton(); + acceptableAaguid.removeItem(0); + items = getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()); + + assertThat(items, notNullValue()); + assertThat(items, hasSize(2)); + assertThat(items.contains(YUBICO_AAGUID), is(true)); + assertThat(items.contains(YUBIKEY_5_AAGUID), is(true)); + assertThat(items.contains(ALL_ONE_AAGUID), is(false)); + + assertThat(getPolicyPage().isSaveButtonEnabled(), is(true)); + getPolicyPage().clickSaveButton(); + pause(100); + + WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); assertThat(realm, notNullValue()); - assertThat(realm.isWebAuthnPolicyAvoidSameAuthenticatorRegister(), is(false)); + assertThat(realm.getAcceptableAaguids(), is(getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()))); } - @Test - public void acceptableAaguidSettings() { - checkAcceptableAaguid(); + protected List getAcceptableAaguid(WebAuthnPolicyPage.MultivaluedAcceptableAaguid acceptableAaguid) { + return acceptableAaguid.getItems() + .stream() + .map(UIUtils::getTextInputValue) + .collect(Collectors.toList()); + } - RealmRepresentation realm = testRealmResource().toRepresentation(); + /** + * Assert WebAuthn Realm data before and after modification + * + * @param actualValue actual value before modification + * @param expectedValue expected value after modification + * @param getCurrentValue get updated value + * @param setData exact approach, how to change the realm data + */ + private void assertDataAfterModification(T actualValue, T expectedValue, + Function getCurrentValue, + Consumer setData) { + + WebAuthnRealmData realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); assertThat(realm, notNullValue()); - assertThat(realm.getWebAuthnPolicyAcceptableAaguids(), is(getAcceptableAaguid(getPolicyPage().getAcceptableAaguid()))); + + assertThat(getCurrentValue.apply(realm), is(actualValue)); + + WebAuthnRealmData.Builder builder = realm.builder(); + assertThat(builder, notNullValue()); + setData.accept(builder); + + final RealmRepresentation newRealm = builder.build(); + assertThat(newRealm, notNullValue()); + + testRealmResource().update(newRealm); + + realm = new WebAuthnRealmData(testRealmResource().toRepresentation(), isPasswordless()); + assertThat(realm, notNullValue()); + + assertThat(getCurrentValue.apply(realm), is(expectedValue)); } } diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/AppInitiatedActionPwdLessTest.java b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/AppInitiatedActionPwdLessTest.java similarity index 86% rename from testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/AppInitiatedActionPwdLessTest.java rename to testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/AppInitiatedActionPwdLessTest.java index 11825a4e44..63212ae3bb 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/AppInitiatedActionPwdLessTest.java +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/AppInitiatedActionPwdLessTest.java @@ -15,13 +15,16 @@ * limitations under the License. */ -package org.keycloak.testsuite.webauthn; +package org.keycloak.testsuite.webauthn.passwordless; + +import org.keycloak.testsuite.webauthn.AppInitiatedActionWebAuthnTest; /** * @author Martin Bartos */ public class AppInitiatedActionPwdLessTest extends AppInitiatedActionWebAuthnTest { + @Override protected boolean isPasswordless() { return true; } diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/WebAuthnPwdLessPropertyTest.java b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/WebAuthnPwdLessPropertyTest.java new file mode 100644 index 0000000000..c53a2bebcc --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/test/java/org/keycloak/testsuite/webauthn/passwordless/WebAuthnPwdLessPropertyTest.java @@ -0,0 +1,39 @@ +/* + * 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.webauthn.passwordless; + +import org.junit.Ignore; +import org.junit.Test; +import org.keycloak.testsuite.webauthn.WebAuthnPropertyTest; + +/** + * @author Martin Bartos + */ +public class WebAuthnPwdLessPropertyTest extends WebAuthnPropertyTest { + + @Override + public boolean isPasswordless() { + return true; + } + + @Override + @Ignore("Not usable for Passwordless") + @Test + public void requiredActionRegistration() { + } +}