diff --git a/distribution/subsystem-war/src/main/webapp/WEB-INF/web.xml b/distribution/subsystem-war/src/main/webapp/WEB-INF/web.xml index 7228afe95f..4257449a98 100755 --- a/distribution/subsystem-war/src/main/webapp/WEB-INF/web.xml +++ b/distribution/subsystem-war/src/main/webapp/WEB-INF/web.xml @@ -25,11 +25,6 @@ org.keycloak.services.listeners.KeycloakSessionDestroyListener - - Keycloak Client Connection Filter - org.keycloak.services.filters.ClientConnectionFilter - - Keycloak Session Management org.keycloak.services.filters.KeycloakSessionServletFilter diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java old mode 100644 new mode 100755 index ac13d3fbcc..4d33403586 --- a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java +++ b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java @@ -1,5 +1,7 @@ package org.keycloak.models; +import org.keycloak.ClientConnection; + import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriInfo; @@ -20,4 +22,8 @@ public interface KeycloakContext { void setClient(ClientModel client); + ClientConnection getConnection(); + + void setConnection(ClientConnection connection); + } diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java index c27fb1efa4..bf97994876 100755 --- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java +++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java @@ -434,7 +434,7 @@ public class AuthenticationProcessor { if (authenticator.requiresUser() && authUser != null) { configuredFor = authenticator.configuredFor(session, realm, authUser); if (!configuredFor) { - if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) { + if (model.isRequired()) { if (model.isUserSetupAllowed()) { clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED); String requiredAction = authenticator.getRequiredAction(); @@ -445,6 +445,9 @@ public class AuthenticationProcessor { } else { throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED); } + } else if (model.isOptional()) { + clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED); + continue; } } } @@ -477,7 +480,9 @@ public class AuthenticationProcessor { clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED); return context.challenge; } else if (result == Status.ATTEMPTED) { - if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS); + if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) { + throw new AuthException(Error.INVALID_CREDENTIALS); + } clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED); continue; } else { diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java old mode 100644 new mode 100755 index 1b0e567def..99849a90db --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java @@ -1,6 +1,7 @@ package org.keycloak.services; import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.ClientConnection; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakContext; import org.keycloak.models.RealmModel; @@ -17,6 +18,8 @@ public class DefaultKeycloakContext implements KeycloakContext { private ClientModel client; + private ClientConnection connection; + @Override public UriInfo getUri() { return ResteasyProviderFactory.getContextData(UriInfo.class); @@ -47,4 +50,13 @@ public class DefaultKeycloakContext implements KeycloakContext { this.client = client; } + @Override + public ClientConnection getConnection() { + return connection; + } + + @Override + public void setConnection(ClientConnection connection) { + this.connection = connection; + } } diff --git a/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java b/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java index f172d39123..e412c0d9c1 100755 --- a/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java +++ b/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java @@ -15,6 +15,7 @@ import java.io.IOException; * @author Bill Burke * @version $Revision: 1 $ */ +@Deprecated public class ClientConnectionFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java index 411556784f..cfd482e3a9 100755 --- a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java +++ b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java @@ -1,6 +1,7 @@ package org.keycloak.services.filters; import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.ClientConnection; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakTransaction; @@ -26,11 +27,29 @@ public class KeycloakSessionServletFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest)servletRequest; + final HttpServletRequest request = (HttpServletRequest)servletRequest; KeycloakSessionFactory sessionFactory = (KeycloakSessionFactory) servletRequest.getServletContext().getAttribute(KeycloakSessionFactory.class.getName()); KeycloakSession session = sessionFactory.create(); ResteasyProviderFactory.pushContext(KeycloakSession.class, session); + ClientConnection connection = new ClientConnection() { + @Override + public String getRemoteAddr() { + return request.getRemoteAddr(); + } + + @Override + public String getRemoteHost() { + return request.getRemoteHost(); + } + + @Override + public int getReportPort() { + return request.getRemotePort(); + } + }; + session.getContext().setConnection(connection); + ResteasyProviderFactory.pushContext(ClientConnection.class, connection); KeycloakTransaction tx = session.getTransaction(); ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx); diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index 9221268e8a..a1da98545f 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -315,6 +315,8 @@ public class LoginActionsService { event.error(Errors.CLIENT_NOT_FOUND); return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER); } + session.getContext().setClient(client); + if (!client.isEnabled()) { event.error(Errors.CLIENT_NOT_FOUND); return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java index 939231f031..1e00be8ab6 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java @@ -1,267 +1,267 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2012, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.keycloak.testsuite.oauth; - -import org.junit.Assert; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.keycloak.OAuth2Constants; -import org.keycloak.constants.KerberosConstants; -import org.keycloak.events.Details; -import org.keycloak.events.Event; -import org.keycloak.events.EventType; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserModel; -import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; -import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; -import org.keycloak.representations.AccessToken; -import org.keycloak.services.managers.RealmManager; -import org.keycloak.testsuite.AssertEvents; -import org.keycloak.testsuite.OAuthClient; -import org.keycloak.testsuite.pages.AccountApplicationsPage; -import org.keycloak.testsuite.pages.AppPage; -import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.pages.OAuthGrantPage; -import org.keycloak.testsuite.rule.KeycloakRule; -import org.keycloak.testsuite.rule.WebResource; -import org.keycloak.testsuite.rule.WebRule; -import org.openqa.selenium.WebDriver; - -import java.io.IOException; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -/** - * @author Viliam Rockai - */ -public class OAuthGrantTest { - - @ClassRule - public static KeycloakRule keycloakRule = new KeycloakRule(); - - @Rule - public AssertEvents events = new AssertEvents(keycloakRule); - - @Rule - public WebRule webRule = new WebRule(this); - - @WebResource - protected WebDriver driver; - - @WebResource - protected OAuthClient oauth; - - @WebResource - protected LoginPage loginPage; - - @WebResource - protected OAuthGrantPage grantPage; - - @WebResource - protected AccountApplicationsPage accountAppsPage; - - @WebResource - protected AppPage appPage; - - private static String ROLE_USER = "Have User privileges"; - private static String ROLE_CUSTOMER = "Have Customer User privileges"; - - @Test - public void oauthGrantAcceptTest() { - oauth.clientId("third-party"); - oauth.doLoginGrant("test-user@localhost", "password"); - - grantPage.assertCurrent(); - Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); - Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); - - grantPage.accept(); - - Assert.assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE)); - - Event loginEvent = events.expectLogin().client("third-party").assertEvent(); - String codeId = loginEvent.getDetails().get(Details.CODE_ID); - String sessionId = loginEvent.getSessionId(); - - OAuthClient.AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password"); - - String tokenString = accessToken.getAccessToken(); - Assert.assertNotNull(tokenString); - AccessToken token = oauth.verifyToken(tokenString); - assertEquals(sessionId, token.getSessionState()); - - AccessToken.Access realmAccess = token.getRealmAccess(); - assertEquals(1, realmAccess.getRoles().size()); - Assert.assertTrue(realmAccess.isUserInRole("user")); - - Map resourceAccess = token.getResourceAccess(); - assertEquals(1, resourceAccess.size()); - assertEquals(1, resourceAccess.get("test-app").getRoles().size()); - Assert.assertTrue(resourceAccess.get("test-app").isUserInRole("customer-user")); - - events.expectCodeToToken(codeId, loginEvent.getSessionId()).client("third-party").assertEvent(); - - accountAppsPage.open(); - accountAppsPage.revokeGrant("third-party"); - - events.expect(EventType.REVOKE_GRANT) - .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent(); - } - - @Test - public void oauthGrantCancelTest() { - oauth.clientId("third-party"); - oauth.doLoginGrant("test-user@localhost", "password"); - - grantPage.assertCurrent(); - Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); - Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); - - grantPage.cancel(); - - Assert.assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.ERROR)); - assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR)); - - events.expectLogin().client("third-party").error("rejected_by_user").assertEvent(); - } - - @Test - public void oauthGrantNotShownWhenAlreadyGranted() { - // Grant permissions on grant screen - oauth.clientId("third-party"); - oauth.doLoginGrant("test-user@localhost", "password"); - - grantPage.assertCurrent(); - grantPage.accept(); - - events.expectLogin().client("third-party").assertEvent(); - - // Assert permissions granted on Account mgmt. applications page - accountAppsPage.open(); - AccountApplicationsPage.AppEntry thirdPartyEntry = accountAppsPage.getApplications().get("third-party"); - Assert.assertTrue(thirdPartyEntry.getRolesGranted().contains(ROLE_USER)); - Assert.assertTrue(thirdPartyEntry.getRolesGranted().contains("Have Customer User privileges in test-app")); - Assert.assertTrue(thirdPartyEntry.getProtocolMappersGranted().contains("Full name")); - Assert.assertTrue(thirdPartyEntry.getProtocolMappersGranted().contains("Email")); - - // Open login form and assert grantPage not shown - oauth.openLoginForm(); - appPage.assertCurrent(); - events.expectLogin().detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).client("third-party").assertEvent(); - - // Revoke grant in account mgmt. - accountAppsPage.open(); - accountAppsPage.revokeGrant("third-party"); - - events.expect(EventType.REVOKE_GRANT) - .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent(); - - // Open login form again and assert grant Page is shown - oauth.openLoginForm(); - grantPage.assertCurrent(); - Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); - Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); - } - - @Test - public void oauthGrantAddAnotherRoleAndMapper() { - // Grant permissions on grant screen - oauth.clientId("third-party"); - oauth.doLoginGrant("test-user@localhost", "password"); - - // Add new protocolMapper and role before showing grant page - keycloakRule.update(new KeycloakRule.KeycloakSetup() { - - @Override - public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { - ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, - KerberosConstants.GSS_DELEGATION_CREDENTIAL, - KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", - true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, - true, false); - - ClientModel thirdPartyApp = appRealm.getClientByClientId("third-party"); - thirdPartyApp.addProtocolMapper(protocolMapper); - - RoleModel newRole = appRealm.addRole("new-role"); - thirdPartyApp.addScopeMapping(newRole); - UserModel testUser = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm); - testUser.grantRole(newRole); - } - - }); - - // Confirm grant page - grantPage.assertCurrent(); - grantPage.accept(); - events.expectLogin().client("third-party").assertEvent(); - - // Assert new role and protocol mapper not in account mgmt. - accountAppsPage.open(); - AccountApplicationsPage.AppEntry appEntry = accountAppsPage.getApplications().get("third-party"); - Assert.assertFalse(appEntry.getRolesGranted().contains("new-role")); - Assert.assertFalse(appEntry.getProtocolMappersGranted().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); - - // Show grant page another time. Just new role and protocol mapper are on the page - oauth.openLoginForm(); - grantPage.assertCurrent(); - Assert.assertFalse(driver.getPageSource().contains(ROLE_USER)); - Assert.assertFalse(driver.getPageSource().contains("Full name")); - Assert.assertTrue(driver.getPageSource().contains("new-role")); - Assert.assertTrue(driver.getPageSource().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); - grantPage.accept(); - events.expectLogin().client("third-party").assertEvent(); - - // Go to account mgmt. Everything is granted now - accountAppsPage.open(); - appEntry = accountAppsPage.getApplications().get("third-party"); - Assert.assertTrue(appEntry.getRolesGranted().contains("new-role")); - Assert.assertTrue(appEntry.getProtocolMappersGranted().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); - - // Revoke - accountAppsPage.revokeGrant("third-party"); - events.expect(EventType.REVOKE_GRANT) - .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent(); - - // Cleanup - keycloakRule.update(new KeycloakRule.KeycloakSetup() { - - @Override - public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { - ClientModel thirdPartyApp = appRealm.getClientByClientId("third-party"); - ProtocolMapperModel gssMapper = thirdPartyApp.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME); - thirdPartyApp.removeProtocolMapper(gssMapper); - - RoleModel newRole = appRealm.getRole("new-role"); - appRealm.removeRole(newRole); - } - - }); - } - -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.testsuite.oauth; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.OAuth2Constants; +import org.keycloak.constants.KerberosConstants; +import org.keycloak.events.Details; +import org.keycloak.events.Event; +import org.keycloak.events.EventType; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; +import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; +import org.keycloak.representations.AccessToken; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.OAuthClient; +import org.keycloak.testsuite.pages.AccountApplicationsPage; +import org.keycloak.testsuite.pages.AppPage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.pages.OAuthGrantPage; +import org.keycloak.testsuite.rule.KeycloakRule; +import org.keycloak.testsuite.rule.WebResource; +import org.keycloak.testsuite.rule.WebRule; +import org.openqa.selenium.WebDriver; + +import java.io.IOException; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * @author Viliam Rockai + */ +public class OAuthGrantTest { + + @ClassRule + public static KeycloakRule keycloakRule = new KeycloakRule(); + + @Rule + public AssertEvents events = new AssertEvents(keycloakRule); + + @Rule + public WebRule webRule = new WebRule(this); + + @WebResource + protected WebDriver driver; + + @WebResource + protected OAuthClient oauth; + + @WebResource + protected LoginPage loginPage; + + @WebResource + protected OAuthGrantPage grantPage; + + @WebResource + protected AccountApplicationsPage accountAppsPage; + + @WebResource + protected AppPage appPage; + + private static String ROLE_USER = "Have User privileges"; + private static String ROLE_CUSTOMER = "Have Customer User privileges"; + + @Test + public void oauthGrantAcceptTest() { + oauth.clientId("third-party"); + oauth.doLoginGrant("test-user@localhost", "password"); + + grantPage.assertCurrent(); + Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); + Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); + + grantPage.accept(); + + Assert.assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE)); + + Event loginEvent = events.expectLogin().client("third-party").assertEvent(); + String codeId = loginEvent.getDetails().get(Details.CODE_ID); + String sessionId = loginEvent.getSessionId(); + + OAuthClient.AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password"); + + String tokenString = accessToken.getAccessToken(); + Assert.assertNotNull(tokenString); + AccessToken token = oauth.verifyToken(tokenString); + assertEquals(sessionId, token.getSessionState()); + + AccessToken.Access realmAccess = token.getRealmAccess(); + assertEquals(1, realmAccess.getRoles().size()); + Assert.assertTrue(realmAccess.isUserInRole("user")); + + Map resourceAccess = token.getResourceAccess(); + assertEquals(1, resourceAccess.size()); + assertEquals(1, resourceAccess.get("test-app").getRoles().size()); + Assert.assertTrue(resourceAccess.get("test-app").isUserInRole("customer-user")); + + events.expectCodeToToken(codeId, loginEvent.getSessionId()).client("third-party").assertEvent(); + + accountAppsPage.open(); + accountAppsPage.revokeGrant("third-party"); + + events.expect(EventType.REVOKE_GRANT) + .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent(); + } + + @Test + public void oauthGrantCancelTest() { + oauth.clientId("third-party"); + oauth.doLoginGrant("test-user@localhost", "password"); + + grantPage.assertCurrent(); + Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); + Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); + + grantPage.cancel(); + + Assert.assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.ERROR)); + assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR)); + + events.expectLogin().client("third-party").error("rejected_by_user").assertEvent(); + } + + @Test + public void oauthGrantNotShownWhenAlreadyGranted() { + // Grant permissions on grant screen + oauth.clientId("third-party"); + oauth.doLoginGrant("test-user@localhost", "password"); + + grantPage.assertCurrent(); + grantPage.accept(); + + events.expectLogin().client("third-party").assertEvent(); + + // Assert permissions granted on Account mgmt. applications page + accountAppsPage.open(); + AccountApplicationsPage.AppEntry thirdPartyEntry = accountAppsPage.getApplications().get("third-party"); + Assert.assertTrue(thirdPartyEntry.getRolesGranted().contains(ROLE_USER)); + Assert.assertTrue(thirdPartyEntry.getRolesGranted().contains("Have Customer User privileges in test-app")); + Assert.assertTrue(thirdPartyEntry.getProtocolMappersGranted().contains("Full name")); + Assert.assertTrue(thirdPartyEntry.getProtocolMappersGranted().contains("Email")); + + // Open login form and assert grantPage not shown + oauth.openLoginForm(); + appPage.assertCurrent(); + events.expectLogin().removeDetail(Details.USERNAME).client("third-party").assertEvent(); + + // Revoke grant in account mgmt. + accountAppsPage.open(); + accountAppsPage.revokeGrant("third-party"); + + events.expect(EventType.REVOKE_GRANT) + .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent(); + + // Open login form again and assert grant Page is shown + oauth.openLoginForm(); + grantPage.assertCurrent(); + Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); + Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); + } + + @Test + public void oauthGrantAddAnotherRoleAndMapper() { + // Grant permissions on grant screen + oauth.clientId("third-party"); + oauth.doLoginGrant("test-user@localhost", "password"); + + // Add new protocolMapper and role before showing grant page + keycloakRule.update(new KeycloakRule.KeycloakSetup() { + + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, + KerberosConstants.GSS_DELEGATION_CREDENTIAL, + KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", + true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, + true, false); + + ClientModel thirdPartyApp = appRealm.getClientByClientId("third-party"); + thirdPartyApp.addProtocolMapper(protocolMapper); + + RoleModel newRole = appRealm.addRole("new-role"); + thirdPartyApp.addScopeMapping(newRole); + UserModel testUser = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm); + testUser.grantRole(newRole); + } + + }); + + // Confirm grant page + grantPage.assertCurrent(); + grantPage.accept(); + events.expectLogin().client("third-party").assertEvent(); + + // Assert new role and protocol mapper not in account mgmt. + accountAppsPage.open(); + AccountApplicationsPage.AppEntry appEntry = accountAppsPage.getApplications().get("third-party"); + Assert.assertFalse(appEntry.getRolesGranted().contains("new-role")); + Assert.assertFalse(appEntry.getProtocolMappersGranted().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); + + // Show grant page another time. Just new role and protocol mapper are on the page + oauth.openLoginForm(); + grantPage.assertCurrent(); + Assert.assertFalse(driver.getPageSource().contains(ROLE_USER)); + Assert.assertFalse(driver.getPageSource().contains("Full name")); + Assert.assertTrue(driver.getPageSource().contains("new-role")); + Assert.assertTrue(driver.getPageSource().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); + grantPage.accept(); + events.expectLogin().client("third-party").assertEvent(); + + // Go to account mgmt. Everything is granted now + accountAppsPage.open(); + appEntry = accountAppsPage.getApplications().get("third-party"); + Assert.assertTrue(appEntry.getRolesGranted().contains("new-role")); + Assert.assertTrue(appEntry.getProtocolMappersGranted().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); + + // Revoke + accountAppsPage.revokeGrant("third-party"); + events.expect(EventType.REVOKE_GRANT) + .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent(); + + // Cleanup + keycloakRule.update(new KeycloakRule.KeycloakSetup() { + + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + ClientModel thirdPartyApp = appRealm.getClientByClientId("third-party"); + ProtocolMapperModel gssMapper = thirdPartyApp.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME); + thirdPartyApp.removeProtocolMapper(gssMapper); + + RoleModel newRole = appRealm.getRole("new-role"); + appRealm.removeRole(newRole); + } + + }); + } + +}