/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.http.servlet.peoplesearch;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import password.pwm.bean.UserIdentity;
import password.pwm.config.FormConfiguration;
import password.pwm.config.PwmSetting;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmException;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.http.HttpMethod;
import password.pwm.http.JspUrl;
import password.pwm.http.ProcessStatus;
import password.pwm.http.PwmHttpRequestWrapper;
import password.pwm.http.PwmRequest;
import password.pwm.http.PwmRequestFlag;
import password.pwm.http.servlet.ControlledPwmServlet;
import password.pwm.svc.stats.Statistic;
import password.pwm.svc.stats.StatisticsManager;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.logging.PwmLogger;
import password.pwm.ws.server.RestResultBean;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public abstract class PeopleSearchServlet extends ControlledPwmServlet {
private static final PwmLogger LOGGER = PwmLogger.forClass(PeopleSearchServlet.class);
private static final String PARAM_USERKEY = "userKey";
public enum PeopleSearchActions implements ProcessAction {
search(HttpMethod.GET),
detail(HttpMethod.GET),
photo(HttpMethod.GET),
clientData(HttpMethod.GET),
orgChartData(HttpMethod.GET),
;
private final HttpMethod method;
PeopleSearchActions(final HttpMethod method) {
this.method = method;
}
public Collection<HttpMethod> permittedMethods() {
return Collections.singletonList(method);
}
}
@Override
public Class<? extends ProcessAction> getProcessActionsClass() {
return PeopleSearchActions.class;
}
@Override
protected void nextStep(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException {
if (pwmRequest.getURL().isPublicUrl()) {
pwmRequest.setFlag(PwmRequestFlag.HIDE_IDLE, true);
pwmRequest.setFlag(PwmRequestFlag.NO_IDLE_TIMEOUT, true);
}
pwmRequest.forwardToJsp(JspUrl.PEOPLE_SEARCH);
}
@Override
public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
if (!pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_ENABLE)) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE));
}
return ProcessStatus.Continue;
}
@ActionHandler(action = "clientData")
private ProcessStatus restLoadClientData(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final PeopleSearchConfiguration peopleSearchConfiguration = new PeopleSearchConfiguration(pwmRequest.getConfig());
final Map<String, String> searchColumns = new LinkedHashMap<>();
final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_RESULT_FORM);
for (final FormConfiguration formConfiguration : searchForm) {
searchColumns.put(formConfiguration.getName(),
formConfiguration.getLabel(pwmRequest.getLocale()));
}
final PeopleSearchClientConfigBean peopleSearchClientConfigBean = new PeopleSearchClientConfigBean();
peopleSearchClientConfigBean.setPeoplesearch_search_columns(searchColumns);
peopleSearchClientConfigBean.setPeoplesearch_enablePhoto(peopleSearchConfiguration.isPhotosEnabled());
peopleSearchClientConfigBean.setPeoplesearch_orgChartEnabled(peopleSearchConfiguration.isOrgChartEnabled());
final RestResultBean restResultBean = new RestResultBean(peopleSearchClientConfigBean);
LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
pwmRequest.outputJsonResult(restResultBean);
return ProcessStatus.Halt;
}
@ActionHandler(action = "search")
private ProcessStatus restSearchRequest(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final String username = pwmRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
final boolean includeDisplayName = pwmRequest.readParameterAsBoolean("includeDisplayName");
final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader(pwmRequest);
final SearchResultBean searchResultBean = peopleSearchDataReader.makeSearchResultBean(username, includeDisplayName);
final RestResultBean restResultBean = new RestResultBean(searchResultBean);
addExpiresHeadersToResponse(pwmRequest);
pwmRequest.outputJsonResult(restResultBean);
LOGGER.trace(pwmRequest, "returning " + searchResultBean.getSearchResults().size() + " results for search request '" + username + "'");
return ProcessStatus.Halt;
}
@ActionHandler(action = "orgChartData")
private ProcessStatus restOrgChartData(
final PwmRequest pwmRequest
)
throws IOException, PwmUnrecoverableException, ServletException
{
final PeopleSearchConfiguration peopleSearchConfiguration = new PeopleSearchConfiguration(pwmRequest.getConfig());
if (!peopleSearchConfiguration.isOrgChartEnabled()) {
throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);
}
final UserIdentity userIdentity;
{
final String userKey = pwmRequest.readParameterAsString(PARAM_USERKEY, PwmHttpRequestWrapper.Flag.BypassValidation);
if (userKey == null || userKey.isEmpty()) {
userIdentity = pwmRequest.getUserInfoIfLoggedIn();
if (userIdentity == null) {
return ProcessStatus.Halt;
}
} else {
userIdentity = UserIdentity.fromObfuscatedKey(userKey, pwmRequest.getPwmApplication());
}
}
final boolean noChildren = pwmRequest.readParameterAsBoolean("noChildren");
try {
final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader(pwmRequest);
final OrgChartDataBean orgChartData = peopleSearchDataReader.makeOrgChartData(userIdentity, noChildren);
addExpiresHeadersToResponse(pwmRequest);
pwmRequest.outputJsonResult(new RestResultBean(orgChartData));
StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_ORGCHART);
} catch (PwmException e) {
LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
pwmRequest.respondWithError(e.getErrorInformation());
}
return ProcessStatus.Halt;
}
@ActionHandler(action = "detail")
private ProcessStatus restUserDetailRequest(
final PwmRequest pwmRequest
)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final String userKey = pwmRequest.readParameterAsString(PARAM_USERKEY, PwmHttpRequestWrapper.Flag.BypassValidation);
if (userKey == null || userKey.isEmpty()) {
return ProcessStatus.Halt;
}
try {
final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader(pwmRequest);
final UserDetailBean detailData = peopleSearchDataReader.makeUserDetailRequest(userKey);
addExpiresHeadersToResponse(pwmRequest);
pwmRequest.outputJsonResult(new RestResultBean(detailData));
pwmRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PEOPLESEARCH_DETAILS);
} catch (PwmOperationalException e) {
LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
pwmRequest.respondWithError(e.getErrorInformation());
}
return ProcessStatus.Halt;
}
@ActionHandler(action = "photo")
private ProcessStatus processUserPhotoImageRequest(final PwmRequest pwmRequest)
throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
{
final String userKey = pwmRequest.readParameterAsString(PARAM_USERKEY, PwmHttpRequestWrapper.Flag.BypassValidation);
if (userKey.length() < 1) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, PARAM_USERKEY + " parameter is missing");
LOGGER.error(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader(pwmRequest);
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
try {
peopleSearchDataReader.checkIfUserIdentityViewable(userIdentity);
} catch (PwmOperationalException e) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "error during photo request while checking if requested userIdentity is within search scope: " + e.getMessage());
LOGGER.error(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
LOGGER.debug(pwmRequest, "received user photo request to view user " + userIdentity.toString());
final PhotoDataBean photoData;
try {
photoData = peopleSearchDataReader.readPhotoDataFromLdap(userIdentity);
} catch (PwmOperationalException e) {
final ErrorInformation errorInformation = e.getErrorInformation();
LOGGER.error(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
addExpiresHeadersToResponse(pwmRequest);
try (OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream()) {
final HttpServletResponse resp = pwmRequest.getPwmResponse().getHttpServletResponse();
resp.setContentType(photoData.getMimeType());
outputStream.write(photoData.getContents());
}
return ProcessStatus.Halt;
}
private void addExpiresHeadersToResponse(final PwmRequest pwmRequest) {
final long maxCacheSeconds = pwmRequest.getConfig().readSettingAsLong(PwmSetting.PEOPLE_SEARCH_MAX_CACHE_SECONDS);
final HttpServletResponse resp = pwmRequest.getPwmResponse().getHttpServletResponse();
resp.setDateHeader("Expires", System.currentTimeMillis() + (maxCacheSeconds * 1000L));
resp.setHeader("Cache-Control", "private, max-age=" + maxCacheSeconds);
}
}