All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zaproxy.zap.authentication.ScriptBasedAuthenticationMethodType Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * Copyright 2014 The ZAP Development Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zaproxy.zap.authentication;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.script.ScriptException;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import net.sf.json.JSONObject;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.RecordContext;
import org.parosproxy.paros.extension.ExtensionHook;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpSender;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.ZAP;
import org.zaproxy.zap.authentication.GenericAuthenticationCredentials.GenericAuthenticationCredentialsOptionsPanel;
import org.zaproxy.zap.extension.api.ApiDynamicActionImplementor;
import org.zaproxy.zap.extension.api.ApiException;
import org.zaproxy.zap.extension.api.ApiResponse;
import org.zaproxy.zap.extension.authentication.AuthenticationAPI;
import org.zaproxy.zap.extension.script.ExtensionScript;
import org.zaproxy.zap.extension.script.ScriptType;
import org.zaproxy.zap.extension.script.ScriptWrapper;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.session.SessionManagementMethod;
import org.zaproxy.zap.session.WebSession;
import org.zaproxy.zap.users.User;
import org.zaproxy.zap.utils.ApiUtils;
import org.zaproxy.zap.utils.EncodingUtils;
import org.zaproxy.zap.view.DynamicFieldsPanel;
import org.zaproxy.zap.view.LayoutHelper;

public class ScriptBasedAuthenticationMethodType extends AuthenticationMethodType {

    public static final String CONTEXT_CONFIG_AUTH_SCRIPT =
            AuthenticationMethod.CONTEXT_CONFIG_AUTH + ".script";
    public static final String CONTEXT_CONFIG_AUTH_SCRIPT_NAME =
            CONTEXT_CONFIG_AUTH_SCRIPT + ".name";
    public static final String CONTEXT_CONFIG_AUTH_SCRIPT_PARAMS =
            CONTEXT_CONFIG_AUTH_SCRIPT + ".params";

    public static final int METHOD_IDENTIFIER = 4;

    private static final Logger log = Logger.getLogger(ScriptBasedAuthenticationMethodType.class);

    /** The Constant SCRIPT_TYPE_AUTH. */
    public static final String SCRIPT_TYPE_AUTH = "authentication";

    private static final String API_METHOD_NAME = "scriptBasedAuthentication";

    /** The SCRIPT ICON. */
    private static final ImageIcon SCRIPT_ICON_AUTH =
            new ImageIcon(ZAP.class.getResource("/resource/icon/16/script-auth.png"));

    /** The Authentication method's name. */
    private static final String METHOD_NAME =
            Constant.messages.getString("authentication.method.script.name");

    private ExtensionScript extensionScript;

    public class ScriptBasedAuthenticationMethod extends AuthenticationMethod {

        private ScriptWrapper script;

        private String[] credentialsParamNames;

        private Map paramValues;

        private HttpSender httpSender;

        protected HttpSender getHttpSender() {
            if (this.httpSender == null) {
                this.httpSender =
                        new HttpSender(
                                Model.getSingleton().getOptionsParam().getConnectionParam(),
                                true,
                                HttpSender.AUTHENTICATION_INITIATOR);
            }
            return httpSender;
        }

        /**
         * Load a script and fills in the method's filled according to the values specified by the
         * script.
         *
         * 

If the method already had a loaded script and a set of values for the parameters, it * tries to provide new values for the new parameters if they match any previous parameter * names. * * @param scriptW the script wrapper * @throws IllegalArgumentException if an error occurs while loading the script. */ public void loadScript(ScriptWrapper scriptW) { AuthenticationScript script = getScriptInterfaceV2(scriptW); if (script == null) { script = getScriptInterface(scriptW); } if (script == null) { log.warn( "The script " + scriptW.getName() + " does not properly implement the Authentication Script interface."); throw new IllegalArgumentException( Constant.messages.getString( "authentication.method.script.dialog.error.text.interface", scriptW.getName())); } try { if (script instanceof AuthenticationScriptV2) { AuthenticationScriptV2 scriptV2 = (AuthenticationScriptV2) script; setLoggedInIndicatorPattern(scriptV2.getLoggedInIndicator()); setLoggedOutIndicatorPattern(scriptV2.getLoggedOutIndicator()); } String[] requiredParams = script.getRequiredParamsNames(); String[] optionalParams = script.getOptionalParamsNames(); this.credentialsParamNames = script.getCredentialsParamsNames(); if (log.isDebugEnabled()) { log.debug( "Loaded authentication script - required parameters: " + Arrays.toString(requiredParams) + " - optional parameters: " + Arrays.toString(optionalParams)); } // If there's an already loaded script, make sure we save its values and _try_ // to use them Map oldValues = this.paramValues != null ? this.paramValues : Collections.emptyMap(); this.paramValues = new HashMap<>(requiredParams.length + optionalParams.length); for (String param : requiredParams) this.paramValues.put(param, oldValues.get(param)); for (String param : optionalParams) this.paramValues.put(param, oldValues.get(param)); this.script = scriptW; log.info("Successfully loaded new script for ScriptBasedAuthentication: " + this); } catch (Exception e) { log.error("Error while loading authentication script", e); getScriptsExtension().handleScriptException(this.script, e); throw new IllegalArgumentException( Constant.messages.getString( "authentication.method.script.dialog.error.text.loading", e.getMessage())); } } @Override public String toString() { return "ScriptBasedAuthenticationMethod [script=" + script + ", paramValues=" + paramValues + ", credentialsParamNames=" + Arrays.toString(credentialsParamNames) + "]"; } @Override public boolean isConfigured() { return true; } @Override protected AuthenticationMethod duplicate() { ScriptBasedAuthenticationMethod method = new ScriptBasedAuthenticationMethod(); method.script = script; method.paramValues = this.paramValues != null ? new HashMap(this.paramValues) : null; method.credentialsParamNames = this.credentialsParamNames; return method; } @Override public boolean validateCreationOfAuthenticationCredentials() { if (credentialsParamNames != null) { return true; } if (View.isInitialised()) { View.getSingleton() .showMessageDialog( Constant.messages.getString( "authentication.method.script.dialog.error.text.notLoaded")); } return false; } @Override public AuthenticationCredentials createAuthenticationCredentials() { return new GenericAuthenticationCredentials(this.credentialsParamNames); } @Override public AuthenticationMethodType getType() { return new ScriptBasedAuthenticationMethodType(); } @Override public WebSession authenticate( SessionManagementMethod sessionManagementMethod, AuthenticationCredentials credentials, User user) throws UnsupportedAuthenticationCredentialsException { // type check if (!(credentials instanceof GenericAuthenticationCredentials)) { throw new UnsupportedAuthenticationCredentialsException( "Script based Authentication method only supports " + GenericAuthenticationCredentials.class.getSimpleName() + ". Received: " + credentials.getClass()); } GenericAuthenticationCredentials cred = (GenericAuthenticationCredentials) credentials; // Call the script to get an authenticated message from which we can then extract the // session AuthenticationScript script = getScriptInterfaceV2(this.script); if (script == null) { script = getScriptInterface(this.script); } if (script == null) { return null; } HttpMessage msg = null; try { if (script instanceof AuthenticationScriptV2) { AuthenticationScriptV2 scriptV2 = (AuthenticationScriptV2) script; setLoggedInIndicatorPattern(scriptV2.getLoggedInIndicator()); setLoggedOutIndicatorPattern(scriptV2.getLoggedOutIndicator()); } msg = script.authenticate( new AuthenticationHelper( getHttpSender(), sessionManagementMethod, user), this.paramValues, cred); } catch (Exception e) { // Catch Exception instead of ScriptException and IOException because script engine // implementations // might throw other exceptions on script errors (e.g. // jdk.nashorn.internal.runtime.ECMAException) log.error( "An error occurred while trying to authenticate using the Authentication Script: " + this.script.getName(), e); getScriptsExtension().handleScriptException(this.script, e); return null; } if (msg.getRequestHeader().getURI() == null) { String error = String.format( "Auth request returned by the script '%s' does not have the request-target.", this.script.getName()); log.error(error); error = "ERROR: " + error + "\n"; getScriptsExtension().handleScriptError(this.script, error); if (View.isInitialised()) { View.getSingleton().getOutputPanel().appendAsync(error); } return null; } if (this.isAuthenticated(msg)) { // Let the user know it worked AuthenticationHelper.notifyOutputAuthSuccessful(msg); } else { // Let the user know it failed AuthenticationHelper.notifyOutputAuthFailure(msg); } // Add message to history AuthenticationHelper.addAuthMessageToHistory(msg); // Return the web session as extracted by the session management method return sessionManagementMethod.extractWebSession(msg); } @Override public ApiResponse getApiResponseRepresentation() { Map values = new HashMap<>(); values.put("methodName", API_METHOD_NAME); values.put("scriptName", script.getName()); values.putAll(paramValues); return new AuthMethodApiResponseRepresentation<>(values); } } public class ScriptBasedAuthenticationMethodOptionsPanel extends AbstractAuthenticationMethodOptionsPanel { private static final long serialVersionUID = 7812841049435409987L; private final String SCRIPT_NAME_LABEL = Constant.messages.getString("authentication.method.script.field.label.scriptName"); private final String LABEL_NOT_LOADED = Constant.messages.getString("authentication.method.script.field.label.notLoaded"); private JComboBox scriptsComboBox; private JButton loadScriptButton; private ScriptBasedAuthenticationMethod method; private AuthenticationIndicatorsPanel indicatorsPanel; private ScriptWrapper loadedScript; private JPanel dynamicContentPanel; private DynamicFieldsPanel dynamicFieldsPanel; private String[] loadedCredentialParams; public ScriptBasedAuthenticationMethodOptionsPanel() { super(); initialize(); } @SuppressWarnings("unchecked") private void initialize() { this.setLayout(new GridBagLayout()); this.add(new JLabel(SCRIPT_NAME_LABEL), LayoutHelper.getGBC(0, 0, 1, 0.0d, 0.0d)); this.scriptsComboBox = new JComboBox<>(); this.scriptsComboBox.setRenderer(new ScriptWrapperRenderer(this)); this.add(this.scriptsComboBox, LayoutHelper.getGBC(1, 0, 1, 1.0d, 0.0d)); this.loadScriptButton = new JButton("Load"); this.add(this.loadScriptButton, LayoutHelper.getGBC(2, 0, 1, 0.0d, 0.0d)); this.loadScriptButton.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadScript((ScriptWrapper) scriptsComboBox.getSelectedItem(), true); } }); // Make sure the 'Load' button is disabled when nothing is selected this.loadScriptButton.setEnabled(false); this.scriptsComboBox.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadScriptButton.setEnabled(scriptsComboBox.getSelectedIndex() >= 0); } }); this.dynamicContentPanel = new JPanel(new BorderLayout()); this.add(this.dynamicContentPanel, LayoutHelper.getGBC(0, 1, 3, 1.0d, 0.0d)); this.dynamicContentPanel.add(new JLabel(LABEL_NOT_LOADED)); } @Override public void validateFields() throws IllegalStateException { if (this.loadedScript == null) { this.scriptsComboBox.requestFocusInWindow(); throw new IllegalStateException( Constant.messages.getString( "authentication.method.script.dialog.error.text.notLoadedNorConfigured")); } this.dynamicFieldsPanel.validateFields(); } @Override public void saveMethod() { this.method.script = (ScriptWrapper) this.scriptsComboBox.getSelectedItem(); // This method will also be called when switching panels to save a temporary state so // the state of the authentication method might not be valid if (this.dynamicFieldsPanel != null) this.method.paramValues = this.dynamicFieldsPanel.getFieldValues(); else this.method.paramValues = Collections.emptyMap(); if (this.loadedScript != null) this.method.credentialsParamNames = this.loadedCredentialParams; } @Override public void bindMethod(AuthenticationMethod method) throws UnsupportedAuthenticationMethodException { this.method = (ScriptBasedAuthenticationMethod) method; // Make sure the list of scripts is refreshed List scripts = getScriptsExtension().getScripts(SCRIPT_TYPE_AUTH); DefaultComboBoxModel model = new DefaultComboBoxModel<>(scripts.toArray(new ScriptWrapper[scripts.size()])); this.scriptsComboBox.setModel(model); this.scriptsComboBox.setSelectedItem(this.method.script); this.loadScriptButton.setEnabled(this.method.script != null); // Load the selected script, if any if (this.method.script != null) { loadScript(this.method.script, false); if (this.dynamicFieldsPanel != null) this.dynamicFieldsPanel.bindFieldValues(this.method.paramValues); } } @Override public void bindMethod( AuthenticationMethod method, AuthenticationIndicatorsPanel indicatorsPanel) throws UnsupportedAuthenticationMethodException { this.indicatorsPanel = indicatorsPanel; bindMethod(method); } @Override public AuthenticationMethod getMethod() { return this.method; } private void loadScript(ScriptWrapper scriptW, boolean adaptOldValues) { AuthenticationScript script = getScriptInterfaceV2(scriptW); if (script == null) { script = getScriptInterface(scriptW); } if (script == null) { log.warn( "The script " + scriptW.getName() + " does not properly implement the Authentication Script interface."); warnAndResetPanel( Constant.messages.getString( "authentication.method.script.dialog.error.text.interface", scriptW.getName())); return; } try { if (script instanceof AuthenticationScriptV2) { AuthenticationScriptV2 scriptV2 = (AuthenticationScriptV2) script; String toolTip = Constant.messages.getString( "authentication.method.script.dialog.loggedInOutIndicatorsInScript.toolTip"); String loggedInIndicator = scriptV2.getLoggedInIndicator(); this.method.setLoggedInIndicatorPattern(loggedInIndicator); this.indicatorsPanel.setLoggedInIndicatorPattern(loggedInIndicator); this.indicatorsPanel.setLoggedInIndicatorEnabled(false); this.indicatorsPanel.setLoggedInIndicatorToolTip(toolTip); String loggedOutIndicator = scriptV2.getLoggedOutIndicator(); this.method.setLoggedOutIndicatorPattern(loggedOutIndicator); this.indicatorsPanel.setLoggedOutIndicatorPattern(loggedOutIndicator); this.indicatorsPanel.setLoggedOutIndicatorEnabled(false); this.indicatorsPanel.setLoggedOutIndicatorToolTip(toolTip); } else { this.indicatorsPanel.setLoggedInIndicatorEnabled(true); this.indicatorsPanel.setLoggedInIndicatorToolTip(null); this.indicatorsPanel.setLoggedOutIndicatorEnabled(true); this.indicatorsPanel.setLoggedOutIndicatorToolTip(null); } String[] requiredParams = script.getRequiredParamsNames(); String[] optionalParams = script.getOptionalParamsNames(); this.loadedCredentialParams = script.getCredentialsParamsNames(); if (log.isDebugEnabled()) { log.debug( "Loaded authentication script - required parameters: " + Arrays.toString(requiredParams) + " - optional parameters: " + Arrays.toString(optionalParams)); } // If there's an already loaded script, make sure we save its values and _try_ // to place them in the new panel Map oldValues = null; if (adaptOldValues && dynamicFieldsPanel != null) { oldValues = dynamicFieldsPanel.getFieldValues(); if (log.isDebugEnabled()) log.debug("Trying to adapt old values: " + oldValues); } this.dynamicFieldsPanel = new DynamicFieldsPanel(requiredParams, optionalParams); this.loadedScript = scriptW; if (adaptOldValues && oldValues != null) this.dynamicFieldsPanel.bindFieldValues(oldValues); this.dynamicContentPanel.removeAll(); this.dynamicContentPanel.add(dynamicFieldsPanel, BorderLayout.CENTER); this.dynamicContentPanel.revalidate(); } catch (Exception e) { getScriptsExtension().handleScriptException(scriptW, e); log.error("Error while calling authentication script", e); warnAndResetPanel( Constant.messages.getString( "authentication.method.script.dialog.error.text.loading", ExceptionUtils.getRootCauseMessage(e))); } } private void warnAndResetPanel(String errorMessage) { JOptionPane.showMessageDialog( this, errorMessage, Constant.messages.getString("authentication.method.script.dialog.error.title"), JOptionPane.ERROR_MESSAGE); this.loadedScript = null; this.scriptsComboBox.setSelectedItem(null); this.dynamicFieldsPanel = null; this.dynamicContentPanel.removeAll(); this.dynamicContentPanel.add(new JLabel(LABEL_NOT_LOADED), BorderLayout.CENTER); this.dynamicContentPanel.revalidate(); } } /** * A renderer for properly displaying the name of a {@link ScriptWrapper} in a ComboBox and * putting emphasis on loaded script. */ private static class ScriptWrapperRenderer extends BasicComboBoxRenderer { private static final long serialVersionUID = 3654541772447187317L; private static final Border BORDER = new EmptyBorder(2, 3, 3, 3); private ScriptBasedAuthenticationMethodOptionsPanel panel; public ScriptWrapperRenderer(ScriptBasedAuthenticationMethodOptionsPanel panel) { super(); this.panel = panel; } @Override @SuppressWarnings("rawtypes") public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value != null) { setBorder(BORDER); ScriptWrapper item = (ScriptWrapper) value; if (panel.loadedScript == item) setText( "" + StringEscapeUtils.unescapeHtml(item.getName()) + " (loaded)"); else setText(item.getName()); } return this; } } @Override public void hook(ExtensionHook extensionHook) { // Hook up the Script Type if (getScriptsExtension() != null) { log.debug("Registering Script..."); getScriptsExtension() .registerScriptType( new ScriptType( SCRIPT_TYPE_AUTH, "authentication.method.script.type", SCRIPT_ICON_AUTH, false, new String[] {ScriptType.CAPABILITY_APPEND})); } } @Override public ScriptBasedAuthenticationMethod createAuthenticationMethod(int contextId) { return new ScriptBasedAuthenticationMethod(); } @Override public String getName() { return METHOD_NAME; } @Override public int getUniqueIdentifier() { return METHOD_IDENTIFIER; } @Override public AbstractAuthenticationMethodOptionsPanel buildOptionsPanel(Context uiSharedContext) { return new ScriptBasedAuthenticationMethodOptionsPanel(); } @Override public boolean hasOptionsPanel() { return true; } @Override public AbstractCredentialsOptionsPanel buildCredentialsOptionsPanel( AuthenticationCredentials credentials, Context uiSharedContext) { return new GenericAuthenticationCredentialsOptionsPanel( (GenericAuthenticationCredentials) credentials); } @Override public boolean hasCredentialsOptionsPanel() { return true; } @Override public boolean isTypeForMethod(AuthenticationMethod method) { return (method instanceof ScriptBasedAuthenticationMethod); } @Override public ScriptBasedAuthenticationMethod loadMethodFromSession(Session session, int contextId) throws DatabaseException { ScriptBasedAuthenticationMethod method = createAuthenticationMethod(contextId); // Load the script and make sure it still exists and still follows the required interface this.loadMethod( method, session.getContextDataStrings(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_1), session.getContextDataStrings(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_2)); return method; } public void loadMethod( ScriptBasedAuthenticationMethod method, List scripts, List paramValuesS) { // Load the script and make sure it still exists and still follows the required interface String scriptName = ""; if (scripts != null && scripts.size() > 0) { scriptName = scripts.get(0); ScriptWrapper script = getScriptsExtension().getScript(scriptName); if (script == null) { log.error( "Unable to find script while loading Script Based Authentication Method for name: " + scriptName); if (View.isInitialised()) { View.getSingleton() .showMessageDialog( Constant.messages.getString( "authentication.method.script.load.errorScriptNotFound", scriptName)); } return; } log.info("Loaded script:" + script.getName()); method.script = script; // Check script interface and make sure we load the credentials parameter names AuthenticationScript s = getScriptInterfaceV2(script); if (s == null) { s = getScriptInterface(script); } if (s == null) { log.error( "Unable to load Script Based Authentication method. The script " + scriptName + " does not properly implement the Authentication Script interface."); return; } try { if (s instanceof AuthenticationScriptV2) { AuthenticationScriptV2 sV2 = (AuthenticationScriptV2) s; method.setLoggedInIndicatorPattern(sV2.getLoggedInIndicator()); method.setLoggedOutIndicatorPattern(sV2.getLoggedOutIndicator()); } method.credentialsParamNames = s.getCredentialsParamsNames(); } catch (Exception e) { getScriptsExtension().handleScriptException(script, e); } } // Load the parameter values Map paramValues = null; if (paramValuesS != null && paramValuesS.size() > 0) { paramValues = EncodingUtils.stringToMap(paramValuesS.get(0)); method.paramValues = paramValues; } else { method.paramValues = new HashMap(); log.error( "Unable to load script parameter values loading Script Based Authentication Method for name: " + scriptName); } } @Override public void persistMethodToSession( Session session, int contextId, AuthenticationMethod authMethod) throws UnsupportedAuthenticationMethodException, DatabaseException { if (!(authMethod instanceof ScriptBasedAuthenticationMethod)) throw new UnsupportedAuthenticationMethodException( "Script based authentication type only supports: " + ScriptBasedAuthenticationMethod.class); ScriptBasedAuthenticationMethod method = (ScriptBasedAuthenticationMethod) authMethod; session.setContextData( contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_1, method.script.getName()); session.setContextData( contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_2, EncodingUtils.mapToString(method.paramValues)); } @Override public AuthenticationCredentials createAuthenticationCredentials() { // NOTE: This method will initialize a set of Credentials without any required parameters // and, thus, should be later modified explicitly (e.g. through calls to decode()) return new GenericAuthenticationCredentials(new String[0]); } @Override public Class getAuthenticationCredentialsType() { return GenericAuthenticationCredentials.class; } private ExtensionScript getScriptsExtension() { if (extensionScript == null) extensionScript = Control.getSingleton().getExtensionLoader().getExtension(ExtensionScript.class); return extensionScript; } private AuthenticationScript getScriptInterface(ScriptWrapper script) { try { return getScriptsExtension().getInterface(script, AuthenticationScript.class); } catch (Exception e) { getScriptsExtension() .handleFailedScriptInterface( script, Constant.messages.getString( "authentication.method.script.dialog.error.text.interface", script.getName())); } return null; } private AuthenticationScriptV2 getScriptInterfaceV2(ScriptWrapper script) { try { AuthenticationScriptV2 authScript = getScriptsExtension().getInterface(script, AuthenticationScriptV2.class); // Some ScriptEngines do not verify if all Interface Methods are contained in the // script. // So we must invoke them to ensure that they are defined in the loaded script! // Otherwise some ScriptEngines loads successfully AuthenticationScriptV2 without the // methods // getLoggedInIndicator() / getLoggedOutIndicator(). // Though it should fallback to interface AuthenticationScript. authScript.getLoggedInIndicator(); authScript.getLoggedOutIndicator(); return authScript; } catch (Exception ignore) { // The interface is optional, the AuthenticationScript will be checked after this one. if (log.isDebugEnabled()) { log.debug( "Script '" + script.getName() + "' is not a AuthenticationScriptV2 interface!", ignore); } } return null; } /** The Interface that needs to be implemented by an Authentication Script. */ public interface AuthenticationScript { public String[] getRequiredParamsNames(); public String[] getOptionalParamsNames(); public String[] getCredentialsParamsNames(); public HttpMessage authenticate( AuthenticationHelper helper, Map paramsValues, GenericAuthenticationCredentials credentials) throws ScriptException; } /** * An {@code AuthenticationScript} that allows to specify the logged in/out indicators. * * @since 2.5.0 */ public interface AuthenticationScriptV2 extends AuthenticationScript { /** * Gets the logged in indicator pattern. * * @return the logged in indicator pattern */ String getLoggedInIndicator(); /** * Gets the logged out indicator pattern. * * @return the logged out indicator pattern */ String getLoggedOutIndicator(); } /* API related constants and methods. */ private static final String PARAM_SCRIPT_NAME = "scriptName"; private static final String PARAM_SCRIPT_CONFIG_PARAMS = "scriptConfigParams"; @Override public ApiDynamicActionImplementor getSetMethodForContextApiAction() { return new ApiDynamicActionImplementor( API_METHOD_NAME, new String[] {PARAM_SCRIPT_NAME}, new String[] {PARAM_SCRIPT_CONFIG_PARAMS}) { @Override public void handleAction(JSONObject params) throws ApiException { Context context = ApiUtils.getContextByParamId(params, AuthenticationAPI.PARAM_CONTEXT_ID); String scriptName = ApiUtils.getNonEmptyStringParam(params, PARAM_SCRIPT_NAME); // Prepare the method ScriptBasedAuthenticationMethod method = createAuthenticationMethod(context.getId()); // Load the script and make sure it exists and follows the required interface ScriptWrapper script = getScriptsExtension().getScript(scriptName); if (script == null) { log.error( "Unable to find script while loading Script Based Authentication Method for name: " + scriptName); throw new ApiException(ApiException.Type.SCRIPT_NOT_FOUND, scriptName); } else log.info("Loaded script for API:" + script.getName()); method.script = script; // Check script interface and make sure we load the credentials parameter names AuthenticationScript s = getScriptInterfaceV2(script); if (s == null) { s = getScriptInterface(script); } if (s == null) { log.error( "Unable to load Script Based Authentication method. The script " + script.getName() + " does not properly implement the Authentication Script interface."); throw new ApiException( ApiException.Type.BAD_SCRIPT_FORMAT, "Does not follow Authentication script interface"); } try { if (s instanceof AuthenticationScriptV2) { AuthenticationScriptV2 sV2 = (AuthenticationScriptV2) s; method.setLoggedInIndicatorPattern(sV2.getLoggedInIndicator()); method.setLoggedOutIndicatorPattern(sV2.getLoggedOutIndicator()); } method.credentialsParamNames = s.getCredentialsParamsNames(); // Load config param names + values and make sure all of the required ones // are there String[] requiredParams = s.getRequiredParamsNames(); String[] optionalParams = s.getOptionalParamsNames(); if (log.isDebugEnabled()) { log.debug( "Loaded authentication script - required parameters: " + Arrays.toString(requiredParams) + " - optional parameters: " + Arrays.toString(optionalParams)); } Map paramValues = new HashMap(); for (String rp : requiredParams) { // If one of the required parameters is not present, it will throw // an exception String val = ApiUtils.getNonEmptyStringParam(params, rp); paramValues.put(rp, val); } for (String op : optionalParams) paramValues.put(op, ApiUtils.getOptionalStringParam(params, op)); method.paramValues = paramValues; if (log.isDebugEnabled()) log.debug("Loaded authentication script parameters:" + paramValues); } catch (ApiException e) { throw e; } catch (Exception e) { getScriptsExtension().handleScriptException(script, e); log.error( "Unable to load Script Based Authentication method. The script " + script.getName() + " contains errors."); throw new ApiException(ApiException.Type.BAD_SCRIPT_FORMAT, e.getMessage()); } context.setAuthenticationMethod(method); } }; } @Override public ApiDynamicActionImplementor getSetCredentialsForUserApiAction() { return GenericAuthenticationCredentials.getSetCredentialsForUserApiAction(this); } @Override public void exportData(Configuration config, AuthenticationMethod authMethod) { if (!(authMethod instanceof ScriptBasedAuthenticationMethod)) { throw new UnsupportedAuthenticationMethodException( "Script based authentication type only supports: " + ScriptBasedAuthenticationMethod.class.getName()); } ScriptBasedAuthenticationMethod method = (ScriptBasedAuthenticationMethod) authMethod; config.setProperty(CONTEXT_CONFIG_AUTH_SCRIPT_NAME, method.script.getName()); config.setProperty( CONTEXT_CONFIG_AUTH_SCRIPT_PARAMS, EncodingUtils.mapToString(method.paramValues)); } @Override public void importData(Configuration config, AuthenticationMethod authMethod) throws ConfigurationException { if (!(authMethod instanceof ScriptBasedAuthenticationMethod)) { throw new UnsupportedAuthenticationMethodException( "Script based authentication type only supports: " + ScriptBasedAuthenticationMethod.class.getName()); } ScriptBasedAuthenticationMethod method = (ScriptBasedAuthenticationMethod) authMethod; this.loadMethod( method, objListToStrList(config.getList(CONTEXT_CONFIG_AUTH_SCRIPT_NAME)), objListToStrList(config.getList(CONTEXT_CONFIG_AUTH_SCRIPT_PARAMS))); } private List objListToStrList(List oList) { List sList = new ArrayList(oList.size()); for (Object o : oList) { sList.add(o.toString()); } return sList; } }