Details
diff --git a/forms/common-themes/src/main/resources/theme/base/login/impersonate.ftl b/forms/common-themes/src/main/resources/theme/base/login/impersonate.ftl
new file mode 100755
index 0000000..0a138be
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/impersonate.ftl
@@ -0,0 +1,45 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout displayInfo=social.displayInfo; section>
+ <#if section = "title">
+ ${msg("imperonateTitle",(realm.name!''))}
+ <#elseif section = "header">
+ ${msg("impersonateTitleHtml",(realm.name!''))}
+ <#elseif section = "form">
+ <form id="kc-form-login" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
+ <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
+ <#if realmList??>
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="realm" class="${properties.kcLabelClass!}">${msg("realmChoice")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <select class="${properties.kcInputClass!}" id="selectRealm" name="realm">
+ <#list realmList as r>
+ <option value="${r}">${r}</option>
+ </#list>
+ </select>
+ </div>
+ </div>
+ </#if>
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="username" class="${properties.kcLabelClass!}"><#if !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input id="username" class="${properties.kcInputClass!}" name="username" value="" type="text" autofocus />
+ </div>
+ </div>
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="username" class="${properties.kcLabelClass!}"></label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="impersonate" id="kc-impersonate" type="submit" value="${msg("doImpersonate")}"/>
+ <input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
+ </div>
+ </div>
+ </form>
+ </#if>
+</@layout.registrationLayout>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
index 4fe6ab3..5fab8e3 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
@@ -9,6 +9,7 @@ doDecline=Decline
doContinue=Continue
doForgotPassword=Passwort vergessen?
doClickHere=hier klicken
+doImpersonate=Impersonate
kerberosNotConfigured=Kerberos Not Configured
kerberosNotConfiguredTitle=Kerberos Not Configured
bypassKerberos=Your browser is not set up for Kerberos login. Please click continue to login in through other means
@@ -24,6 +25,10 @@ loginOauthTitle=
loginOauthTitleHtml=Tempor\u00E4rer zugriff auf <strong>{0}</strong> angefordert von <strong>{1}</strong>.
loginTotpTitle=Mobile Authentifizierung Einrichten
loginProfileTitle=Benutzerkonto Informationen aktualisieren
+impersonateTitle={0} Impersonate User
+impersonateTitleHtml=<strong>{0}</strong> Impersonate User</strong>
+unknownUser=Unknown user
+realmChoice=Realm
oauthGrantTitle=OAuth gew\u00E4hren
oauthGrantTitleHtml=Tempor\u00E4rer zugriff auf <strong>{0}</strong> angefordert von
errorTitle=Es tut uns leid...
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
index 3fdb91c..8fc0d2e 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -9,6 +9,7 @@ doAccept=Accept
doDecline=Decline
doForgotPassword=Forgot Password?
doClickHere=Click here
+doImpersonate=Impersonate
kerberosNotConfigured=Kerberos Not Configured
kerberosNotConfiguredTitle=Kerberos Not Configured
bypassKerberosDetail=Either you are not logged in via Kerberos or your browser is not set up for Kerberos login. Please click continue to login in through other means
@@ -17,6 +18,10 @@ registerWithTitle=Register with {0}
registerWithTitleHtml=Register with <strong>{0}</strong>
loginTitle=Log in to {0}
loginTitleHtml=Log in to <strong>{0}</strong>
+impersonateTitle={0} Impersonate User
+impersonateTitleHtml=<strong>{0}</strong> Impersonate User</strong>
+realmChoice=Realm
+unknownUser=Unknown user
loginTotpTitle=Mobile Authenticator Setup
loginProfileTitle=Update Account Information
oauthGrantTitle=OAuth Grant
@@ -76,7 +81,6 @@ emailInstruction=Enter your username or email address and we will send you instr
copyCodeInstruction=Please copy this code and paste it into your application:
personalInfo=Personal Info:
-
role_admin=Admin
role_realm-admin=Realm Admin
role_create-realm=Create realm
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_it.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_it.properties
index b239ecc..df610f9 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_it.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_it.properties
@@ -9,6 +9,7 @@ doDecline=Decline
doContinue=Continue
doForgotPassword=Password Dimenticata?
doClickHere=Clicca qui
+doImpersonate=Impersonate
bypassKerberos=Your browser is not set up for Kerberos login. Please click continue to login in through other means
kerberosNotSetUp=Kerberos is not set up. You cannot login.
kerberosNotConfigured=Kerberos Not Configured
@@ -22,6 +23,10 @@ loginTitle=Accedi a {0}
loginTitleHtml=Accedi a <strong>{0}</strong>
loginTotpTitle=Configura Autenticazione Mobile
loginProfileTitle=Aggiorna Profilo
+impersonateTitle={0} Impersonate User
+impersonateTitleHtml=<strong>{0}</strong> Impersonate User</strong>
+unknownUser=Unknown user
+realmChoice=Realm
oauthGrantTitle=OAuth Grant
oauthGrantTitleHtml=Accesso temporaneo per <strong>{0}</strong> richiesto da
errorTitle=Siamo spiacenti...
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
index 85b28fd..9329282 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
@@ -9,6 +9,7 @@ doNo=N\u00E3o
doContinue=Continue
doForgotPassword=Esqueceu sua senha?
doClickHere=Clique aqui
+doImpersonate=Impersonate
bypassKerberos=Your browser is not set up for Kerberos login. Please click continue to login in through other means
kerberosNotSetUp=Kerberos is not set up. You cannot login.
kerberosNotConfigured=Kerberos Not Configured
@@ -20,6 +21,10 @@ registerWithTitle=Registre-se com {0}
registerWithTitleHtml=Registre-se com <strong>{0}</strong>
loginTitle=Entrar em {0}
loginTitleHtml=Entrar em <strong>{0}</strong>
+impersonateTitle={0} Impersonate User
+impersonateTitleHtml=<strong>{0}</strong> Impersonate User</strong>
+unknownUser=Unknown user
+realmChoice=Realm
loginTotpTitle=Configura\u00E7\u00E3o do autenticador mobile
loginProfileTitle=Atualiza\u00E7\u00E3o das Informa\u00E7\u00F5es da Conta
oauthGrantTitle=Concess\u00E3o OAuth
diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
index 0b7e8f8..734c472 100755
--- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
@@ -1,6 +1,7 @@
package org.keycloak.migration.migrators;
import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.ImpersonationServiceConstants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
@@ -15,7 +16,6 @@ import java.util.List;
public class MigrateTo1_4_0 {
public static final ModelVersion VERSION = new ModelVersion("1.4.0");
-
public void migrate(KeycloakSession session) {
List<RealmModel> realms = session.realms().getRealms();
for (RealmModel realm : realms) {
@@ -23,6 +23,7 @@ public class MigrateTo1_4_0 {
DefaultAuthenticationFlows.addFlows(realm);
DefaultRequiredActions.addActions(realm);
}
+ ImpersonationServiceConstants.setupImpersonationService(session, realm, session.getContext().getContextPath());
}
diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index eef214f..a3104e1 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -8,6 +8,7 @@ public interface Constants {
String ADMIN_CONSOLE_CLIENT_ID = "security-admin-console";
String ACCOUNT_MANAGEMENT_CLIENT_ID = "account";
+ String IMPERSONATION_SERVICE_CLIENT_ID = "impersonation";
String BROKER_SERVICE_CLIENT_ID = "broker";
String REALM_MANAGEMENT_CLIENT_ID = "realm-management";
diff --git a/model/api/src/main/java/org/keycloak/models/ImpersonationServiceConstants.java b/model/api/src/main/java/org/keycloak/models/ImpersonationServiceConstants.java
new file mode 100755
index 0000000..3f9e6ae
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/ImpersonationServiceConstants.java
@@ -0,0 +1,59 @@
+package org.keycloak.models;
+
+import org.keycloak.Config;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ImpersonationServiceConstants {
+ public static String IMPERSONATION_ALLOWED = "impersonation";
+
+ public static void setupMasterRealmRole(RealmProvider model, RealmModel realm) {
+ RealmModel adminRealm;
+ RoleModel adminRole;
+
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ adminRealm = realm;
+ adminRole = realm.getRole(AdminRoles.ADMIN);
+ } else {
+ adminRealm = model.getRealmByName(Config.getAdminRealm());
+ adminRole = adminRealm.getRole(AdminRoles.ADMIN);
+ }
+ ClientModel realmAdminApp = adminRealm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(realm));
+ RoleModel impersonationRole = realmAdminApp.addRole(IMPERSONATION_ALLOWED);
+ impersonationRole.setDescription("${role_" + IMPERSONATION_ALLOWED + "}");
+ adminRole.addCompositeRole(impersonationRole);
+ }
+
+ public static void setupRealmRole(RealmModel realm) {
+ if (realm.getName().equals(Config.getAdminRealm())) { return; } // don't need to do this for master realm
+ String realmAdminApplicationClientId = Constants.REALM_MANAGEMENT_CLIENT_ID;
+ ClientModel realmAdminApp = realm.getClientByClientId(realmAdminApplicationClientId);
+ RoleModel impersonationRole = realmAdminApp.addRole(IMPERSONATION_ALLOWED);
+ impersonationRole.setDescription("${role_" + IMPERSONATION_ALLOWED + "}");
+ RoleModel adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
+ adminRole.addCompositeRole(impersonationRole);
+ }
+
+
+ public static void setupImpersonationService(KeycloakSession session, RealmModel realm, String contextPath) {
+ ClientModel client = realm.getClientNameMap().get(Constants.IMPERSONATION_SERVICE_CLIENT_ID);
+ if (client == null) {
+ client = KeycloakModelUtils.createClient(realm, Constants.IMPERSONATION_SERVICE_CLIENT_ID);
+ client.setName("${client_" + Constants.IMPERSONATION_SERVICE_CLIENT_ID + "}");
+ client.setEnabled(true);
+ client.setFullScopeAllowed(false);
+ String base = contextPath + "/realms/" + realm.getName() + "/impersonate";
+ String redirectUri = base + "/*";
+ client.addRedirectUri(redirectUri);
+ client.setBaseUrl(base);
+
+ setupMasterRealmRole(session.realms(), realm);
+ setupRealmRole(realm);
+ }
+ }
+
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
index 4d33403..ec280cf 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
@@ -10,6 +10,8 @@ import javax.ws.rs.core.UriInfo;
*/
public interface KeycloakContext {
+ String getContextPath();
+
UriInfo getUri();
HttpHeaders getRequestHeaders();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index bc1e1bd..16594fb 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -131,7 +131,7 @@ public class SamlService {
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, false);
if (authResult == null) {
logger.warn("Unknown saml response.");
event.event(EventType.LOGOUT);
@@ -354,7 +354,7 @@ public class SamlService {
}
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, false);
if (authResult != null) {
String logoutBinding = getBindingType();
if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING)))
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
index 7e68a02..bbeb73e 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
@@ -21,7 +21,7 @@ public class CookieAuthenticator implements Authenticator {
@Override
public void authenticate(AuthenticatorContext context) {
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(context.getSession(),
- context.getRealm(), context.getUriInfo(), context.getConnection(), context.getHttpRequest().getHttpHeaders(), true);
+ context.getRealm(), true);
if (authResult == null) {
context.attempted();
} else {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
index 25ccda5..e86895f 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
@@ -117,7 +117,7 @@ public class LogoutEndpoint {
}
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, false);
if (authResult != null) {
userSession = userSession != null ? userSession : authResult.getSession();
if (redirect != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI, redirect);
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
index 99849a9..f592f03 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -5,6 +5,7 @@ import org.keycloak.ClientConnection;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.RealmModel;
+import org.keycloak.services.resources.KeycloakApplication;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
@@ -21,6 +22,12 @@ public class DefaultKeycloakContext implements KeycloakContext {
private ClientConnection connection;
@Override
+ public String getContextPath() {
+ KeycloakApplication app = ResteasyProviderFactory.getContextData(KeycloakApplication.class);
+ return app.getContextPath();
+ }
+
+ @Override
public UriInfo getUri() {
return ResteasyProviderFactory.getContextData(UriInfo.class);
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index 9e0e4ab..457388b 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -18,12 +18,12 @@ public class AppAuthManager extends AuthenticationManager {
protected static Logger logger = Logger.getLogger(AppAuthManager.class);
@Override
- public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
- AuthResult authResult = super.authenticateIdentityCookie(session, realm, uriInfo, connection, headers);
+ public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm) {
+ AuthResult authResult = super.authenticateIdentityCookie(session, realm);
if (authResult == null) return null;
// refresh the cookies!
- createLoginCookie(realm, authResult.getUser(), authResult.getSession(), uriInfo, connection);
- if (authResult.getSession().isRememberMe()) createRememberMeCookie(realm, authResult.getUser().getUsername(), uriInfo, connection);
+ createLoginCookie(realm, authResult.getUser(), authResult.getSession(), session.getContext().getUri(), session.getContext().getConnection());
+ if (authResult.getSession().isRememberMe()) createRememberMeCookie(realm, authResult.getUser().getUsername(), session.getContext().getUri(), session.getContext().getConnection());
return authResult;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 9695a4e..0bb41c1 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -359,21 +359,21 @@ public class AuthenticationManager {
CookieHelper.addCookie(cookieName, "", path, null, "Expiring cookie", 0, secureOnly, httpOnly);
}
- public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
- return authenticateIdentityCookie(session, realm, uriInfo, connection, headers, true);
+ public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm) {
+ return authenticateIdentityCookie(session, realm, true);
}
- public static AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers, boolean checkActive) {
- Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE);
+ public static AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, boolean checkActive) {
+ Cookie cookie = session.getContext().getRequestHeaders().getCookies().get(KEYCLOAK_IDENTITY_COOKIE);
if (cookie == null || "".equals(cookie.getValue())) {
logger.debugv("Could not find cookie: {0}", KEYCLOAK_IDENTITY_COOKIE);
return null;
}
String tokenString = cookie.getValue();
- AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, checkActive, tokenString, headers);
+ AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, tokenString, session.getContext().getRequestHeaders());
if (authResult == null) {
- expireIdentityCookie(realm, uriInfo, connection);
+ expireIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
return null;
}
authResult.getSession().setLastSessionRefresh(Time.currentTime());
@@ -399,9 +399,9 @@ public class AuthenticationManager {
}
}
}
- if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN);
// refresh the cookies!
createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
+ if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN);
if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
protocol.setRealm(realm)
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 99b893c..b99c1c8 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -9,6 +9,7 @@ import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.Constants;
+import org.keycloak.models.ImpersonationServiceConstants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
@@ -88,6 +89,7 @@ public class RealmManager {
setupAccountManagement(realm);
setupBrokerService(realm);
setupAdminConsole(realm);
+ setupImpersonationService(realm);
setupAuthenticationFlows(realm);
setupRequiredActions(realm);
@@ -233,6 +235,10 @@ public class RealmManager {
}
}
+ public void setupImpersonationService(RealmModel realm) {
+ ImpersonationServiceConstants.setupImpersonationService(session, realm, contextPath);
+ }
+
public void setupBrokerService(RealmModel realm) {
ClientModel client = realm.getClientNameMap().get(Constants.BROKER_SERVICE_CLIENT_ID);
if (client == null) {
@@ -261,6 +267,8 @@ public class RealmManager {
setupMasterAdminManagement(realm);
if (!hasRealmAdminManagementClient(rep)) setupRealmAdminManagement(realm);
if (!hasAccountManagementClient(rep)) setupAccountManagement(realm);
+ if (!hasImpersonationServiceClient(rep)) setupImpersonationService(realm);
+
if (!hasBrokerClient(rep)) setupBrokerService(realm);
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
@@ -297,6 +305,15 @@ public class RealmManager {
}
return false;
}
+ private boolean hasImpersonationServiceClient(RealmRepresentation rep) {
+ if (rep.getClients() == null) return false;
+ for (ClientRepresentation clientRep : rep.getClients()) {
+ if (clientRep.getClientId().equals(Constants.IMPERSONATION_SERVICE_CLIENT_ID)) {
+ return true;
+ }
+ }
+ return false;
+ }
private boolean hasBrokerClient(RealmRepresentation rep) {
if (rep.getClients() == null) return false;
for (ClientRepresentation clientRep : rep.getClients()) {
diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java
new file mode 100755
index 0000000..6c7b5a0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java
@@ -0,0 +1,246 @@
+package org.keycloak.services.resources;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.BadRequestException;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.AbstractOAuthClient;
+import org.keycloak.ClientConnection;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.Urls;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.Auth;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.util.CookieHelper;
+import org.keycloak.util.UriUtils;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper class for securing local services. Provides login basics as well as CSRF check basics
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class AbstractSecuredLocalService {
+ private static final Logger logger = Logger.getLogger(AbstractSecuredLocalService.class);
+ protected final ClientModel client;
+ protected RealmModel realm;
+
+ @Context
+ protected UriInfo uriInfo;
+ @Context
+ protected HttpHeaders headers;
+ @Context
+ protected ClientConnection clientConnection;
+ protected String stateChecker;
+ @Context
+ protected KeycloakSession session;
+ @Context
+ protected HttpRequest request;
+ protected Auth auth;
+
+ public AbstractSecuredLocalService(RealmModel realm, ClientModel client) {
+ this.realm = realm;
+ this.client = client;
+ }
+
+ @Path("login-redirect")
+ @GET
+ public Response loginRedirect(@QueryParam("code") String code,
+ @QueryParam("state") String state,
+ @QueryParam("error") String error,
+ @QueryParam("path") String path,
+ @QueryParam("referrer") String referrer,
+ @Context HttpHeaders headers) {
+ try {
+ if (error != null) {
+ logger.debug("error from oauth");
+ throw new ForbiddenException("error");
+ }
+ if (path != null && !getValidPaths().contains(path)) {
+ throw new BadRequestException("Invalid path");
+ }
+ if (!realm.isEnabled()) {
+ logger.debug("realm not enabled");
+ throw new ForbiddenException();
+ }
+ if (!client.isEnabled()) {
+ logger.debug("account management app not enabled");
+ throw new ForbiddenException();
+ }
+ if (code == null) {
+ logger.debug("code not specified");
+ throw new BadRequestException("code not specified");
+ }
+ if (state == null) {
+ logger.debug("state not specified");
+ throw new BadRequestException("state not specified");
+ }
+
+ URI uri = getBaseRedirectUri();
+ URI redirectUri = path != null ? uri.resolve(path) : uri;
+ if (referrer != null) {
+ redirectUri = redirectUri.resolve("?referrer=" + referrer);
+ }
+
+ return Response.status(302).location(redirectUri).build();
+ } finally {
+ }
+ }
+
+ protected void updateCsrfChecks() {
+ Cookie cookie = headers.getCookies().get(AccountService.KEYCLOAK_STATE_CHECKER);
+ if (cookie != null) {
+ stateChecker = cookie.getValue();
+ } else {
+ stateChecker = UUID.randomUUID().toString();
+ String cookiePath = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
+ boolean secureOnly = realm.getSslRequired().isRequired(clientConnection);
+ CookieHelper.addCookie(AccountService.KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
+ }
+ }
+
+ protected abstract Set<String> getValidPaths();
+
+ /**
+ * Check to see if form post has sessionId hidden field and match it against the session id.
+ *
+ * @param formData
+ */
+ protected void csrfCheck(final MultivaluedMap<String, String> formData) {
+ if (!auth.isCookieAuthenticated()) return;
+ String stateChecker = formData.getFirst("stateChecker");
+ if (!this.stateChecker.equals(stateChecker)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+ /**
+ * Check to see if form post has sessionId hidden field and match it against the session id.
+ *
+ */
+ protected void csrfCheck(String stateChecker) {
+ if (!auth.isCookieAuthenticated()) return;
+ if (auth.getSession() == null) return;
+ if (!this.stateChecker.equals(stateChecker)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+ protected abstract URI getBaseRedirectUri();
+
+ protected Response login(String path) {
+ OAuthRedirect oauth = new OAuthRedirect();
+ String authUrl = OIDCLoginProtocolService.authUrl(uriInfo).build(realm.getName()).toString();
+ oauth.setAuthUrl(authUrl);
+
+ oauth.setClientId(client.getClientId());
+
+ UriBuilder uriBuilder = UriBuilder.fromUri(getBaseRedirectUri()).path("login-redirect");
+
+ if (path != null) {
+ uriBuilder.queryParam("path", path);
+ }
+
+ String referrer = uriInfo.getQueryParameters().getFirst("referrer");
+ if (referrer != null) {
+ uriBuilder.queryParam("referrer", referrer);
+ }
+
+ String referrerUri = uriInfo.getQueryParameters().getFirst("referrer_uri");
+ if (referrerUri != null) {
+ uriBuilder.queryParam("referrer_uri", referrerUri);
+ }
+
+ URI accountUri = uriBuilder.build(realm.getName());
+
+ oauth.setStateCookiePath(accountUri.getRawPath());
+ return oauth.redirect(uriInfo, accountUri.toString());
+ }
+
+ protected Response authenticateBrowser() {
+ AppAuthManager authManager = new AppAuthManager();
+ AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm);
+ if (authResult != null) {
+ auth = new Auth(realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), true);
+ } else {
+ return login(null);
+ }
+ // don't allow cors requests
+ // This is to prevent CSRF attacks.
+ String requestOrigin = UriUtils.getOrigin(uriInfo.getBaseUri());
+ String origin = headers.getRequestHeaders().getFirst("Origin");
+ if (origin != null && !requestOrigin.equals(origin)) {
+ throw new ForbiddenException();
+ }
+
+ if (!request.getHttpMethod().equals("GET")) {
+ String referrer = headers.getRequestHeaders().getFirst("Referer");
+ if (referrer != null && !requestOrigin.equals(UriUtils.getOrigin(referrer))) {
+ throw new ForbiddenException();
+ }
+ }
+ updateCsrfChecks();
+ return null;
+ }
+
+ static class OAuthRedirect extends AbstractOAuthClient {
+
+ /**
+ * closes client
+ */
+ public void stop() {
+ }
+
+ public Response redirect(UriInfo uriInfo, String redirectUri) {
+ String state = getStateCode();
+
+ UriBuilder uriBuilder = UriBuilder.fromUri(authUrl)
+ .queryParam(OAuth2Constants.CLIENT_ID, clientId)
+ .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
+ .queryParam(OAuth2Constants.STATE, state)
+ .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE);
+ if (scope != null) {
+ uriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
+ }
+
+ URI url = uriBuilder.build();
+
+ // todo httpOnly!
+ NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure);
+ logger.debug("NewCookie: " + cookie.toString());
+ logger.debug("Oauth Redirect to: " + url);
+ return Response.status(302)
+ .location(url)
+ .cookie(cookie).build();
+ }
+
+ private String getStateCookiePath(UriInfo uriInfo) {
+ if (stateCookiePath != null) return stateCookiePath;
+ return uriInfo.getBaseUri().getRawPath();
+ }
+
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 02762af..05f8c81 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -98,7 +98,7 @@ import java.util.UUID;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
-public class AccountService {
+public class AccountService extends AbstractSecuredLocalService {
private static final Logger logger = Logger.getLogger(AccountService.class);
@@ -128,34 +128,13 @@ public class AccountService {
public static final String KEYCLOAK_STATE_CHECKER = "KEYCLOAK_STATE_CHECKER";
- private RealmModel realm;
-
- @Context
- private HttpRequest request;
-
- @Context
- protected HttpHeaders headers;
-
- @Context
- private UriInfo uriInfo;
-
- @Context
- private ClientConnection clientConnection;
-
- @Context
- private KeycloakSession session;
-
private final AppAuthManager authManager;
- private final ClientModel client;
private EventBuilder event;
private AccountProvider account;
- private Auth auth;
private EventStoreProvider eventStore;
- private String stateChecker;
public AccountService(RealmModel realm, ClientModel client, EventBuilder event) {
- this.realm = realm;
- this.client = client;
+ super(realm, client);
this.event = event;
this.authManager = new AppAuthManager();
}
@@ -169,18 +148,10 @@ public class AccountService {
if (authResult != null) {
auth = new Auth(realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), false);
} else {
- authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers);
+ authResult = authManager.authenticateIdentityCookie(session, realm);
if (authResult != null) {
auth = new Auth(realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), true);
- Cookie cookie = headers.getCookies().get(KEYCLOAK_STATE_CHECKER);
- if (cookie != null) {
- stateChecker = cookie.getValue();
- } else {
- stateChecker = UUID.randomUUID().toString();
- String cookiePath = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
- boolean secureOnly = realm.getSslRequired().isRequired(clientConnection);
- CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
- }
+ updateCsrfChecks();
account.setStateChecker(stateChecker);
}
}
@@ -236,10 +207,18 @@ public class AccountService {
return base;
}
+ public static UriBuilder accountServiceApplicationPage(UriInfo uriInfo) {
+ return accountServiceBaseUrl(uriInfo).path(AccountService.class, "applicationsPage");
+ }
+
public static UriBuilder accountServiceBaseUrl(UriBuilder base) {
return base.path(RealmsResource.class).path(RealmsResource.class, "getAccountService");
}
+ protected Set<String> getValidPaths() {
+ return AccountService.VALID_PATHS;
+ }
+
private Response forwardToPage(String path, AccountPages page) {
if (auth != null) {
try {
@@ -368,33 +347,6 @@ public class AccountService {
}
/**
- * Check to see if form post has sessionId hidden field and match it against the session id.
- *
- * @param formData
- */
- protected void csrfCheck(final MultivaluedMap<String, String> formData) {
- if (!auth.isCookieAuthenticated()) return;
- String stateChecker = formData.getFirst("stateChecker");
- if (!this.stateChecker.equals(stateChecker)) {
- throw new ForbiddenException();
- }
-
- }
-
- /**
- * Check to see if form post has sessionId hidden field and match it against the session id.
- *
- */
- protected void csrfCheck(String stateChecker) {
- if (!auth.isCookieAuthenticated()) return;
- if (auth.getSession() == null) return;
- if (!this.stateChecker.equals(stateChecker)) {
- throw new ForbiddenException();
- }
-
- }
-
- /**
* Update account information.
*
* Form params:
@@ -799,77 +751,9 @@ public class AccountService {
return RealmsResource.accountUrl(base).path(AccountService.class, "loginRedirect");
}
- @Path("login-redirect")
- @GET
- public Response loginRedirect(@QueryParam("code") String code,
- @QueryParam("state") String state,
- @QueryParam("error") String error,
- @QueryParam("path") String path,
- @QueryParam("referrer") String referrer,
- @Context HttpHeaders headers) {
- try {
- if (error != null) {
- logger.debug("error from oauth");
- throw new ForbiddenException("error");
- }
- if (path != null && !VALID_PATHS.contains(path)) {
- throw new BadRequestException("Invalid path");
- }
- if (!realm.isEnabled()) {
- logger.debug("realm not enabled");
- throw new ForbiddenException();
- }
- if (!client.isEnabled()) {
- logger.debug("account management app not enabled");
- throw new ForbiddenException();
- }
- if (code == null) {
- logger.debug("code not specified");
- throw new BadRequestException("code not specified");
- }
- if (state == null) {
- logger.debug("state not specified");
- throw new BadRequestException("state not specified");
- }
-
- URI accountUri = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName());
- URI redirectUri = path != null ? accountUri.resolve(path) : accountUri;
- if (referrer != null) {
- redirectUri = redirectUri.resolve("?referrer=" + referrer);
- }
-
- return Response.status(302).location(redirectUri).build();
- } finally {
- }
- }
-
- private Response login(String path) {
- OAuthRedirect oauth = new OAuthRedirect();
- String authUrl = OIDCLoginProtocolService.authUrl(uriInfo).build(realm.getName()).toString();
- oauth.setAuthUrl(authUrl);
-
- oauth.setClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
-
- UriBuilder uriBuilder = Urls.accountPageBuilder(uriInfo.getBaseUri()).path(AccountService.class, "loginRedirect");
-
- if (path != null) {
- uriBuilder.queryParam("path", path);
- }
-
- String referrer = uriInfo.getQueryParameters().getFirst("referrer");
- if (referrer != null) {
- uriBuilder.queryParam("referrer", referrer);
- }
-
- String referrerUri = uriInfo.getQueryParameters().getFirst("referrer_uri");
- if (referrerUri != null) {
- uriBuilder.queryParam("referrer_uri", referrerUri);
- }
-
- URI accountUri = uriBuilder.build(realm.getName());
-
- oauth.setStateCookiePath(accountUri.getRawPath());
- return oauth.redirect(uriInfo, accountUri.toString());
+ @Override
+ protected URI getBaseRedirectUri() {
+ return Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName());
}
public static boolean isPasswordSet(UserModel user) {
@@ -954,43 +838,4 @@ public class AccountService {
}
}
}
-
- class OAuthRedirect extends AbstractOAuthClient {
-
- /**
- * closes client
- */
- public void stop() {
- }
-
- public Response redirect(UriInfo uriInfo, String redirectUri) {
- String state = getStateCode();
-
- UriBuilder uriBuilder = UriBuilder.fromUri(authUrl)
- .queryParam(OAuth2Constants.CLIENT_ID, clientId)
- .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
- .queryParam(OAuth2Constants.STATE, state)
- .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE);
- if (scope != null) {
- uriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
- }
-
- URI url = uriBuilder.build();
-
- // todo httpOnly!
- NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure);
- logger.debug("NewCookie: " + cookie.toString());
- logger.debug("Oauth Redirect to: " + url);
- return Response.status(302)
- .location(url)
- .cookie(cookie).build();
- }
-
- private String getStateCookiePath(UriInfo uriInfo) {
- if (stateCookiePath != null) return stateCookiePath;
- return uriInfo.getBaseUri().getRawPath();
- }
-
- }
-
}
diff --git a/services/src/main/java/org/keycloak/services/resources/ImpersonationService.java b/services/src/main/java/org/keycloak/services/resources/ImpersonationService.java
new file mode 100755
index 0000000..2c14691
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/ImpersonationService.java
@@ -0,0 +1,177 @@
+package org.keycloak.services.resources;
+
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.Config;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.ImpersonationServiceConstants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.ErrorPage;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.Urls;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.messages.Messages;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ImpersonationService extends AbstractSecuredLocalService {
+
+ public static final String UNKNOWN_USER_MESSAGE = "unknownUser";
+ private EventBuilder event;
+
+ public ImpersonationService(RealmModel realm, ClientModel client, EventBuilder event) {
+ super(realm, client);
+ this.event = event;
+ }
+
+ private static Set<String> VALID_PATHS = new HashSet<String>();
+
+ static {
+ }
+
+ @Override
+ protected Set<String> getValidPaths() {
+ return VALID_PATHS;
+ }
+
+ @Override
+ protected URI getBaseRedirectUri() {
+ return Urls.realmBase(uriInfo.getBaseUri()).path(RealmsResource.class, "getImpersonationService").build(realm.getName());
+ }
+
+ @GET
+ public Response impersonatePage() {
+ Response challenge = authenticateBrowser();
+ if (challenge != null) return challenge;
+ LoginFormsProvider page = page();
+ return renderPage(page);
+ }
+
+ protected LoginFormsProvider page() {
+ UserModel user = auth.getUser();
+ LoginFormsProvider page = session.getProvider(LoginFormsProvider.class)
+ .setActionUri(getBaseRedirectUri())
+ .setAttribute("stateChecker", stateChecker);
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ List<String> realms = new LinkedList<>();
+ for (RealmModel possibleRealm : session.realms().getRealms()) {
+ ClientModel realmAdminApp = realm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(possibleRealm));
+ RoleModel role = realmAdminApp.getRole(ImpersonationServiceConstants.IMPERSONATION_ALLOWED);
+ if (user.hasRole(role)) {
+ realms.add(possibleRealm.getName());
+ }
+ }
+ if (realms.isEmpty()) {
+ throw new ForbiddenException("not authorized to access impersonation", ErrorPage.error(session, Messages.NO_ACCESS));
+ }
+ if (realms.size() > 1 || !realms.get(0).equals(realm.getName())) {
+ page.setAttribute("realmList", realms);
+ }
+ } else {
+ authorizeCurrentRealm();
+ } return page;
+ }
+
+ protected Response renderPage(LoginFormsProvider page) {
+ return page
+ .createForm("impersonate.ftl", new HashMap<String, Object>());
+ }
+
+ protected void authorizeMaster(String realmName) {
+ RealmModel possibleRealm = session.realms().getRealmByName(realmName);
+ if (possibleRealm == null) {
+ throw new NotFoundException("Could not find realm");
+ }
+ ClientModel realmAdminApp = realm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(possibleRealm));
+ RoleModel role = realmAdminApp.getRole(ImpersonationServiceConstants.IMPERSONATION_ALLOWED);
+ if (!auth.getUser().hasRole(role)) {
+ throw new ForbiddenException("not authorized to access impersonation", ErrorPage.error(session, Messages.NO_ACCESS));
+ }
+ }
+
+ private void authorizeCurrentRealm() {
+ UserModel user = auth.getUser();
+ String realmAdminApplicationClientId = Constants.REALM_MANAGEMENT_CLIENT_ID;
+ ClientModel realmAdminApp = realm.getClientByClientId(realmAdminApplicationClientId);
+ RoleModel role = realmAdminApp.getRole(ImpersonationServiceConstants.IMPERSONATION_ALLOWED);
+ if (!user.hasRole(role)) {
+ throw new ForbiddenException("not authorized to access impersonation", ErrorPage.error(session, Messages.NO_ACCESS));
+ }
+ }
+
+ @POST
+ public Response impersonate() {
+ Response challenge = authenticateBrowser();
+ if (challenge != null) return challenge;
+ MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
+ String realmName = formData.getFirst("realm");
+ RealmModel chosenRealm = null;
+ if (realmName == null) {
+ chosenRealm = realm;
+ } else{
+ chosenRealm = session.realms().getRealmByName(realmName);
+ if (chosenRealm == null) {
+ throw new NotFoundException("Could not find realm");
+ }
+ }
+
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ authorizeMaster(chosenRealm.getName());
+ } else {
+ if (realmName == null) authorizeCurrentRealm();
+ else {
+ throw new ForbiddenException("not authorized to access impersonation", ErrorPage.error(session, Messages.NO_ACCESS));
+ }
+ }
+
+ csrfCheck(formData);
+
+ if (formData.containsKey("cancel")) {
+ return renderPage(page());
+ }
+ String username = formData.getFirst(AuthenticationManager.FORM_USERNAME);
+ if (username == null) {
+ return renderPage(
+ page().setError(UNKNOWN_USER_MESSAGE)
+ );
+ }
+ UserModel user = session.users().getUserByUsername(username, chosenRealm);
+ if (user == null) {
+ user = session.users().getUserByEmail(username, chosenRealm);
+ }
+ if (user == null) {
+ return renderPage(
+ page().setError(UNKNOWN_USER_MESSAGE)
+ );
+ }
+ // if same realm logout before impersonation
+ if (chosenRealm.getId().equals(realm.getId())) {
+ AuthenticationManager.backchannelLogout(session, realm, auth.getSession(), uriInfo, clientConnection, headers, true);
+ }
+ UserSessionModel userSession = session.sessions().createUserSession(chosenRealm, user, username, clientConnection.getRemoteAddr(), "impersonate", false, null, null);
+ AuthenticationManager.createLoginCookie(chosenRealm, userSession.getUser(), userSession, uriInfo, clientConnection);
+ URI redirect = AccountService.accountServiceApplicationPage(uriInfo).build(chosenRealm.getName());
+ return Response.status(302).location(redirect).build();
+
+
+ }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 0e32fe8..0601882 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -61,13 +61,14 @@ public class KeycloakApplication extends Application {
public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
loadConfig();
+ this.contextPath = context.getContextPath();
this.sessionFactory = createSessionFactory();
dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
- this.contextPath = context.getContextPath();
BruteForceProtector protector = new BruteForceProtector(sessionFactory);
dispatcher.getDefaultContextObjects().put(BruteForceProtector.class, protector);
ResteasyProviderFactory.pushContext(BruteForceProtector.class, protector); // for injection
+ ResteasyProviderFactory.pushContext(KeycloakApplication.class, this); // for injection
protector.start();
context.setAttribute(BruteForceProtector.class.getName(), protector);
context.setAttribute(KeycloakSessionFactory.class.getName(), this.sessionFactory);
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index 656c4dc..a5fe083 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -4,7 +4,6 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ClientConnection;
-import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
@@ -149,6 +148,22 @@ public class RealmsResource {
return accountService;
}
+ @Path("{realm}/impersonate")
+ public ImpersonationService getImpersonationService(final @PathParam("realm") String name) {
+ RealmModel realm = init(name);
+
+ ClientModel client = realm.getClientNameMap().get(Constants.IMPERSONATION_SERVICE_CLIENT_ID);
+ if (client == null || !client.isEnabled()) {
+ logger.debug("impersonate service not enabled");
+ throw new NotFoundException("impersonate service not enabled");
+ }
+
+ EventBuilder event = new EventBuilder(realm, session, clientConnection);
+ ImpersonationService impersonateService = new ImpersonationService(realm, client, event);
+ ResteasyProviderFactory.getInstance().injectProperties(impersonateService);
+ return impersonateService;
+ }
+
@Path("{realm}")
public PublicRealmResource getRealmResource(final @PathParam("realm") String name) {
RealmModel realm = init(name);
diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index 2934375..5254993 100755
--- a/services/src/main/java/org/keycloak/services/Urls.java
+++ b/services/src/main/java/org/keycloak/services/Urls.java
@@ -172,7 +172,7 @@ public class Urls {
return realmBase(baseUri).path("{realm}").build(realmId).toString();
}
- private static UriBuilder realmBase(URI baseUri) {
+ public static UriBuilder realmBase(URI baseUri) {
return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
}