/*
* 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.ws.server;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import password.pwm.AppProperty;
import password.pwm.Permission;
import password.pwm.PwmApplication;
import password.pwm.PwmApplicationMode;
import password.pwm.PwmConstants;
import password.pwm.bean.UserIdentity;
import password.pwm.config.PwmSetting;
import password.pwm.config.profile.HelpdeskProfile;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.http.PwmRequest;
import password.pwm.http.PwmSession;
import password.pwm.http.filter.AuthenticationFilter;
import password.pwm.ldap.search.UserSearchEngine;
import password.pwm.svc.intruder.RecordType;
import password.pwm.util.LocaleHelper;
import password.pwm.util.PasswordData;
import password.pwm.util.logging.PwmLogger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
public abstract class RestServerHelper {
private static final PwmLogger LOGGER = PwmLogger.forClass(RestServerHelper.class);
public static javax.ws.rs.core.Response doHtmlRedirect() throws URISyntaxException {
final URI uri = javax.ws.rs.core.UriBuilder.fromUri("../reference/rest.jsp?forwardedFromRestServer=true").build();
return javax.ws.rs.core.Response.temporaryRedirect(uri).build();
}
public static RestRequestBean initializeRestRequest(
final HttpServletRequest request,
final HttpServletResponse response,
final ServicePermissions servicePermissions,
final String requestedUsername
)
throws PwmUnrecoverableException
{
final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
final PwmSession pwmSession = pwmRequest.getPwmSession();
if (pwmSession.getSessionStateBean().getLocale() == null) {
final List<Locale> knownLocales = pwmApplication.getConfig().getKnownLocales();
final Locale userLocale = LocaleHelper.localeResolver(request.getLocale(), knownLocales);
pwmSession.getSessionStateBean().setLocale(userLocale == null ? PwmConstants.DEFAULT_LOCALE : userLocale);
}
pwmRequest.debugHttpRequestToLog("[REST WebService Request]");
try {
handleAuthentication(request,response,pwmApplication,pwmSession);
} catch (ChaiUnavailableException e) {
throw new PwmUnrecoverableException(PwmError.ERROR_DIRECTORY_UNAVAILABLE);
}
final RestRequestBean restRequestBean = new RestRequestBean();
restRequestBean.setAuthenticated(pwmSession.isAuthenticated());
restRequestBean.setExternal(determineIfRestClientIsExternal(pwmSession,request));
restRequestBean.setUserIdentity(
lookupUsername(pwmApplication, pwmSession, restRequestBean.isExternal(), servicePermissions, requestedUsername));
restRequestBean.setPwmApplication(pwmApplication);
restRequestBean.setPwmSession(pwmSession);
if (servicePermissions.isPublicDuringConfig()) {
if (pwmApplication.getApplicationMode() == PwmApplicationMode.NEW || pwmApplication.getApplicationMode() == PwmApplicationMode.CONFIGURATION) {
return restRequestBean;
}
}
// check permissions
final boolean authenticated = pwmSession.isAuthenticated();
if (servicePermissions.isAuthRequired()) {
if (!authenticated) {
throw new PwmUnrecoverableException(PwmError.ERROR_AUTHENTICATION_REQUIRED);
}
}
if (restRequestBean.isExternal()) {
if (servicePermissions.isBlockExternal()) {
final boolean allowExternal = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ENABLE_EXTERNAL_WEBSERVICES);
if (!allowExternal) {
final String errorMsg = "external web services are not enabled";
LOGGER.warn(pwmSession, errorMsg);
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, errorMsg));
}
}
if (restRequestBean.isAuthenticated()) {
if (!pwmSession.getSessionManager().checkPermission(pwmApplication, Permission.WEBSERVICE)) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "authenticated user does not have external webservices permission");
throw new PwmUnrecoverableException(errorInformation);
}
}
final PasswordData secretKey = pwmApplication.getConfig().readSettingAsPassword(PwmSetting.WEBSERVICES_EXTERNAL_SECRET);
if (secretKey != null) {
final String headerName = pwmApplication.getConfig().readAppProperty(AppProperty.SECURITY_WS_REST_SERVER_SECRET_HEADER);
final String headerValue = pwmRequest.readHeaderValueAsString(headerName);
if (headerValue == null || headerValue.isEmpty()) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "request is missing security header " + headerName);
throw new PwmUnrecoverableException(errorInformation);
}
if (!headerValue.equals(secretKey.getStringValue())) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "authenticated user does not have correct security header");
throw new PwmUnrecoverableException(errorInformation);
}
}
}
final boolean adminPermission= restRequestBean.getPwmSession().getSessionManager().checkPermission(pwmApplication, Permission.PWMADMIN);
if (servicePermissions.isAdminOnly()) {
if (!adminPermission) {
final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"admin authorization is required");
throw new PwmUnrecoverableException(errorInfo);
}
}
return restRequestBean;
}
private static UserIdentity lookupUsername(
final PwmApplication pwmApplication,
final PwmSession pwmSession,
final boolean isExternal,
final ServicePermissions servicePermissions,
final String username
)
throws PwmUnrecoverableException
{
if (username == null || username.length() < 1) {
return null;
}
if (!pwmSession.isAuthenticated()) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_AUTHENTICATION_REQUIRED));
}
pwmApplication.getIntruderManager().check(RecordType.USERNAME,username);
if (isExternal) {
if (!pwmSession.getSessionManager().checkPermission(pwmApplication, Permission.WEBSERVICE_THIRDPARTY)) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"authenticated user does not match thirdparty webservices query filter");
throw new PwmUnrecoverableException(errorInformation);
}
}
if (!isExternal) {
if (servicePermissions.isHelpdeskPermitted()) {
final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
if (helpdeskProfile == null) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"authenticated non-external request for third-party does not match third-party webservices query filter");
throw new PwmUnrecoverableException(errorInformation);
}
} else {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"web service is not permitted for internal third-party username");
throw new PwmUnrecoverableException(errorInformation);
}
}
final String ldapProfileID;
final String effectiveUsername;
if (username.contains("|")) {
final int pipeIndex = username.indexOf("|");
ldapProfileID = username.substring(0, pipeIndex);
effectiveUsername = username.substring(pipeIndex + 1, username.length());
} else {
ldapProfileID = null;
effectiveUsername = username;
}
try {
final UserSearchEngine userSearchEngine = pwmApplication.getUserSearchEngine();
return userSearchEngine.resolveUsername(effectiveUsername, null, ldapProfileID, pwmSession.getLabel());
} catch (PwmOperationalException e) {
throw new PwmUnrecoverableException(e.getErrorInformation());
} catch (ChaiUnavailableException e) {
throw new PwmUnrecoverableException(PwmError.ERROR_DIRECTORY_UNAVAILABLE);
}
}
private static void handleAuthentication(
final HttpServletRequest request,
final HttpServletResponse response,
final PwmApplication pwmApplication,
final PwmSession pwmSession
)
throws PwmUnrecoverableException, ChaiUnavailableException {
if (pwmSession.isAuthenticated()) {
return;
}
final PwmRequest pwmRequest = PwmRequest.forRequest(request,response);
new AuthenticationFilter.BasicFilterAuthenticationProvider().attemptAuthentication(pwmRequest);
}
public static boolean determineIfRestClientIsExternal(final PwmSession pwmSession, final HttpServletRequest request)
throws PwmUnrecoverableException
{
final String requestClientKey = request.getHeader(PwmConstants.HTTP_HEADER_REST_CLIENT_KEY);
if (requestClientKey == null || requestClientKey.length() < 1) {
return true;
}
final String sessionClientKey = pwmSession.getRestClientKey();
return !requestClientKey.equals(sessionClientKey);
}
public static void handleNonJsonErrorResult(final ErrorInformation errorInformation) {
final Response.ResponseBuilder responseBuilder = Response.serverError();
responseBuilder.entity(errorInformation.toDebugStr() + "\n");
throw new WebApplicationException(responseBuilder.build());
}
}