impersonation redo in admin console

This commit is contained in:
Bill Burke committed 2015-07-11 10:10:05 -04:00
1 parent ef255c6a5c
commit 608701aea7
17 files changed
+114 -278

No files matched your search

@@ -9,7 +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.ImpersonationConstants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
@@ -236,7 +236,7 @@ public class RealmManager {
}
public void setupImpersonationService(RealmModel realm) {
ImpersonationServiceConstants.setupImpersonationService(session, realm, contextPath);
ImpersonationConstants.setupImpersonationService(session, realm);
}
public void setupBrokerService(RealmModel realm) {
@@ -1,177 +0,0 @@
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();
}
}
@@ -148,22 +148,6 @@ 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);
@@ -56,7 +56,7 @@ public class AuthenticationManagementResource {
this.realm = realm;
this.session = session;
this.auth = auth;
this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
this.auth.init(RealmAuth.Resource.REALM);
this.adminEvent = adminEvent;
}
@@ -140,6 +140,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<AuthenticationFlowModel> getFlows() {
this.auth.requireView();
List<AuthenticationFlowModel> flows = new LinkedList<>();
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
if (flow.isTopLevel()) {
@@ -265,6 +266,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigModel getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id) {
this.auth.requireView();
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -328,6 +330,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RequiredActionProviderRepresentation> getRequiredActions() {
this.auth.requireView();
List<RequiredActionProviderRepresentation> list = new LinkedList<>();
for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
RequiredActionProviderRepresentation rep = toRepresentation(model);
@@ -351,6 +354,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) {
this.auth.requireView();
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action: " + alias);
@@ -363,6 +367,7 @@ public class AuthenticationManagementResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateRequiredAction(@PathParam("alias") String alias, RequiredActionProviderRepresentation rep) {
this.auth.requireManage();
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action: " + alias);
@@ -381,6 +386,7 @@ public class AuthenticationManagementResource {
@Path("required-actions/{alias}")
@DELETE
public void updateRequiredAction(@PathParam("alias") String alias) {
this.auth.requireManage();
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action: " + alias);
@@ -434,6 +440,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigDescription getAuthenticatorConfigDescription(@PathParam("providerId") String providerId) {
this.auth.requireView();
ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(providerId);
if (factory == null) {
throw new NotFoundException("Could not find authenticator provider");
@@ -460,6 +467,7 @@ public class AuthenticationManagementResource {
@POST
@NoCache
public Response createAuthenticatorConfig(AuthenticatorConfigModel config) {
this.auth.requireManage();
config = realm.addAuthenticatorConfig(config);
return Response.created(uriInfo.getAbsolutePathBuilder().path(config.getId()).build()).build();
}
@@ -469,6 +477,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigModel getAuthenticatorConfig(@PathParam("id") String id) {
this.auth.requireView();
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -480,6 +489,7 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void removeAuthenticatorConfig(@PathParam("id") String id) {
this.auth.requireManage();
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -502,6 +512,7 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public void updateAuthenticatorConfig(@PathParam("id") String id, AuthenticatorConfigModel config) {
this.auth.requireManage();
AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id);
if (exists == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -2,6 +2,7 @@ package org.keycloak.services.resources.admin;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ImpersonationConstants;
import org.keycloak.services.ForbiddenException;
@@ -13,7 +14,7 @@ public class RealmAuth {
private Resource resource;
public enum Resource {
CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER
CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION
}
private AdminAuth auth;
@@ -29,6 +30,10 @@ public class RealmAuth {
return this;
}
public AdminAuth getAuth() {
return auth;
}
public void requireAny() {
if (!auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) {
throw new ForbiddenException();
@@ -84,6 +89,8 @@ public class RealmAuth {
return AdminRoles.MANAGE_EVENTS;
case IDENTITY_PROVIDER:
return AdminRoles.MANAGE_IDENTITY_PROVIDERS;
case IMPERSONATION:
return ImpersonationConstants.IMPERSONATION_ROLE;
default:
throw new IllegalStateException();
}
@@ -61,6 +61,7 @@ import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.WebApplicationException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -72,6 +73,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.resources.AccountService;
/**
* Base resource for managing users
@@ -276,6 +278,37 @@ public class UsersResource {
return rep;
}
@Path("{id}/impersonation")
@POST
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> impersonate(final @PathParam("id") String id) {
auth.init(RealmAuth.Resource.IMPERSONATION);
auth.requireManage();
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
RealmModel authenticatedRealm = auth.getAuth().getRealm();
// if same realm logout before impersonation
boolean sameRealm = false;
if (authenticatedRealm.getId().equals(realm.getId())) {
sameRealm = true;
UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, auth.getAuth().getToken().getSessionState());
AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection);
AuthenticationManager.expireRememberMeCookie(realm, uriInfo, clientConnection);
AuthenticationManager.backchannelLogout(session, authenticatedRealm, userSession, uriInfo, clientConnection, headers, true);
}
UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
AuthenticationManager.createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
URI redirect = AccountService.accountServiceApplicationPage(uriInfo).build(realm.getName());
Map<String, Object> result = new HashMap<>();
result.put("sameRealm", sameRealm);
result.put("redirect", redirect.toString());
return result;
}
/**
* List set of sessions associated with this user.
*