diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java index 2df2cbfb7a..eebefe79ce 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java @@ -146,4 +146,8 @@ public interface UserResource { @Path("consents/{client}") public void revokeConsent(@PathParam("client") String clientId); + @POST + @Path("impersonation") + @Produces(MediaType.APPLICATION_JSON) + Map impersonate(); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ConsentPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ConsentPage.java new file mode 100644 index 0000000000..b8b244d545 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ConsentPage.java @@ -0,0 +1,44 @@ +/* + * 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.pages; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Marko Strukelj + */ +public class ConsentPage extends AbstractPage { + + @FindBy(id = "kc-login") + private WebElement submitButton; + + public void confirm() { + submitButton.click(); + } + + @Override + public boolean isCurrent() { + return driver.getTitle().equalsIgnoreCase("grant access"); + } + + @Override + public void open() throws Exception { + + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java index fc7a3412be..68a8f8db47 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java @@ -294,6 +294,9 @@ public class ClientTest extends AbstractAdminTest { Map offlineSessionCount = realm.clients().get(id).getOfflineSessionCount(); assertEquals(new Long(0), offlineSessionCount.get("count")); + List userSessions = realm.users().get(userId).getOfflineSessions(id); + assertEquals("There should be no offline sessions", 0, userSessions.size()); + oauth.realm(REALM_NAME); oauth.redirectUri(client.getRedirectUris().get(0)); oauth.scope(OAuth2Constants.OFFLINE_ACCESS); @@ -307,6 +310,17 @@ public class ClientTest extends AbstractAdminTest { List offlineUserSessions = realm.clients().get(id).getOfflineUserSessions(0, 100); assertEquals(1, offlineUserSessions.size()); assertEquals("testuser", offlineUserSessions.get(0).getUsername()); + + userSessions = realm.users().get(userId).getOfflineSessions(id); + assertEquals("There should be one offline session", 1, userSessions.size()); + assertOfflineSession(offlineUserSessions.get(0), userSessions.get(0)); + } + + private void assertOfflineSession(UserSessionRepresentation expected, UserSessionRepresentation actual) { + assertEquals("id", expected.getId(), actual.getId()); + assertEquals("userId", expected.getUserId(), actual.getUserId()); + assertEquals("userName", expected.getUsername(), actual.getUsername()); + assertEquals("clients", expected.getClients(), actual.getClients()); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ConsentsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ConsentsTest.java new file mode 100644 index 0000000000..fb0a8635db --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ConsentsTest.java @@ -0,0 +1,300 @@ +/* + * 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 org.jboss.arquillian.graphene.page.Page; +import org.junit.Before; +import org.junit.Test; +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.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.UserSessionRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.pages.ConsentPage; +import org.keycloak.testsuite.pages.LoginPage; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient; +import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword; + +/** + * @author Marko Strukelj + */ +public class ConsentsTest extends AbstractKeycloakTest { + + final static String REALM_PROV_NAME = "provider"; + final static String REALM_CONS_NAME = "consumer"; + + final static String IDP_OIDC_ALIAS = "kc-oidc-idp"; + final static String IDP_OIDC_PROVIDER_ID = "keycloak-oidc"; + + final static String CLIENT_ID = "brokerapp"; + final static String CLIENT_SECRET = "secret"; + + final static String USER_LOGIN = "testuser"; + final static String USER_EMAIL = "user@localhost.com"; + final static String USER_PASSWORD = "password"; + final static String USER_FIRSTNAME = "User"; + final static String USER_LASTNAME = "Tester"; + + protected RealmRepresentation createProviderRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_PROV_NAME); + realm.setEnabled(true); + + return realm; + } + + protected RealmRepresentation createConsumerRealm() { + RealmRepresentation realm = new RealmRepresentation(); + realm.setRealm(REALM_CONS_NAME); + realm.setEnabled(true); + + return realm; + } + + protected List createProviderClients() { + ClientRepresentation client = new ClientRepresentation(); + client.setId(CLIENT_ID); + client.setName(CLIENT_ID); + client.setSecret(CLIENT_SECRET); + client.setEnabled(true); + client.setConsentRequired(true); + + client.setRedirectUris(Collections.singletonList(getAuthRoot() + + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint/*")); + + client.setAdminUrl(getAuthRoot() + + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint"); + + return Collections.singletonList(client); + } + + protected IdentityProviderRepresentation setUpIdentityProvider() { + IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID); + + Map config = idp.getConfig(); + + config.put("clientId", CLIENT_ID); + config.put("clientSecret", CLIENT_SECRET); + config.put("prompt", "login"); + config.put("authorizationUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/auth"); + config.put("tokenUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/token"); + config.put("logoutUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/logout"); + config.put("userInfoUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/userinfo"); + config.put("defaultScope", "email profile"); + config.put("backchannelSupported", "true"); + + return idp; + } + + protected String getUserLogin() { + return USER_LOGIN; + } + + protected String getUserPassword() { + return USER_PASSWORD; + } + + protected String getUserEmail() { + return USER_EMAIL; + } + + protected String getUserFirstName() { + return USER_FIRSTNAME; + } + + protected String getUserLastName() { + return USER_LASTNAME; + } + protected String providerRealmName() { + return REALM_PROV_NAME; + } + + protected String consumerRealmName() { + return REALM_CONS_NAME; + } + + protected String getIDPAlias() { + return IDP_OIDC_ALIAS; + } + + + @Page + protected LoginPage accountLoginPage; + + @Page + protected ConsentPage consentPage; + + @Override + public void addTestRealms(List testRealms) { + RealmRepresentation providerRealm = createProviderRealm(); + RealmRepresentation consumerRealm = createConsumerRealm(); + + testRealms.add(providerRealm); + testRealms.add(consumerRealm); + } + + @Before + public void createUser() { + log.debug("creating user for realm " + providerRealmName()); + + UserRepresentation user = new UserRepresentation(); + user.setUsername(getUserLogin()); + user.setEmail(getUserEmail()); + user.setFirstName(getUserFirstName()); + user.setLastName(getUserLastName()); + user.setEmailVerified(true); + user.setEnabled(true); + + RealmResource realmResource = adminClient.realm(providerRealmName()); + String userId = createUserWithAdminClient(realmResource, user); + + resetUserPassword(realmResource.users().get(userId), getUserPassword(), false); + } + + @Before + public void addIdentityProviderToProviderRealm() { + log.debug("adding identity provider to realm " + consumerRealmName()); + + RealmResource realm = adminClient.realm(consumerRealmName()); + realm.identityProviders().create(setUpIdentityProvider()); + } + + @Before + public void addClients() { + List clients = createProviderClients(); + if (clients != null) { + RealmResource providerRealm = adminClient.realm(providerRealmName()); + for (ClientRepresentation client : clients) { + log.debug("adding client " + client.getName() + " to realm " + providerRealmName()); + + providerRealm.clients().create(client); + } + } + } + + protected String getAuthRoot() { + return suiteContext.getAuthServerInfo().getContextRoot().toString(); + } + + protected IdentityProviderRepresentation createIdentityProvider(String alias, String providerId) { + IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation(); + + identityProviderRepresentation.setAlias(alias); + identityProviderRepresentation.setProviderId(providerId); + identityProviderRepresentation.setEnabled(true); + + return identityProviderRepresentation; + } + + private void waitForPage(String title) { + long startAt = System.currentTimeMillis(); + + while (!driver.getTitle().toLowerCase().contains(title) + && System.currentTimeMillis() - startAt < 200) { + try { + Thread.sleep(5); + } catch (InterruptedException ignore) {} + } + } + + @Test + public void testConsents() { + driver.navigate().to(getAccountUrl(consumerRealmName())); + + log.debug("Clicking social " + getIDPAlias()); + accountLoginPage.clickSocial(getIDPAlias()); + + if (!driver.getCurrentUrl().contains("/auth/realms/" + providerRealmName() + "/")) { + log.debug("Not on provider realm page, url: " + driver.getCurrentUrl()); + } + + Assert.assertTrue("Driver should be on the provider realm page right now", + driver.getCurrentUrl().contains("/auth/realms/" + providerRealmName() + "/")); + + log.debug("Logging in"); + accountLoginPage.login(getUserLogin(), getUserPassword()); + + waitForPage("grant access"); + + Assert.assertTrue(consentPage.isCurrent()); + consentPage.confirm(); + + Assert.assertTrue("We must be on correct realm right now", + driver.getCurrentUrl().contains("/auth/realms/" + consumerRealmName() + "/")); + + UsersResource consumerUsers = adminClient.realm(consumerRealmName()).users(); + Assert.assertTrue("There must be at least one user", consumerUsers.count() > 0); + + List users = consumerUsers.search("", 0, 5); + + UserRepresentation foundUser = null; + for (UserRepresentation user : users) { + if (user.getUsername().equals(getUserLogin()) && user.getEmail().equals(getUserEmail())) { + foundUser = user; + break; + } + } + + Assert.assertNotNull("There must be user " + getUserLogin() + " in realm " + consumerRealmName(), + foundUser); + + // get user with the same username from provider realm + RealmResource providerRealm = adminClient.realm(providerRealmName()); + users = providerRealm.users().search(null, foundUser.getFirstName(), foundUser.getLastName(), null, 0, 1); + Assert.assertEquals("Same user should be in provider realm", 1, users.size()); + + String userId = users.get(0).getId(); + UserResource userResource = providerRealm.users().get(userId); + + // list consents + List> consents = userResource.getConsents(); + Assert.assertEquals("There should be one consent", 1, consents.size()); + + Map consent = consents.get(0); + Assert.assertEquals("Consent should be given to " + CLIENT_ID, CLIENT_ID, consent.get("clientId")); + + // list sessions + List sessions = userResource.getUserSessions(); + Assert.assertEquals("There should be one active session", 1, sessions.size()); + + // revoke consent + userResource.revokeConsent(CLIENT_ID); + + // list consents + consents = userResource.getConsents(); + Assert.assertEquals("There should be no consents", 0, consents.size()); + + // list sessions + sessions = userResource.getUserSessions(); + Assert.assertEquals("There should be no active session", 0, sessions.size()); + } + + private String getAccountUrl(String realmName) { + return getAuthRoot() + "/auth/realms/" + realmName + "/account"; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java index 7ec1da25dc..ca01045b96 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java @@ -21,6 +21,7 @@ import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.keycloak.Config; +import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.events.Details; @@ -29,25 +30,20 @@ import org.keycloak.models.AdminRoles; import org.keycloak.models.Constants; import org.keycloak.models.ImpersonationConstants; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; +import org.keycloak.testsuite.auth.page.AuthRealm; import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.CredentialBuilder; -import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse; import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.UserBuilder; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.ClientErrorException; import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -85,21 +81,6 @@ public class ImpersonationTest extends AbstractKeycloakTest { testRealms.add(realm.build()); } - private String createAdminToken(String username, String realm) { - try { - String password = username.equals("admin") ? "admin" : "password"; - String clientId = realm.equals("master") ? Constants.ADMIN_CLI_CLIENT_ID : "myclient"; - AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest(realm, username, password, null, clientId, null); - if (tokenResponse.getStatusCode() != 200) { - throw new RuntimeException("Failed to get token: " + tokenResponse.getErrorDescription()); - } - events.clear(); - return tokenResponse.getAccessToken(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - @Test public void testImpersonateByMasterAdmin() { // test that composite is set up right for impersonation role @@ -157,51 +138,65 @@ public class ImpersonationTest extends AbstractKeycloakTest { } - protected void testSuccessfulImpersonation(String admin, String adminRealm) { - Client client = createClient(admin, adminRealm); - WebTarget impersonate = createImpersonateTarget(client); - Map data = impersonate.request().post(null, Map.class); - Assert.assertNotNull(data); - Assert.assertNotNull(data.get("redirect")); - // TODO Events not working - events.expect(EventType.IMPERSONATE) - .session(AssertEvents.isUUID()) - .user(impersonatedUserId) - .detail(Details.IMPERSONATOR, admin) - .detail(Details.IMPERSONATOR_REALM, adminRealm) - .client((String) null).assertEvent(); + Keycloak client = login(admin, adminRealm); + try { + Map data = client.realms().realm("test").users().get(impersonatedUserId).impersonate(); + Assert.assertNotNull(data); + Assert.assertNotNull(data.get("redirect")); - client.close(); + events.expect(EventType.IMPERSONATE) + .session(AssertEvents.isUUID()) + .user(impersonatedUserId) + .detail(Details.IMPERSONATOR, admin) + .detail(Details.IMPERSONATOR_REALM, adminRealm) + .client((String) null).assertEvent(); + } finally { + client.close(); + } } protected void testForbiddenImpersonation(String admin, String adminRealm) { - Client client = createClient(admin, adminRealm); - WebTarget impersonate = createImpersonateTarget(client); - Response response = impersonate.request().post(null); - response.close(); - client.close(); + Keycloak client = createAdminClient(adminRealm, establishClientId(adminRealm), admin); + try { + client.realms().realm("test").users().get(impersonatedUserId).impersonate(); + } catch (ClientErrorException e) { + Assert.assertTrue(e.getMessage().indexOf("403 Forbidden") != -1); + } finally { + client.close(); + } } - - protected WebTarget createImpersonateTarget(Client client) { - UriBuilder authBase = UriBuilder.fromUri(getAuthServerRoot()); - WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase)); - WebTarget realmTarget = adminRealms.path("test"); - return realmTarget.path("users").path(impersonatedUserId).path("impersonation"); + Keycloak createAdminClient(String realm, String clientId, String username) { + return createAdminClient(realm, clientId, username, null); } - protected Client createClient(String admin, String adminRealm) { - String token = createAdminToken(admin, adminRealm); - final String authHeader = "Bearer " + token; - ClientRequestFilter authFilter = new ClientRequestFilter() { - @Override - public void filter(ClientRequestContext requestContext) throws IOException { - requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader); - } - }; - return javax.ws.rs.client.ClientBuilder.newBuilder().register(authFilter).build(); + String establishClientId(String realm) { + return realm.equals("master") ? Constants.ADMIN_CLI_CLIENT_ID : "myclient"; } + Keycloak createAdminClient(String realm, String clientId, String username, String password) { + if (password == null) { + password = username.equals("admin") ? "admin" : "password"; + } + return Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", + realm, username, password, clientId); + } + + private Keycloak login(String username, String realm) { + String clientId = establishClientId(realm); + Keycloak client = createAdminClient(realm, clientId, username); + + client.tokenManager().grantToken(); + // only poll for LOGIN event if realm is not master + // - since for master testing event listener is not installed + if (!AuthRealm.MASTER.equals(realm)) { + EventRepresentation e = events.poll(); + Assert.assertEquals("Event type", EventType.LOGIN.toString(), e.getType()); + Assert.assertEquals("Client ID", clientId, e.getClientId()); + Assert.assertEquals("Username", username, e.getDetails().get("username")); + } + return client; + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java new file mode 100644 index 0000000000..4831e298ef --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java @@ -0,0 +1,102 @@ +/* + * 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 org.jboss.arquillian.graphene.page.Page; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.events.Details; +import org.keycloak.events.EventType; +import org.keycloak.events.admin.OperationType; +import org.keycloak.models.utils.TimeBasedOTP; +import org.keycloak.representations.idm.AdminEventRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.services.resources.AccountService; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.TestRealmKeycloakTest; +import org.keycloak.testsuite.pages.AccountTotpPage; +import org.keycloak.testsuite.pages.AccountUpdateProfilePage; +import org.keycloak.testsuite.pages.LoginPage; + +import javax.ws.rs.core.UriBuilder; +import java.util.List; + + +/** + * @author Marko Strukelj + */ +public class UserTotpTest extends TestRealmKeycloakTest { + + private static final UriBuilder BASE = UriBuilder.fromUri("http://localhost:8180/auth"); + public static String ACCOUNT_REDIRECT = AccountService.loginRedirectUrl(BASE.clone()).build("test").toString(); + + @Rule + public AssertEvents events = new AssertEvents(this); + + @Page + protected AccountTotpPage totpPage; + + @Page + protected AccountUpdateProfilePage profilePage; + + @Page + protected LoginPage loginPage; + + private TimeBasedOTP totp = new TimeBasedOTP(); + + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + } + + @Test + public void setupTotp() { + totpPage.open(); + loginPage.login("test-user@localhost", "password"); + + events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=totp").assertEvent(); + + Assert.assertTrue(totpPage.isCurrent()); + + Assert.assertFalse(driver.getPageSource().contains("Remove Google")); + + totpPage.configure(totp.generateTOTP(totpPage.getTotpSecret())); + + Assert.assertEquals("Mobile authenticator configured.", profilePage.getSuccess()); + + events.expectAccount(EventType.UPDATE_TOTP).assertEvent(); + + Assert.assertTrue(driver.getPageSource().contains("pficon-delete")); + + List users = adminClient.realms().realm("test").users().search("test-user@localhost", null, null, null, 0, 1); + String userId = users.get(0).getId(); + + adminClient.realms().realm("test").users().get(userId).removeTotp(); + + totpPage.open(); + Assert.assertFalse(driver.getPageSource().contains("pficon-delete")); + + AdminEventRepresentation event = testingClient.testing().pollAdminEvent(); + Assert.assertNotNull(event); + Assert.assertEquals(OperationType.ACTION.name(), event.getOperationType()); + Assert.assertEquals("users/" + userId + "/remove-totp", event.getResourcePath()); + } +}