keycloak-aplcache
Changes
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java 4(+4 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ConsentPage.java 44(+44 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ConsentsTest.java 300(+300 -0)
Details
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 2df2cbf..eebefe7 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<String, Object> 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 0000000..b8b244d
--- /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 <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+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 fc7a341..68a8f8d 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<String, Long> offlineSessionCount = realm.clients().get(id).getOfflineSessionCount();
assertEquals(new Long(0), offlineSessionCount.get("count"));
+ List<UserSessionRepresentation> 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<UserSessionRepresentation> 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 0000000..fb0a863
--- /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 <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+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<ClientRepresentation> 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<String, String> 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<RealmRepresentation> 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<ClientRepresentation> 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<UserRepresentation> 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<Map<String, Object>> consents = userResource.getConsents();
+ Assert.assertEquals("There should be one consent", 1, consents.size());
+
+ Map<String, Object> consent = consents.get(0);
+ Assert.assertEquals("Consent should be given to " + CLIENT_ID, CLIENT_ID, consent.get("clientId"));
+
+ // list sessions
+ List<UserSessionRepresentation> 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 7ec1da2..ca01045 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();
-
- client.close();
+
+ Keycloak client = login(admin, adminRealm);
+ try {
+ Map data = client.realms().realm("test").users().get(impersonatedUserId).impersonate();
+ Assert.assertNotNull(data);
+ Assert.assertNotNull(data.get("redirect"));
+
+ 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();
+ }
}
+ Keycloak createAdminClient(String realm, String clientId, String username) {
+ return createAdminClient(realm, clientId, username, null);
+ }
- 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");
+ String establishClientId(String realm) {
+ return realm.equals("master") ? Constants.ADMIN_CLI_CLIENT_ID : "myclient";
}
- 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();
+ 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 0000000..4831e29
--- /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 <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+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<UserRepresentation> 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());
+ }
+}