/*
* 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.util.operations;
import com.novell.ldapchai.ChaiUser;
import com.novell.ldapchai.exception.ChaiOperationException;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import password.pwm.PwmApplication;
import password.pwm.bean.UserIdentity;
import password.pwm.config.ActionConfiguration;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.http.HttpMethod;
import password.pwm.http.PwmSession;
import password.pwm.http.client.PwmHttpClient;
import password.pwm.http.client.PwmHttpClientConfiguration;
import password.pwm.http.client.PwmHttpClientRequest;
import password.pwm.http.client.PwmHttpClientResponse;
import password.pwm.util.java.JavaHelper;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.macro.MacroMachine;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ActionExecutor {
private static final PwmLogger LOGGER = PwmLogger.forClass(ActionExecutor.class);
private PwmApplication pwmApplication;
private ActionExecutorSettings settings;
private ActionExecutor(final PwmApplication pwmApplication, final ActionExecutorSettings settings) {
this.pwmApplication = pwmApplication;
this.settings = settings;
}
public void executeActions(
final List<ActionConfiguration> configValues,
final PwmSession pwmSession
)
throws ChaiUnavailableException, PwmOperationalException, PwmUnrecoverableException
{
for (final ActionConfiguration loopAction : configValues) {
this.executeAction(loopAction, pwmSession);
}
}
public void executeAction(
final ActionConfiguration actionConfiguration,
final PwmSession pwmSession
)
throws ChaiUnavailableException, PwmOperationalException, PwmUnrecoverableException
{
LOGGER.trace(pwmSession, "preparing to execute " + actionConfiguration.getType() + " action " + actionConfiguration.getName());
switch (actionConfiguration.getType()) {
case ldap:
executeLdapAction(pwmSession, actionConfiguration);
break;
case webservice:
executeWebserviceAction(pwmSession, actionConfiguration);
break;
default:
JavaHelper.unhandledSwitchStatement(actionConfiguration.getType());
}
LOGGER.info(pwmSession, "action " + actionConfiguration.getName() + " completed successfully");
}
private void executeLdapAction(
final PwmSession pwmSession,
final ActionConfiguration actionConfiguration
)
throws ChaiUnavailableException, PwmOperationalException, PwmUnrecoverableException
{
String attributeName = actionConfiguration.getAttributeName();
String attributeValue = actionConfiguration.getAttributeValue();
final ChaiUser theUser;
if (settings.getChaiUser() != null) {
theUser = settings.getChaiUser();
} else {
if (settings.getUserIdentity() == null) {
final String errorMsg = "attempt to execute lap action but neither chaiUser or userIdentity is specified";
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN,errorMsg);
throw new PwmUnrecoverableException(errorInformation);
}
theUser = pwmApplication.getProxiedChaiUser(settings.getUserIdentity());
}
if (settings.isExpandPwmMacros()) {
if (settings.getMacroMachine() == null) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"executor specified macro expansion but did not supply macro machine"));
}
final MacroMachine macroMachine = settings.getMacroMachine();
attributeName = macroMachine.expandMacros(attributeName);
attributeValue = macroMachine.expandMacros(attributeValue);
}
writeLdapAttribute(
pwmSession,
theUser,
attributeName,
attributeValue,
actionConfiguration.getLdapMethod(),
settings.getMacroMachine()
);
}
private void executeWebserviceAction(
final PwmSession pwmSession,
final ActionConfiguration actionConfiguration
)
throws PwmOperationalException, PwmUnrecoverableException
{
String url = actionConfiguration.getUrl();
String body = actionConfiguration.getBody();
final Map<String,String> headers = new LinkedHashMap<>();
if (actionConfiguration.getHeaders() != null) {
headers.putAll(actionConfiguration.getHeaders());
}
try {
// expand using pwm macros
if (settings.isExpandPwmMacros()) {
if (settings.getMacroMachine() == null) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"executor specified macro expansion but did not supply macro machine"));
}
final MacroMachine macroMachine = settings.getMacroMachine();
url = macroMachine.expandMacros(url, new MacroMachine.URLEncoderReplacer());
body = body == null ? "" : macroMachine.expandMacros(body);
for (final String headerName : headers.keySet()) {
final String headerValue = headers.get(headerName);
if (headerValue != null) {
headers.put(headerName, macroMachine.expandMacros(headerValue));
}
}
}
final HttpMethod method = HttpMethod.fromString(actionConfiguration.getMethod().toString());
final PwmHttpClientRequest clientRequest = new PwmHttpClientRequest(method, url, body, headers);
final PwmHttpClient client;
{
if (actionConfiguration.getCertificates() != null) {
final PwmHttpClientConfiguration clientConfiguration = new PwmHttpClientConfiguration.Builder().setCertificate(actionConfiguration.getCertificates()).create();
client = new PwmHttpClient(pwmApplication, pwmSession.getLabel(), clientConfiguration);
} else {
client = new PwmHttpClient(pwmApplication, pwmSession.getLabel());
}
}
final PwmHttpClientResponse clientResponse = client.makeRequest(clientRequest);
if (clientResponse.getStatusCode() != 200) {
throw new PwmOperationalException(new ErrorInformation(
PwmError.ERROR_SERVICE_UNREACHABLE,
"unexpected HTTP status code while calling external web service: "
+ clientResponse.getStatusCode() + " " + clientResponse.getStatusPhrase()
));
}
} catch (Exception e) {
if (e instanceof PwmOperationalException) {
throw (PwmOperationalException)e;
}
final String errorMsg = "unexpected error during API execution: " + e.getMessage();
LOGGER.error(errorMsg);
throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg));
}
}
private static void writeLdapAttribute(
final PwmSession pwmSession,
final ChaiUser theUser,
final String attrName,
final String attrValue,
final ActionConfiguration.LdapMethod ldapMethod,
final MacroMachine macroMachine
)
throws PwmOperationalException, ChaiUnavailableException
{
final ActionConfiguration.LdapMethod effectiveLdapMethod = (ldapMethod == null)
? ActionConfiguration.LdapMethod.replace
: ldapMethod;
final String effectiveAttrValue = (macroMachine != null)
? macroMachine.expandMacros(attrValue)
: attrValue;
LOGGER.trace(pwmSession,"beginning ldap " + effectiveLdapMethod.toString() + " operation on " + theUser.getEntryDN() + ", attribute " + attrName);
switch (effectiveLdapMethod) {
case replace:
{
try {
theUser.writeStringAttribute(attrName, effectiveAttrValue);
LOGGER.info(pwmSession,"replaced attribute on user " + theUser.getEntryDN() + " (" + attrName + "=" + effectiveAttrValue + ")");
} catch (ChaiOperationException e) {
final String errorMsg = "error setting '" + attrName + "' attribute on user " + theUser.getEntryDN() + ", error: " + e.getMessage();
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
final PwmOperationalException newException = new PwmOperationalException(errorInformation);
newException.initCause(e);
throw newException;
}
}
break;
case add:
{
try {
theUser.addAttribute(attrName, effectiveAttrValue);
LOGGER.info(pwmSession,"added attribute on user " + theUser.getEntryDN() + " (" + attrName + "=" + effectiveAttrValue + ")");
} catch (ChaiOperationException e) {
final String errorMsg = "error adding '" + attrName + "' attribute value from user " + theUser.getEntryDN() + ", error: " + e.getMessage();
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
final PwmOperationalException newException = new PwmOperationalException(errorInformation);
newException.initCause(e);
throw newException;
}
}
break;
case remove:
{
try {
theUser.deleteAttribute(attrName, effectiveAttrValue);
LOGGER.info(pwmSession,"deleted attribute value on user " + theUser.getEntryDN() + " (" + attrName + ")");
} catch (ChaiOperationException e) {
final String errorMsg = "error deletig '" + attrName + "' attribute value on user " + theUser.getEntryDN() + ", error: " + e.getMessage();
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
final PwmOperationalException newException = new PwmOperationalException(errorInformation);
newException.initCause(e);
throw newException;
}
}
break;
default:
throw new IllegalStateException("unexpected ldap method type " + effectiveLdapMethod);
}
}
public static class ActionExecutorSettings {
private final UserIdentity userIdentity;
private final PwmApplication pwmApplication;
private final ChaiUser chaiUser;
private boolean expandPwmMacros = true;
private MacroMachine macroMachine;
public ActionExecutorSettings(final PwmApplication pwmApplication, final ChaiUser chaiUser) {
this.pwmApplication = pwmApplication;
this.chaiUser = chaiUser;
this.userIdentity = null;
}
public ActionExecutorSettings(final PwmApplication pwmApplication, final UserIdentity userIdentity) {
this.pwmApplication = pwmApplication;
this.userIdentity = userIdentity;
this.chaiUser = null;
}
private boolean isExpandPwmMacros() {
return expandPwmMacros;
}
private ChaiUser getChaiUser()
{
return chaiUser;
}
private MacroMachine getMacroMachine()
{
return macroMachine;
}
private UserIdentity getUserIdentity()
{
return userIdentity;
}
public ActionExecutorSettings setExpandPwmMacros(final boolean expandPwmMacros) {
this.expandPwmMacros = expandPwmMacros;
return this;
}
public ActionExecutorSettings setMacroMachine(final MacroMachine macroMachine)
{
this.macroMachine = macroMachine;
return this;
}
public ActionExecutor createActionExecutor() {
return new ActionExecutor(this.pwmApplication,this);
}
}
}