diff --git a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx index b255ed73..8f520d58 100644 --- a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx +++ b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -12,7 +12,6 @@ export interface StateProps { } export interface DispatchProps { - onInit: () => void; onOneTimePasswordMethodClicked: () => void; onSecurityKeyMethodClicked: () => void; onDuoPushMethodClicked: () => void; @@ -27,10 +26,6 @@ interface MethodDescription { } class UseAnotherMethod extends Component { - componentDidMount() { - this.props.onInit(); - } - render() { const methods: MethodDescription[] = [ { diff --git a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts index 018b97fe..f2cb5038 100644 --- a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts +++ b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts @@ -5,7 +5,10 @@ import LogoutBehavior from '../../../behaviors/LogoutBehavior'; import { RootState } from '../../../reducers'; import { StateProps, DispatchProps } from '../../../components/SecondFactorForm/SecondFactorForm'; import FetchPrefered2faMethod from '../../../behaviors/FetchPrefered2faMethod'; -import { setUseAnotherMethod } from '../../../reducers/Portal/SecondFactor/actions'; +import { setUseAnotherMethod, setSecurityKeySupported } from '../../../reducers/Portal/SecondFactor/actions'; +import GetAvailable2faMethods from '../../../behaviors/GetAvailable2faMethods'; +import u2fApi from 'u2f-api'; + const mapStateToProps = (state: RootState): StateProps => { return { @@ -16,7 +19,11 @@ const mapStateToProps = (state: RootState): StateProps => { const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { - onInit: () => FetchPrefered2faMethod(dispatch), + onInit: async () => { + dispatch(setSecurityKeySupported(await u2fApi.isSupported())); + FetchPrefered2faMethod(dispatch); + GetAvailable2faMethods(dispatch); + }, onLogoutClicked: () => LogoutBehavior(dispatch), onUseAnotherMethodClicked: () => dispatch(setUseAnotherMethod(true)), } diff --git a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx index 5e1e5db6..adcd88ee 100644 --- a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx +++ b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -5,9 +5,6 @@ import SetPrefered2faMethod from '../../../behaviors/SetPrefered2faMethod'; import { getPreferedMethodSuccess, setUseAnotherMethod, setSecurityKeySupported } from '../../../reducers/Portal/SecondFactor/actions'; import Method2FA from '../../../types/Method2FA'; import UseAnotherMethod, {StateProps, DispatchProps} from '../../../components/UseAnotherMethod/UseAnotherMethod'; -import GetAvailable2faMethods from '../../../behaviors/GetAvailable2faMethods'; -import u2fApi from 'u2f-api'; - const mapStateToProps = (state: RootState): StateProps => ({ availableMethods: state.secondFactor.getAvailableMethodResponse, @@ -25,10 +22,6 @@ async function storeMethod(dispatch: Dispatch, method: Method2FA) { const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { - onInit: async () => { - dispatch(setSecurityKeySupported(await u2fApi.isSupported())); - await GetAvailable2faMethods(dispatch); - }, onOneTimePasswordMethodClicked: () => storeMethod(dispatch, 'totp'), onSecurityKeyMethodClicked: () => storeMethod(dispatch, 'u2f'), onDuoPushMethodClicked: () => storeMethod(dispatch, "duo_push"), diff --git a/test/helpers/assertions/VerifyButtonDoesNotExist.ts b/test/helpers/assertions/VerifyButtonDoesNotExist.ts index e0a63d71..d22c9eb0 100644 --- a/test/helpers/assertions/VerifyButtonDoesNotExist.ts +++ b/test/helpers/assertions/VerifyButtonDoesNotExist.ts @@ -8,5 +8,9 @@ import VerifyElementDoesNotExist from "./VerifyElementDoesNotExist"; * @param content The content of the button to select. */ export default async function(driver: WebDriver, content: string) { - await VerifyElementDoesNotExist(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")) + try { + await VerifyElementDoesNotExist(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); + } catch (err) { + throw new Error(`Button with content "${content}" should not exist.`); + } } \ No newline at end of file diff --git a/test/helpers/assertions/VerifyButtonExists.ts b/test/helpers/assertions/VerifyButtonHasAppeared.ts similarity index 60% rename from test/helpers/assertions/VerifyButtonExists.ts rename to test/helpers/assertions/VerifyButtonHasAppeared.ts index a9f45085..84ce2ad5 100644 --- a/test/helpers/assertions/VerifyButtonExists.ts +++ b/test/helpers/assertions/VerifyButtonHasAppeared.ts @@ -1,5 +1,5 @@ import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; -import VerifyElementExists from "./VerifyElementExists"; +import VerifyHasAppeared from "./VerifyHasAppeared"; /** * Verify if a button with given content exists in the DOM. @@ -7,5 +7,9 @@ import VerifyElementExists from "./VerifyElementExists"; * @param content The content of the button to find in the DOM. */ export default async function(driver: WebDriver, content: string) { - await VerifyElementExists(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); + try { + await VerifyHasAppeared(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); + } catch (err) { + throw new Error(`Button with content "${content}" should have appeared.`); + } } \ No newline at end of file diff --git a/test/helpers/assertions/VerifyIsDuoPushNotificationView.ts b/test/helpers/assertions/VerifyIsDuoPushNotificationView.ts new file mode 100644 index 00000000..9c327706 --- /dev/null +++ b/test/helpers/assertions/VerifyIsDuoPushNotificationView.ts @@ -0,0 +1,6 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; + +export default async function(driver: WebDriver, timeout: number = 5000) { + await driver.wait(SeleniumWebDriver.until.elementLocated( + SeleniumWebDriver.By.className('duo-push-view')), timeout); +} \ No newline at end of file diff --git a/test/suites/basic/scenarii/NoDuoPushOption.ts b/test/suites/basic/scenarii/NoDuoPushOption.ts index d5929d5a..4ab0a7ea 100644 --- a/test/suites/basic/scenarii/NoDuoPushOption.ts +++ b/test/suites/basic/scenarii/NoDuoPushOption.ts @@ -3,10 +3,8 @@ import LoginAs from "../../../helpers/LoginAs"; import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; import ClickOnLink from "../../../helpers/ClickOnLink"; import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUseAnotherMethodView"; -import VerifyElementDoesNotExist from "../../../helpers/assertions/VerifyElementDoesNotExist"; -import SeleniumWebDriver from "selenium-webdriver"; import VerifyButtonDoesNotExist from "../../../helpers/assertions/VerifyButtonDoesNotExist"; -import VerifyButtonExists from "../../../helpers/assertions/VerifyButtonExists"; +import VerifyButtonHasAppeared from "../../../helpers/assertions/VerifyButtonHasAppeared"; @@ -26,8 +24,7 @@ export default function() { await ClickOnLink(this.driver, 'Use another method'); await VerifyIsUseAnotherMethodView(this.driver); - await VerifyButtonExists(this.driver, "Security Key (U2F)"); - await VerifyButtonExists(this.driver, "One-Time Password"); + await VerifyButtonHasAppeared(this.driver, "One-Time Password"); await VerifyButtonDoesNotExist(this.driver, "Duo Push Notification"); }); } \ No newline at end of file diff --git a/test/suites/basic/test.ts b/test/suites/basic/test.ts index 12ecce4b..02e03593 100644 --- a/test/suites/basic/test.ts +++ b/test/suites/basic/test.ts @@ -10,7 +10,6 @@ import LogoutRedirectToAlreadyLoggedIn from './scenarii/LogoutRedirectToAlreadyL import { exec } from '../../helpers/utils/exec'; import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; import BypassPolicy from "./scenarii/BypassPolicy"; -import Prefered2faMethod from "./scenarii/Prefered2faMethod"; import NoDuoPushOption from "./scenarii/NoDuoPushOption"; AutheliaSuite(__dirname, function() { @@ -30,6 +29,5 @@ AutheliaSuite(__dirname, function() { describe('TOTP Validation', TOTPValidation); describe('Required two factor', RequiredTwoFactor); describe('Logout endpoint redirect to already logged in page', LogoutRedirectToAlreadyLoggedIn); - describe('Prefered 2FA method', Prefered2faMethod); describe('No Duo Push method available', NoDuoPushOption); }); \ No newline at end of file diff --git a/test/suites/basic/scenarii/Prefered2faMethod.ts b/test/suites/duo-push/scenarii/Prefered2faMethod.ts similarity index 88% rename from test/suites/basic/scenarii/Prefered2faMethod.ts rename to test/suites/duo-push/scenarii/Prefered2faMethod.ts index 65ccff2c..20895dd7 100644 --- a/test/suites/basic/scenarii/Prefered2faMethod.ts +++ b/test/suites/duo-push/scenarii/Prefered2faMethod.ts @@ -6,6 +6,7 @@ import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUs import ClickOnButton from "../../../helpers/behaviors/ClickOnButton"; import VerifyIsSecurityKeyView from "../../../helpers/assertions/VerifyIsSecurityKeyView"; import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; +import VerifyIsDuoPushNotificationView from "../../../helpers/assertions/VerifyIsDuoPushNotificationView"; // This fixture tests that the latest used method is still used when the user gets back. @@ -26,10 +27,10 @@ export default function() { await ClickOnLink(this.driver, 'Use another method'); await VerifyIsUseAnotherMethodView(this.driver); - await ClickOnButton(this.driver, 'Security Key (U2F)'); + await ClickOnButton(this.driver, 'Duo Push Notification'); // Verify that the user is redirected to the new method - await VerifyIsSecurityKeyView(this.driver); + await VerifyIsDuoPushNotificationView(this.driver); await ClickOnLink(this.driver, "Logout"); // Login with another user to check that he gets TOTP view. @@ -39,7 +40,7 @@ export default function() { // Log john again to check that the prefered method has been persisted await LoginAs(this.driver, "john", "password", "https://secure.example.com:8080/"); - await VerifyIsSecurityKeyView(this.driver); + await VerifyIsDuoPushNotificationView(this.driver); // Restore the prefered method to one-time password. await ClickOnLink(this.driver, 'Use another method'); diff --git a/test/suites/duo-push/test.ts b/test/suites/duo-push/test.ts index 1dab3d4d..d8863bc2 100644 --- a/test/suites/duo-push/test.ts +++ b/test/suites/duo-push/test.ts @@ -1,6 +1,7 @@ import AutheliaSuite from "../../helpers/context/AutheliaSuite"; import { exec } from '../../helpers/utils/exec'; import DuoPushNotification from "./scenarii/DuoPushNotification"; +import Prefered2faMethod from "./scenarii/Prefered2faMethod"; // required to query duo-api over https process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; @@ -13,4 +14,5 @@ AutheliaSuite(__dirname, function() { }); describe("Duo Push Notication", DuoPushNotification); + describe("Prefered 2FA methods", Prefered2faMethod); }); \ No newline at end of file