Explorar el Código

- copy profile feature
- refactor token names
- setting for user-stored history events

jrivard hace 10 años
padre
commit
04da0cd1cb
Se han modificado 24 ficheros con 331 adiciones y 125 borrados
  1. 0 1
      pwm/servlet/local-build.xml
  2. 1 0
      pwm/servlet/src/password/pwm/AppProperty.java
  3. 1 0
      pwm/servlet/src/password/pwm/AppProperty.properties
  4. 2 0
      pwm/servlet/src/password/pwm/config/PwmSetting.java
  5. 43 0
      pwm/servlet/src/password/pwm/config/PwmSetting.xml
  6. 26 1
      pwm/servlet/src/password/pwm/config/StoredConfiguration.java
  7. 4 0
      pwm/servlet/src/password/pwm/error/PwmUnrecoverableException.java
  8. 29 35
      pwm/servlet/src/password/pwm/event/AuditEvent.java
  9. 13 30
      pwm/servlet/src/password/pwm/event/AuditManager.java
  10. 56 0
      pwm/servlet/src/password/pwm/event/AuditSettings.java
  11. 6 6
      pwm/servlet/src/password/pwm/http/PwmHttpRequestWrapper.java
  12. 0 5
      pwm/servlet/src/password/pwm/http/bean/NewUserBean.java
  13. 3 3
      pwm/servlet/src/password/pwm/http/servlet/ActivateUserServlet.java
  14. 38 5
      pwm/servlet/src/password/pwm/http/servlet/ConfigEditorServlet.java
  15. 2 5
      pwm/servlet/src/password/pwm/http/servlet/ConfigGuideServlet.java
  16. 3 1
      pwm/servlet/src/password/pwm/http/servlet/ConfigManagerServlet.java
  17. 5 4
      pwm/servlet/src/password/pwm/http/servlet/NewUserServlet.java
  18. 3 6
      pwm/servlet/src/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  19. 2 0
      pwm/servlet/src/password/pwm/i18n/ConfigEditor.properties
  20. 7 7
      pwm/servlet/src/password/pwm/token/TokenService.java
  21. 34 0
      pwm/servlet/src/password/pwm/token/TokenType.java
  22. 8 10
      pwm/servlet/src/password/pwm/util/localdb/LocalDBUtility.java
  23. 39 4
      pwm/servlet/web/public/resources/js/configeditor-settings.js
  24. 6 2
      pwm/servlet/web/public/resources/js/uilibrary.js

+ 0 - 1
pwm/servlet/local-build.xml

@@ -119,7 +119,6 @@
     <target name="makeZIP" depends="makeWAR,javadoc">
         <zip zipfile="${destination}/pwm.zip" filesonly="true">
             <zipfileset file="${destination.war}"/>
-            <zipfileset dir="${src.root}/build_lib" prefix="servlet/build_lib"/>
             <zipfileset dir="${src.root}/src" prefix="servlet/src"/>
             <zipfileset dir="${src.root}/web" prefix="servlet/web"/>
             <zipfileset dir="${src.root}/../supplemental" prefix="supplemental"/>

+ 1 - 0
pwm/servlet/src/password/pwm/AppProperty.java

@@ -101,6 +101,7 @@ public enum AppProperty {
     HTTP_PARAM_OAUTH_REFRESH_TOKEN                  ("http.parameter.oauth.refreshToken"),
     HTTP_PARAM_OAUTH_STATE                          ("http.parameter.oauth.state"),
     HTTP_PARAM_OAUTH_GRANT_TYPE                     ("http.parameter.oauth.grantType"),
+    HTTP_DOWNLOAD_BUFFER_SIZE                       ("http.download.buffer.size"),
     HTTP_SESSION_RECYCLE_AT_AUTH                    ("http.session.recycleAtAuth"),
     HTTP_SESSION_VALIDATION_KEY_LENGTH              ("http.session.validationKeyLength"),
     LOCALDB_COMPRESSION_ENABLED                     ("localdb.compression.enabled"),

+ 1 - 0
pwm/servlet/src/password/pwm/AppProperty.properties

@@ -104,6 +104,7 @@ http.parameter.oauth.redirectUri=redirect_uri
 http.parameter.oauth.refreshToken=refresh_token
 http.parameter.oauth.state=state
 http.parameter.oauth.grantType=grant_type
+http.download.buffer.size=102400
 http.session.recycleAtAuth=true
 http.session.validationKeyLength=32
 intruder.retentionTimeMS=86400000

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

@@ -571,6 +571,8 @@ public enum PwmSetting {
 
     EVENTS_USER_STORAGE_METHOD(
             "events.user.storageMethod", PwmSettingSyntax.SELECT, PwmSettingCategory.USER_HISTORY),
+    EVENTS_USER_EVENT_TYPES(
+            "events.user.eventList", PwmSettingSyntax.OPTIONLIST, PwmSettingCategory.USER_HISTORY),
     EVENTS_LDAP_ATTRIBUTE(
             "events.ldap.attribute", PwmSettingSyntax.STRING, PwmSettingCategory.USER_HISTORY),
     EVENTS_LDAP_MAX_EVENTS(

+ 43 - 0
pwm/servlet/src/password/pwm/config/PwmSetting.xml

@@ -1610,6 +1610,49 @@
             <option value="LDAP">LDAP</option>
         </options>
     </setting>
+    <setting key="events.user.eventList" level="1">
+        <default>
+            <value>CHANGE_PASSWORD</value>
+            <value>UNLOCK_PASSWORD</value>
+            <value>RECOVER_PASSWORD</value>
+            <value>SET_RESPONSES</value>
+            <value>SET_OTP_SECRET</value>
+            <value>ACTIVATE_USER</value>
+            <value>CREATE_USER</value>
+            <value>UPDATE_PROFILE</value>
+            <value>INTRUDER_USER</value>
+            <value>CLEAR_RESPONSES</value>
+            <value>HELPDESK_SET_PASSWORD</value>
+            <value>HELPDESK_UNLOCK_PASSWORD</value>
+            <value>HELPDESK_CLEAR_RESPONSES</value>
+            <value>HELPDESK_CLEAR_OTP_SECRET</value>
+            <value>HELPDESK_ACTION</value>
+            <value>HELPDESK_VERIFY_OTP</value>
+        </default>
+        <options>
+            <option value="AUTHENTICATE">Authenticate</option>
+            <option value="AGREEMENT_PASSED">Agreement Passed</option>
+            <option value="CHANGE_PASSWORD">Change Password</option>
+            <option value="UNLOCK_PASSWORD">Unlock Password</option>
+            <option value="RECOVER_PASSWORD">Recover Password</option>
+            <option value="SET_RESPONSES">Set Responses</option>
+            <option value="SET_OTP_SECRET">Set OTP</option>
+            <option value="ACTIVATE_USER">Activate User</option>
+            <option value="CREATE_USER">New User</option>
+            <option value="UPDATE_PROFILE">Update Profile</option>
+            <option value="INTRUDER_USER">Intruder Lock User</option>
+            <option value="TOKEN_ISSUED">Token Issued</option>
+            <option value="TOKEN_CLAIMED">Token Claimed</option>
+            <option value="CLEAR_RESPONSES">Clear Responses</option>
+            <option value="HELPDESK_SET_PASSWORD">Helpdesk Set Password</option>
+            <option value="HELPDESK_UNLOCK_PASSWORD">Helpdesk Unlock Password</option>
+            <option value="HELPDESK_CLEAR_RESPONSES">Helpdesk Clear Responses</option>
+            <option value="HELPDESK_CLEAR_OTP_SECRET">Helpdesk Clear OTP</option>
+            <option value="HELPDESK_VIEW_DETAIL">Helpdesk View Detail</option>
+            <option value="HELPDESK_ACTION">Helpdesk Action</option>
+            <option value="HELPDESK_VERIFY_OTP">Helpdesk Verify OTP</option>
+        </options>
+    </setting>
     <setting key="events.ldap.attribute" level="2">
         <regex>^[a-zA-Z][a-zA-Z0-9-]*$</regex>
         <default>

+ 26 - 1
pwm/servlet/src/password/pwm/config/StoredConfiguration.java

@@ -301,7 +301,7 @@ public class StoredConfiguration implements Serializable {
 
         final HashMap<String,Object> outputObj = new HashMap<>();
         outputObj.put("settings",settingData);
-        outputObj.put("template",this.getTemplate().toString());
+        outputObj.put("template", this.getTemplate().toString());
 
         return Collections.unmodifiableMap(outputObj);
     }
@@ -754,6 +754,31 @@ public class StoredConfiguration implements Serializable {
         }
     }
 
+    public void copyProfileID(final PwmSettingCategory category, final String sourceID, final String destinationID, final UserIdentity userIdentity)
+            throws PwmUnrecoverableException
+    {
+        if (!category.hasProfiles()) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category " + category + ", category does not have profiles");
+        }
+        final List<String> existingProfiles = this.profilesForSetting(category.getProfileSetting());
+        if (!existingProfiles.contains(sourceID)) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category, source profileID '" + sourceID + "' does not exist");
+        }
+        if (existingProfiles.contains(destinationID)) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category, destination profileID '" + destinationID+ "' already exists");
+        }
+        for (final PwmSetting pwmSetting : category.getSettings()) {
+            if (!isDefaultValue(pwmSetting, sourceID)) {
+                final StoredValue value = readSetting(pwmSetting, sourceID);
+                writeSetting(pwmSetting, destinationID, value, userIdentity);
+            }
+        }
+        final List<String> newProfileIDList = new ArrayList<>();
+        newProfileIDList.addAll(existingProfiles);
+        newProfileIDList.add(destinationID);
+        writeSetting(category.getProfileSetting(), new StringArrayValue(newProfileIDList), userIdentity);
+    }
+
 
     public void writeSetting(
             final PwmSetting setting,

+ 4 - 0
pwm/servlet/src/password/pwm/error/PwmUnrecoverableException.java

@@ -52,5 +52,9 @@ public class PwmUnrecoverableException extends PwmException {
         }
         return new PwmUnrecoverableException(errorInformation);
     }
+
+    public static PwmUnrecoverableException newException(final PwmError error, final String message) {
+        return new PwmUnrecoverableException(new ErrorInformation(error, message));
+    }
 }
 

+ 29 - 35
pwm/servlet/src/password/pwm/event/AuditEvent.java

@@ -30,51 +30,49 @@ import java.util.Locale;
 public enum AuditEvent {
 
     // system events
-    STARTUP(Message.EventLog_Startup, Type.SYSTEM, false),
-    SHUTDOWN(Message.EventLog_Shutdown, Type.SYSTEM, false),
-    FATAL_EVENT(Message.EventLog_FatalEvent, Type.SYSTEM, false),
-    MODIFY_CONFIGURATION(Message.EventLog_ModifyConfiguration, Type.SYSTEM, false),
-    INTRUDER_LOCK(Message.EventLog_IntruderLockout, Type.SYSTEM, true),
-    INTRUDER_ATTEMPT(Message.EventLog_IntruderAttempt, Type.SYSTEM, false),
+    STARTUP(Message.EventLog_Startup, Type.SYSTEM),
+    SHUTDOWN(Message.EventLog_Shutdown, Type.SYSTEM),
+    FATAL_EVENT(Message.EventLog_FatalEvent, Type.SYSTEM),
+    MODIFY_CONFIGURATION(Message.EventLog_ModifyConfiguration, Type.SYSTEM),
+    INTRUDER_LOCK(Message.EventLog_IntruderLockout, Type.SYSTEM),
+    INTRUDER_ATTEMPT(Message.EventLog_IntruderAttempt, Type.SYSTEM),
 
     // user events not stored in user event history
-    AUTHENTICATE(Message.EventLog_Authenticate, Type.USER, false),
-    AGREEMENT_PASSED(Message.EventLog_AgreementPassed, Type.USER, false),
-    TOKEN_ISSUED(Message.EventLog_TokenIssued, Type.USER, false),
-    TOKEN_CLAIMED(Message.EventLog_TokenClaimed, Type.USER, false),
-    CLEAR_RESPONSES(Message.EventLog_ClearResponses, Type.USER, false),
+    AUTHENTICATE(Message.EventLog_Authenticate, Type.USER),
+    AGREEMENT_PASSED(Message.EventLog_AgreementPassed, Type.USER),
+    TOKEN_ISSUED(Message.EventLog_TokenIssued, Type.USER),
+    TOKEN_CLAIMED(Message.EventLog_TokenClaimed, Type.USER),
+    CLEAR_RESPONSES(Message.EventLog_ClearResponses, Type.USER),
 
     // user events stored in user event history
-    CHANGE_PASSWORD(Message.EventLog_ChangePassword, Type.USER, true),
-    UNLOCK_PASSWORD(Message.EventLog_UnlockPassword, Type.USER, true),
-    RECOVER_PASSWORD(Message.EventLog_RecoverPassword, Type.USER, true),
-    SET_RESPONSES(Message.EventLog_SetupResponses, Type.USER, true),
-    SET_OTP_SECRET(Message.Eventlog_SetupOtpSecret, Type.USER, true),
-    ACTIVATE_USER(Message.EventLog_ActivateUser, Type.USER, true),
-    CREATE_USER(Message.EventLog_CreateUser, Type.USER, true),
-    UPDATE_PROFILE(Message.EventLog_UpdateProfile, Type.USER, true),
-    INTRUDER_USER(Message.EventLog_IntruderUser, Type.USER, true),
+    CHANGE_PASSWORD(Message.EventLog_ChangePassword, Type.USER),
+    UNLOCK_PASSWORD(Message.EventLog_UnlockPassword, Type.USER),
+    RECOVER_PASSWORD(Message.EventLog_RecoverPassword, Type.USER),
+    SET_RESPONSES(Message.EventLog_SetupResponses, Type.USER),
+    SET_OTP_SECRET(Message.Eventlog_SetupOtpSecret, Type.USER),
+    ACTIVATE_USER(Message.EventLog_ActivateUser, Type.USER),
+    CREATE_USER(Message.EventLog_CreateUser, Type.USER),
+    UPDATE_PROFILE(Message.EventLog_UpdateProfile, Type.USER),
+    INTRUDER_USER(Message.EventLog_IntruderUser, Type.USER),
 
     // helpdesk events
-    HELPDESK_SET_PASSWORD(Message.EventLog_HelpdeskSetPassword, Type.HELPDESK, true),
-    HELPDESK_UNLOCK_PASSWORD(Message.EventLog_HelpdeskUnlockPassword, Type.HELPDESK, true),
-    HELPDESK_CLEAR_RESPONSES(Message.EventLog_HelpdeskClearResponses, Type.HELPDESK, true),
-    HELPDESK_CLEAR_OTP_SECRET(Message.EventLog_HelpdeskClearOtpSecret, Type.HELPDESK, true),
-    HELPDESK_ACTION(Message.EventLog_HelpdeskAction, Type.HELPDESK, true),
-    HELPDESK_DELETE_USER(Message.EventLog_HelpdeskDeleteUser, Type.HELPDESK, false),
-    HELPDESK_VIEW_DETAIL(Message.EventLog_HelpdeskViewDetail, Type.HELPDESK, false),
-    HELPDESK_VERIFY_OTP(Message.EventLog_HelpdeskViewDetail, Type.HELPDESK, false),
+    HELPDESK_SET_PASSWORD(Message.EventLog_HelpdeskSetPassword, Type.HELPDESK),
+    HELPDESK_UNLOCK_PASSWORD(Message.EventLog_HelpdeskUnlockPassword, Type.HELPDESK),
+    HELPDESK_CLEAR_RESPONSES(Message.EventLog_HelpdeskClearResponses, Type.HELPDESK),
+    HELPDESK_CLEAR_OTP_SECRET(Message.EventLog_HelpdeskClearOtpSecret, Type.HELPDESK),
+    HELPDESK_ACTION(Message.EventLog_HelpdeskAction, Type.HELPDESK),
+    HELPDESK_DELETE_USER(Message.EventLog_HelpdeskDeleteUser, Type.HELPDESK),
+    HELPDESK_VIEW_DETAIL(Message.EventLog_HelpdeskViewDetail, Type.HELPDESK),
+    HELPDESK_VERIFY_OTP(Message.EventLog_HelpdeskViewDetail, Type.HELPDESK),
 
 
     ;
 
     final private Message message;
-    final private boolean storeOnUser;
     private Type type;
 
-    AuditEvent(final Message message, final Type type, boolean storeOnUser) {
+    AuditEvent(final Message message, final Type type) {
         this.message = message;
-        this.storeOnUser = storeOnUser;
         this.type = type;
     }
 
@@ -82,10 +80,6 @@ public enum AuditEvent {
         return message;
     }
 
-    public boolean isStoreOnUser() {
-        return storeOnUser;
-    }
-
     public static AuditEvent forKey(final String key) {
         for (final AuditEvent loopEvent : AuditEvent.values()) {
             final Message message = loopEvent.getMessage();

+ 13 - 30
pwm/servlet/src/password/pwm/event/AuditManager.java

@@ -51,14 +51,13 @@ import password.pwm.util.macro.MacroMachine;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Serializable;
 import java.util.*;
 
 public class AuditManager implements PwmService {
     private static final PwmLogger LOGGER = PwmLogger.forClass(AuditManager.class);
 
     private STATUS status = STATUS.NEW;
-    private Settings settings = new Settings();
+    private AuditSettings settings;
     private ServiceInfo serviceInfo = new ServiceInfo(Collections.<DataStorageMethod>emptyList());
 
     private SyslogAuditService syslogManager;
@@ -189,10 +188,7 @@ public class AuditManager implements PwmService {
         this.status = STATUS.OPENING;
         this.pwmApplication = pwmApplication;
 
-        settings.systemEmailAddresses = pwmApplication.getConfig().readSettingAsStringArray(PwmSetting.AUDIT_EMAIL_SYSTEM_TO);
-        settings.userEmailAddresses = pwmApplication.getConfig().readSettingAsStringArray(PwmSetting.AUDIT_EMAIL_USER_TO);
-        settings.alertFromAddress = pwmApplication.getConfig().readAppProperty(AppProperty.AUDIT_EVENTS_EMAILFROM);
-        settings.permittedEvents = figurePermittedEvents(pwmApplication.getConfig());
+        settings = new AuditSettings(pwmApplication.getConfig());
 
         if (pwmApplication.getApplicationMode() == null || pwmApplication.getApplicationMode() == PwmApplication.MODE.READ_ONLY) {
             this.status = STATUS.CLOSED;
@@ -317,20 +313,20 @@ public class AuditManager implements PwmService {
         if (record == null || record.getEventCode() == null) {
             return;
         }
-        if (settings.alertFromAddress == null || settings.alertFromAddress.length() < 1) {
+        if (settings.getAlertFromAddress() == null || settings.getAlertFromAddress().length() < 1) {
             return;
         }
 
         switch (record.getEventCode().getType()) {
             case SYSTEM:
-                for (final String toAddress : settings.systemEmailAddresses) {
-                    sendAsEmail(pwmApplication, null, record, toAddress, settings.alertFromAddress);
+                for (final String toAddress : settings.getSystemEmailAddresses()) {
+                    sendAsEmail(pwmApplication, null, record, toAddress, settings.getAlertFromAddress());
                 }
                 break;
 
             case USER:
-                for (final String toAddress : settings.userEmailAddresses) {
-                    sendAsEmail(pwmApplication, null, record, toAddress, settings.alertFromAddress);
+                for (final String toAddress : settings.getUserEmailAddresses()) {
+                    sendAsEmail(pwmApplication, null, record, toAddress, settings.getAlertFromAddress());
                 }
                 break;
         }
@@ -350,8 +346,7 @@ public class AuditManager implements PwmService {
 
         final StringBuilder body = new StringBuilder();
         final String jsonRecord = JsonUtil.serialize(record);
-        HashMap<String,Serializable> mapRecord = new HashMap<>();
-        mapRecord = JsonUtil.deserialize(jsonRecord,mapRecord.getClass());
+        final Map<String,Object> mapRecord = JsonUtil.deserializeMap(jsonRecord);
 
         for (final String key : mapRecord.keySet()) {
             body.append(key);
@@ -396,7 +391,7 @@ public class AuditManager implements PwmService {
             return;
         }
 
-        if (!settings.permittedEvents.contains(auditRecord.getEventCode())) {
+        if (!settings.getPermittedEvents().contains(auditRecord.getEventCode())) {
             LOGGER.debug("discarding event, " + auditRecord.getEventCode() + " are being ignored; event=" + jsonRecord);
             return;
         }
@@ -413,8 +408,10 @@ public class AuditManager implements PwmService {
         sendAsEmail(auditRecord);
 
         // add to user ldap record
-        if (auditRecord instanceof UserAuditRecord && auditRecord.getEventCode().isStoreOnUser()) {
-            userHistoryStore.updateUserHistory((UserAuditRecord)auditRecord);
+        if (auditRecord instanceof UserAuditRecord) {
+            if (settings.getUserStoredEvents().contains(auditRecord.getEventCode())) {
+                userHistoryStore.updateUserHistory((UserAuditRecord) auditRecord);
+        }
         }
 
         // send to syslog
@@ -492,13 +489,6 @@ public class AuditManager implements PwmService {
         return counter;
     }
 
-    private static class Settings {
-        private List<String> systemEmailAddresses = new ArrayList<>();
-        private List<String> userEmailAddresses = new ArrayList<>();
-        private String alertFromAddress = "";
-        private Set<AuditEvent> permittedEvents = new HashSet<>();
-    }
-
     public ServiceInfo serviceInfo()
     {
         return serviceInfo;
@@ -507,11 +497,4 @@ public class AuditManager implements PwmService {
     public int syslogQueueSize() {
         return syslogManager != null ? syslogManager.queueSize() : 0;
     }
-
-    private static Set<AuditEvent> figurePermittedEvents(final Configuration configuration) {
-        final Set<AuditEvent> eventSet = new HashSet<>();
-        eventSet.addAll(configuration.readSettingAsOptionList(PwmSetting.AUDIT_SYSTEM_EVENTS,AuditEvent.class));
-        eventSet.addAll(configuration.readSettingAsOptionList(PwmSetting.AUDIT_USER_EVENTS,AuditEvent.class));
-        return Collections.unmodifiableSet(eventSet);
-    }
 }

+ 56 - 0
pwm/servlet/src/password/pwm/event/AuditSettings.java

@@ -0,0 +1,56 @@
+package password.pwm.event;
+
+import password.pwm.AppProperty;
+import password.pwm.config.Configuration;
+import password.pwm.config.PwmSetting;
+
+import java.util.*;
+
+class AuditSettings {
+    private List<String> systemEmailAddresses = new ArrayList<>();
+    private List<String> userEmailAddresses = new ArrayList<>();
+    private String alertFromAddress = "";
+    private Set<AuditEvent> userStoredEvents = new HashSet<>();
+    private Set<AuditEvent> permittedEvents = new HashSet<>();
+
+    AuditSettings(final Configuration configuration) {
+        systemEmailAddresses = configuration.readSettingAsStringArray(PwmSetting.AUDIT_EMAIL_SYSTEM_TO);
+        userEmailAddresses = configuration.readSettingAsStringArray(PwmSetting.AUDIT_EMAIL_USER_TO);
+        alertFromAddress = configuration.readAppProperty(AppProperty.AUDIT_EVENTS_EMAILFROM);
+        permittedEvents = figurePermittedEvents(configuration);
+        userStoredEvents = figureUserStoredEvents(configuration);
+    }
+
+    List<String> getSystemEmailAddresses() {
+        return systemEmailAddresses;
+    }
+
+    List<String> getUserEmailAddresses() {
+        return userEmailAddresses;
+    }
+
+    Set<AuditEvent> getUserStoredEvents() {
+        return userStoredEvents;
+    }
+
+    String getAlertFromAddress() {
+        return alertFromAddress;
+    }
+
+    Set<AuditEvent> getPermittedEvents() {
+        return permittedEvents;
+    }
+
+    private static Set<AuditEvent> figurePermittedEvents(final Configuration configuration) {
+        final Set<AuditEvent> eventSet = new HashSet<>();
+        eventSet.addAll(configuration.readSettingAsOptionList(PwmSetting.AUDIT_SYSTEM_EVENTS,AuditEvent.class));
+        eventSet.addAll(configuration.readSettingAsOptionList(PwmSetting.AUDIT_USER_EVENTS,AuditEvent.class));
+        return Collections.unmodifiableSet(eventSet);
+    }
+
+    private static Set<AuditEvent> figureUserStoredEvents(final Configuration configuration) {
+        final Set<AuditEvent> eventSet = new HashSet<>();
+        eventSet.addAll(configuration.readSettingAsOptionList(PwmSetting.EVENTS_USER_EVENT_TYPES,AuditEvent.class));
+        return Collections.unmodifiableSet(eventSet);
+    }
+}

+ 6 - 6
pwm/servlet/src/password/pwm/http/PwmHttpRequestWrapper.java

@@ -46,6 +46,10 @@ public abstract class PwmHttpRequestWrapper {
     private final HttpServletRequest httpServletRequest;
     private final Configuration configuration;
 
+    public enum Flag {
+        BypassValidation
+    }
+
     protected PwmHttpRequestWrapper(HttpServletRequest request, final Configuration configuration) {
         this.httpServletRequest = request;
         this.configuration = configuration;
@@ -95,14 +99,10 @@ public abstract class PwmHttpRequestWrapper {
         return stringValue;
     }
 
-    public Map<String, String> readBodyAsJsonStringMap()
-            throws IOException, PwmUnrecoverableException {
-        return readBodyAsJsonStringMap(false);
-    }
-
-    public Map<String, String> readBodyAsJsonStringMap(boolean bypassInputValidation)
+    public Map<String, String> readBodyAsJsonStringMap(final Flag... flags)
             throws IOException, PwmUnrecoverableException
     {
+        boolean bypassInputValidation = flags != null && Arrays.asList(flags).contains(Flag.BypassValidation);
         final String bodyString = readRequestBodyAsString();
         final Map<String, String> inputMap = JsonUtil.deserializeStringMap(bodyString);
 

+ 0 - 5
pwm/servlet/src/password/pwm/http/bean/NewUserBean.java

@@ -182,11 +182,6 @@ public class NewUserBean implements PwmSessionBean {
     	NONE,
     	EMAIL,
     	SMS,
-        ;
-
-        public String getTokenName() {
-            return NewUserServlet.class.getName() + "_" + this.toString();
-        }
     }
 
     public Date getCreateStartTime()

+ 3 - 3
pwm/servlet/src/password/pwm/http/servlet/ActivateUserServlet.java

@@ -46,6 +46,7 @@ import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserSearchEngine;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.token.TokenType;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenService;
 import password.pwm.util.PostChangePasswordAction;
@@ -79,7 +80,6 @@ public class ActivateUserServlet extends AbstractPwmServlet {
 // ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(ActivateUserServlet.class);
-    private static final String TOKEN_NAME = ActivateUserServlet.class.getName();
 
     public enum ActivateUserAction implements AbstractPwmServlet.ProcessAction {
         activate(HttpMethod.POST),
@@ -622,7 +622,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
         final String tokenKey;
         final TokenPayload tokenPayload;
         try {
-            tokenPayload = pwmApplication.getTokenService().createTokenPayload(TOKEN_NAME, tokenMapData, userIdentity, destinationValues);
+            tokenPayload = pwmApplication.getTokenService().createTokenPayload(TokenType.ACTIVATION, tokenMapData, userIdentity, destinationValues);
             tokenKey = pwmApplication.getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel());
             LOGGER.debug(pwmSession.getLabel(), "generated activate user tokenKey code for session");
         } catch (PwmOperationalException e) {
@@ -649,7 +649,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
             final TokenPayload tokenPayload = pwmApplication.getTokenService().processUserEnteredCode(
                     pwmSession,
                     activateUserBean.getUserIdentity(),
-                    TOKEN_NAME,
+                    TokenType.ACTIVATION,
                     userEnteredCode
             );
             if (tokenPayload != null) {

+ 38 - 5
pwm/servlet/src/password/pwm/http/servlet/ConfigEditorServlet.java

@@ -36,6 +36,7 @@ import password.pwm.config.value.X509CertificateValue;
 import password.pwm.error.*;
 import password.pwm.health.*;
 import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ConfigManagerBean;
@@ -90,6 +91,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         settingData(HttpMethod.GET),
         testMacro(HttpMethod.POST),
         browseLdap(HttpMethod.POST),
+        copyProfile(HttpMethod.POST),
 
         ;
 
@@ -244,7 +246,11 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                     return;
 
                 case browseLdap:
-                    restBrowseLdap(pwmRequest);
+                    restBrowseLdap(pwmRequest, configManagerBean);
+                    return;
+
+                case copyProfile:
+                    restCopyProfile(pwmRequest, configManagerBean);
                     return;
             }
         }
@@ -1026,7 +1032,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
 
     private void restTestMacro(final PwmRequest pwmRequest) throws IOException, ServletException {
         try {
-            final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(true);
+            final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
             if (inputMap == null || !inputMap.containsKey("input")) {
                 pwmRequest.outputJsonResult(new RestResultBean("missing input"));
                 return;
@@ -1047,13 +1053,15 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         }
     }
 
-    private void restBrowseLdap(final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException {
+    private void restBrowseLdap(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
+            throws IOException, ServletException, PwmUnrecoverableException
+    {
         final Date startTime = new Date();
-        final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(true);
+        final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
         final String profile = inputMap.get("profile");
         final String dn = inputMap.containsKey("dn") ? inputMap.get("dn") : "";
 
-        final LdapBrowser ldapBrowser = new LdapBrowser(pwmRequest.getPwmSession().getConfigManagerBean().getStoredConfiguration());
+        final LdapBrowser ldapBrowser = new LdapBrowser(configManagerBean.getStoredConfiguration());
         final LdapBrowser.LdapBrowseResult result = ldapBrowser.doBrowse(profile, dn);
         ldapBrowser.close();
 
@@ -1064,5 +1072,30 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         pwmRequest.outputJsonResult(new RestResultBean(result));
     }
 
+    private void restCopyProfile(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
+            throws IOException, ServletException, PwmUnrecoverableException
+    {
+        final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
+
+        final String settingKey = inputMap.get("setting");
+        final PwmSetting setting = PwmSetting.forKey(settingKey);
+        PwmSettingCategory category = null;
+        for (final PwmSettingCategory loopCategory : PwmSettingCategory.values()) {
+            if (loopCategory.hasProfiles()) {
+                if (loopCategory.getProfileSetting() == setting) {
+                    category = loopCategory;
+                }
+            }
+        }
+
+        final String sourceID = inputMap.get("sourceID");
+        final String destinationID = inputMap.get("destinationID");
+        try {
+            configManagerBean.getStoredConfiguration().copyProfileID(category, sourceID, destinationID, pwmRequest.getUserInfoIfLoggedIn());
+            pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest,Message.Success_Unknown));
+        } catch (PwmUnrecoverableException e) {
+            pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
+        }
+    }
 
 }

+ 2 - 5
pwm/servlet/src/password/pwm/http/servlet/ConfigGuideServlet.java

@@ -38,10 +38,7 @@ import password.pwm.config.profile.LdapProfile;
 import password.pwm.config.value.*;
 import password.pwm.error.*;
 import password.pwm.health.*;
-import password.pwm.http.ContextManager;
-import password.pwm.http.HttpMethod;
-import password.pwm.http.PwmRequest;
-import password.pwm.http.PwmSession;
+import password.pwm.http.*;
 import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.ldap.LdapBrowser;
 import password.pwm.ldap.schema.SchemaManager;
@@ -460,7 +457,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
         }
 
         final Date startTime = new Date();
-        final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(true);
+        final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
         final String profile = inputMap.get("profile");
         final String dn = inputMap.containsKey("dn") ? inputMap.get("dn") : "";
 

+ 3 - 1
pwm/servlet/src/password/pwm/http/servlet/ConfigManagerServlet.java

@@ -601,7 +601,9 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
         resp.setHeader(PwmConstants.HttpHeader.ContentTransferEncoding, "binary");
         final LocalDBUtility localDBUtility = new LocalDBUtility(pwmRequest.getPwmApplication().getLocalDB());
         try {
-            localDBUtility.exportLocalDB(resp.getOutputStream(), LOGGER.asAppendable(PwmLogLevel.DEBUG, pwmRequest.getSessionLabel()), false);
+            final int bufferSize = Integer.parseInt(pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_DOWNLOAD_BUFFER_SIZE));
+            final OutputStream bos = new BufferedOutputStream(resp.getOutputStream(),bufferSize);
+            localDBUtility.exportLocalDB(bos, LOGGER.asAppendable(PwmLogLevel.DEBUG, pwmRequest.getSessionLabel()), false);
             LOGGER.debug(pwmRequest, "completed localDBExport process in " + TimeDuration.fromCurrent(startTime).asCompactString());
         } catch (Exception e) {
             LOGGER.error(pwmRequest, "error downloading export localdb: " + e.getMessage());

+ 5 - 4
pwm/servlet/src/password/pwm/http/servlet/NewUserServlet.java

@@ -52,6 +52,7 @@ import password.pwm.i18n.Message;
 import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserSearchEngine;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.token.TokenType;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenService;
 import password.pwm.util.*;
@@ -405,7 +406,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                 final NewUserTokenData newUserTokenData = NewUserFormUtils.fromTokenPayload(pwmRequest, tokenPayload);
                 newUserBean.setProfileID(newUserTokenData.profileID);
                 final NewUserBean.NewUserForm newUserFormFromToken = newUserTokenData.formData;
-                if (NewUserBean.NewUserVerificationPhase.EMAIL.getTokenName().equals(tokenPayload.getName())) {
+                if (TokenType.NEWUSER_EMAIL.matchesName(tokenPayload.getName())) {
                     LOGGER.debug(pwmRequest, "email token passed");
 
                     try {
@@ -421,7 +422,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                     newUserBean.setEmailTokenIssued(true);
                     newUserBean.setVerificationPhase(NewUserBean.NewUserVerificationPhase.NONE);
                     tokenPassed = true;
-                } else if (NewUserBean.NewUserVerificationPhase.SMS.getTokenName().equals(tokenPayload.getName())) {
+                } else if (TokenType.NEWUSER_SMS.matchesName(tokenPayload.getName())) {
                     if (newUserBean.getNewUserForm() != null && newUserBean.getNewUserForm().isConsistentWith(newUserFormFromToken)) {
                         LOGGER.debug(pwmRequest, "SMS token passed");
                         newUserBean.setSmsTokenPassed(true);
@@ -832,7 +833,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                 final String tokenKey;
                 try {
                     final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload(
-                            NewUserBean.NewUserVerificationPhase.SMS.getTokenName(),
+                            TokenType.NEWUSER_SMS,
                             tokenPayloadMap,
                             null,
                             Collections.singleton(outputDestTokenData.getSms())
@@ -877,7 +878,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                 final String tokenKey;
                 try {
                     final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload(
-                            NewUserBean.NewUserVerificationPhase.EMAIL.getTokenName(),
+                            TokenType.NEWUSER_EMAIL,
                             tokenPayloadMap,
                             null,
                             Collections.singleton(outputDestTokenData.getEmail())

+ 3 - 6
pwm/servlet/src/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -59,6 +59,7 @@ import password.pwm.ldap.UserStatusReader;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationUtility;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.token.TokenType;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenService;
 import password.pwm.util.JsonUtil;
@@ -104,10 +105,6 @@ public class ForgottenPasswordServlet extends AbstractPwmServlet {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(ForgottenPasswordServlet.class);
 
-    private static final String TOKEN_NAME = ForgottenPasswordServlet.class.getName();
-
-
-
     public enum ForgottenPasswordAction implements AbstractPwmServlet.ProcessAction {
         search(HttpMethod.POST),
         checkResponses(HttpMethod.POST),
@@ -406,7 +403,7 @@ public class ForgottenPasswordServlet extends AbstractPwmServlet {
             final TokenPayload tokenPayload = pwmRequest.getPwmApplication().getTokenService().processUserEnteredCode(
                     pwmRequest.getPwmSession(),
                     forgottenPasswordBean.getUserInfo() == null ? null : forgottenPasswordBean.getUserInfo().getUserIdentity(),
-                    TOKEN_NAME,
+                    TokenType.FORGOTTEN_PW,
                     userEnteredCode
             );
             if (tokenPayload != null) {
@@ -1009,7 +1006,7 @@ public class ForgottenPasswordServlet extends AbstractPwmServlet {
         final String tokenKey;
         TokenPayload tokenPayload;
         try {
-            tokenPayload = pwmRequest.getPwmApplication().getTokenService().createTokenPayload(TOKEN_NAME, tokenMapData, userIdentity, destinationValues);
+            tokenPayload = pwmRequest.getPwmApplication().getTokenService().createTokenPayload(TokenType.FORGOTTEN_PW, tokenMapData, userIdentity, destinationValues);
             tokenKey = pwmRequest.getPwmApplication().getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel());
         } catch (PwmOperationalException e) {
             throw new PwmUnrecoverableException(e.getErrorInformation());

+ 2 - 0
pwm/servlet/src/password/pwm/i18n/ConfigEditor.properties

@@ -262,6 +262,7 @@ Setting_Description_events.pwmDB.logLevel=Level at which to log events in the Lo
 Setting_Description_events.pwmDB.maxAge=Maximum age of events stored in LocalDB. <br/><br/>This setting does not effect log files configured in log4jconfig.xml or tomcat's log file settings.<br/><br/>Events older than the configured value here will be periodically purged.  Value is specified in seconds.  Default is 4 weeks (60s * 60m * 24h * 7d * 4w \= 2419200).  A value of zero will cause events not to be removed due to age.
 Setting_Description_events.pwmDB.maxEvents=Maximum log events stored in LocalDB.  This number of events will be retained in the LocalDB database and use these to display in the admin event log screen.<br/><br/>This setting does not effect the normal log files configured in log4jconfig.xml or tomcat's log file settings.<br/><br/>Approximately 100MB of disk space will be consumed for each 100,000 log events.
 Setting_Description_events.user.storageMethod=Data store used for user-specific audit history.  If using LDAP, this number should be kept small to avoid an unreasonably lengthy LDAP attribute value size.
+Setting_Description_events.user.eventList=Event types to store on user-specific audit history.
 Setting_Description_expireCheckDuringAuth=When the user is authenticated, should there be a check to see if the user's password is expired (or about to expire based on the expirePreTime).  If this is set to true, and the user's password is expired, they will be forced to the expire password page.
 Setting_Description_expirePreTime=Number of seconds before a user's password expires in which to force the user to change their password.  If the user's password will expire within this time frame, the system will behave as if the user's password has already expired. <br/><br/>Setting this value to a day or so will prevent most cases of a user's password expiring while they are logged in.  The recommend setting for this value is 86400 (1 day).
 Setting_Description_expireWarnTime=Number of seconds before a user's password expires in which to warn the user to change their password.  If the user's password will expire within this time frame, the system will warn the user during a CommandServlet checkExpire or checkAll operation. <br/><br/>If this time is zero or less than the expirePreTime, this feature will be disabled.  The recommended setting for this value is 432000 (5 days).
@@ -697,6 +698,7 @@ Setting_Label_events.pwmDB.logLevel=LocalDB Log Level
 Setting_Label_events.pwmDB.maxAge=Maximum Age LocalDB Events
 Setting_Label_events.pwmDB.maxEvents=Maximum LocalDB Events
 Setting_Label_events.user.storageMethod=User History Storage Location
+Setting_Label_events.user.eventList=User History Events
 Setting_Label_expireCheckDuringAuth=Check Expire During Authentication
 Setting_Label_expirePreTime=Password Pre-Expire Time
 Setting_Label_expireWarnTime=Password Expire Warn Time

+ 7 - 7
pwm/servlet/src/password/pwm/token/TokenService.java

@@ -90,7 +90,7 @@ public class TokenService implements PwmService {
     {
     }
 
-    public synchronized TokenPayload createTokenPayload(final String name, final Map<String, String> data, final UserIdentity userIdentity, final Set<String> dest) {
+    public synchronized TokenPayload createTokenPayload(final TokenType name, final Map<String, String> data, final UserIdentity userIdentity, final Set<String> dest) {
         final long count = counter++;
         final StringBuilder guid = new StringBuilder();
         try {
@@ -100,7 +100,7 @@ public class TokenService implements PwmService {
         } catch (Exception e) {
             LOGGER.error("error making payload guid: " + e.getMessage(),e);
         }
-        return new TokenPayload(name, data, userIdentity, dest, guid.toString());
+        return new TokenPayload(name.name(), data, userIdentity, dest, guid.toString());
     }
 
     public void init(final PwmApplication pwmApplication)
@@ -502,7 +502,7 @@ public class TokenService implements PwmService {
     public TokenPayload processUserEnteredCode(
             final PwmSession pwmSession,
             final UserIdentity sessionUserIdentity,
-            final String tokenName,
+            final TokenType tokenType,
             final String userEnteredCode
     )
             throws PwmOperationalException, PwmUnrecoverableException
@@ -512,7 +512,7 @@ public class TokenService implements PwmService {
                     pwmApplication,
                     pwmSession,
                     sessionUserIdentity,
-                    tokenName,
+                    tokenType,
                     userEnteredCode
             );
             if (tokenPayload.getDest() != null) {
@@ -547,7 +547,7 @@ public class TokenService implements PwmService {
             final PwmApplication pwmApplication,
             final PwmSession pwmSession,
             final UserIdentity sessionUserIdentity,
-            final String tokenName,
+            final TokenType tokenType,
             final String userEnteredCode
     )
             throws PwmOperationalException, PwmUnrecoverableException
@@ -567,8 +567,8 @@ public class TokenService implements PwmService {
 
         LOGGER.trace(pwmSession, "retrieved tokenPayload: " + JsonUtil.serialize(tokenPayload));
 
-        if (tokenName != null && pwmApplication.getTokenService().supportsName()) {
-            if (!tokenName.equals(tokenPayload.getName()) ) {
+        if (tokenType != null && pwmApplication.getTokenService().supportsName()) {
+            if (!tokenType.matchesName(tokenPayload.getName()) ) {
                 final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT,"incorrect token/name format");
                 throw new PwmOperationalException(errorInformation);
             }

+ 34 - 0
pwm/servlet/src/password/pwm/token/TokenType.java

@@ -0,0 +1,34 @@
+package password.pwm.token;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public enum TokenType {
+    FORGOTTEN_PW("password.pwm.servlet.ForgottenPasswordServlet"),
+    ACTIVATION("password.pwm.servlet.ActivateUserServlet"),
+    NEWUSER_SMS("password.pwm.servlet.NewUserServlet_SMS"),
+    NEWUSER_EMAIL("password.pwm.servlet.NewUserServlet_EMAIL"),
+
+    ;
+
+    private final Set<String> otherNames;
+
+    TokenType(final String... otherNames) {
+        final Set<String> otherNamesSet = new HashSet<>();
+        if (otherNames != null) {
+            otherNamesSet.addAll(Arrays.asList(otherNames));
+        }
+        otherNamesSet.add(getName());
+        this.otherNames = Collections.unmodifiableSet(otherNamesSet);
+    }
+
+    public String getName() {
+        return this.toString();
+    }
+
+    public boolean matchesName(final String input) {
+        return otherNames.contains(input);
+    }
+}

+ 8 - 10
pwm/servlet/src/password/pwm/util/localdb/LocalDBUtility.java

@@ -82,15 +82,13 @@ public class LocalDBUtility {
         statTimer.schedule(new TimerTask() {
             @Override
             public void run() {
-                final String percentStr;
                 if (showLineCount) {
                     final float percentComplete = (float) exportLineCounter / (float) totalLines;
-                    percentStr = DecimalFormat.getPercentInstance().format(percentComplete);
+                    final String percentStr = DecimalFormat.getPercentInstance().format(percentComplete);
+                    writeStringToOut(debugOutput," exported " + exportLineCounter + " records, " + percentStr + " complete");
                 } else {
-                    percentStr = "n/a";
+                    writeStringToOut(debugOutput," exported " + exportLineCounter + " records");
                 }
-
-                writeStringToOut(debugOutput," exported " + exportLineCounter + " records, " + percentStr + " complete");
             }
         },30 * 1000, 30 * 1000);
 
@@ -114,15 +112,15 @@ public class LocalDBUtility {
                     }
                 }
             }
+        } catch (IOException e) {
+            writeStringToOut(debugOutput,"IO error during localDB export: " + e.getMessage());
         } finally {
-            if (csvPrinter != null) {
-                csvPrinter.printComment("export completed at " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
-                csvPrinter.close();
-            }
+            csvPrinter.printComment("export completed at " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
+            csvPrinter.close();
+            statTimer.cancel();
         }
 
         writeStringToOut(debugOutput, "export complete, exported " + exportLineCounter + " records in " + TimeDuration.fromCurrent(startTime).asLongString());
-        statTimer.cancel();
     }
 
     private static void writeStringToOut(final Appendable out, final String string) {

+ 39 - 4
pwm/servlet/web/public/resources/js/configeditor-settings.js

@@ -265,31 +265,42 @@ StringArrayValueHandler.drawRow = function(settingKey, iteration, value, itemCou
     valueRow.setAttribute("style", "border-width: 0");
     valueRow.setAttribute("id",inputID + "_row");
 
-    var rowHtml = '<td id="button-' + inputID + '" style="border-width:0; width: 15px"><span class="fa fa-edit"/></ta>';
+    var rowHtml = '';
+    if (syntax != 'PROFILE') {
+        rowHtml = '<td id="button-' + inputID + '" style="border-width:0; width: 15px"><span class="fa fa-edit"/></td>';
+    }
     rowHtml += '<td style=""><div class="configStringPanel" id="' + inputID + '"></div></td>';
 
+    if (syntax == 'PROFILE') {
+        var copyButtonID = 'button-' + settingKey + '-' + iteration + '-copy';
+        rowHtml += '<td style="border:0; padding:0; width:10px" title="Copy">';
+        rowHtml += '<span id="' + copyButtonID + '" class="action-icon fa fa-copy"></span>';
+        rowHtml += '</td>';
+    }
+
     var downButtonID = 'button-' + settingKey + '-' + iteration + '-moveDown';
-    rowHtml += '<td style="border:0">';
+    rowHtml += '<td style="border:0; padding:0; width:10px" title="Move Down">';
     if (itemCount > 1 && iteration != (itemCount -1)) {
         rowHtml += '<span id="' + downButtonID + '" class="action-icon fa fa-chevron-down"></span>';
     }
     rowHtml += '</td>';
 
     var upButtonID = 'button-' + settingKey + '-' + iteration + '-moveUp';
-    rowHtml += '<td style="border:0">';
+    rowHtml += '<td style="border:0; padding:0; width:10px" title="Move Up">';
     if (itemCount > 1 && iteration != 0) {
         rowHtml += '<span id="' + upButtonID + '" class="action-icon fa fa-chevron-up"></span>';
     }
     rowHtml += '</td>';
 
     var deleteButtonID = 'button-' + settingKey + '-' + iteration + '-delete';
-    rowHtml += '<td style="border:0">';
+    rowHtml += '<td style="border:0; padding:0; width:10px" title="Delete">';
 
     if (itemCount > 1 || (!settingInfo['required'] && (syntax != 'PROFILE'))) {
         rowHtml += '<span id="' + deleteButtonID + '" class="delete-row-icon action-icon fa fa-times"></span>';
     }
     rowHtml += '</td>';
 
+
     valueRow.innerHTML = rowHtml;
     parentDivElement.appendChild(valueRow);
 
@@ -301,6 +312,30 @@ StringArrayValueHandler.drawRow = function(settingKey, iteration, value, itemCou
         PWM_MAIN.addEventHandler('button-' + inputID,'click',function(){
             StringArrayValueHandler.valueHandler(settingKey,iteration);
         });
+    } else {
+        PWM_MAIN.addEventHandler(copyButtonID,'click',function(){
+            var editorOptions = {};
+            editorOptions['title'] = 'Copy Profile - New Profile ID';
+            editorOptions['regex'] = PWM_SETTINGS['settings'][settingKey]['pattern'];
+            editorOptions['placeholder'] = PWM_SETTINGS['settings'][settingKey]['placeholder'];
+            editorOptions['completeFunction'] = function(newValue){
+                var options = {};
+                options['setting'] = settingKey;
+                options['sourceID'] = value;
+                options['destinationID'] = newValue;
+                var resultFunction = function(data){
+                    if (data['error']) {
+                        PWM_MAIN.showErrorDialog(data);
+                    } else {
+                        PWM_MAIN.goto('ConfigEditor');
+                    }
+                };
+                PWM_MAIN.showWaitDialog({loadFunction:function(){
+                    PWM_MAIN.ajaxRequest("ConfigEditor?processAction=copyProfile",resultFunction,{content:options});
+                }});
+            };
+            UILibrary.stringEditorDialog(editorOptions);
+        });
     }
 
     if (itemCount > 1 && iteration != (itemCount -1)) {

+ 6 - 2
pwm/servlet/web/public/resources/js/uilibrary.js

@@ -191,9 +191,9 @@ UILibrary.editLdapDN = function(nextFunction, options) {
         }
         body += '<div style="text-align: center">';
         if (currentDN && currentDN.length > 0 ) {
-            body += currentDN;
+            body += '<div class="selectableDN" data-dn="' + currentDN + '"><a><code>' + currentDN + '</code></a></div>';
         } else {
-            body += '[root]';
+            body += '<code>[root]</code>';
         }
         body += '</div><br/>';
 
@@ -240,12 +240,16 @@ UILibrary.editLdapDN = function(nextFunction, options) {
         }
 
         body += '<div class="buttonbar"><button class="btn" id="button-editDN"><span class="btn-icon fa fa-edit"></span>Edit Text</button>';
+        body += '<button class="btn" id="button-refresh"><span class="btn-icon fa fa-refresh"></span>Refresh</button>';
         body += '<button class="btn" id="button-clearDN"><span class="btn-icon fa fa-times"></span>Clear Value</button></div>';
 
         PWM_MAIN.showDialog({title:'LDAP Browser',dialogClass:'auto',showOk:false,showClose:true,text:body,loadFunction:function(){
             PWM_MAIN.addEventHandler('button-editDN','click',function(){
                 UILibrary.stringEditorDialog({value:currentDN,completeFunction:nextFunction});
             });
+            PWM_MAIN.addEventHandler('button-refresh','click',function(){
+                UILibrary.editLdapDN(nextFunction,{profile:profile,currentDN:currentDN});
+            });
             PWM_MAIN.addEventHandler('button-clearDN','click',function(){
                 nextFunction('');
                 PWM_MAIN.closeWaitDialog();