Sfoglia il codice sorgente

remote web service syntax + remote web service form url setting

Jason Rivard 8 anni fa
parent
commit
32cbfab9b7

+ 2 - 0
src/main/java/password/pwm/config/PwmSetting.java

@@ -1116,6 +1116,8 @@ public enum PwmSetting {
             "external.macros.urls", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.REST_CLIENT),
     EXTERNAL_MACROS_REMOTE_RESPONSES_URL(
             "external.remoteResponses.url", PwmSettingSyntax.STRING, PwmSettingCategory.REST_CLIENT),
+    EXTERNAL_REMOTE_DATA_URL(
+            "external.remoteData.url", PwmSettingSyntax.REMOTE_WEB_SERVICE, PwmSettingCategory.REST_CLIENT),
 
 
     //appliance

+ 2 - 0
src/main/java/password/pwm/config/PwmSettingFlag.java

@@ -53,4 +53,6 @@ public enum PwmSettingFlag {
 
     Verification_HideMinimumOptional,
 
+    WebService_NoBody,
+
 }

+ 1 - 1
src/main/java/password/pwm/config/PwmSettingProperty.java

@@ -33,5 +33,5 @@ public enum PwmSettingProperty {
 
     Cert_ImportHandler,
 
-
+    MethodType,
 }

+ 2 - 1
src/main/java/password/pwm/config/PwmSettingSyntax.java

@@ -35,6 +35,7 @@ import password.pwm.config.value.NumericValue;
 import password.pwm.config.value.OptionListValue;
 import password.pwm.config.value.PasswordValue;
 import password.pwm.config.value.PrivateKeyValue;
+import password.pwm.config.value.RemoteWebServiceValue;
 import password.pwm.config.value.StringArrayValue;
 import password.pwm.config.value.StringValue;
 import password.pwm.config.value.UserPermissionValue;
@@ -70,7 +71,7 @@ public enum PwmSettingSyntax {
     VERIFICATION_METHOD(VerificationMethodValue.factory()),
     PRIVATE_KEY(PrivateKeyValue.factory()),
     NAMED_SECRET(NamedSecretValue.factory()),
-    REMOTE_WEB_SERVICE(NamedSecretValue.factory()),
+    REMOTE_WEB_SERVICE(RemoteWebServiceValue.factory()),
 
     ;
 

+ 84 - 0
src/main/java/password/pwm/config/function/RemoteWebServiceCertImportFunction.java

@@ -0,0 +1,84 @@
+/*
+ * 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.config.function;
+
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.stored.StoredConfigurationImpl;
+import password.pwm.config.value.RemoteWebServiceValue;
+import password.pwm.config.value.data.RemoteWebServiceConfiguration;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.java.JsonUtil;
+
+import java.net.URI;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RemoteWebServiceCertImportFunction extends AbstractUriCertImportFunction {
+
+    @Override
+    String getUri(final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData) throws PwmOperationalException {
+        final RemoteWebServiceValue actionValue = (RemoteWebServiceValue)storedConfiguration.readSetting(pwmSetting, profile);
+        final String serviceName = actionNameFromExtraData(extraData);
+        final RemoteWebServiceConfiguration action =  actionValue.forName(serviceName);
+        final String uriString = action.getUrl();
+
+        if (uriString == null || uriString.isEmpty()) {
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"Setting " + pwmSetting.toMenuLocationDebug(profile, null) + " action " + serviceName + " must first be configured");
+            throw new PwmOperationalException(errorInformation);
+        }
+        try {
+            URI.create(uriString);
+        } catch (IllegalArgumentException e) {
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"Setting " + pwmSetting.toMenuLocationDebug(profile, null) + " action " + serviceName + " has an invalid URL syntax");
+            throw new PwmOperationalException(errorInformation);
+        }
+        return uriString;
+    }
+
+    private String actionNameFromExtraData(final String extraData) {
+        return extraData;
+    }
+
+    void store(final X509Certificate[] certs, final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData, final UserIdentity userIdentity) throws PwmOperationalException, PwmUnrecoverableException {
+        final RemoteWebServiceValue actionValue = (RemoteWebServiceValue)storedConfiguration.readSetting(pwmSetting, profile);
+        final String actionName = actionNameFromExtraData(extraData);
+        final List<RemoteWebServiceConfiguration> newList = new ArrayList<>();
+        for (final RemoteWebServiceConfiguration loopConfiguration : actionValue.toNativeObject()) {
+            if (actionName.equals(loopConfiguration.getName())) {
+                final RemoteWebServiceConfiguration newConfig = JsonUtil.cloneUsingJson(loopConfiguration, RemoteWebServiceConfiguration.class);
+                newConfig.setCertificates(certs);
+                newList.add(newConfig);
+            } else {
+                newList.add(JsonUtil.cloneUsingJson(loopConfiguration,RemoteWebServiceConfiguration.class));
+            }
+        }
+        final RemoteWebServiceValue newActionValue = new RemoteWebServiceValue(newList);
+        storedConfiguration.writeSetting(pwmSetting, profile, newActionValue, userIdentity);
+    }
+
+}

+ 9 - 9
src/main/java/password/pwm/config/value/ActionValue.java

@@ -57,14 +57,14 @@ public class ActionValue extends AbstractValue implements StoredValue {
             public ActionValue fromJson(final String input)
             {
                 if (input == null) {
-                    return new ActionValue(Collections.<ActionConfiguration>emptyList());
+                    return new ActionValue(Collections.emptyList());
                 } else {
                     List<ActionConfiguration> srcList = JsonUtil.deserialize(input,
                             new TypeToken<List<ActionConfiguration>>() {
                             }
                     );
 
-                    srcList = srcList == null ? Collections.<ActionConfiguration>emptyList() : srcList;
+                    srcList = srcList == null ? Collections.emptyList() : srcList;
                     while (srcList.contains(null)) {
                         srcList.remove(null);
                     }
@@ -155,18 +155,18 @@ public class ActionValue extends AbstractValue implements StoredValue {
             switch (actionConfiguration.getType()) {
                 case webservice: {
                     sb.append("WebService: ");
-                    sb.append("method=" + actionConfiguration.getMethod());
-                    sb.append(" url=" + actionConfiguration.getUrl());
-                    sb.append(" headers=" + JsonUtil.serializeMap(actionConfiguration.getHeaders()));
-                    sb.append(" body=" + actionConfiguration.getBody());
+                    sb.append("method=").append(actionConfiguration.getMethod());
+                    sb.append(" url=").append(actionConfiguration.getUrl());
+                    sb.append(" headers=").append(JsonUtil.serializeMap(actionConfiguration.getHeaders()));
+                    sb.append(" body=").append(actionConfiguration.getBody());
                 }
                 break;
 
                 case ldap: {
                     sb.append("LDAP: ");
-                    sb.append("method=" + actionConfiguration.getLdapMethod());
-                    sb.append(" attribute=" + actionConfiguration.getAttributeName());
-                    sb.append(" value=" + actionConfiguration.getAttributeValue());
+                    sb.append("method=").append(actionConfiguration.getLdapMethod());
+                    sb.append(" attribute=").append(actionConfiguration.getAttributeName());
+                    sb.append(" value=").append(actionConfiguration.getAttributeValue());
                 }
                 break;
 

+ 158 - 0
src/main/java/password/pwm/config/value/RemoteWebServiceValue.java

@@ -0,0 +1,158 @@
+/*
+ * 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.config.value;
+
+import com.google.gson.reflect.TypeToken;
+import org.jdom2.Element;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.StoredValue;
+import password.pwm.config.value.data.RemoteWebServiceConfiguration;
+import password.pwm.error.PwmOperationalException;
+import password.pwm.util.java.JsonUtil;
+import password.pwm.util.secure.PwmSecurityKey;
+import password.pwm.util.secure.X509Utils;
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public class RemoteWebServiceValue extends AbstractValue implements StoredValue {
+    final List<RemoteWebServiceConfiguration> values;
+
+    public RemoteWebServiceValue(final List<RemoteWebServiceConfiguration> values) {
+        this.values = Collections.unmodifiableList(values);
+    }
+
+    public static StoredValueFactory factory()
+    {
+        return new StoredValueFactory() {
+            public RemoteWebServiceValue fromJson(final String input)
+            {
+                if (input == null) {
+                    return new RemoteWebServiceValue(Collections.emptyList());
+                } else {
+                    List<RemoteWebServiceConfiguration> srcList = JsonUtil.deserialize(input,
+                            new TypeToken<List<RemoteWebServiceConfiguration>>() {
+                            }
+                    );
+
+                    srcList = srcList == null ? Collections.emptyList() : srcList;
+                    srcList.removeIf(Objects::isNull);
+                    return new RemoteWebServiceValue(Collections.unmodifiableList(srcList));
+                }
+            }
+
+            public RemoteWebServiceValue fromXmlElement(
+                    final Element settingElement,
+                    final PwmSecurityKey input
+            )
+                    throws PwmOperationalException
+            {
+                final List valueElements = settingElement.getChildren("value");
+                final List<RemoteWebServiceConfiguration> values = new ArrayList<>();
+                for (final Object loopValue : valueElements) {
+                    final Element loopValueElement = (Element) loopValue;
+                    final String value = loopValueElement.getText();
+                    if (value != null && value.length() > 0) {
+                            values.add(JsonUtil.deserialize(value, RemoteWebServiceConfiguration.class));
+                    }
+                }
+                return new RemoteWebServiceValue(values);
+            }
+        };
+    }
+
+    public List<Element> toXmlValues(final String valueElementName) {
+        final List<Element> returnList = new ArrayList<>();
+        for (final RemoteWebServiceConfiguration value : values) {
+            final Element valueElement = new Element(valueElementName);
+            valueElement.addContent(JsonUtil.serialize(value));
+            returnList.add(valueElement);
+        }
+        return returnList;
+    }
+
+    public List<RemoteWebServiceConfiguration> toNativeObject() {
+        return Collections.unmodifiableList(values);
+    }
+
+    public List<String> validateValue(final PwmSetting pwmSetting) {
+        if (pwmSetting.isRequired()) {
+            if (values == null || values.size() < 1 || values.get(0) == null) {
+                return Collections.singletonList("required value missing");
+            }
+        }
+
+        final Set<String> seenNames = new HashSet<>();
+        for (final RemoteWebServiceConfiguration item : values) {
+            if (seenNames.contains(item.getName().toLowerCase())) {
+                return Collections.singletonList("each action name must be unique: " + item.getName().toLowerCase());
+            }
+            seenNames.add(item.getName().toLowerCase());
+        }
+
+
+        return Collections.emptyList();
+    }
+
+    public List<Map<String,Object>> toInfoMap() {
+        final String originalJson = JsonUtil.serializeCollection(values);
+        final List<Map<String,Object>> tempObj = JsonUtil.deserialize(originalJson, new TypeToken<List<Map<String,Object>>>() {
+        });
+        for (final Map<String,Object> mapObj : tempObj) {
+            final RemoteWebServiceConfiguration serviceConfig = forName((String)mapObj.get("name"));
+            if (serviceConfig != null && serviceConfig.getCertificates() != null) {
+                final List<Map<String,String>> certificateInfos = new ArrayList<>();
+                for (final X509Certificate certificate : serviceConfig.getCertificates()) {
+                    certificateInfos.add(X509Utils.makeDebugInfoMap(certificate, X509Utils.DebugInfoFlag.IncludeCertificateDetail));
+                }
+                mapObj.put("certificateInfos", certificateInfos);
+            }
+        }
+        return tempObj;
+    }
+
+
+    public RemoteWebServiceConfiguration forName(final String name) {
+        if (name==null) {
+            return null;
+        }
+        for (final RemoteWebServiceConfiguration config : values) {
+            if (name.equals(config.getName())) {
+                return config;
+            }
+        }
+        return null;
+    }
+
+    public String toDebugString(final Locale locale) {
+        return JsonUtil.serialize(this);
+    }
+
+}

+ 50 - 0
src/main/java/password/pwm/config/value/data/RemoteWebServiceConfiguration.java

@@ -0,0 +1,50 @@
+/*
+ * 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.config.value.data;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+@Getter
+@Setter
+public class RemoteWebServiceConfiguration implements Serializable {
+
+    public enum WebMethod {
+        delete,
+        get,
+        post,
+        put,
+        patch
+    }
+
+    private String name;
+    private WebMethod method = WebMethod.get;
+    private Map<String,String> headers;
+    private String url;
+    private String body;
+    private X509Certificate[] certificates;
+}

+ 5 - 0
src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -42,6 +42,7 @@ import password.pwm.config.stored.ValueMetaData;
 import password.pwm.config.value.ActionValue;
 import password.pwm.config.value.FileValue;
 import password.pwm.config.value.PrivateKeyValue;
+import password.pwm.config.value.RemoteWebServiceValue;
 import password.pwm.config.value.ValueFactory;
 import password.pwm.config.value.X509CertificateValue;
 import password.pwm.error.ErrorInformation;
@@ -361,6 +362,10 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                     returnValue = ((ActionValue)storedConfig.readSetting(theSetting, profile)).toInfoMap();
                     break;
 
+                case REMOTE_WEB_SERVICE:
+                    returnValue = ((RemoteWebServiceValue)storedConfig.readSetting(theSetting, profile)).toInfoMap();
+                    break;
+
                 case FILE:
                     returnValue = ((FileValue) storedConfig.readSetting(theSetting, profile)).toInfoMap();
                     break;

+ 8 - 0
src/main/resources/password/pwm/config/PwmSetting.xml

@@ -3665,6 +3665,14 @@
     <setting hidden="false" key="external.pwcheck.urls" level="2">
         <default />
     </setting>
+    <setting hidden="false" key="external.remoteData.url" level="2">
+        <properties>
+            <property key="Maximum">1</property>
+            <property key="MethodType">post</property>
+        </properties>
+        <flag>WebService_NoBody</flag>
+        <default />
+    </setting>
     <setting hidden="false" key="webservice.userAttributes" level="2">
         <ldapPermission actor="proxy" access="read"/>
         <default />

File diff suppressed because it is too large
+ 0 - 0
src/main/resources/password/pwm/i18n/PwmSetting.properties


+ 1 - 0
src/main/webapp/WEB-INF/jsp/configeditor.jsp

@@ -157,6 +157,7 @@
 <pwm:script-ref url="/public/resources/js/uilibrary.js"/>
 <pwm:script-ref url="/public/resources/js/configeditor.js"/>
 <pwm:script-ref url="/public/resources/js/configeditor-settings.js"/>
+<pwm:script-ref url="/public/resources/js/configeditor-settings-remotewebservices.js"/>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 <%@ include file="fragment/footer.jsp" %>
 </body>

+ 366 - 0
src/main/webapp/public/resources/js/configeditor-settings-remotewebservices.js

@@ -0,0 +1,366 @@
+/*
+ * 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
+ */
+
+var RemoteWebServiceHandler = {};
+RemoteWebServiceHandler.defaultValue = {
+    name:"",
+    method:"get",
+    url:"",
+    body:"",
+    headers:{}
+};
+RemoteWebServiceHandler.httpMethodOptions = [
+    { label: "Delete", value: "delete" },
+    { label: "Get", value: "get" },
+    { label: "Post", value: "post" },
+    { label: "Put", value: "put" },
+    { label: "Patch", value: "patch" }
+];
+
+RemoteWebServiceHandler.init = function(keyName) {
+    console.log('RemoteWebServiceHandler init for ' + keyName);
+    var parentDiv = 'table_setting_' + keyName;
+    PWM_CFGEDIT.clearDivElements(parentDiv, true);
+    PWM_CFGEDIT.readSetting(keyName, function(resultValue) {
+        PWM_VAR['clientSettingCache'][keyName] = resultValue;
+        RemoteWebServiceHandler.redraw(keyName);
+    });
+};
+
+RemoteWebServiceHandler.redraw = function(keyName) {
+    console.log('RemoteWebServiceHandler redraw for ' + keyName);
+    var resultValue = PWM_VAR['clientSettingCache'][keyName];
+    var parentDiv = 'table_setting_' + keyName;
+    PWM_CFGEDIT.clearDivElements(parentDiv, false);
+    var parentDivElement = PWM_MAIN.getObject(parentDiv);
+
+    var html = '';
+    if (!PWM_MAIN.JSLibrary.isEmpty(resultValue)) {
+        html += '<table class="noborder">';
+        html += '<tr><td>Name</td><td>URL</td></tr>';
+
+        for (var loop in resultValue) {
+            (function (loop) {
+                html += RemoteWebServiceHandler.drawRow(keyName, loop, resultValue[loop]);
+            })(loop);
+        }
+
+        html += '</table>';
+    }
+
+    var rowCount = PWM_MAIN.JSLibrary.itemCount(resultValue);
+    var maxRowCount = PWM_SETTINGS['settings'][keyName]['properties']['Maximum'];
+    if (maxRowCount > 0 && rowCount < maxRowCount) {
+        html += '<br/><button class="btn" id="button-' + keyName + '-addValue"><span class="btn-icon pwm-icon pwm-icon-plus-square"></span>Add Action</button>';
+    }
+
+    parentDivElement.innerHTML = html;
+
+    for (var i in resultValue) {
+        html += RemoteWebServiceHandler.addRowHandlers(keyName, i, resultValue[i]);
+    }
+
+    PWM_MAIN.addEventHandler('button-' + keyName + '-addValue','click',function(){
+        RemoteWebServiceHandler.addRow(keyName);
+    });
+};
+
+RemoteWebServiceHandler.drawRow = function(settingKey, iteration) {
+    var inputID = 'value_' + settingKey + '_' + iteration + "_";
+
+    var newTableRow = document.createElement("tr");
+    newTableRow.setAttribute("style", "border-width: 0");
+
+    var htmlRow = '<tr>';
+    htmlRow += '<td class="border">';
+    htmlRow += '<div class="noWrapTextBox" style="width:50px" id="display-' + inputID + '-name" ></div>';
+    htmlRow += '</td><td>';
+    htmlRow += '<div class="noWrapTextBox" style="width:250px" id="display-' + inputID + '-url" ></div>';
+    htmlRow += '</td><td>';
+    htmlRow += '<button id="button-' + inputID + '-options"><span class="btn-icon pwm-icon pwm-icon-sliders"/> Options</button>';
+    htmlRow += '</td>';
+    htmlRow += '<td><span class="delete-row-icon action-icon pwm-icon pwm-icon-times" id="button-' + inputID + '-deleteRow"></span></td>';
+    htmlRow += '</tr>';
+    return htmlRow;
+};
+
+RemoteWebServiceHandler.addRowHandlers = function(settingKey, iteration, value) {
+    var inputID = 'value_' + settingKey + '_' + iteration + "_";
+    UILibrary.addTextValueToElement('display-' + inputID + '-name',value['name']);
+    UILibrary.addTextValueToElement('display-' + inputID + '-url',value['url']);
+    UILibrary.addTextValueToElement('display-' + inputID + '-description',value['description']);
+    PWM_MAIN.addEventHandler('button-' + inputID + '-options','click',function(){
+        RemoteWebServiceHandler.showOptionsDialog(settingKey, iteration);
+    });
+
+    PWM_MAIN.addEventHandler('button-' + inputID + '-deleteRow','click',function(){
+        RemoteWebServiceHandler.removeRow(settingKey, iteration);
+    });
+};
+
+RemoteWebServiceHandler.write = function(settingKey, finishFunction) {
+    var cachedSetting = PWM_VAR['clientSettingCache'][settingKey];
+    PWM_CFGEDIT.writeSetting(settingKey, cachedSetting, finishFunction);
+};
+
+RemoteWebServiceHandler.removeRow = function(keyName, iteration) {
+    PWM_MAIN.showConfirmDialog({
+        text:'Are you sure you wish to delete this item?',
+        okAction:function(){
+            delete PWM_VAR['clientSettingCache'][keyName][iteration];
+            console.log("removed iteration " + iteration + " from " + keyName + ", cached keyValue=" + PWM_VAR['clientSettingCache'][keyName]);
+            RemoteWebServiceHandler.write(keyName,function(){
+                RemoteWebServiceHandler.init(keyName);
+            });
+        }
+    })
+};
+
+RemoteWebServiceHandler.addRow = function(keyName) {
+    UILibrary.stringEditorDialog({
+        title:'New Remote Web Service',
+        regex:'^[0-9a-zA-Z]+$',
+        instructions:'Please enter a descriptive name for the web service.',
+        placeholder:'Name',
+        completeFunction:function(value){
+            var currentSize = PWM_MAIN.JSLibrary.itemCount(PWM_VAR['clientSettingCache'][keyName]);
+            PWM_VAR['clientSettingCache'][keyName][currentSize + 1] = RemoteWebServiceHandler.defaultValue;
+            PWM_VAR['clientSettingCache'][keyName][currentSize + 1].name = value;
+
+            if (PWM_SETTINGS['settings'][keyName]['properties']['MethodType']) {
+                PWM_VAR['clientSettingCache'][keyName][currentSize + 1].method = PWM_SETTINGS['settings'][keyName]['properties']['MethodType'];
+            }
+
+            RemoteWebServiceHandler.write(keyName,function(){
+                RemoteWebServiceHandler.init(keyName);
+            });
+
+        }
+    });
+};
+
+RemoteWebServiceHandler.showOptionsDialog = function(keyName, iteration) {
+    var inputID = 'value_' + keyName + '_' + iteration + "_";
+    var value = PWM_VAR['clientSettingCache'][keyName][iteration];
+    var titleText = 'Web Service options for ' + value['name'];
+    var bodyText = '<table class="noborder">';
+
+    var hasMethodType = 'MethodType' in PWM_SETTINGS['settings'][keyName]['properties'];
+    var showBody = value['method'] !== 'get' && !(PWM_MAIN.JSLibrary.arrayContains(PWM_SETTINGS['settings'][keyName]['flags'], 'WebService_NoBody'));
+
+    bodyText += '<tr>';
+    bodyText += '<td class="key">HTTP Method</td><td class="noborder" ><select id="select-' + inputID + '-method"'
+        + (hasMethodType ? ' disabled' : '')
+        + '>';
+
+    for (var optionItem in RemoteWebServiceHandler.httpMethodOptions) {
+        var label = RemoteWebServiceHandler.httpMethodOptions[optionItem]['label'];
+        var optionValue = RemoteWebServiceHandler.httpMethodOptions[optionItem]['value'];
+        var selected = optionValue === value['method'];
+        bodyText += '<option value="' + optionValue + '"' + (selected ? ' selected' : '') + '>' + label + '</option>';
+    }
+    bodyText += '</td>';
+    bodyText += '</tr><tr>';
+    bodyText += '<td class="key">HTTP Headers</td><td><button id="button-' + inputID + '-headers"><span class="btn-icon pwm-icon pwm-icon-list-ul"/> Edit</button></td>';
+    bodyText += '</tr><tr>';
+    bodyText += '<td class="key">URL</td><td><input  pattern="^(http|https)://.+$" type="text" class="configstringinput" style="width:400px" placeholder="http://www.example.com/service"  id="input-' + inputID + '-url' + '" value="' + value['url'] + '"/></td>';
+    bodyText += '</tr>';
+    if (showBody) {
+        bodyText += '<tr><td class="key">Body</td><td><textarea style="max-width:400px; height:100px; max-height:100px" class="configStringInput" id="input-' + inputID + '-body' + '"/>' + value['body'] + '</textarea></td></tr>';
+    }
+    if (!PWM_MAIN.JSLibrary.isEmpty(value['certificateInfos'])) {
+        bodyText += '<tr><td class="key">Certificates</td><td><a id="button-' + inputID + '-certDetail">View Certificates</a></td>';
+        bodyText += '</tr>';
+    } else {
+        bodyText += '<tr><td class="key">Certificates</td><td>None</td>';
+        bodyText += '</tr>';
+    }
+    bodyText += '';
+
+    bodyText += '</table>';
+
+    if (!PWM_MAIN.JSLibrary.isEmpty(value['certificateInfos'])) {
+        bodyText += '<button class="btn" id="button-' + inputID + '-clearCertificates"><span class="btn-icon pwm-icon pwm-icon-trash"></span>Clear Certificates</button>'
+    } else {
+        bodyText += '<button class="btn" id="button-' + inputID + '-importCertificates"><span class="btn-icon pwm-icon pwm-icon-download"></span>Import From Server</button>'
+    }
+
+    PWM_MAIN.showDialog({
+        title: titleText,
+        text: bodyText,
+        okAction: function(){
+            RemoteWebServiceHandler.init(keyName);
+        },
+        loadFunction: function(){
+            PWM_MAIN.addEventHandler('button-' + inputID + '-headers','click',function(){
+                RemoteWebServiceHandler.showHeadersDialog(keyName,iteration);
+            });
+
+            PWM_MAIN.addEventHandler('select-' + inputID + '-method','change',function(){
+                var methodValue = PWM_MAIN.getObject('select-' + inputID + '-method').value;
+                if (methodValue === 'get') {
+                    value['body'] = '';
+                }
+                value['method'] = methodValue;
+                RemoteWebServiceHandler.write(keyName, function(){ RemoteWebServiceHandler.showOptionsDialog(keyName,iteration)});
+            });
+            PWM_MAIN.addEventHandler('input-' + inputID + '-url','input',function(){
+                value['url'] = PWM_MAIN.getObject('input-' + inputID + '-url').value;
+                RemoteWebServiceHandler.write(keyName);
+            });
+            PWM_MAIN.addEventHandler('input-' + inputID + '-body','input',function(){
+                value['body'] = PWM_MAIN.getObject('input-' + inputID + '-body').value;
+                RemoteWebServiceHandler.write(keyName);
+            });
+            if (!PWM_MAIN.JSLibrary.isEmpty(value['certificateInfos'])) {
+                PWM_MAIN.addEventHandler('button-' + inputID + '-certDetail','click',function(){
+                    var bodyText = '';
+                    for (var i in value['certificateInfos']) {
+                        var certificate = value['certificateInfos'][i];
+                        bodyText += X509CertificateHandler.certificateToHtml(certificate,keyName,i);
+                    }
+                    var cancelFunction = function(){ RemoteWebServiceHandler.showOptionsDialog(keyName,iteration); };
+                    var loadFunction = function(){
+                        for (var i in value['certificateInfos']) {
+                            var certificate = value['certificateInfos'][i];
+                            X509CertificateHandler.certHtmlActions(certificate,keyName,i);
+                        }
+                    };
+                    PWM_MAIN.showDialog({
+                        title:'Certificate Detail',
+                        dialogClass: 'wide',
+                        text:bodyText,
+                        okAction:cancelFunction,
+                        loadFunction:loadFunction
+                    });
+                });
+                PWM_MAIN.addEventHandler('button-' + inputID + '-clearCertificates','click',function() {
+                    PWM_MAIN.showConfirmDialog({okAction:function(){
+                        delete value['certificates'];
+                        delete value['certificateInfos'];
+                        RemoteWebServiceHandler.write(keyName, function(){ RemoteWebServiceHandler.showOptionsDialog(keyName,iteration)});
+                    },cancelAction:function(){
+                        RemoteWebServiceHandler.showOptionsDialog(keyName,iteration);
+                    }});
+                });
+            } else {
+                PWM_MAIN.addEventHandler('button-' + inputID + '-importCertificates','click',function() {
+                    var dataHandler = function(data) {
+                        var msgBody = '<div style="max-height: 400px; overflow-y: auto">' + data['successMessage'] + '</div>';
+                        PWM_MAIN.showDialog({width:700,title: 'Results', text: msgBody, okAction: function () {
+                            PWM_CFGEDIT.readSetting(keyName, function(resultValue) {
+                                PWM_VAR['clientSettingCache'][keyName] = resultValue;
+                                RemoteWebServiceHandler.showOptionsDialog(keyName, iteration);
+                            });
+                        }});
+                    };
+                    PWM_CFGEDIT.executeSettingFunction(keyName, 'password.pwm.config.function.RemoteWebServiceCertImportFunction', dataHandler, value['name'])
+                });
+            }
+
+        }
+    });
+};
+
+
+
+RemoteWebServiceHandler.showHeadersDialog = function(keyName, iteration) {
+    var settingValue = PWM_VAR['clientSettingCache'][keyName][iteration];
+    var inputID = 'value_' + keyName + '_' + iteration + "_" + "headers_";
+
+    var bodyText = '';
+    bodyText += '<table class="noborder">';
+    bodyText += '<tr><td><b>Name</b></td><td><b>Value</b></td></tr>';
+    for (var iter in settingValue['headers']) {
+        (function(headerName) {
+            var value = settingValue['headers'][headerName];
+            var optionID = inputID + headerName;
+            bodyText += '<tr><td class="border">' + headerName + '</td><td class="border">' + value + '</td>';
+            bodyText += '<td style="width:15px;"><span class="delete-row-icon action-icon pwm-icon pwm-icon-times" id="button-' + optionID + '-deleteRow"></span></td>';
+            bodyText += '</tr>';
+        }(iter));
+    }
+    bodyText += '</table>';
+
+    PWM_MAIN.showDialog({
+        title: 'HTTP Headers for webservice ' + settingValue['name'],
+        text: bodyText,
+        buttonHtml:'<button id="button-' + inputID + '-addHeader" class="btn"><span class="btn-icon pwm-icon pwm-icon-plus-square"></span>Add Header</button>',
+        okAction: function() {
+            RemoteWebServiceHandler.showOptionsDialog(keyName,iteration);
+        },
+        loadFunction: function() {
+            for (var iter in settingValue['headers']) {
+                (function(headerName) {
+                    var headerID = inputID + headerName;
+                    PWM_MAIN.addEventHandler('button-' + headerID + '-deleteRow', 'click', function () {
+                        delete settingValue['headers'][headerName];
+                        RemoteWebServiceHandler.write(keyName);
+                        RemoteWebServiceHandler.showHeadersDialog(keyName, iteration);
+                    });
+                }(iter));
+            }
+            PWM_MAIN.addEventHandler('button-' + inputID + '-addHeader','click',function(){
+                RemoteWebServiceHandler.addHeader(keyName, iteration);
+            });
+        }
+    });
+};
+
+RemoteWebServiceHandler.addHeader = function(keyName, iteration) {
+    var body = '<table class="noborder">';
+    body += '<tr><td>Name</td><td><input class="configStringInput" id="newHeaderName" style="width:300px"/></td></tr>';
+    body += '<tr><td>Value</td><td><input class="configStringInput" id="newHeaderValue" style="width:300px"/></td></tr>';
+    body += '</table>';
+
+    var updateFunction = function(){
+        PWM_MAIN.getObject('dialog_ok_button').disabled = true;
+        PWM_VAR['newHeaderName'] = PWM_MAIN.getObject('newHeaderName').value;
+        PWM_VAR['newHeaderValue'] = PWM_MAIN.getObject('newHeaderValue').value;
+        if (PWM_VAR['newHeaderName'].length > 0 && PWM_VAR['newHeaderValue'].length > 0) {
+            PWM_MAIN.getObject('dialog_ok_button').disabled = false;
+        }
+    };
+
+    PWM_MAIN.showConfirmDialog({
+        title:'New Header',
+        text:body,
+        showClose:true,
+        loadFunction:function(){
+            PWM_MAIN.addEventHandler('newHeaderName','input',function(){
+                updateFunction();
+            });
+            PWM_MAIN.addEventHandler('newHeaderValue','input',function(){
+                updateFunction();
+            });
+            updateFunction();
+        },okAction:function(){
+            var headers = PWM_VAR['clientSettingCache'][keyName][iteration]['headers'];
+            headers[PWM_VAR['newHeaderName']] = PWM_VAR['newHeaderValue'];
+            RemoteWebServiceHandler.write(keyName);
+            RemoteWebServiceHandler.showHeadersDialog(keyName, iteration);
+        },cancelAction:function(){
+            RemoteWebServiceHandler.showHeadersDialog(keyName, iteration);
+        }
+    });
+
+};

+ 8 - 7
src/main/webapp/public/resources/js/configeditor-settings.js

@@ -1534,7 +1534,8 @@ ActionHandler.addRow = function(keyName) {
     UILibrary.stringEditorDialog({
         title:'New Action',
         regex:'^[0-9a-zA-Z]+$',
-        placeholder:'Action Name',
+        instructions: 'Please enter a descriptive name for the action.',
+        placeholder:'Name',
         completeFunction:function(value){
             var currentSize = PWM_MAIN.JSLibrary.itemCount(PWM_VAR['clientSettingCache'][keyName]);
             PWM_VAR['clientSettingCache'][keyName][currentSize + 1] = ActionHandler.defaultValue;
@@ -1554,7 +1555,7 @@ ActionHandler.showOptionsDialog = function(keyName, iteration) {
         var value = PWM_VAR['clientSettingCache'][keyName][iteration];
         var titleText = 'title';
         var bodyText = '<table class="noborder">';
-        if (value['type'] == 'webservice') {
+        if (value['type'] === 'webservice') {
             titleText = 'Web Service options for ' + value['name'];
             bodyText += '<tr>';
             bodyText += '<td class="key">HTTP Method</td><td class="noborder" ><select id="select-' + inputID + '-method">';
@@ -1571,7 +1572,7 @@ ActionHandler.showOptionsDialog = function(keyName, iteration) {
             bodyText += '</tr><tr>';
             bodyText += '<td class="key">URL</td><td><input type="text" class="configstringinput" style="width:400px" placeholder="http://www.example.com/service"  id="input-' + inputID + '-url' + '" value="' + value['url'] + '"/></td>';
             bodyText += '</tr>';
-            if (value['method'] != 'get') {
+            if (value['method'] !== 'get') {
                 bodyText += '<tr><td class="key">Body</td><td><textarea style="max-width:400px; height:100px; max-height:100px" class="configStringInput" id="input-' + inputID + '-body' + '"/>' + value['body'] + '</textarea></td></tr>';
             }
             if (!PWM_MAIN.JSLibrary.isEmpty(value['certificateInfos'])) {
@@ -1582,7 +1583,7 @@ ActionHandler.showOptionsDialog = function(keyName, iteration) {
                 bodyText += '</tr>';
             }
             bodyText += '';
-        } else if (value['type'] == 'ldap') {
+        } else if (value['type'] === 'ldap') {
             titleText = 'LDAP options for ' + value['name'];
             bodyText += '<tr>';
             bodyText += '<td class="key">Attribute Name</td><td><input style="width:300px" class="configStringInput" type="text" id="input-' + inputID + '-attributeName' + '" value="' + value['attributeName'] + '"/></td>';
@@ -1602,7 +1603,7 @@ ActionHandler.showOptionsDialog = function(keyName, iteration) {
         }
         bodyText += '</table>';
 
-        if (value['type'] == 'webservice') {
+        if (value['type'] === 'webservice') {
             if (!PWM_MAIN.JSLibrary.isEmpty(value['certificateInfos'])) {
                 bodyText += '<button class="btn" id="button-' + inputID + '-clearCertificates"><span class="btn-icon pwm-icon pwm-icon-trash"></span>Clear Certificates</button>'
             } else {
@@ -1617,10 +1618,10 @@ ActionHandler.showOptionsDialog = function(keyName, iteration) {
                 PWM_MAIN.addEventHandler('button-' + inputID + '-headers','click',function(){
                     ActionHandler.showHeadersDialog(keyName,iteration);
                 });
-                if (value['type'] == 'webservice') {
+                if (value['type'] === 'webservice') {
                     PWM_MAIN.addEventHandler('select-' + inputID + '-method','change',function(){
                         var methodValue = PWM_MAIN.getObject('select-' + inputID + '-method').value;
-                        if (methodValue == 'get') {
+                        if (methodValue === 'get') {
                             value['body'] = '';
                         }
                         value['method'] = methodValue;

+ 4 - 0
src/main/webapp/public/resources/js/configeditor.js

@@ -1014,6 +1014,10 @@ PWM_CFGEDIT.initSettingDisplay = function(setting, options) {
             VerificationMethodHandler.init(settingKey);
             break;
 
+        case 'REMOTE_WEB_SERVICE':
+            RemoteWebServiceHandler.init(settingKey);
+            break;
+
         case 'NONE':
             break;
 

Some files were not shown because too many files changed in this diff