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();
}
}