/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.testsuite.broker; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.util.Time; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; 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.services.managers.RealmManager; import org.keycloak.testsuite.KeycloakServer; import org.keycloak.testsuite.rule.AbstractKeycloakRule; import org.openqa.selenium.NoSuchElementException; import java.util.List; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityProviderTest { private static final int PORT = 8082; private static Keycloak keycloak1; private static Keycloak keycloak2; @ClassRule public static AbstractKeycloakRule oidcServerRule = new AbstractKeycloakRule() { @Override protected void configureServer(KeycloakServer server) { server.getConfig().setPort(PORT); } @Override protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json")); // Disable update profile RealmModel realm = getRealm(session); setUpdateProfileFirstLogin(realm, IdentityProviderRepresentation.UPFLM_OFF); } @Override protected String[] getTestRealms() { return new String[] { "realm-with-oidc-identity-provider" }; } }; @BeforeClass public static void before() { keycloak1 = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID); keycloak2 = Keycloak.getInstance("http://localhost:8082/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID); // Require broker to show consent screen RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); List<ClientRepresentation> clients = brokeredRealm.clients().findByClientId("broker-app"); Assert.assertEquals(1, clients.size()); ClientRepresentation brokerApp = clients.get(0); brokerApp.setConsentRequired(true); brokeredRealm.clients().get(brokerApp.getId()).update(brokerApp); // Change timeouts on realm-with-broker to lower values RealmResource realmWithBroker = keycloak1.realm("realm-with-broker"); RealmRepresentation realmRep = realmWithBroker.toRepresentation(); realmRep.setAccessCodeLifespanLogin(30);; realmRep.setAccessCodeLifespan(30); realmRep.setAccessCodeLifespanUserAction(30); realmWithBroker.update(realmRep); } @Override protected String getProviderId() { return "kc-oidc-idp"; } // KEYCLOAK-2769 @Test public void testConsentDeniedWithExpiredClientSession() throws Exception { // Login to broker loginIDP("test-user"); // Set time offset Time.setOffset(60); try { // User rejected consent grantPage.assertCurrent(); grantPage.cancel(); // Assert login page with "You took too long to login..." message assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/login-actions/authenticate")); Assert.assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError()); } finally { Time.setOffset(0); } } // KEYCLOAK-2769 @Test public void testConsentDeniedWithExpiredAndClearedClientSession() throws Exception { // Login to broker again loginIDP("test-user"); // Set time offset Time.setOffset(60); try { // Manually remove expiredSessions TODO: Will require custom endpoint when migrate to integration-arquillian brokerServerRule.stopSession(this.session, true); this.session = brokerServerRule.startSession(); session.sessions().removeExpired(getRealm()); session.authenticationSessions().removeExpired(getRealm()); brokerServerRule.stopSession(this.session, true); this.session = brokerServerRule.startSession(); // User rejected consent grantPage.assertCurrent(); grantPage.cancel(); // Assert login page with "You took too long to login..." message assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/login-actions/authenticate")); Assert.assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError()); } finally { Time.setOffset(0); } } // KEYCLOAK-2801 @Test public void testAccountManagementLinkingAndExpiredClientSession() throws Exception { // Login as pedroigor to account management loginToAccountManagement("pedroigor"); // Link my "pedroigor" identity with "test-user" from brokered Keycloak accountFederatedIdentityPage.clickAddProvider(getProviderId()); assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); this.loginPage.login("test-user", "password"); // Set time offset Time.setOffset(60); try { // User rejected consent grantPage.assertCurrent(); grantPage.cancel(); // Assert account error page with "staleCodeAccount" error displayed accountFederatedIdentityPage.assertCurrent(); Assert.assertEquals("The page expired. Please try one more time.", accountFederatedIdentityPage.getError()); // Try to link one more time accountFederatedIdentityPage.clickAddProvider(getProviderId()); assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); this.loginPage.login("test-user", "password"); Time.setOffset(120); // User granted consent grantPage.assertCurrent(); grantPage.accept(); // Assert account error page with "staleCodeAccount" error displayed accountFederatedIdentityPage.assertCurrent(); Assert.assertEquals("The page expired. Please try one more time.", accountFederatedIdentityPage.getError()); } finally { Time.setOffset(0); } // Revoke consent RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); List<UserRepresentation> users = brokeredRealm.users().search("test-user", 0, 1); brokeredRealm.users().get(users.get(0).getId()).revokeConsent("broker-app"); } @Test public void testLoginCancelConsent() throws Exception { // Try to login loginIDP("test-user"); // User rejected consent grantPage.assertCurrent(); grantPage.cancel(); // Assert back on login page assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/")); assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); } // KEYCLOAK-2802 @Test public void testAccountManagementLinkingCancelConsent() throws Exception { // Login as pedroigor to account management loginToAccountManagement("pedroigor"); // Link my "pedroigor" identity with "test-user" from brokered Keycloak accountFederatedIdentityPage.clickAddProvider(getProviderId()); assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); this.loginPage.login("test-user", "password"); // User rejected consent grantPage.assertCurrent(); grantPage.cancel(); // Assert account error page with "consentDenied" error displayed accountFederatedIdentityPage.assertCurrent(); Assert.assertEquals("Consent denied.", accountFederatedIdentityPage.getError()); } private void loginToAccountManagement(String username) { accountFederatedIdentityPage.realm("realm-with-broker"); accountFederatedIdentityPage.open(); assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); loginPage.login(username, "password"); assertTrue(accountFederatedIdentityPage.isCurrent()); } }